[svsm-devel] acpi: Document ACPI tables

Carlos Bilbao carlos.bilbao at amd.com
Fri Sep 8 15:57:32 CEST 2023


Hello,

Just opened pull request to add a system call interface [1]. Sharing below
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)?;

-- 
2.41.0


More information about the Svsm-devel mailing list