1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107
//! Definitions for the ACPI RSDT and XSDT system tables.
//!
//! RSDT is the Root System Descriptor Table, whereas
//! XSDT is the Extended System Descriptor Table.
//! They are identical except that the XSDT uses 64-bit physical addresses
//! to point to other ACPI SDTs, while the RSDT uses 32-bit physical addresses.
//!
//! # Note about alignment
//! Technically the RSDT contains a list of 32-bit addresses (`u32`) and the XSDT has 64-bit addresses (`u64`),
//! but the ACPI tables often aren't aligned to 4-byte and 8-byte addresses.
//! This lack of alignment causes problems with Rust's slice type, which requires proper alignment.
//! Thus, we store them as slices of individual bytes (`u8`) and calculate the physical addresses
//! on demand when requested in the `RsdtXsdt::addresses()` iterator function.
#![no_std]
use core::mem::size_of;
use memory::PhysicalAddress;
use sdt::{Sdt, SDT_SIZE_IN_BYTES};
use acpi_table::{AcpiSignature, AcpiTables};
pub const RSDT_SIGNATURE: &[u8; 4] = b"RSDT";
pub const XSDT_SIGNATURE: &[u8; 4] = b"XSDT";
/// The handler for parsing RSDT/XSDT tables and adding them to the ACPI tables list.
pub fn handle(
acpi_tables: &mut AcpiTables,
signature: AcpiSignature,
length: usize,
phys_addr: PhysicalAddress
) -> Result<(), &'static str> {
// See the crate-level docs for an explanation of why this is always `u8`.
let slice_element_size = match &signature {
RSDT_SIGNATURE => size_of::<u8>(),
XSDT_SIGNATURE => size_of::<u8>(),
_ => return Err("unexpected ACPI table signature (not RSDT or XSDT)"),
};
let slice_paddr = phys_addr + SDT_SIZE_IN_BYTES; // the array of addresses starts right after the SDT header.
let num_addrs = (length - SDT_SIZE_IN_BYTES) / slice_element_size;
acpi_tables.add_table_location(signature, phys_addr, Some((slice_paddr, num_addrs)))
}
/// The Root/Extended System Descriptor Table, RSDT or XSDT.
/// This table primarily contains an array of physical addresses
/// where other ACPI SDTs can be found.
///
/// Use the `addresses()` method to obtain an [`Iterator`] over those physical addresses.
pub struct RsdtXsdt<'t>(RsdtOrXsdt<'t>);
enum RsdtOrXsdt<'t> {
/// RSDT, which contains 32-bit addresses.
Regular(Rsdt<'t>),
/// XSDT, which contains 64-bit addresses.
Extended(Xsdt<'t>),
}
type Rsdt<'t> = (&'t Sdt, &'t [u8]);
type Xsdt<'t> = (&'t Sdt, &'t [u8]);
impl<'t> RsdtXsdt<'t> {
/// Finds the RSDT or XSDT in the given `AcpiTables` and returns a reference to it.
pub fn get(acpi_tables: &'t AcpiTables) -> Option<RsdtXsdt<'t>> {
if let (Ok(sdt), Ok(addrs)) = (acpi_tables.table::<Sdt>(RSDT_SIGNATURE), acpi_tables.table_slice::<u8>(RSDT_SIGNATURE)) {
Some(RsdtXsdt(RsdtOrXsdt::Regular((sdt, addrs))))
}
else if let (Ok(sdt), Ok(addrs)) = (acpi_tables.table::<Sdt>(XSDT_SIGNATURE), acpi_tables.table_slice::<u8>(XSDT_SIGNATURE)) {
Some(RsdtXsdt(RsdtOrXsdt::Extended((sdt, addrs))))
}
else {
None
}
}
/// Returns a reference to the SDT header of this RSDT or XSDT.
pub fn sdt(&self) -> &Sdt {
match &self.0 {
RsdtOrXsdt::Regular(ref r) => r.0,
RsdtOrXsdt::Extended(ref x) => x.0,
}
}
/// Returns an [`Iterator`] over the `PhysicalAddress`es of the SDT entries
/// included in this RSDT or XSDT.
pub fn addresses(&self) -> impl Iterator<Item = PhysicalAddress> + '_ {
let mut rsdt_iter = None;
let mut xsdt_iter = None;
match &self.0 {
RsdtOrXsdt::Regular(ref rsdt) => rsdt_iter = Some(
rsdt.1.chunks_exact(size_of::<u32>()).map(|bytes| {
let arr = [bytes[0], bytes[1], bytes[2], bytes[3]];
PhysicalAddress::new_canonical(u32::from_le_bytes(arr) as usize)
})
),
RsdtOrXsdt::Extended(ref xsdt) => xsdt_iter = Some(
xsdt.1.chunks_exact(size_of::<u64>()).map(|bytes| {
let arr = [bytes[0], bytes[1], bytes[2], bytes[3], bytes[4], bytes[5], bytes[6], bytes[7]];
PhysicalAddress::new_canonical(usize::from_le_bytes(arr))
})
),
}
rsdt_iter.into_iter().flatten().chain(xsdt_iter.into_iter().flatten())
}
}