Skip to content

Commit a0db984

Browse files
rcercIsaacWoods
authored andcommitted
rsdp: Improve handling of RSDP search areas' ends
This ensures the `Rsdp`-sized region being read while searching remains within the area mapped. However, since a potential ACPI 1.0 RSDP at the end of the area is currently being read as a full-size `Rsdp`, the 16 bytes following the area are also mapped to allow this (not ideal).
1 parent 526d622 commit a0db984

File tree

1 file changed

+42
-26
lines changed

1 file changed

+42
-26
lines changed

rsdp/src/lib.rs

Lines changed: 42 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,11 @@ pub enum RsdpError {
2828
InvalidChecksum,
2929
}
3030

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+
3136
/// The first structure found in ACPI. It just tells us where the RSDT is.
3237
///
3338
/// 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 {
7984
where
8085
H: AcpiHandler,
8186
{
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
102122
}
103123
}
104-
}
105-
106-
rsdp_address
107-
};
124+
})
125+
});
108126

109127
match rsdp_address {
110128
Some(address) => {
@@ -120,8 +138,6 @@ impl Rsdp {
120138
/// 2) The checksum is correct
121139
/// 3) For Version 2.0+, that the extension checksum is correct
122140
pub fn validate(&self) -> Result<(), RsdpError> {
123-
const RSDP_V1_LENGTH: usize = 20;
124-
125141
// Check the signature
126142
if self.signature != RSDP_SIGNATURE {
127143
return Err(RsdpError::IncorrectSignature);

0 commit comments

Comments
 (0)