@@ -170,40 +170,64 @@ where
170170 ///
171171 /// ### Safety: Caller must ensure that the provided mapping is a fully validated RSDP.
172172 pub unsafe fn from_validated_rsdp ( handler : H , rsdp_mapping : PhysicalMapping < H , Rsdp > ) -> AcpiResult < Self > {
173- let revision = rsdp_mapping. revision ( ) ;
173+ macro_rules! read_root_table {
174+ ( $signature_name: ident, $address_getter: ident) => { {
175+ #[ repr( transparent) ]
176+ struct RootTable {
177+ header: SdtHeader ,
178+ }
179+
180+ unsafe impl AcpiTable for RootTable {
181+ const SIGNATURE : Signature = Signature :: $signature_name;
182+
183+ fn header( & self ) -> & SdtHeader {
184+ & self . header
185+ }
186+ }
187+
188+ // Unmap RSDP as soon as possible
189+ let table_phys_start = rsdp_mapping. $address_getter( ) as usize ;
190+ drop( rsdp_mapping) ;
191+
192+ // Map and validate root table
193+ // SAFETY: Addresses from a validated `RSDP` are also guaranteed to be valid.
194+ let table_mapping = unsafe { read_table:: <_, RootTable >( handler. clone( ) , table_phys_start) } ?;
195+
196+ // Convert `table_mapping` to header mapping for storage
197+ // Avoid requesting table unmap twice (from both original and converted `table_mapping`s)
198+ let table_mapping = mem:: ManuallyDrop :: new( table_mapping) ;
199+ // SAFETY: `SdtHeader` is equivalent to `Sdt` memory-wise
200+ let table_mapping = unsafe {
201+ PhysicalMapping :: new(
202+ table_mapping. physical_start( ) ,
203+ table_mapping. virtual_start( ) . cast:: <SdtHeader >( ) ,
204+ table_mapping. region_length( ) ,
205+ table_mapping. mapped_length( ) ,
206+ handler. clone( ) ,
207+ )
208+ } ;
209+
210+ table_mapping
211+ } } ;
212+ }
174213
175- if revision == 0 {
214+ let revision = rsdp_mapping. revision ( ) ;
215+ let root_table_mapping = if revision == 0 {
176216 /*
177217 * We're running on ACPI Version 1.0. We should use the 32-bit RSDT address.
178218 */
179219
180- // Safety: Addresses from a validated `RSDP` are also guaranteed to be valid.
181- let rsdt_mapping: PhysicalMapping < H , SdtHeader > = unsafe {
182- handler. map_physical_region :: < SdtHeader > (
183- rsdp_mapping. rsdt_address ( ) as usize ,
184- core:: mem:: size_of :: < SdtHeader > ( ) ,
185- )
186- } ;
187- rsdt_mapping. validate ( Signature :: RSDT ) ?;
188-
189- Ok ( Self { mapping : rsdt_mapping, revision, handler } )
220+ read_root_table ! ( RSDT , rsdt_address)
190221 } else {
191222 /*
192223 * We're running on ACPI Version 2.0+. We should use the 64-bit XSDT address, truncated
193224 * to 32 bits on x86.
194225 */
195226
196- // Safety: Addresses from a validated `RSDP` are also guaranteed to be valid.
197- let xsdt_mapping: PhysicalMapping < H , SdtHeader > = unsafe {
198- handler. map_physical_region :: < SdtHeader > (
199- rsdp_mapping. xsdt_address ( ) as usize ,
200- core:: mem:: size_of :: < SdtHeader > ( ) ,
201- )
202- } ;
203- xsdt_mapping. validate ( Signature :: XSDT ) ?;
227+ read_root_table ! ( XSDT , xsdt_address)
228+ } ;
204229
205- Ok ( Self { mapping : xsdt_mapping, revision, handler } )
206- }
230+ Ok ( Self { mapping : root_table_mapping, revision, handler } )
207231 }
208232
209233 /// The ACPI revision of the tables enumerated by this structure.
@@ -333,35 +357,37 @@ unsafe fn read_table<H: AcpiHandler, T: AcpiTable>(
333357 address : usize ,
334358) -> AcpiResult < PhysicalMapping < H , T > > {
335359 // Attempt to peek at the SDT header to correctly enumerate the entire table.
336- // Safety: `address` needs to be valid for the size of `SdtHeader`, or the ACPI tables are malformed (not a software issue).
337- let mapping = unsafe { handler. map_physical_region :: < SdtHeader > ( address, core:: mem:: size_of :: < SdtHeader > ( ) ) } ;
338- mapping. validate ( T :: SIGNATURE ) ?;
360+ // SAFETY: `address` needs to be valid for the size of `SdtHeader`, or the ACPI tables are malformed (not a software issue).
361+ let header_mapping = unsafe { handler. map_physical_region :: < SdtHeader > ( address, mem:: size_of :: < SdtHeader > ( ) ) } ;
339362
340- // If possible (if the existing mapping covers enough memory), resuse the existing physical mapping.
363+ // If possible (if the existing mapping covers enough memory), reuse the existing physical mapping.
341364 // This allows allocators/memory managers that map in chunks larger than `size_of::<SdtHeader>()` to be used more efficiently.
342- if mapping . mapped_length ( ) >= ( mapping . length as usize ) {
343- // Safety: Pointer is known non-null.
344- let virtual_start =
345- unsafe { core :: ptr :: NonNull :: new_unchecked ( mapping . virtual_start ( ) . as_ptr ( ) as * mut _ ) } ;
365+ let table_length = header_mapping . length as usize ;
366+ let table_mapping = if header_mapping . mapped_length ( ) >= table_length {
367+ // Avoid requesting table unmap twice (from both `header_mapping` and `table_mapping`)
368+ let header_mapping = mem :: ManuallyDrop :: new ( header_mapping ) ;
346369
347- // Safety: Mapping is known-good and validated .
348- Ok ( unsafe {
370+ // SAFETY: `header_mapping` maps entire table .
371+ unsafe {
349372 PhysicalMapping :: new (
350- mapping . physical_start ( ) ,
351- virtual_start,
352- mapping . region_length ( ) ,
353- mapping . mapped_length ( ) ,
354- handler. clone ( ) ,
373+ header_mapping . physical_start ( ) ,
374+ header_mapping . virtual_start ( ) . cast :: < T > ( ) ,
375+ table_length ,
376+ header_mapping . mapped_length ( ) ,
377+ handler,
355378 )
356- } )
379+ }
357380 } else {
358- let sdt_length = mapping. length ;
359381 // Drop the old mapping here, to ensure it's unmapped in software before requesting an overlapping mapping.
360- drop ( mapping ) ;
382+ drop ( header_mapping ) ;
361383
362- // Safety: Address and length are already known-good.
363- Ok ( unsafe { handler. map_physical_region ( address, sdt_length as usize ) } )
364- }
384+ // SAFETY: Address and length are already known-good.
385+ unsafe { handler. map_physical_region ( address, table_length) }
386+ } ;
387+
388+ table_mapping. validate ( ) ?;
389+
390+ Ok ( table_mapping)
365391}
366392
367393/// Iterator that steps through all of the tables, and returns only the SSDTs as `AmlTable`s.
0 commit comments