1use crate::structures::{
8 AcpiDbg2Table, AcpiFacs, AcpiFadt, AcpiHpetTable, AcpiMadtTable, AcpiRsdp, AcpiRsdpV2,
9 AcpiRsdt, AcpiSdtHeader, AcpiSignature, AcpiSratTable, AcpiXsdt, VariableSized,
10};
11use zx_status::Status;
12
13#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
14use crate::structures::{K_BIOS_READ_ONLY_AREA_LENGTH, K_BIOS_READ_ONLY_AREA_START};
15
16unsafe extern "C" {
17 pub fn printf(format: *const core::ffi::c_char, ...) -> core::ffi::c_int;
18}
19
20pub trait PhysMemReader {
23 fn phys_to_slice(&self, phys: usize, length: usize) -> Result<&[u8], Status>;
24}
25
26pub trait AcpiParserInterface {
28 fn num_tables(&self) -> usize;
30
31 fn get_table_at_index(&self, index: usize) -> Option<&AcpiSdtHeader>;
36}
37
38pub trait AcpiTable: VariableSized {
39 const SIGNATURE: AcpiSignature;
40}
41
42impl AcpiTable for AcpiRsdt {
43 const SIGNATURE: AcpiSignature = Self::K_SIGNATURE;
44}
45impl AcpiTable for AcpiXsdt {
46 const SIGNATURE: AcpiSignature = Self::K_SIGNATURE;
47}
48impl AcpiTable for AcpiFadt {
49 const SIGNATURE: AcpiSignature = Self::K_SIGNATURE;
50}
51impl AcpiTable for AcpiFacs {
52 const SIGNATURE: AcpiSignature = Self::K_SIGNATURE;
53}
54impl AcpiTable for AcpiMadtTable {
55 const SIGNATURE: AcpiSignature = Self::K_SIGNATURE;
56}
57impl AcpiTable for AcpiHpetTable {
58 const SIGNATURE: AcpiSignature = Self::K_SIGNATURE;
59}
60impl AcpiTable for AcpiSratTable {
61 const SIGNATURE: AcpiSignature = Self::K_SIGNATURE;
62}
63impl AcpiTable for AcpiDbg2Table {
64 const SIGNATURE: AcpiSignature = Self::K_SIGNATURE;
65}
66
67pub fn get_table_by_signature<'a>(
69 parser: &'a dyn AcpiParserInterface,
70 sig: AcpiSignature,
71) -> Option<&'a AcpiSdtHeader> {
72 let num_tables = parser.num_tables();
73 for i in 0..num_tables {
74 let header = match parser.get_table_at_index(i) {
75 Some(h) => h,
76 None => continue,
77 };
78 if sig != header.sig {
79 continue;
80 }
81 let slice = unsafe {
84 core::slice::from_raw_parts(header as *const AcpiSdtHeader as *const u8, header.size())
85 };
86 if !acpi_checksum_valid(slice) {
87 continue;
88 }
89 return Some(header);
90 }
91 None
92}
93
94pub fn get_table_by_type<'a, T>(parser: &'a dyn AcpiParserInterface) -> Option<&'a T>
97where
98 T: AcpiTable + 'static,
99{
100 let header = get_table_by_signature(parser, T::SIGNATURE)?;
101 if header.size() < core::mem::size_of::<T>() {
104 return None;
105 }
106 unsafe { Some(&*(header as *const AcpiSdtHeader as *const T)) }
109}
110
111pub fn acpi_checksum(buf: &[u8]) -> u8 {
113 let mut c: u8 = 0;
114 for &b in buf {
115 c = c.wrapping_add(b);
116 }
117 c.wrapping_neg()
118}
119
120pub fn acpi_checksum_valid(buf: &[u8]) -> bool {
122 #[cfg(fuzz)]
123 {
124 let _ = acpi_checksum(buf);
125 true
126 }
127 #[cfg(not(fuzz))]
128 {
129 acpi_checksum(buf) == 0
130 }
131}
132
133fn map_structure<'a, T>(reader: &'a dyn PhysMemReader, phys: usize) -> Result<&'a T, Status>
148where
149 T: VariableSized + zerocopy::FromBytes + zerocopy::Immutable + zerocopy::KnownLayout,
150{
151 let bytes = reader.phys_to_slice(phys, core::mem::size_of::<T>())?;
152 let r = zerocopy::Ref::<_, T>::from_bytes(bytes).map_err(|_| Status::IO_DATA_INTEGRITY)?;
153 let header = zerocopy::Ref::into_ref(r);
154 let size = header.size();
155 if size < core::mem::size_of::<T>() {
156 return Err(Status::IO_DATA_INTEGRITY);
157 }
158 let bytes = reader.phys_to_slice(phys, size)?;
159 let prefix = &bytes[..core::mem::size_of::<T>()];
160 let r = zerocopy::Ref::<_, T>::from_bytes(prefix).map_err(|_| Status::IO_DATA_INTEGRITY)?;
161 Ok(zerocopy::Ref::into_ref(r))
162}
163
164fn validate_rsdp(rsdp: &AcpiRsdp) -> bool {
166 if rsdp.sig1 != AcpiRsdp::K_SIGNATURE1 || rsdp.sig2 != AcpiRsdp::K_SIGNATURE2 {
167 return false;
168 }
169 let slice = zerocopy::IntoBytes::as_bytes(rsdp);
170 acpi_checksum_valid(slice)
171}
172
173struct RootSystemTableDetails {
174 rsdp_address: usize,
175 rsdt_address: u32,
176 xsdt_address: u64,
177}
178
179fn parse_rsdp(
180 reader: &dyn PhysMemReader,
181 rsdp_pa: usize,
182) -> Result<RootSystemTableDetails, Status> {
183 let maybe_rsdp_v1 = reader.phys_to_slice(rsdp_pa, core::mem::size_of::<AcpiRsdp>())?;
185 let r = zerocopy::Ref::<_, AcpiRsdp>::from_bytes(maybe_rsdp_v1)
186 .map_err(|_| Status::IO_DATA_INTEGRITY)?;
187 let rsdp_v1 = zerocopy::Ref::into_ref(r);
188
189 if !validate_rsdp(rsdp_v1) {
191 return Err(Status::NOT_FOUND);
192 }
193
194 let revision = rsdp_v1.revision;
196 let rsdt_address = rsdp_v1.rsdt_address;
197 if revision < 2 {
198 return Ok(RootSystemTableDetails { rsdp_address: rsdp_pa, rsdt_address, xsdt_address: 0 });
199 }
200
201 let rsdp_v2 = map_structure::<AcpiRsdpV2>(reader, rsdp_pa)?;
203 let rsdp_v2_slice = reader.phys_to_slice(rsdp_pa, rsdp_v2.size())?;
204 if !acpi_checksum_valid(rsdp_v2_slice) {
206 return Err(Status::NOT_FOUND);
207 }
208
209 let rsdt_address = rsdp_v2.v1.rsdt_address;
210 let xsdt_address = rsdp_v2.xsdt_address;
211 Ok(RootSystemTableDetails { rsdp_address: rsdp_pa, rsdt_address, xsdt_address })
212}
213
214#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
215fn find_rsdp_pc(reader: &dyn PhysMemReader) -> Result<usize, Status> {
222 let bios_section =
223 reader.phys_to_slice(K_BIOS_READ_ONLY_AREA_START, K_BIOS_READ_ONLY_AREA_LENGTH)?;
224 let rsdp_size = core::mem::size_of::<AcpiRsdp>();
225 if bios_section.len() < rsdp_size {
226 return Err(Status::NOT_FOUND);
227 }
228 for offset in (0..=K_BIOS_READ_ONLY_AREA_LENGTH - rsdp_size).step_by(16) {
229 let slice = &bios_section[offset..offset + rsdp_size];
230 let r = zerocopy::Ref::<_, AcpiRsdp>::from_bytes(slice).map_err(|_| Status::NOT_FOUND)?;
231 let rsdp = zerocopy::Ref::into_ref(r);
232 if validate_rsdp(rsdp) {
233 return Ok(K_BIOS_READ_ONLY_AREA_START + offset);
234 }
235 }
236 Err(Status::NOT_FOUND)
237}
238
239fn find_root_tables(
240 physmem_reader: &dyn PhysMemReader,
241 rsdp_pa: usize,
242) -> Result<RootSystemTableDetails, Status> {
243 if rsdp_pa != 0 {
245 return parse_rsdp(physmem_reader, rsdp_pa);
246 }
247
248 #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
249 {
250 if let Ok(addr) = find_rsdp_pc(physmem_reader) {
251 unsafe {
252 printf(
253 b"ACPI LITE: Found RSDP at physical address 0x%zx.\n\0".as_ptr()
254 as *const core::ffi::c_char,
255 addr,
256 );
257 }
258 return parse_rsdp(physmem_reader, addr);
259 }
260 unsafe {
261 printf(b"ACPI LITE: Couldn't find ACPI RSDP in BIOS area\n\0".as_ptr()
262 as *const core::ffi::c_char);
263 }
264 }
265
266 Err(Status::NOT_FOUND)
267}
268
269pub fn validate_rsdt<'a>(
271 reader: &'a dyn PhysMemReader,
272 rsdt_pa: usize,
273) -> Result<(&'a AcpiRsdt, usize), Status> {
274 let rsdt = map_structure::<AcpiRsdt>(reader, rsdt_pa)?;
276 if rsdt.header.sig != AcpiRsdt::K_SIGNATURE {
278 return Err(Status::NOT_FOUND);
279 }
280 let length = rsdt.header.size();
281 let slice = reader.phys_to_slice(rsdt_pa, length)?;
282 if !acpi_checksum_valid(slice) {
284 return Err(Status::IO_DATA_INTEGRITY);
285 }
286 if rsdt.header.revision != 1 {
288 return Err(Status::NOT_SUPPORTED);
289 }
290 let num_tables = (length - core::mem::size_of::<AcpiSdtHeader>()) / 4;
291 Ok((rsdt, num_tables))
292}
293
294pub fn validate_xsdt<'a>(
296 reader: &'a dyn PhysMemReader,
297 xsdt_pa: usize,
298) -> Result<(&'a AcpiXsdt, usize), Status> {
299 let xsdt = map_structure::<AcpiXsdt>(reader, xsdt_pa)?;
301 if xsdt.header.sig != AcpiXsdt::K_SIGNATURE {
303 return Err(Status::NOT_FOUND);
304 }
305 let length = xsdt.header.size();
306 let slice = reader.phys_to_slice(xsdt_pa, length)?;
307 if !acpi_checksum_valid(slice) {
309 return Err(Status::IO_DATA_INTEGRITY);
310 }
311 if xsdt.header.revision != 1 {
313 return Err(Status::NOT_SUPPORTED);
314 }
315 let num_tables = (length - core::mem::size_of::<AcpiSdtHeader>()) / 8;
316 Ok((xsdt, num_tables))
317}
318
319pub struct AcpiParser<'a> {
321 reader: &'a dyn PhysMemReader,
322 rsdt: Option<&'a AcpiRsdt>,
323 xsdt: Option<&'a AcpiXsdt>,
324 num_tables: usize,
325 #[allow(dead_code)]
326 root_table_addr: usize,
327 rsdp_addr: usize,
328}
329
330impl<'a> AcpiParser<'a> {
331 pub fn init(physmem_reader: &'a dyn PhysMemReader, rsdp_pa: usize) -> Result<Self, Status> {
335 let root_tables = find_root_tables(physmem_reader, rsdp_pa)?;
336
337 if root_tables.xsdt_address != 0 {
339 match validate_xsdt(physmem_reader, root_tables.xsdt_address as usize) {
340 Ok((xsdt, count)) => {
341 unsafe {
342 printf(
343 b"ACPI LITE: Found valid XSDT table at physical address 0x%llx\n\0"
344 .as_ptr() as *const core::ffi::c_char,
345 root_tables.xsdt_address,
346 );
347 }
348 return Ok(AcpiParser {
349 reader: physmem_reader,
350 rsdt: None,
351 xsdt: Some(xsdt),
352 num_tables: count,
353 root_table_addr: root_tables.xsdt_address as usize,
354 rsdp_addr: root_tables.rsdp_address,
355 });
356 }
357 Err(_) => unsafe {
358 printf(
359 b"ACPI LITE: Invalid XSDT table at physical address 0x%llx\n\0".as_ptr()
360 as *const core::ffi::c_char,
361 root_tables.xsdt_address,
362 );
363 },
364 }
365 }
366
367 if root_tables.rsdt_address != 0 {
369 match validate_rsdt(physmem_reader, root_tables.rsdt_address as usize) {
370 Ok((rsdt, count)) => {
371 unsafe {
372 printf(
373 b"ACPI LITE: Found valid RSDT table at physical address 0x%x\n\0"
374 .as_ptr() as *const core::ffi::c_char,
375 root_tables.rsdt_address,
376 );
377 }
378 return Ok(AcpiParser {
379 reader: physmem_reader,
380 rsdt: Some(rsdt),
381 xsdt: None,
382 num_tables: count,
383 root_table_addr: root_tables.rsdt_address as usize,
384 rsdp_addr: root_tables.rsdp_address,
385 });
386 }
387 Err(_) => unsafe {
388 printf(
389 b"ACPI LITE: Invalid RSDT table at physical address 0x%x\n\0".as_ptr()
390 as *const core::ffi::c_char,
391 root_tables.rsdt_address,
392 );
393 },
394 }
395 }
396
397 Err(Status::NOT_FOUND)
398 }
399
400 pub fn rsdp_pa(&self) -> usize {
401 self.rsdp_addr
402 }
403
404 fn get_table_phys_addr(&self, index: usize) -> usize {
406 if index >= self.num_tables {
407 return 0;
408 }
409 if let Some(xsdt) = self.xsdt {
410 unsafe { xsdt.get_entry(index) as usize }
412 } else if let Some(rsdt) = self.rsdt {
413 unsafe { rsdt.get_entry(index) as usize }
415 } else {
416 0
417 }
418 }
419
420 pub fn dump_tables(&self) {
422 struct StdoutWriter;
423
424 impl core::fmt::Write for StdoutWriter {
425 fn write_str(&mut self, s: &str) -> core::fmt::Result {
426 unsafe {
427 printf(
428 b"%.*s\0".as_ptr() as *const core::ffi::c_char,
429 s.len() as core::ffi::c_int,
430 s.as_ptr() as *const core::ffi::c_char,
431 );
432 }
433 Ok(())
434 }
435 }
436 let mut writer = StdoutWriter;
437 unsafe {
438 printf(
439 b"root table at paddr 0x%zx:\n\0".as_ptr() as *const core::ffi::c_char,
440 self.root_table_addr,
441 );
442 }
443 if let Some(xsdt) = self.xsdt {
444 let slice = unsafe {
446 core::slice::from_raw_parts(xsdt as *const AcpiXsdt as *const u8, xsdt.size())
447 };
448 let _ = pretty::hexdump_very_ex_rs(&mut writer, slice, xsdt as *const _ as u64);
449 } else if let Some(rsdt) = self.rsdt {
450 let slice = unsafe {
452 core::slice::from_raw_parts(rsdt as *const AcpiRsdt as *const u8, rsdt.size())
453 };
454 let _ = pretty::hexdump_very_ex_rs(&mut writer, slice, rsdt as *const _ as u64);
455 }
456
457 for i in 0..self.num_tables {
458 if let Some(header) = self.get_table_at_index(i) {
459 let mut name = [0u8; 5];
460 header.sig.write_to_buffer(&mut name);
461 let name_str = core::str::from_utf8(&name[..4]).unwrap_or("????");
462 unsafe {
463 printf(
464 b"table %zx: '%.*s' at paddr 0x%zx, len %zx\n\0".as_ptr()
465 as *const core::ffi::c_char,
466 i,
467 name_str.len() as core::ffi::c_int,
468 name_str.as_ptr() as *const core::ffi::c_char,
469 self.get_table_phys_addr(i),
470 header.size(),
471 );
472 }
473 let slice = unsafe {
475 core::slice::from_raw_parts(
476 header as *const AcpiSdtHeader as *const u8,
477 header.size(),
478 )
479 };
480 let _ = pretty::hexdump_very_ex_rs(&mut writer, slice, header as *const _ as u64);
481 }
482 }
483 }
484}
485
486impl<'a> AcpiParserInterface for AcpiParser<'a> {
487 fn num_tables(&self) -> usize {
488 self.num_tables
489 }
490
491 fn get_table_at_index(&self, index: usize) -> Option<&AcpiSdtHeader> {
492 let paddr = self.get_table_phys_addr(index);
493 if paddr == 0 {
494 return None;
495 }
496 map_structure::<AcpiSdtHeader>(self.reader, paddr).ok()
497 }
498}