@@ -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 ) ]
337339pub 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 >
395397where
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 >
405405where
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