[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