Skip to content

Commit 526d622

Browse files
rcercIsaacWoods
authored andcommitted
acpi: Reduce duplicate code to iterate over root table entries
This also fixes `SsdtIterator`, which was not iterating reliably.
1 parent 245258b commit 526d622

File tree

2 files changed

+70
-72
lines changed

2 files changed

+70
-72
lines changed

acpi/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ edition = "2018"
1111

1212
[dependencies]
1313
bit_field = "0.10"
14+
log = "0.4"
1415
rsdp = { version = "2", path = "../rsdp" }
1516

1617
[features]

acpi/src/lib.rs

Lines changed: 69 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -236,43 +236,52 @@ where
236236
self.revision
237237
}
238238

239-
/// Searches through the ACPI table headers and attempts to locate the table with a matching `T::SIGNATURE`.
240-
pub fn find_table<T: AcpiTable>(&self) -> AcpiResult<PhysicalMapping<H, T>> {
241-
use core::mem::size_of;
239+
/// Constructs a [`TablesPhysPtrsIter`] over this table.
240+
fn tables_phys_ptrs(&self) -> TablesPhysPtrsIter<'_> {
241+
// SAFETY: The virtual address of the array of pointers follows the virtual address of the table in memory.
242+
let ptrs_virt_start = unsafe { self.mapping.virtual_start().as_ptr().add(1).cast::<u8>() };
243+
let ptrs_bytes_len = self.mapping.region_length() - mem::size_of::<SdtHeader>();
244+
// SAFETY: `ptrs_virt_start` points to an array of `ptrs_bytes_len` bytes that lives as long as `self`.
245+
let ptrs_bytes = unsafe { core::slice::from_raw_parts(ptrs_virt_start, ptrs_bytes_len) };
246+
let ptr_size = if self.revision == 0 {
247+
4 // RSDT entry size
248+
} else {
249+
8 // XSDT entry size
250+
};
242251

243-
if self.revision == 0 {
244-
let num_tables = ((self.mapping.length as usize) - size_of::<SdtHeader>()) / size_of::<u32>();
245-
// Safety: Table pointer is known-good for these offsets and types.
246-
let tables_base = unsafe { self.mapping.virtual_start().as_ptr().add(1).cast::<u32>() };
252+
ptrs_bytes.chunks(ptr_size).map(|ptr_bytes_src| {
253+
// Construct a native pointer using as many bytes as required from `ptr_bytes_src` (note that ACPI is
254+
// little-endian)
247255

248-
for offset in 0..num_tables {
249-
// Safety: Table pointer is known-good for these offsets and types.
250-
let sdt_header_address = unsafe { tables_base.add(offset).read_unaligned() } as usize;
256+
let mut ptr_bytes_dst = [0; mem::size_of::<usize>()];
257+
let common_ptr_size = usize::min(mem::size_of::<usize>(), ptr_bytes_src.len());
258+
ptr_bytes_dst[..common_ptr_size].copy_from_slice(&ptr_bytes_src[..common_ptr_size]);
251259

252-
// Safety: `RSDT` guarantees its contained addresses to be valid.
253-
let table_result = unsafe { read_table(self.handler.clone(), sdt_header_address) };
254-
if table_result.is_ok() {
255-
return table_result;
256-
}
257-
}
258-
} else {
259-
let num_tables = ((self.mapping.length as usize) - size_of::<SdtHeader>()) / size_of::<u64>();
260-
// Safety: Table pointer is known-good for these offsets and types.
261-
let tables_base = unsafe { self.mapping.virtual_start().as_ptr().add(1).cast::<u64>() };
262-
263-
for offset in 0..num_tables {
264-
// Safety: Table pointer is known-good for these offsets and types.
265-
let sdt_header_address = unsafe { tables_base.add(offset).read_unaligned() } as usize;
266-
267-
// Safety: `XSDT` guarantees its contained addresses to be valid.
268-
let table_result = unsafe { read_table(self.handler.clone(), sdt_header_address) };
269-
if table_result.is_ok() {
270-
return table_result;
271-
}
272-
}
273-
}
260+
usize::from_le_bytes(ptr_bytes_dst) as *const SdtHeader
261+
})
262+
}
274263

275-
Err(AcpiError::TableMissing(T::SIGNATURE))
264+
/// Searches through the ACPI table headers and attempts to locate the table with a matching `T::SIGNATURE`.
265+
pub fn find_table<T: AcpiTable>(&self) -> AcpiResult<PhysicalMapping<H, T>> {
266+
self.tables_phys_ptrs()
267+
.find_map(|table_phys_ptr| {
268+
// SAFETY: Table guarantees its contained addresses to be valid.
269+
match unsafe { read_table(self.handler.clone(), table_phys_ptr as usize) } {
270+
Ok(table_mapping) => Some(table_mapping),
271+
Err(AcpiError::SdtInvalidSignature(_)) => None,
272+
Err(e) => {
273+
log::warn!(
274+
"Found invalid {} table at physical address {:p}: {:?}",
275+
T::SIGNATURE,
276+
table_phys_ptr,
277+
e
278+
);
279+
280+
None
281+
}
282+
}
283+
})
284+
.ok_or(AcpiError::TableMissing(T::SIGNATURE))
276285
}
277286

