@@ -28,6 +28,11 @@ pub enum RsdpError {
28
28
InvalidChecksum ,
29
29
}
30
30
31
+ /// The size in bytes of the ACPI 1.0 RSDP.
32
+ const RSDP_V1_LENGTH : usize = 20 ;
33
+ /// The total size in bytes of the RSDP fields introduced in ACPI 2.0.
34
+ const RSDP_V2_EXT_LENGTH : usize = mem:: size_of :: < Rsdp > ( ) - RSDP_V1_LENGTH ;
35
+
31
36
/// The first structure found in ACPI. It just tells us where the RSDT is.
32
37
///
33
38
/// On BIOS systems, it is either found in the first 1KB of the Extended Bios Data Area, or between
@@ -79,32 +84,45 @@ impl Rsdp {
79
84
where
80
85
H : AcpiHandler ,
81
86
{
82
- let rsdp_address = {
83
- let mut rsdp_address = None ;
84
- let areas = find_search_areas ( handler. clone ( ) ) ;
85
-
86
- ' areas: for area in areas. iter ( ) {
87
- let mapping = unsafe { handler. map_physical_region :: < u8 > ( area. start , area. end - area. start ) } ;
88
-
89
- for address in area. clone ( ) . step_by ( 16 ) {
90
- let ptr_in_mapping =
91
- unsafe { mapping. virtual_start ( ) . as_ptr ( ) . add ( address - area. start ) } ;
92
- let signature = unsafe { * ( ptr_in_mapping as * const [ u8 ; 8 ] ) } ;
93
-
94
- if signature == RSDP_SIGNATURE {
95
- match unsafe { * ( ptr_in_mapping as * const Rsdp ) } . validate ( ) {
96
- Ok ( ( ) ) => {
97
- rsdp_address = Some ( address) ;
98
- break ' areas;
99
- }
100
- Err ( err) => warn ! ( "Invalid RSDP found at {:#x}: {:?}" , address, err) ,
101
- }
87
+ let rsdp_address = find_search_areas ( handler. clone ( ) ) . iter ( ) . find_map ( |area| {
88
+ // Map the search area for the RSDP followed by `RSDP_V2_EXT_LENGTH` bytes so an ACPI 1.0 RSDP at the
89
+ // end of the area can be read as an `Rsdp` (which always has the size of an ACPI 2.0 RSDP)
90
+ let mapping = unsafe {
91
+ handler. map_physical_region :: < u8 > ( area. start , area. end - area. start + RSDP_V2_EXT_LENGTH )
92
+ } ;
93
+
94
+ // SAFETY: The entirety of `mapping` maps the search area and an additional `RSDP_V2_EXT_LENGTH` bytes
95
+ // that are assumed to be safe to read while `extended_area_bytes` lives. (Ideally, an assumption about
96
+ // the memory following the search area would not be made.)
97
+ let extended_area_bytes = unsafe {
98
+ slice:: from_raw_parts (
99
+ mapping. virtual_start ( ) . as_ptr ( ) ,
100
+ mapping. region_length ( ) + RSDP_V2_EXT_LENGTH ,
101
+ )
102
+ } ;
103
+
104
+ // Search `Rsdp`-sized windows at 16-byte boundaries relative to the base of the area (which is also
105
+ // aligned to 16 bytes due to the implementation of `find_search_areas`)
106
+ extended_area_bytes. windows ( mem:: size_of :: < Rsdp > ( ) ) . step_by ( 16 ) . find_map ( |maybe_rsdp_bytes_slice| {
107
+ let maybe_rsdp_virt_ptr = maybe_rsdp_bytes_slice. as_ptr ( ) . cast :: < Rsdp > ( ) ;
108
+ let maybe_rsdp_phys_start = maybe_rsdp_virt_ptr as usize
109
+ - mapping. virtual_start ( ) . as_ptr ( ) as usize
110
+ + mapping. physical_start ( ) ;
111
+ // SAFETY: `maybe_rsdp_virt_ptr` points to an aligned, readable `Rsdp`-sized value, and the `Rsdp`
112
+ // struct's fields are always initialized.
113
+ let maybe_rsdp = unsafe { & * maybe_rsdp_virt_ptr } ;
114
+
115
+ match maybe_rsdp. validate ( ) {
116
+ Ok ( ( ) ) => Some ( maybe_rsdp_phys_start) ,
117
+ Err ( RsdpError :: IncorrectSignature ) => None ,
118
+ Err ( e) => {
119
+ warn ! ( "Invalid RSDP found at {:#x}: {:?}" , maybe_rsdp_phys_start, e) ;
120
+
121
+ None
102
122
}
103
123
}
104
- }
105
-
106
- rsdp_address
107
- } ;
124
+ } )
125
+ } ) ;
108
126
109
127
match rsdp_address {
110
128
Some ( address) => {
@@ -120,8 +138,6 @@ impl Rsdp {
120
138
/// 2) The checksum is correct
121
139
/// 3) For Version 2.0+, that the extension checksum is correct
122
140
pub fn validate ( & self ) -> Result < ( ) , RsdpError > {
123
- const RSDP_V1_LENGTH : usize = 20 ;
124
-
125
141
// Check the signature
126
142
if self . signature != RSDP_SIGNATURE {
127
143
return Err ( RsdpError :: IncorrectSignature ) ;
0 commit comments