[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