Skip to content

Commit

Permalink
rsdp: Improve handling of RSDP search areas' ends
Browse files Browse the repository at this point in the history
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).
  • Loading branch information
rcerc authored and IsaacWoods committed Mar 1, 2023
1 parent 526d622 commit a0db984
Showing 1 changed file with 42 additions and 26 deletions.
68 changes: 42 additions & 26 deletions rsdp/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,11 @@ pub enum RsdpError {
InvalidChecksum,
}

/// The size in bytes of the ACPI 1.0 RSDP.
const RSDP_V1_LENGTH: usize = 20;
/// The total size in bytes of the RSDP fields introduced in ACPI 2.0.
const RSDP_V2_EXT_LENGTH: usize = mem::size_of::<Rsdp>() - RSDP_V1_LENGTH;

/// The first structure found in ACPI. It just tells us where the RSDT is.
///
/// On BIOS systems, it is either found in the first 1KB of the Extended Bios Data Area, or between
Expand Down Expand Up @@ -79,32 +84,45 @@ impl Rsdp {
where
H: AcpiHandler,
{
let rsdp_address = {
let mut rsdp_address = None;
let areas = find_search_areas(handler.clone());

'areas: for area in areas.iter() {
let mapping = unsafe { handler.map_physical_region::<u8>(area.start, area.end - area.start) };

for address in area.clone().step_by(16) {
let ptr_in_mapping =
unsafe { mapping.virtual_start().as_ptr().add(address - area.start) };
let signature = unsafe { *(ptr_in_mapping as *const [u8; 8]) };

if signature == RSDP_SIGNATURE {
match unsafe { *(ptr_in_mapping as *const Rsdp) }.validate() {
Ok(()) => {
rsdp_address = Some(address);
break 'areas;
}
Err(err) => warn!("Invalid RSDP found at {:#x}: {:?}", address, err),
}
let rsdp_address = find_search_areas(handler.clone()).iter().find_map(|area| {
// Map the search area for the RSDP followed by `RSDP_V2_EXT_LENGTH` bytes so an ACPI 1.0 RSDP at the
// end of the area can be read as an `Rsdp` (which always has the size of an ACPI 2.0 RSDP)
let mapping = unsafe {
handler.map_physical_region::<u8>(area.start, area.end - area.start + RSDP_V2_EXT_LENGTH)
};

// SAFETY: The entirety of `mapping` maps the search area and an additional `RSDP_V2_EXT_LENGTH` bytes
// that are assumed to be safe to read while `extended_area_bytes` lives. (Ideally, an assumption about
// the memory following the search area would not be made.)
let extended_area_bytes = unsafe {
slice::from_raw_parts(
mapping.virtual_start().as_ptr(),
mapping.region_length() + RSDP_V2_EXT_LENGTH,
)
};

// Search `Rsdp`-sized windows at 16-byte boundaries relative to the base of the area (which is also
// aligned to 16 bytes due to the implementation of `find_search_areas`)
extended_area_bytes.windows(mem::size_of::<Rsdp>()).step_by(16).find_map(|maybe_rsdp_bytes_slice| {
let maybe_rsdp_virt_ptr = maybe_rsdp_bytes_slice.as_ptr().cast::<Rsdp>();
let maybe_rsdp_phys_start = maybe_rsdp_virt_ptr as usize
- mapping.virtual_start().as_ptr() as usize
+ mapping.physical_start();
// SAFETY: `maybe_rsdp_virt_ptr` points to an aligned, readable `Rsdp`-sized value, and the `Rsdp`
// struct's fields are always initialized.
let maybe_rsdp = unsafe { &*maybe_rsdp_virt_ptr };

match maybe_rsdp.validate() {
Ok(()) => Some(maybe_rsdp_phys_start),
Err(RsdpError::IncorrectSignature) => None,
Err(e) => {
warn!("Invalid RSDP found at {:#x}: {:?}", maybe_rsdp_phys_start, e);

None
}
}
}

rsdp_address
};
})
});

match rsdp_address {
Some(address) => {
Expand All @@ -120,8 +138,6 @@ impl Rsdp {
/// 2) The checksum is correct
/// 3) For Version 2.0+, that the extension checksum is correct
pub fn validate(&self) -> Result<(), RsdpError> {
const RSDP_V1_LENGTH: usize = 20;

// Check the signature
if self.signature != RSDP_SIGNATURE {
return Err(RsdpError::IncorrectSignature);
Expand Down

0 comments on commit a0db984

Please sign in to comment.