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