1use crate::TagType;
16use crate::tag::TagHeader;
17use core::slice;
18use core::str;
19use core::str::Utf8Error;
20use multiboot2_common::{MaybeDynSized, Tag};
21
22fn sum_bytes(parts: &[&[u8]]) -> u8 {
23 parts
24 .iter()
25 .flat_map(|part| part.iter().copied())
26 .fold(0u8, |acc, val| acc.wrapping_add(val))
27}
28
29fn compute_checksum(bytes: &[&[u8]]) -> u8 {
30 0u8.wrapping_sub(sum_bytes(bytes))
31}
32
33#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
35#[repr(C, align(8))]
36pub struct RsdpV1Tag {
37 header: TagHeader,
38 signature: [u8; 8],
39 checksum: u8,
40 oem_id: [u8; 6],
41 revision: u8,
42 rsdt_address: u32,
44}
45
46impl RsdpV1Tag {
47 pub const SIGNATURE: [u8; 8] = *b"RSD PTR ";
49
50 const BASE_SIZE: usize = size_of::<TagHeader>() + 16 + 4;
51
52 #[must_use]
54 pub fn new(oem_id: [u8; 6], revision: u8, rsdt_address: u32) -> Self {
55 let rsdt_address_bytes = rsdt_address.to_le_bytes();
56 let checksum_seed = [0u8];
57 let revision_bytes = [revision];
58 let checksum = compute_checksum(&[
59 Self::SIGNATURE.as_slice(),
60 checksum_seed.as_slice(),
61 oem_id.as_slice(),
62 revision_bytes.as_slice(),
63 rsdt_address_bytes.as_slice(),
64 ]);
65 Self {
66 header: TagHeader::new(Self::ID, Self::BASE_SIZE as u32),
67 signature: Self::SIGNATURE,
68 checksum,
69 oem_id,
70 revision,
71 rsdt_address,
72 }
73 }
74
75 pub const fn signature(&self) -> Result<&str, Utf8Error> {
79 str::from_utf8(&self.signature)
80 }
81
82 #[must_use]
84 pub fn checksum_is_valid(&self) -> bool {
85 let rsdp_ptr = (self as *const Self)
86 .cast::<u8>()
87 .wrapping_add(size_of::<TagHeader>());
89 let rsdp_len = Self::BASE_SIZE - size_of::<TagHeader>();
90 let bytes = unsafe { slice::from_raw_parts(rsdp_ptr, rsdp_len) };
93 sum_bytes(&[bytes]) == 0
94 }
95
96 pub const fn oem_id(&self) -> Result<&str, Utf8Error> {
98 str::from_utf8(&self.oem_id)
99 }
100
101 #[must_use]
103 pub const fn revision(&self) -> u8 {
104 self.revision
105 }
106
107 #[must_use]
109 pub const fn rsdt_address(&self) -> usize {
110 self.rsdt_address as usize
111 }
112}
113
114impl MaybeDynSized for RsdpV1Tag {
115 type Header = TagHeader;
116
117 const BASE_SIZE: usize = size_of::<Self>();
118}
119
120impl Tag for RsdpV1Tag {
121 type IDType = TagType;
122
123 const ID: TagType = TagType::AcpiV1;
124}
125
126#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
128#[repr(C, align(8))]
129pub struct RsdpV2Tag {
130 header: TagHeader,
131 signature: [u8; 8],
132 checksum: u8,
133 oem_id: [u8; 6],
134 revision: u8,
135 rsdt_address: u32,
136 length: u32,
137 xsdt_address: u64,
139 ext_checksum: u8,
140 _reserved: [u8; 3],
141}
142
143impl RsdpV2Tag {
144 pub const SIGNATURE: [u8; 8] = *b"RSD PTR ";
146
147 const BASE_SIZE: usize =
148 size_of::<TagHeader>() + 16 + 2 * size_of::<u32>() + size_of::<u64>() + 4;
149
150 #[must_use]
152 pub fn new(
153 oem_id: [u8; 6],
154 revision: u8,
155 rsdt_address: u32,
156 length: u32,
157 xsdt_address: u64,
158 ) -> Self {
159 let rsdt_address_bytes = rsdt_address.to_le_bytes();
160 let length_bytes = length.to_le_bytes();
161 let xsdt_address_bytes = xsdt_address.to_le_bytes();
162 let checksum_seed = [0u8];
163 let ext_checksum_seed = [0u8];
164 let reserved = [0u8; 3];
165 let revision_bytes = [revision];
166 let checksum = compute_checksum(&[
167 Self::SIGNATURE.as_slice(),
168 checksum_seed.as_slice(),
169 oem_id.as_slice(),
170 revision_bytes.as_slice(),
171 rsdt_address_bytes.as_slice(),
172 ]);
173 let ext_checksum = compute_checksum(&[
174 Self::SIGNATURE.as_slice(),
175 core::slice::from_ref(&checksum),
176 oem_id.as_slice(),
177 revision_bytes.as_slice(),
178 rsdt_address_bytes.as_slice(),
179 length_bytes.as_slice(),
180 xsdt_address_bytes.as_slice(),
181 ext_checksum_seed.as_slice(),
182 reserved.as_slice(),
183 ]);
184 Self {
185 header: TagHeader::new(Self::ID, Self::BASE_SIZE as u32),
186 signature: Self::SIGNATURE,
187 checksum,
188 oem_id,
189 revision,
190 rsdt_address,
191 length,
192 xsdt_address,
193 ext_checksum,
194 _reserved: [0; 3],
195 }
196 }
197
198 pub const fn signature(&self) -> Result<&str, Utf8Error> {
202 str::from_utf8(&self.signature)
203 }
204
205 #[must_use]
207 pub fn checksum_is_valid(&self) -> bool {
208 let bytes =
211 unsafe { slice::from_raw_parts((self as *const Self).cast::<u8>(), size_of::<Self>()) };
212 let length = self.length as usize;
213 if length != Self::BASE_SIZE - size_of::<TagHeader>() {
214 return false;
215 }
216 let ext_end = size_of::<TagHeader>() + length;
217 if ext_end > bytes.len() {
218 return false;
219 }
220
221 sum_bytes(&[&bytes[8..28]]) == 0 && sum_bytes(&[&bytes[8..ext_end]]) == 0
222 }
223
224 pub const fn oem_id(&self) -> Result<&str, Utf8Error> {
226 str::from_utf8(&self.oem_id)
227 }
228
229 #[must_use]
231 pub const fn revision(&self) -> u8 {
232 self.revision
233 }
234
235 #[must_use]
239 pub const fn xsdt_address(&self) -> usize {
240 self.xsdt_address as usize
241 }
242
243 #[must_use]
245 pub const fn ext_checksum(&self) -> u8 {
246 self.ext_checksum
247 }
248}
249
250impl MaybeDynSized for RsdpV2Tag {
251 type Header = TagHeader;
252
253 const BASE_SIZE: usize = size_of::<Self>();
254}
255
256impl Tag for RsdpV2Tag {
257 type IDType = TagType;
258
259 const ID: TagType = TagType::AcpiV2;
260}
261
262#[cfg(test)]
263mod tests {
264 use super::*;
265
266 #[test]
267 fn v1_new_computes_valid_checksum() {
268 let tag = RsdpV1Tag::new(*b"ABCDEF", 1, 0x1234_5678);
269 assert!(tag.checksum_is_valid());
270 }
271
272 #[test]
273 fn v2_new_computes_valid_checksums() {
274 let tag = RsdpV2Tag::new(*b"ABCDEF", 2, 0x1234_5678, 36, 0x1234_5678_9abc_def0);
275 assert!(tag.checksum_is_valid());
276 }
277
278 #[test]
279 fn v2_checksum_validation_rejects_corruption() {
280 let mut tag = RsdpV2Tag::new(*b"ABCDEF", 2, 0x1234_5678, 36, 0x1234_5678_9abc_def0);
281 tag.ext_checksum ^= 1;
282 assert!(!tag.checksum_is_valid());
283 }
284
285 #[test]
286 fn v2_checksum_validation_rejects_checksum_corruption() {
287 let mut tag = RsdpV2Tag::new(*b"ABCDEF", 2, 0x1234_5678, 36, 0x1234_5678_9abc_def0);
288 tag.checksum ^= 1;
289 assert!(!tag.checksum_is_valid());
290 }
291
292 #[test]
293 fn v2_checksum_validation_rejects_invalid_length() {
294 let mut tag = RsdpV2Tag::new(*b"ABCDEF", 2, 0x1234_5678, 36, 0x1234_5678_9abc_def0);
295 tag.length = 0;
296 assert!(!tag.checksum_is_valid());
297 }
298}