acpi_tables/
lib.rs

1// Copyright © 2019 Intel Corporation
2// Copyright 2023 Rivos, Inc.
3// Copyright 2024 Amazon.com, Inc. or its affiliates. All Rights Reserved.
4//
5// SPDX-License-Identifier: Apache-2.0
6
7use vm_memory::{GuestAddress, GuestMemory, GuestMemoryError};
8
9pub mod aml;
10pub mod dsdt;
11pub mod fadt;
12pub mod madt;
13pub mod mcfg;
14pub mod rsdp;
15pub mod xsdt;
16
17pub use aml::Aml;
18pub use dsdt::Dsdt;
19pub use fadt::Fadt;
20pub use madt::Madt;
21pub use mcfg::Mcfg;
22pub use rsdp::Rsdp;
23pub use xsdt::Xsdt;
24use zerocopy::little_endian::{U32, U64};
25use zerocopy::{Immutable, IntoBytes};
26
27// This is the creator ID that we will embed in ACPI tables that are created using this crate.
28const FC_ACPI_CREATOR_ID: [u8; 4] = *b"FCAT";
29// This is the created ID revision that we will embed in ACPI tables that are created using this
30// crate.
31const FC_ACPI_CREATOR_REVISION: u32 = 0x20240119;
32
33fn checksum(buf: &[&[u8]]) -> u8 {
34    (255 - buf
35        .iter()
36        .flat_map(|b| b.iter())
37        .fold(0u8, |acc, x| acc.wrapping_add(*x)))
38    .wrapping_add(1)
39}
40
41#[derive(Debug, thiserror::Error, displaydoc::Display)]
42pub enum AcpiError {
43    /// Guest memory error: {0}
44    GuestMemory(#[from] GuestMemoryError),
45    /// Invalid guest address
46    InvalidGuestAddress,
47    /// Invalid register size
48    InvalidRegisterSize,
49}
50
51pub type Result<T> = std::result::Result<T, AcpiError>;
52
53/// ACPI type representing memory addresses
54#[repr(C, packed)]
55#[derive(IntoBytes, Immutable, Clone, Copy, Debug, Default)]
56pub struct GenericAddressStructure {
57    pub address_space_id: u8,
58    pub register_bit_width: u8,
59    pub register_bit_offset: u8,
60    pub access_size: u8,
61    pub address: U64,
62}
63
64impl GenericAddressStructure {
65    pub fn new(
66        address_space_id: u8,
67        register_bit_width: u8,
68        register_bit_offset: u8,
69        access_size: u8,
70        address: u64,
71    ) -> Self {
72        Self {
73            address_space_id,
74            register_bit_width,
75            register_bit_offset,
76            access_size,
77            address: U64::new(address),
78        }
79    }
80}
81
82/// Header included in all System Descriptor Tables
83#[repr(C, packed)]
84#[derive(Clone, Debug, Copy, Default, IntoBytes, Immutable)]
85pub struct SdtHeader {
86    pub signature: [u8; 4],
87    pub length: U32,
88    pub revision: u8,
89    pub checksum: u8,
90    pub oem_id: [u8; 6],
91    pub oem_table_id: [u8; 8],
92    pub oem_revision: U32,
93    pub creator_id: [u8; 4],
94    pub creator_revision: U32,
95}
96
97impl SdtHeader {
98    pub(crate) fn new(
99        signature: [u8; 4],
100        length: u32,
101        table_revision: u8,
102        oem_id: [u8; 6],
103        oem_table_id: [u8; 8],
104        oem_revision: u32,
105    ) -> Self {
106        SdtHeader {
107            signature,
108            length: U32::new(length),
109            revision: table_revision,
110            checksum: 0,
111            oem_id,
112            oem_table_id,
113            oem_revision: U32::new(oem_revision),
114            creator_id: FC_ACPI_CREATOR_ID,
115            creator_revision: U32::new(FC_ACPI_CREATOR_REVISION),
116        }
117    }
118}
119
120/// A trait for functionality around System Descriptor Tables.
121pub trait Sdt {
122    /// Get the length of the table
123    fn len(&self) -> usize;
124
125    /// Return true if Sdt is empty
126    fn is_empty(&self) -> bool {
127        self.len() == 0
128    }
129
130    /// Write the table in guest memory
131    fn write_to_guest<M: GuestMemory>(&mut self, mem: &M, address: GuestAddress) -> Result<()>;
132}
133
134#[cfg(test)]
135mod tests {
136    use super::checksum;
137
138    #[test]
139    fn test_checksum() {
140        assert_eq!(checksum(&[&[]]), 0u8);
141        assert_eq!(checksum(&[]), 0u8);
142        assert_eq!(checksum(&[&[1, 2, 3]]), 250u8);
143        assert_eq!(checksum(&[&[1, 2, 3], &[]]), 250u8);
144        assert_eq!(checksum(&[&[1, 2], &[3]]), 250u8);
145        assert_eq!(checksum(&[&[1, 2], &[3], &[250]]), 0u8);
146        assert_eq!(checksum(&[&[255]]), 1u8);
147        assert_eq!(checksum(&[&[1, 2], &[3], &[250], &[255]]), 1u8);
148    }
149}