multiboot2/
rsdp.rs

1//! Module for [`RsdpV1Tag`] and  [`RsdpV2Tag`].
2
3//! Module for RSDP/ACPI. RSDP (Root System Description Pointer) is a data structure used in the
4//! ACPI programming interface.
5//!
6//! The tag that the bootloader passes will depend on the ACPI version the hardware supports.
7//! For ACPI Version 1.0, a `RsdpV1Tag` will be provided, which can be accessed from
8//! `BootInformation` using the `rsdp_v1_tag` function. For subsequent versions of ACPI, a
9//! `RsdpV2Tag` will be provided, which can be accessed with `rsdp_v2_tag`.
10//!
11//! Even though the bootloader should give the address of the real RSDP/XSDT, the checksum and
12//! signature should be manually verified.
13//!
14
15use crate::TagType;
16use crate::tag::TagHeader;
17#[cfg(feature = "builder")]
18use core::mem::size_of;
19use core::slice;
20use core::str;
21use core::str::Utf8Error;
22use multiboot2_common::{MaybeDynSized, Tag};
23
24const RSDPV1_LENGTH: usize = 20;
25
26/// This tag contains a copy of RSDP as defined per ACPI 1.0 specification.
27#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
28#[repr(C, align(8))]
29pub struct RsdpV1Tag {
30    header: TagHeader,
31    signature: [u8; 8],
32    checksum: u8,
33    oem_id: [u8; 6],
34    revision: u8,
35    rsdt_address: u32, // This is the PHYSICAL address of the RSDT
36}
37
38impl RsdpV1Tag {
39    /// Signature of RSDP v1.
40    pub const SIGNATURE: [u8; 8] = *b"RSD PTR ";
41
42    const BASE_SIZE: usize = size_of::<TagHeader>() + 16 + 4;
43
44    /// Constructs a new tag.
45    #[must_use]
46    pub fn new(checksum: u8, oem_id: [u8; 6], revision: u8, rsdt_address: u32) -> Self {
47        Self {
48            header: TagHeader::new(Self::ID, Self::BASE_SIZE as u32),
49            signature: Self::SIGNATURE,
50            checksum,
51            oem_id,
52            revision,
53            rsdt_address,
54        }
55    }
56
57    /// The "RSD PTR " marker signature.
58    ///
59    /// This is originally a 8-byte C string (not null terminated!) that must contain "RSD PTR "
60    pub const fn signature(&self) -> Result<&str, Utf8Error> {
61        str::from_utf8(&self.signature)
62    }
63
64    /// Validation of the RSDPv1 checksum
65    #[must_use]
66    pub fn checksum_is_valid(&self) -> bool {
67        let bytes =
68            unsafe { slice::from_raw_parts(self as *const _ as *const u8, RSDPV1_LENGTH + 8) };
69        bytes[8..]
70            .iter()
71            .fold(0u8, |acc, val| acc.wrapping_add(*val))
72            == 0
73    }
74
75    /// An OEM-supplied string that identifies the OEM.
76    pub const fn oem_id(&self) -> Result<&str, Utf8Error> {
77        str::from_utf8(&self.oem_id)
78    }
79
80    /// The revision of the ACPI.
81    #[must_use]
82    pub const fn revision(&self) -> u8 {
83        self.revision
84    }
85
86    /// The physical (I repeat: physical) address of the RSDT table.
87    #[must_use]
88    pub const fn rsdt_address(&self) -> usize {
89        self.rsdt_address as usize
90    }
91}
92
93impl MaybeDynSized for RsdpV1Tag {
94    type Header = TagHeader;
95
96    const BASE_SIZE: usize = size_of::<Self>();
97
98    fn dst_len(_: &TagHeader) {}
99}
100
101impl Tag for RsdpV1Tag {
102    type IDType = TagType;
103
104    const ID: TagType = TagType::AcpiV1;
105}
106
107/// This tag contains a copy of RSDP as defined per ACPI 2.0 or later specification.
108#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
109#[repr(C, align(8))]
110pub struct RsdpV2Tag {
111    header: TagHeader,
112    signature: [u8; 8],
113    checksum: u8,
114    oem_id: [u8; 6],
115    revision: u8,
116    rsdt_address: u32,
117    length: u32,
118    xsdt_address: u64,
119    // This is the PHYSICAL address of the XSDT
120    ext_checksum: u8,
121    _reserved: [u8; 3],
122}
123
124impl RsdpV2Tag {
125    /// Signature of RSDP v2.
126    pub const SIGNATURE: [u8; 8] = *b"RSD PTR ";
127
128    const BASE_SIZE: usize =
129        size_of::<TagHeader>() + 16 + 2 * size_of::<u32>() + size_of::<u64>() + 4;
130
131    /// Constructs a new tag.
132    #[allow(clippy::too_many_arguments)]
133    #[must_use]
134    pub fn new(
135        checksum: u8,
136        oem_id: [u8; 6],
137        revision: u8,
138        rsdt_address: u32,
139        length: u32,
140        xsdt_address: u64,
141        ext_checksum: u8,
142    ) -> Self {
143        Self {
144            header: TagHeader::new(Self::ID, Self::BASE_SIZE as u32),
145            signature: Self::SIGNATURE,
146            checksum,
147            oem_id,
148            revision,
149            rsdt_address,
150            length,
151            xsdt_address,
152            ext_checksum,
153            _reserved: [0; 3],
154        }
155    }
156
157    /// The "RSD PTR " marker signature.
158    ///
159    /// This is originally a 8-byte C string (not null terminated!) that must contain "RSD PTR ".
160    pub const fn signature(&self) -> Result<&str, Utf8Error> {
161        str::from_utf8(&self.signature)
162    }
163
164    /// Validation of the RSDPv2 extended checksum
165    #[must_use]
166    pub fn checksum_is_valid(&self) -> bool {
167        let bytes = unsafe {
168            slice::from_raw_parts(self as *const _ as *const u8, self.length as usize + 8)
169        };
170        bytes[8..]
171            .iter()
172            .fold(0u8, |acc, val| acc.wrapping_add(*val))
173            == 0
174    }
175
176    /// An OEM-supplied string that identifies the OEM.
177    pub const fn oem_id(&self) -> Result<&str, Utf8Error> {
178        str::from_utf8(&self.oem_id)
179    }
180
181    /// The revision of the ACPI.
182    #[must_use]
183    pub const fn revision(&self) -> u8 {
184        self.revision
185    }
186
187    /// Physical address of the XSDT table.
188    ///
189    /// On x86, this is truncated from 64-bit to 32-bit.
190    #[must_use]
191    pub const fn xsdt_address(&self) -> usize {
192        self.xsdt_address as usize
193    }
194
195    /// This field is used to calculate the checksum of the entire table, including both checksum fields.
196    #[must_use]
197    pub const fn ext_checksum(&self) -> u8 {
198        self.ext_checksum
199    }
200}
201
202impl MaybeDynSized for RsdpV2Tag {
203    type Header = TagHeader;
204
205    const BASE_SIZE: usize = size_of::<Self>();
206
207    fn dst_len(_: &TagHeader) {}
208}
209
210impl Tag for RsdpV2Tag {
211    type IDType = TagType;
212
213    const ID: TagType = TagType::AcpiV2;
214}