td_shim_interface/
acpi.rs

1// Copyright (c) 2021 Intel Corporation
2// Copyright (c) 2022 Alibaba Cloud
3//
4// SPDX-License-Identifier: BSD-2-Clause-Patent
5use core::mem::size_of;
6use zerocopy::{AsBytes, FromBytes, FromZeroes};
7
8pub const ACPI_TABLES_MAX_NUM: usize = 20;
9pub const ACPI_RSDP_REVISION: u8 = 2;
10
11#[derive(Debug, PartialEq, Eq)]
12pub enum Error {
13    InvalidParameter,
14    TooManyAcpiTables,
15}
16
17pub fn calculate_checksum(data: &[u8]) -> u8 {
18    (255 - data.iter().fold(0u8, |acc, x| acc.wrapping_add(*x))).wrapping_add(1)
19}
20
21#[repr(packed)]
22#[derive(Default, AsBytes, FromBytes, FromZeroes)]
23pub struct Rsdp {
24    pub signature: [u8; 8],
25    pub checksum: u8,
26    pub oem_id: [u8; 6],
27    pub revision: u8,
28    pub _rsdt_addr: u32,
29    pub length: u32,
30    pub xsdt_addr: u64,
31    pub extended_checksum: u8,
32    pub _reserved: [u8; 3],
33}
34
35impl Rsdp {
36    pub fn new(xsdt_addr: u64) -> Rsdp {
37        let mut rsdp = Rsdp {
38            signature: *b"RSD PTR ",
39            checksum: 0,
40            oem_id: *b"INTEL ",
41            revision: ACPI_RSDP_REVISION,
42            length: size_of::<Rsdp>() as u32,
43            xsdt_addr,
44            ..Default::default()
45        };
46        rsdp.checksum();
47        rsdp
48    }
49
50    pub fn set_xsdt(&mut self, xsdt: u64) {
51        self.xsdt_addr = xsdt;
52        self.checksum();
53    }
54
55    fn checksum(&mut self) {
56        self.checksum = 0;
57        self.extended_checksum = 0;
58        self.checksum = calculate_checksum(&self.as_bytes()[0..20]);
59        self.extended_checksum = calculate_checksum(self.as_bytes());
60    }
61}
62
63#[repr(C, packed)]
64#[derive(Default, AsBytes, FromBytes, FromZeroes)]
65pub struct GenericSdtHeader {
66    pub signature: [u8; 4],
67    pub length: u32,
68    pub revision: u8,
69    pub checksum: u8,
70    pub oem_id: [u8; 6],
71    pub oem_table_id: u64,
72    pub oem_revision: u32,
73    pub creator_id: u32,
74    pub creator_revision: u32,
75}
76
77impl GenericSdtHeader {
78    pub fn new(signature: &[u8; 4], length: u32, revision: u8) -> Self {
79        GenericSdtHeader {
80            signature: *signature,
81            length,
82            revision,
83            checksum: 0,
84            oem_id: *b"INTEL ",
85            oem_table_id: u64::from_le_bytes(*b"SHIM    "),
86            oem_revision: 1,
87            creator_id: u32::from_le_bytes(*b"SHIM"),
88            creator_revision: 1,
89        }
90    }
91
92    pub fn set_checksum(&mut self, checksum: u8) {
93        self.checksum = checksum;
94    }
95}
96
97#[repr(C, packed)]
98#[derive(Default, AsBytes, FromBytes, FromZeroes)]
99pub struct Xsdt {
100    pub header: GenericSdtHeader,
101    pub tables: [u64; ACPI_TABLES_MAX_NUM],
102}
103
104impl Xsdt {
105    pub fn new() -> Self {
106        Xsdt {
107            header: GenericSdtHeader::new(b"XSDT", size_of::<GenericSdtHeader>() as u32, 1),
108            tables: [0; ACPI_TABLES_MAX_NUM],
109        }
110    }
111
112    pub fn add_table(&mut self, addr: u64) -> Result<(), Error> {
113        if self.header.length < size_of::<GenericSdtHeader>() as u32 {
114            Err(Error::InvalidParameter)
115        } else {
116            let table_num =
117                (self.header.length as usize - size_of::<GenericSdtHeader>()) / size_of::<u64>();
118            if table_num < ACPI_TABLES_MAX_NUM {
119                self.tables[table_num] = addr;
120                self.header.length += size_of::<u64>() as u32;
121                Ok(())
122            } else {
123                Err(Error::TooManyAcpiTables)
124            }
125        }
126    }
127
128    pub fn checksum(&mut self) {
129        self.header.set_checksum(0);
130        self.header.set_checksum(calculate_checksum(
131            &self.as_bytes()[..self.header.length as usize],
132        ));
133    }
134}
135
136#[repr(C, packed)]
137#[derive(Default, AsBytes, FromBytes, FromZeroes)]
138pub struct Ccel {
139    pub header: GenericSdtHeader,
140    pub cc_type: u8,
141    pub cc_subtype: u8,
142    pub reserved: u16,
143    pub laml: u64,
144    pub lasa: u64,
145}
146
147impl Ccel {
148    pub fn new(cc_type: u8, cc_subtype: u8, laml: u64, lasa: u64) -> Ccel {
149        let mut ccel = Ccel {
150            header: GenericSdtHeader::new(b"CCEL", size_of::<Ccel>() as u32, 1),
151            cc_type,
152            cc_subtype,
153            reserved: 0,
154            laml,
155            lasa,
156        };
157        ccel.checksum();
158        ccel
159    }
160
161    pub fn checksum(&mut self) {
162        self.header.checksum = 0;
163        self.header
164            .set_checksum(calculate_checksum(self.as_bytes()));
165    }
166}
167
168#[cfg(test)]
169mod tests {
170    use super::*;
171
172    #[test]
173    fn test_calculate_checksum() {
174        let mut buf = [0xac; 8];
175        buf[7] = 0;
176        buf[7] = calculate_checksum(&buf);
177        let sum = buf.iter().fold(0u8, |s, v| s.wrapping_add(*v));
178        assert_eq!(sum, 0);
179
180        buf[3] = 0xcd;
181        buf[6] = 0x1c;
182        buf[4] = 0;
183        buf[4] = calculate_checksum(&buf);
184        let sum = buf.iter().fold(0u8, |s, v| s.wrapping_add(*v));
185        assert_eq!(sum, 0);
186    }
187
188    #[test]
189    fn test_rsdp() {
190        let mut rsdp = Rsdp::new(0xabcd1234);
191        let sum = rsdp.as_bytes()[0..20]
192            .iter()
193            .fold(0u8, |s, v| s.wrapping_add(*v));
194        assert_eq!(sum, 0);
195        let sum = rsdp.as_bytes().iter().fold(0u8, |s, v| s.wrapping_add(*v));
196        assert_eq!(sum, 0);
197
198        rsdp.set_xsdt(0xdeadbeaf);
199        let sum = rsdp.as_bytes()[0..20]
200            .iter()
201            .fold(0u8, |s, v| s.wrapping_add(*v));
202        assert_eq!(sum, 0);
203        let sum = rsdp.as_bytes().iter().fold(0u8, |s, v| s.wrapping_add(*v));
204        assert_eq!(sum, 0);
205    }
206
207    #[test]
208    fn test_xsdt() {
209        const CHECK_SUM: u8 = 186;
210        let mut xsdt = Xsdt::new();
211        assert_eq!(xsdt.header.length as usize, size_of::<GenericSdtHeader>());
212        for idx in 0..ACPI_TABLES_MAX_NUM {
213            assert!(!xsdt.add_table(idx as u64).is_err());
214            assert_eq!(
215                xsdt.header.length as usize,
216                size_of::<GenericSdtHeader>() + (idx + 1) * 8
217            );
218        }
219
220        assert!(xsdt
221            .add_table(100)
222            .is_err_and(|e| e == Error::TooManyAcpiTables));
223        assert_eq!(
224            xsdt.header.length as usize,
225            size_of::<GenericSdtHeader>() + ACPI_TABLES_MAX_NUM * 8
226        );
227        assert!(xsdt
228            .add_table(101)
229            .is_err_and(|e| e == Error::TooManyAcpiTables));
230        assert_eq!(
231            xsdt.header.length as usize,
232            size_of::<GenericSdtHeader>() + ACPI_TABLES_MAX_NUM * 8
233        );
234
235        xsdt.checksum();
236        assert_eq!(xsdt.header.checksum, CHECK_SUM);
237    }
238
239    #[test]
240    fn test_ccel() {
241        let ccel = Ccel::new(2, 0, 0x100, 0);
242
243        assert_eq!(&ccel.header.signature, b"CCEL");
244        assert_eq!(ccel.header.checksum, 45);
245    }
246}