1use 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}