278287
/// Finds and returns the DSDT AML table, if it exists.
@@ -298,17 +307,7 @@ where
298307

299308
/// Iterates through all of the SSDT tables.
300309
pub fn ssdts(&self) -> SsdtIterator<H> {
301-
let header_ptrs_base_ptr =
302-
unsafe { self.mapping.virtual_start().as_ptr().add(1).cast::<*const SdtHeader>() };
303-
let header_ptr_count = ((self.mapping.length as usize) - mem::size_of::<SdtHeader>())
304-
/ core::mem::size_of::<*const *const SdtHeader>();
305-
306-
SsdtIterator {
307-
current_sdt_ptr: header_ptrs_base_ptr,
308-
remaining: header_ptr_count,
309-
handler: self.handler.clone(),
310-
marker: core::marker::PhantomData,
311-
}
310+
SsdtIterator { tables_phys_ptrs: self.tables_phys_ptrs(), handler: self.handler.clone() }
312311
}
313312

314313
/// Convenience method for contructing a [`PlatformInfo`](crate::platform::PlatformInfo). This is one of the
@@ -333,6 +332,9 @@ pub struct Sdt {
333332
pub validated: bool,
334333
}
335334

335+
/// An iterator over the physical table addresses in an RSDT or XSDT.
336+
type TablesPhysPtrsIter<'t> = core::iter::Map<core::slice::Chunks<'t, u8>, fn(&[u8]) -> *const SdtHeader>;
337+
336338
#[derive(Debug)]
337339
pub struct AmlTable {
338340
/// Physical address of the start of the AML stream (excluding the table header).
@@ -391,55 +393,50 @@ unsafe fn read_table<H: AcpiHandler, T: AcpiTable>(
391393
}
392394

393395
/// Iterator that steps through all of the tables, and returns only the SSDTs as `AmlTable`s.
394-
pub struct SsdtIterator<'a, H>
396+
pub struct SsdtIterator<'t, H>
395397
where
396398
H: AcpiHandler,
397399
{
398-
current_sdt_ptr: *const *const SdtHeader,
399-
remaining: usize,
400+
tables_phys_ptrs: TablesPhysPtrsIter<'t>,
400401
handler: H,
401-
marker: core::marker::PhantomData<&'a ()>,
402402
}
403403

404-
impl<'a, H> Iterator for SsdtIterator<'a, H>
404+
impl<'t, H> Iterator for SsdtIterator<'t, H>
405405
where
406406
H: AcpiHandler,
407407
{
408408
type Item = AmlTable;
409409

410410
fn next(&mut self) -> Option<Self::Item> {
411-
struct Ssdt;
412-
// Safety: Implementation properly represents a valid SSDT.
411+
#[repr(transparent)]
412+
struct Ssdt {
413+
header: SdtHeader,
414+
}
415+
416+
// SAFETY: Implementation properly represents a valid SSDT.
413417
unsafe impl AcpiTable for Ssdt {
414418
const SIGNATURE: Signature = Signature::SSDT;
415419

416-
fn header(&self) -> &sdt::SdtHeader {
417-
// Safety: DSDT will always be valid for an SdtHeader at its `self` pointer.
418-
unsafe { &*(self as *const Self as *const sdt::SdtHeader) }
420+
fn header(&self) -> &SdtHeader {
421+
&self.header
419422
}
420423
}
421424

422-
if self.remaining == 0 {
423-
None
424-
} else {
425-
loop {
426-
// Attempt to peek at the SDT header to correctly enumerate the entire table.
427-
// Safety: `address` needs to be valid for the size of `SdtHeader`, or the ACPI tables are malformed (not a software issue).
428-
let sdt_header = unsafe {
429-
self.handler.map_physical_region::<SdtHeader>(
430-
self.current_sdt_ptr.read() as usize,
431-
core::mem::size_of::<SdtHeader>(),
432-
)
433-
};
425+
// Borrow single field for closure to avoid immutable reference to `self` that inhibits `find_map`
426+
let handler = &self.handler;
434427

435-
self.remaining -= 1;
428+
// Consume iterator until next valid SSDT and return the latter
429+
self.tables_phys_ptrs.find_map(|table_phys_ptr| {
430+
// SAFETY: Table guarantees its contained addresses to be valid.
431+
match unsafe { read_table::<_, Ssdt>(handler.clone(), table_phys_ptr as usize) } {
432+
Ok(ssdt_mapping) => Some(AmlTable::new(ssdt_mapping.physical_start(), ssdt_mapping.header.length)),
433+
Err(AcpiError::SdtInvalidSignature(_)) => None,
434+
Err(e) => {
435+
log::warn!("Found invalid SSDT at physical address {:p}: {:?}", table_phys_ptr, e);
436436

437-
if sdt_header.validate(Ssdt::SIGNATURE).is_err() {
438-
continue;
439-
} else {
440-
return Some(AmlTable { address: sdt_header.physical_start(), length: sdt_header.length });
437+
None
441438
}
442439
}
443-
}
440+
})
444441
}
445442
}

0 commit comments

Comments
 (0)