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