[svsm-devel] acpi: Document ACPI tables
Carlos Bilbao
carlos.bilbao at amd.com
Fri Sep 8 15:58:15 CEST 2023
On 9/8/23 08:57, Carlos Bilbao wrote:
> Hello,
>
> Just opened pull request to add a system call interface [1]. Sharing below
... to add documentation for ACPI tables
> too in case anyone wants to review.
>
> [1] https://github.com/roy-hopkins/svsm/pull/5
>
> Thanks,
> Carlos
> ---
>
> Subject: [PATCH] acpi: Document ACPI tables
>
> Document ACPI tables structs and code, including function usage examples.
> This is related to issue #74 titled "Document COCONUT-SVSM".
>
> Signed-off-by: Carlos Bilbao <carlos.bilbao at amd.com>
> ---
> src/acpi/tables.rs | 196 ++++++++++++++++++++++++++++++++++++++++++++-
> 1 file changed, 195 insertions(+), 1 deletion(-)
>
> diff --git a/src/acpi/tables.rs b/src/acpi/tables.rs
> index bc8e90c..8b4cb0e 100644
> --- a/src/acpi/tables.rs
> +++ b/src/acpi/tables.rs
> @@ -13,17 +13,34 @@ use alloc::vec::Vec;
> use core::mem;
> use log;
>
> -#[derive(Debug)]
> +/// ACPI Root System Description Pointer (RSDP)
> +/// used by ACPI programming interface
> +#[derive(Debug, Default)]
> #[repr(C, packed)]
> struct RSDPDesc {
> + /// Signature must contain "RSD PTR"
> sig: [u8; 8],
> + /// Checksum to add to all other bytes
> chksum: u8,
> + /// OEM-supplied string
> oem_id: [u8; 6],
> + /// Revision of the ACPI
> rev: u8,
> + /// Physical address of the RSDT
> rsdt_addr: u32,
> }
>
> impl RSDPDesc {
> + /// Create an RSPDesc instance from FwCfg
> + ///
> + /// # Arguments
> + ///
> + /// - `fw_cfg`: A reference to the FwCfg instance.
> + ///
> + /// # Returns
> + ///
> + /// A Result containing the RSDPDesc if successful, or an SvsmError on
> failure.
> + ///
> fn from_fwcfg(fw_cfg: &FwCfg) -> Result<Self, SvsmError> {
> let mut buf = mem::MaybeUninit::<Self>::uninit();
> let file = fw_cfg.file_selector("etc/acpi/rsdp")?;
> @@ -46,20 +63,32 @@ impl RSDPDesc {
>
> #[derive(Copy, Clone, Debug)]
> #[repr(C, packed)]
> +/// Raw header of an ACPI table. It corresponds to the beginning
> +/// portion of ACPI tables, before any specific table data
> struct RawACPITableHeader {
> + /// Signature specificies the type of ACPI table
> sig: [u8; 4],
> + /// Length of the table
> len: u32,
> + /// Revision (signature field)
> rev: u8,
> + /// Checksum for data integrity
> chksum: u8,
> + /// OEM-supplied string to identify OEM
> oem_id: [u8; 6],
> + /// OEM-supplied string to identify tables
> oem_table_id: [u8; 8],
> + /// OEM-supplied version number
> oem_rev: u32,
> + /// ID for compiler
> compiler_id: [u8; 4],
> + /// Revision of compiler used to create the table
> compiler_rev: u32,
> }
>
> #[derive(Debug)]
> #[allow(dead_code)]
> +/// Higher level representation of the raw ACPI table header
> struct ACPITableHeader {
> sig: [u8; 4],
> len: u32,
> @@ -73,6 +102,17 @@ struct ACPITableHeader {
> }
>
> impl ACPITableHeader {
> + /// Create a new `ACPITableHeader` from a raw `RawACPITableHeader`.
> + ///
> + /// This constructor converts a raw ACPI table header into a
> higher-level `ACPITableHeader`.
> + ///
> + /// # Arguments
> + ///
> + /// * `raw` - A `RawACPITableHeader` containing the raw header data.
> + ///
> + /// # Returns
> + ///
> + /// A new `ACPITableHeader` instance.
> const fn new(raw: RawACPITableHeader) -> Self {
> Self {
> sig: raw.sig,
> @@ -87,6 +127,7 @@ impl ACPITableHeader {
> }
> }
>
> + /// Print a human-readable summary of the ACPI table header's fields
> #[allow(dead_code)]
> fn print_summary(&self) {
> let sig = FixedString::from(self.sig);
> @@ -109,12 +150,25 @@ impl ACPITableHeader {
> }
>
> #[derive(Debug)]
> +/// ACPI table, both header and contents
> struct ACPITable {
> header: ACPITableHeader,
> + /// Raw binary content of ACPI table
> buf: Vec<u8>,
> }
>
> impl ACPITable {
> + /// Create a new `ACPITable` from raw binary data.
> + ///
> + /// This constructor creates an `ACPITable` instance by parsing raw
> binary data.
> + ///
> + /// # Arguments
> + ///
> + /// * `ptr` - A slice containing the raw binary data of the ACPI table.
> + ///
> + /// # Returns
> + ///
> + /// A new `ACPITable` instance on success, or an `SvsmError` if
> parsing fails.
> fn new(ptr: &[u8]) -> Result<Self, SvsmError> {
> let raw_header = ptr
> .get(..mem::size_of::<RawACPITableHeader>())
> @@ -134,43 +188,100 @@ impl ACPITable {
> Ok(Self { header, buf })
> }
>
> + /// Get the signature of the ACPI table.
> + ///
> + /// This method returns the 4-character signature of the ACPI table,
> such as "APIC."
> +
> #[allow(dead_code)]
> fn signature(&self) -> FixedString<4> {
> FixedString::from(self.header.sig)
> }
>
> + /// Get the content of the ACPI table.
> + ///
> + /// This method returns a reference to the binary content of the ACPI
> table,
> + /// excluding the header.
> + ///
> + /// # Returns
> + ///
> + /// A reference to the ACPI table content, or `None` if the content is
> empty.
> +
> fn content(&self) -> Option<&[u8]> {
> let offset = mem::size_of::<RawACPITableHeader>();
> // Zero-length slices are valid, but we do not want them
> self.buf.get(offset..).filter(|b| !b.is_empty())
> }
>
> + /// Get a pointer to the content of the ACPI table at a specific offset.
> + ///
> + /// This method returns a pointer to the content of the ACPI table at
> the specified offset,
> + /// converted to the desired type `T`.
> + ///
> + /// # Arguments
> + ///
> + /// * `offset` - The offset at which to obtain the pointer.
> + ///
> + /// # Returns
> + ///
> +
> fn content_ptr<T>(&self, offset: usize) -> Option<*const T> {
> let end = offset.checked_add(mem::size_of::<T>())?;
> Some(self.content()?.get(offset..end)?.as_ptr().cast::<T>())
> }
> }
>
> +/// ACPI Table Metadata
> +/// Metadata associated with an ACPI, information about signature and offset
> #[derive(Debug)]
> struct ACPITableMeta {
> + /// 4-character signature of the table
> sig: FixedString<4>,
> + /// The offset of the table within the table buffer
> offset: usize,
> }
>
> impl ACPITableMeta {
> + /// Create a new `ACPITableMeta` instance.
> + ///
> + /// This constructor creates an `ACPITableMeta` instance with the
> specified signature and offset.
> + ///
> + /// # Arguments
> + ///
> + /// * `header` - The raw ACPI table header containing the signature.
> + /// * `offset` - The offset of the ACPI table within the ACPI table
> buffer.
> + ///
> + /// # Returns
> + ///
> + /// A new `ACPITableMeta` instance.
> fn new(header: &RawACPITableHeader, offset: usize) -> Self {
> let sig = FixedString::from(header.sig);
> Self { sig, offset }
> }
> }
>
> +/// ACPI Table Buffer
> +/// A buffer containing ACPI tables. Responsible for loading the tables
> +/// from a firmware configuration
> #[derive(Debug)]
> struct ACPITableBuffer {
> buf: Vec<u8>,
> + /// Collection of metadata for ACPI tables, including signatures
> tables: Vec<ACPITableMeta>,
> }
>
> impl ACPITableBuffer {
> + /// Create a new `ACPITableBuffer` instance from a firmware
> configuration source.
> + ///
> + /// This constructor creates an `ACPITableBuffer` instance by reading
> ACPI tables from the specified FwCfg source.
> + ///
> + /// # Arguments
> + ///
> + /// * `fw_cfg` - The firmware configuration source (FwCfg) from which
> ACPI tables will be loaded.
> + ///
> + /// # Returns
> + ///
> + /// A new `ACPITableBuffer` instance containing ACPI tables and their
> metadata.
> +
> fn from_fwcfg(fw_cfg: &FwCfg) -> Result<Self, SvsmError> {
> let file = fw_cfg.file_selector("etc/acpi/tables")?;
> let size = file.size() as usize;
> @@ -194,6 +305,19 @@ impl ACPITableBuffer {
> Ok(acpibuf)
> }
>
> + /// Load ACPI tables and their metadata from the ACPI Root System
> Description Pointer (RSDP).
> + ///
> + /// This method populates the `tables` field of the `ACPITableBuffer`
> with metadata for ACPI tables
> + /// found within the ACPI Root System Description Pointer (RSDP)
> structure.
> + ///
> + /// # Arguments
> + ///
> + /// * `fw_cfg` - The firmware configuration source (FwCfg) containing
> ACPI tables.
> + ///
> + /// # Returns
> + ///
> + /// An `Result` indicating success or an error if ACPI tables cannot
> be loaded.
> +
> fn load_tables(&mut self, fw_cfg: &FwCfg) -> Result<(), SvsmError> {
> let desc = RSDPDesc::from_fwcfg(fw_cfg)?;
>
> @@ -222,11 +346,37 @@ impl ACPITableBuffer {
> Ok(())
> }
>
> + /// Retrieve an ACPI table from a specified offset within the ACPI
> table buffer.
> + ///
> + /// This function attempts to retrieve an ACPI table from the ACPI
> table buffer starting from the
> + /// specified offset. It parses the table header and creates an
> `ACPITable` instance representing
> + /// the ACPI table's content.
> + ///
> + /// # Arguments
> + ///
> + /// * `offset` - The offset within the ACPI table buffer from which to
> retrieve the ACPI table.
> + ///
> + /// # Returns
> + ///
> + /// An `Result` containing the `ACPITable` instance if successfully
> retrieved, or an `SvsmError`
> + /// if the table cannot be retrieved or parsed.
> fn acpi_table_from_offset(&self, offset: usize) -> Result<ACPITable,
> SvsmError> {
> let buf = self.buf.get(offset..).ok_or(SvsmError::Acpi)?;
> ACPITable::new(buf)
> }
>
> + /// Retrieve an ACPI table by its signature.
> + ///
> + /// This method attempts to retrieve an ACPI table by its 4-character
> signature.
> + ///
> + /// # Arguments
> + ///
> + /// * `sig` - The signature of the ACPI table to retrieve.
> + ///
> + /// # Returns
> + ///
> + /// An `Option` containing the ACPI table if found, or `None` if not
> found.
> +
> fn acp_table_by_sig(&self, sig: &str) -> Option<ACPITable> {
> let offset = self
> .tables
> @@ -240,6 +390,7 @@ impl ACPITableBuffer {
>
> const MADT_HEADER_SIZE: usize = 8;
>
> +/// Header of an entry within MADT
> #[derive(Clone, Copy, Debug)]
> #[allow(dead_code)]
> #[repr(C, packed)]
> @@ -248,6 +399,7 @@ struct RawMADTEntryHeader {
> entry_len: u8,
> }
>
> +/// Entry for a local APIC within MADT
> #[derive(Clone, Copy, Debug)]
> #[allow(dead_code)]
> #[repr(C, packed)]
> @@ -258,6 +410,7 @@ struct RawMADTEntryLocalApic {
> flags: u32,
> }
>
> +/// Entry for a local X2APIC within MADT
> #[derive(Clone, Copy, Debug)]
> #[allow(dead_code)]
> #[repr(C, packed)]
> @@ -269,12 +422,53 @@ struct RawMADTEntryLocalX2Apic {
> acpi_id: u32,
> }
>
> +/// Information about an ACPI CPU
> #[derive(Clone, Copy, Debug)]
> pub struct ACPICPUInfo {
> + /// The APIC ID for the CPU
> pub apic_id: u32,
> + /// Indicates whether the CPU is enabled
> pub enabled: bool,
> }
>
> +/// Loads ACPI CPU information by parsing the ACPI tables.
> +///
> +/// This function retrieves CPU information from the ACPI tables provided
> by the firmware.
> +/// It processes the Multiple APIC Description Table (MADT) to extract
> information about each CPU's
> +/// APIC ID and enabled status.
> +///
> +/// # Arguments
> +///
> +/// * `fw_cfg`: A reference to the Firmware Configuration (FwCfg)
> interface for accessing ACPI tables.
> +///
> +/// # Returns
> +///
> +/// A `Result` containing a vector of `ACPICPUInfo` structs representing
> CPU information.
> +/// If successful, the vector contains information about each detected
> CPU; otherwise, an error is returned.
> +///
> +/// # Errors
> +///
> +/// This function returns an error if there are issues with reading or
> parsing ACPI tables,
> +/// or if the required ACPI tables are not found.
> +///
> +/// # Example
> +///
> +/// ```
> +/// use svsm::acpi::load_acpi_cpu_info;
> +/// use svsm::fw_cfg::FwCfg;
> +///
> +/// let fw_cfg = FwCfg::new(/* initialize your FwCfg interface */);
> +/// match load_acpi_cpu_info(&fw_cfg) {
> +/// Ok(cpu_info) => {
> +/// for info in cpu_info {
> +/// // You can print id (info.apic_id) and whether it is
> enabled (info.enabled)
> +/// }
> +/// },
> +/// Err(err) => {
> +/// // Print error
> +/// }
> +/// }
> +/// ```
> pub fn load_acpi_cpu_info(fw_cfg: &FwCfg) -> Result<Vec<ACPICPUInfo>,
> SvsmError> {
> let buffer = ACPITableBuffer::from_fwcfg(fw_cfg)?;
>
More information about the Svsm-devel
mailing list