xref: /aosp_15_r20/external/crosvm/acpi_tables/src/sdt.rs (revision bb4ee6a4ae7042d18b07a98463b9c8b875e44b39)
1*bb4ee6a4SAndroid Build Coastguard Worker // Copyright 2020 The ChromiumOS Authors
2*bb4ee6a4SAndroid Build Coastguard Worker // Use of this source code is governed by a BSD-style license that can be
3*bb4ee6a4SAndroid Build Coastguard Worker // found in the LICENSE file.
4*bb4ee6a4SAndroid Build Coastguard Worker 
5*bb4ee6a4SAndroid Build Coastguard Worker use std::fs::File;
6*bb4ee6a4SAndroid Build Coastguard Worker use std::io::ErrorKind;
7*bb4ee6a4SAndroid Build Coastguard Worker use std::io::Read;
8*bb4ee6a4SAndroid Build Coastguard Worker use std::io::Result;
9*bb4ee6a4SAndroid Build Coastguard Worker use std::path::Path;
10*bb4ee6a4SAndroid Build Coastguard Worker 
11*bb4ee6a4SAndroid Build Coastguard Worker use zerocopy::AsBytes;
12*bb4ee6a4SAndroid Build Coastguard Worker use zerocopy::FromBytes;
13*bb4ee6a4SAndroid Build Coastguard Worker 
14*bb4ee6a4SAndroid Build Coastguard Worker /// SDT represents for System Description Table. The structure SDT is a
15*bb4ee6a4SAndroid Build Coastguard Worker /// generic format for creating various ACPI tables like DSDT/FADT/MADT.
16*bb4ee6a4SAndroid Build Coastguard Worker #[derive(Clone)]
17*bb4ee6a4SAndroid Build Coastguard Worker pub struct SDT {
18*bb4ee6a4SAndroid Build Coastguard Worker     data: Vec<u8>,
19*bb4ee6a4SAndroid Build Coastguard Worker }
20*bb4ee6a4SAndroid Build Coastguard Worker 
21*bb4ee6a4SAndroid Build Coastguard Worker pub const HEADER_LEN: u32 = 36;
22*bb4ee6a4SAndroid Build Coastguard Worker const LENGTH_OFFSET: usize = 4;
23*bb4ee6a4SAndroid Build Coastguard Worker const CHECKSUM_OFFSET: usize = 9;
24*bb4ee6a4SAndroid Build Coastguard Worker 
25*bb4ee6a4SAndroid Build Coastguard Worker #[allow(clippy::len_without_is_empty)]
26*bb4ee6a4SAndroid Build Coastguard Worker impl SDT {
27*bb4ee6a4SAndroid Build Coastguard Worker     /// Set up the ACPI table header at the front of the SDT.
28*bb4ee6a4SAndroid Build Coastguard Worker     /// The arguments correspond to the elements in the ACPI
29*bb4ee6a4SAndroid Build Coastguard Worker     /// table headers.
new( signature: [u8; 4], length: u32, revision: u8, oem_id: [u8; 6], oem_table: [u8; 8], oem_revision: u32, ) -> Self30*bb4ee6a4SAndroid Build Coastguard Worker     pub fn new(
31*bb4ee6a4SAndroid Build Coastguard Worker         signature: [u8; 4],
32*bb4ee6a4SAndroid Build Coastguard Worker         length: u32,
33*bb4ee6a4SAndroid Build Coastguard Worker         revision: u8,
34*bb4ee6a4SAndroid Build Coastguard Worker         oem_id: [u8; 6],
35*bb4ee6a4SAndroid Build Coastguard Worker         oem_table: [u8; 8],
36*bb4ee6a4SAndroid Build Coastguard Worker         oem_revision: u32,
37*bb4ee6a4SAndroid Build Coastguard Worker     ) -> Self {
38*bb4ee6a4SAndroid Build Coastguard Worker         // The length represents for the length of the entire table
39*bb4ee6a4SAndroid Build Coastguard Worker         // which includes this header. And the header is 36 bytes, so
40*bb4ee6a4SAndroid Build Coastguard Worker         // lenght should be >= 36. For the case who gives a number less
41*bb4ee6a4SAndroid Build Coastguard Worker         // than the header len, use the header len directly.
42*bb4ee6a4SAndroid Build Coastguard Worker         let len: u32 = if length < HEADER_LEN {
43*bb4ee6a4SAndroid Build Coastguard Worker             HEADER_LEN
44*bb4ee6a4SAndroid Build Coastguard Worker         } else {
45*bb4ee6a4SAndroid Build Coastguard Worker             length
46*bb4ee6a4SAndroid Build Coastguard Worker         };
47*bb4ee6a4SAndroid Build Coastguard Worker         let mut data = Vec::with_capacity(length as usize);
48*bb4ee6a4SAndroid Build Coastguard Worker         data.extend_from_slice(&signature);
49*bb4ee6a4SAndroid Build Coastguard Worker         data.extend_from_slice(&len.to_le_bytes());
50*bb4ee6a4SAndroid Build Coastguard Worker         data.push(revision);
51*bb4ee6a4SAndroid Build Coastguard Worker         data.push(0); // checksum
52*bb4ee6a4SAndroid Build Coastguard Worker         data.extend_from_slice(&oem_id);
53*bb4ee6a4SAndroid Build Coastguard Worker         data.extend_from_slice(&oem_table);
54*bb4ee6a4SAndroid Build Coastguard Worker         data.extend_from_slice(&oem_revision.to_le_bytes());
55*bb4ee6a4SAndroid Build Coastguard Worker         data.extend_from_slice(b"CROS");
56*bb4ee6a4SAndroid Build Coastguard Worker         data.extend_from_slice(&0u32.to_le_bytes());
57*bb4ee6a4SAndroid Build Coastguard Worker 
58*bb4ee6a4SAndroid Build Coastguard Worker         data.resize(length as usize, 0);
59*bb4ee6a4SAndroid Build Coastguard Worker         let mut sdt = SDT { data };
60*bb4ee6a4SAndroid Build Coastguard Worker 
61*bb4ee6a4SAndroid Build Coastguard Worker         sdt.update_checksum();
62*bb4ee6a4SAndroid Build Coastguard Worker         sdt
63*bb4ee6a4SAndroid Build Coastguard Worker     }
64*bb4ee6a4SAndroid Build Coastguard Worker 
65*bb4ee6a4SAndroid Build Coastguard Worker     /// Set up the ACPI table from file content. Verify file checksum.
from_file(path: &Path) -> Result<Self>66*bb4ee6a4SAndroid Build Coastguard Worker     pub fn from_file(path: &Path) -> Result<Self> {
67*bb4ee6a4SAndroid Build Coastguard Worker         let mut file = File::open(path)?;
68*bb4ee6a4SAndroid Build Coastguard Worker         let mut data = Vec::new();
69*bb4ee6a4SAndroid Build Coastguard Worker         file.read_to_end(&mut data)?;
70*bb4ee6a4SAndroid Build Coastguard Worker         let checksum = super::generate_checksum(data.as_slice());
71*bb4ee6a4SAndroid Build Coastguard Worker         if checksum == 0 {
72*bb4ee6a4SAndroid Build Coastguard Worker             Ok(SDT { data })
73*bb4ee6a4SAndroid Build Coastguard Worker         } else {
74*bb4ee6a4SAndroid Build Coastguard Worker             Err(ErrorKind::InvalidData.into())
75*bb4ee6a4SAndroid Build Coastguard Worker         }
76*bb4ee6a4SAndroid Build Coastguard Worker     }
77*bb4ee6a4SAndroid Build Coastguard Worker 
is_signature(&self, signature: &[u8; 4]) -> bool78*bb4ee6a4SAndroid Build Coastguard Worker     pub fn is_signature(&self, signature: &[u8; 4]) -> bool {
79*bb4ee6a4SAndroid Build Coastguard Worker         self.data[0..4] == *signature
80*bb4ee6a4SAndroid Build Coastguard Worker     }
81*bb4ee6a4SAndroid Build Coastguard Worker 
update_checksum(&mut self)82*bb4ee6a4SAndroid Build Coastguard Worker     fn update_checksum(&mut self) {
83*bb4ee6a4SAndroid Build Coastguard Worker         self.data[CHECKSUM_OFFSET] = 0;
84*bb4ee6a4SAndroid Build Coastguard Worker         let checksum = super::generate_checksum(self.data.as_slice());
85*bb4ee6a4SAndroid Build Coastguard Worker         self.data[CHECKSUM_OFFSET] = checksum;
86*bb4ee6a4SAndroid Build Coastguard Worker     }
87*bb4ee6a4SAndroid Build Coastguard Worker 
as_slice(&self) -> &[u8]88*bb4ee6a4SAndroid Build Coastguard Worker     pub fn as_slice(&self) -> &[u8] {
89*bb4ee6a4SAndroid Build Coastguard Worker         self.data.as_slice()
90*bb4ee6a4SAndroid Build Coastguard Worker     }
91*bb4ee6a4SAndroid Build Coastguard Worker 
append<T: AsBytes>(&mut self, value: T)92*bb4ee6a4SAndroid Build Coastguard Worker     pub fn append<T: AsBytes>(&mut self, value: T) {
93*bb4ee6a4SAndroid Build Coastguard Worker         self.data.extend_from_slice(value.as_bytes());
94*bb4ee6a4SAndroid Build Coastguard Worker         self.write(LENGTH_OFFSET, self.data.len() as u32);
95*bb4ee6a4SAndroid Build Coastguard Worker     }
96*bb4ee6a4SAndroid Build Coastguard Worker 
append_slice(&mut self, value: &[u8])97*bb4ee6a4SAndroid Build Coastguard Worker     pub fn append_slice(&mut self, value: &[u8]) {
98*bb4ee6a4SAndroid Build Coastguard Worker         self.data.extend_from_slice(value);
99*bb4ee6a4SAndroid Build Coastguard Worker         self.write(LENGTH_OFFSET, self.data.len() as u32);
100*bb4ee6a4SAndroid Build Coastguard Worker     }
101*bb4ee6a4SAndroid Build Coastguard Worker 
102*bb4ee6a4SAndroid Build Coastguard Worker     /// Read a value at the given offset
read<T: FromBytes + AsBytes + Default>(&self, offset: usize) -> T103*bb4ee6a4SAndroid Build Coastguard Worker     pub fn read<T: FromBytes + AsBytes + Default>(&self, offset: usize) -> T {
104*bb4ee6a4SAndroid Build Coastguard Worker         let value_len = std::mem::size_of::<T>();
105*bb4ee6a4SAndroid Build Coastguard Worker         T::read_from(
106*bb4ee6a4SAndroid Build Coastguard Worker             self.as_slice()
107*bb4ee6a4SAndroid Build Coastguard Worker                 .get(offset..offset + value_len)
108*bb4ee6a4SAndroid Build Coastguard Worker                 .unwrap_or(T::default().as_bytes()),
109*bb4ee6a4SAndroid Build Coastguard Worker         )
110*bb4ee6a4SAndroid Build Coastguard Worker         .unwrap()
111*bb4ee6a4SAndroid Build Coastguard Worker     }
112*bb4ee6a4SAndroid Build Coastguard Worker 
113*bb4ee6a4SAndroid Build Coastguard Worker     /// Write a value at the given offset
write<T: AsBytes>(&mut self, offset: usize, value: T)114*bb4ee6a4SAndroid Build Coastguard Worker     pub fn write<T: AsBytes>(&mut self, offset: usize, value: T) {
115*bb4ee6a4SAndroid Build Coastguard Worker         let value_len = std::mem::size_of::<T>();
116*bb4ee6a4SAndroid Build Coastguard Worker         if (offset + value_len) > self.data.len() {
117*bb4ee6a4SAndroid Build Coastguard Worker             return;
118*bb4ee6a4SAndroid Build Coastguard Worker         }
119*bb4ee6a4SAndroid Build Coastguard Worker         self.data[offset..offset + value_len].copy_from_slice(value.as_bytes());
120*bb4ee6a4SAndroid Build Coastguard Worker         self.update_checksum();
121*bb4ee6a4SAndroid Build Coastguard Worker     }
122*bb4ee6a4SAndroid Build Coastguard Worker 
len(&self) -> usize123*bb4ee6a4SAndroid Build Coastguard Worker     pub fn len(&self) -> usize {
124*bb4ee6a4SAndroid Build Coastguard Worker         self.data.len()
125*bb4ee6a4SAndroid Build Coastguard Worker     }
126*bb4ee6a4SAndroid Build Coastguard Worker }
127*bb4ee6a4SAndroid Build Coastguard Worker 
128*bb4ee6a4SAndroid Build Coastguard Worker #[cfg(test)]
129*bb4ee6a4SAndroid Build Coastguard Worker mod tests {
130*bb4ee6a4SAndroid Build Coastguard Worker     use std::io::Write;
131*bb4ee6a4SAndroid Build Coastguard Worker 
132*bb4ee6a4SAndroid Build Coastguard Worker     use tempfile::NamedTempFile;
133*bb4ee6a4SAndroid Build Coastguard Worker 
134*bb4ee6a4SAndroid Build Coastguard Worker     use super::SDT;
135*bb4ee6a4SAndroid Build Coastguard Worker 
136*bb4ee6a4SAndroid Build Coastguard Worker     #[test]
test_sdt()137*bb4ee6a4SAndroid Build Coastguard Worker     fn test_sdt() {
138*bb4ee6a4SAndroid Build Coastguard Worker         let mut sdt = SDT::new(*b"TEST", 40, 1, *b"CROSVM", *b"TESTTEST", 1);
139*bb4ee6a4SAndroid Build Coastguard Worker         let sum: u8 = sdt
140*bb4ee6a4SAndroid Build Coastguard Worker             .as_slice()
141*bb4ee6a4SAndroid Build Coastguard Worker             .iter()
142*bb4ee6a4SAndroid Build Coastguard Worker             .fold(0u8, |acc, x| acc.wrapping_add(*x));
143*bb4ee6a4SAndroid Build Coastguard Worker         assert_eq!(sum, 0);
144*bb4ee6a4SAndroid Build Coastguard Worker         sdt.write(36, 0x12345678_u32);
145*bb4ee6a4SAndroid Build Coastguard Worker         let sum: u8 = sdt
146*bb4ee6a4SAndroid Build Coastguard Worker             .as_slice()
147*bb4ee6a4SAndroid Build Coastguard Worker             .iter()
148*bb4ee6a4SAndroid Build Coastguard Worker             .fold(0u8, |acc, x| acc.wrapping_add(*x));
149*bb4ee6a4SAndroid Build Coastguard Worker         assert_eq!(sum, 0);
150*bb4ee6a4SAndroid Build Coastguard Worker     }
151*bb4ee6a4SAndroid Build Coastguard Worker 
152*bb4ee6a4SAndroid Build Coastguard Worker     #[test]
test_sdt_read_write() -> Result<(), std::io::Error>153*bb4ee6a4SAndroid Build Coastguard Worker     fn test_sdt_read_write() -> Result<(), std::io::Error> {
154*bb4ee6a4SAndroid Build Coastguard Worker         let temp_file = NamedTempFile::new()?;
155*bb4ee6a4SAndroid Build Coastguard Worker         let expected_sdt = SDT::new(*b"TEST", 40, 1, *b"CROSVM", *b"TESTTEST", 1);
156*bb4ee6a4SAndroid Build Coastguard Worker 
157*bb4ee6a4SAndroid Build Coastguard Worker         // Write SDT to file.
158*bb4ee6a4SAndroid Build Coastguard Worker         {
159*bb4ee6a4SAndroid Build Coastguard Worker             let mut writer = temp_file.as_file();
160*bb4ee6a4SAndroid Build Coastguard Worker             writer.write_all(expected_sdt.as_slice())?;
161*bb4ee6a4SAndroid Build Coastguard Worker         }
162*bb4ee6a4SAndroid Build Coastguard Worker 
163*bb4ee6a4SAndroid Build Coastguard Worker         // Read it back and verify.
164*bb4ee6a4SAndroid Build Coastguard Worker         let actual_sdt = SDT::from_file(temp_file.path())?;
165*bb4ee6a4SAndroid Build Coastguard Worker         assert!(actual_sdt.is_signature(b"TEST"));
166*bb4ee6a4SAndroid Build Coastguard Worker         assert_eq!(actual_sdt.as_slice(), expected_sdt.as_slice());
167*bb4ee6a4SAndroid Build Coastguard Worker         Ok(())
168*bb4ee6a4SAndroid Build Coastguard Worker     }
169*bb4ee6a4SAndroid Build Coastguard Worker }
170