kernel_acpi/
rsdp.rs

1//! # RSDP/XSDP (Root/Extended System Description Pointer)
2
3use crate::{PhysMapRo, sum};
4
5pub struct AcpiRoots {
6    pub rsdp_addr: u64,
7    pub xsdt_addr: Option<u64>,
8    pub rsdt_addr: Option<u64>,
9}
10
11/// ACPI 1.0 Root System Description Pointer (RSDP)
12#[derive(Clone)]
13#[repr(C, packed)]
14struct Rsdp {
15    pub(crate) signature: [u8; 8], // "RSD PTR "
16    checksum: u8,                  // sum of first 20 bytes == 0
17    oem_id: [u8; 6],
18    pub(crate) revision: u8, // 0 for ACPI 1.0
19    pub(crate) rsdt_addr: u32,
20}
21
22/// ACPI 2.0 Extended System Description Pointer (XSDP)
23#[derive(Clone)]
24#[repr(C, packed)]
25struct Xsdp {
26    signature: [u8; 8], // "RSD PTR "
27    checksum: u8,       // sum of first 20 bytes == 0
28    oem_id: [u8; 6],
29    revision: u8, // 2 for ACPI 2.0
30    _deprecated: u32,
31    pub(crate) length: u32,
32    pub(crate) xsdt_addr: u64,
33    ext_checksum: u8, // checksum of entire table
34    reserved: [u8; 3],
35}
36
37impl AcpiRoots {
38    /// Validate the RSDP/XSDP from the physical address.
39    ///
40    /// # Safety
41    /// This function validates that the provided address is non-zero (i.e., not `null`).
42    /// It validates the supported ACPI 1.0/2.0 variants by revision and checksum/extended checksum.
43    #[must_use]
44    #[allow(clippy::similar_names)]
45    pub unsafe fn parse(map: &impl PhysMapRo, rsdp_addr: u64) -> Option<Self> {
46        if rsdp_addr == 0 {
47            return None;
48        }
49
50        unsafe {
51            let v1 = map.map_ro(rsdp_addr, size_of::<Rsdp>());
52            if &v1[0..8] != b"RSD PTR " {
53                return None;
54            }
55            if sum(&v1[0..20]) != 0 {
56                return None;
57            }
58
59            let v1p = &*v1.as_ptr().cast::<Rsdp>();
60            let rsdt_addr = Some(u64::from(v1p.rsdt_addr));
61
62            if v1p.revision >= 2 {
63                // Need full v2 to read length + xsdt
64                let min_v2 = core::mem::size_of::<Xsdp>();
65                let v2 = map.map_ro(rsdp_addr, min_v2);
66                let v2p = &*v2.as_ptr().cast::<Xsdp>();
67                let len = v2p.length as usize;
68                let full = map.map_ro(rsdp_addr, len);
69                if sum(full) != 0 {
70                    return None;
71                }
72                return Some(Self {
73                    rsdp_addr,
74                    xsdt_addr: Some(v2p.xsdt_addr),
75                    rsdt_addr,
76                });
77            }
78
79            Some(Self {
80                rsdp_addr,
81                xsdt_addr: None,
82                rsdt_addr,
83            })
84        }
85    }
86}