#![no_std]
#![deny(unsafe_op_in_unsafe_fn)]
#[cfg(test)]
extern crate std;
pub mod handler;
use core::{mem, ops::Range, slice, str};
use handler::{AcpiHandler, PhysicalMapping};
use log::warn;
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub enum RsdpError {
NoValidRsdp,
IncorrectSignature,
InvalidOemId,
InvalidChecksum,
}
const RSDP_V1_LENGTH: usize = 20;
const RSDP_V2_EXT_LENGTH: usize = mem::size_of::<Rsdp>() - RSDP_V1_LENGTH;
#[derive(Clone, Copy, Debug)]
#[repr(C, packed)]
pub struct Rsdp {
signature: [u8; 8],
checksum: u8,
oem_id: [u8; 6],
revision: u8,
rsdt_address: u32,
length: u32,
xsdt_address: u64,
ext_checksum: u8,
reserved: [u8; 3],
}
impl Rsdp {
pub unsafe fn search_for_on_bios<H>(handler: H) -> Result<PhysicalMapping<H, Rsdp>, RsdpError>
where
H: AcpiHandler,
{
let rsdp_address = find_search_areas(handler.clone()).iter().find_map(|area| {
let mapping = unsafe {
handler.map_physical_region::<u8>(area.start, area.end - area.start + RSDP_V2_EXT_LENGTH)
};
let extended_area_bytes =
unsafe { slice::from_raw_parts(mapping.virtual_start().as_ptr(), mapping.region_length()) };
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();
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
}
}
})
});
match rsdp_address {
Some(address) => {
let rsdp_mapping = unsafe { handler.map_physical_region::<Rsdp>(address, mem::size_of::<Rsdp>()) };
Ok(rsdp_mapping)
}
None => Err(RsdpError::NoValidRsdp),
}
}
pub fn validate(&self) -> Result<(), RsdpError> {
if self.signature != RSDP_SIGNATURE {
return Err(RsdpError::IncorrectSignature);
}
if str::from_utf8(&self.oem_id).is_err() {
return Err(RsdpError::InvalidOemId);
}
let length = if self.revision > 0 {
self.length as usize
} else {
RSDP_V1_LENGTH
};
let bytes = unsafe { slice::from_raw_parts(self as *const Rsdp as *const u8, length) };
let sum = bytes.iter().fold(0u8, |sum, &byte| sum.wrapping_add(byte));
if sum != 0 {
return Err(RsdpError::InvalidChecksum);
}
Ok(())
}
pub fn signature(&self) -> [u8; 8] {
self.signature
}
pub fn checksum(&self) -> u8 {
self.checksum
}
pub fn oem_id(&self) -> &str {
str::from_utf8(&self.oem_id).unwrap()
}
pub fn revision(&self) -> u8 {
self.revision
}
pub fn rsdt_address(&self) -> u32 {
self.rsdt_address
}
pub fn length(&self) -> u32 {
assert!(self.revision > 0, "Tried to read extended RSDP field with ACPI Version 1.0");
self.length
}
pub fn xsdt_address(&self) -> u64 {
assert!(self.revision > 0, "Tried to read extended RSDP field with ACPI Version 1.0");
self.xsdt_address
}
pub fn ext_checksum(&self) -> u8 {
assert!(self.revision > 0, "Tried to read extended RSDP field with ACPI Version 1.0");
self.ext_checksum
}
}
pub fn find_search_areas<H>(handler: H) -> [Range<usize>; 2]
where
H: AcpiHandler,
{
let ebda_start_mapping =
unsafe { handler.map_physical_region::<u16>(EBDA_START_SEGMENT_PTR, mem::size_of::<u16>()) };
let ebda_start = (*ebda_start_mapping as usize) << 4;
[
RSDP_BIOS_AREA_START..(RSDP_BIOS_AREA_END + 1),
if (EBDA_EARLIEST_START..EBDA_END).contains(&ebda_start) {
ebda_start..ebda_start + 1024
} else {
EBDA_EARLIEST_START..(EBDA_END + 1)
},
]
}
const EBDA_START_SEGMENT_PTR: usize = 0x40e;
const EBDA_EARLIEST_START: usize = 0x80000;
const EBDA_END: usize = 0x9ffff;
const RSDP_BIOS_AREA_START: usize = 0xe0000;
const RSDP_BIOS_AREA_END: usize = 0xfffff;
const RSDP_SIGNATURE: [u8; 8] = *b"RSD PTR ";