[svsm-devel] acpi: Document ACPI tables

Carlos Bilbao carlos.bilbao at amd.com
Fri Sep 8 15:59:03 CEST 2023


On 9/8/23 08:58, Carlos Bilbao wrote:
> 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

https://github.com/coconut-svsm/svsm/pull/89

> 
>> 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