diff --git a/Cargo.lock b/Cargo.lock index d05c4c74e..9adea4216 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,5 +1,12 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. +[[package]] +name = "acpi_tables" +version = "0.1.0" +dependencies = [ + "vm-memory 0.1.0 (git+https://github.com/rust-vmm/vm-memory)", +] + [[package]] name = "aho-corasick" version = "0.6.10" @@ -1024,6 +1031,7 @@ dependencies = [ name = "vmm" version = "0.1.0" dependencies = [ + "acpi_tables 0.1.0", "arch 0.1.0", "devices 0.1.0", "epoll 4.1.0 (registry+https://github.com/rust-lang/crates.io-index)", diff --git a/acpi_tables/Cargo.toml b/acpi_tables/Cargo.toml new file mode 100644 index 000000000..08bbb56e8 --- /dev/null +++ b/acpi_tables/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "acpi_tables" +version = "0.1.0" +authors = ["The Cloud Hypervisor Authors"] +edition = "2018" + +[dependencies] +vm-memory = { git = "https://github.com/rust-vmm/vm-memory" } + diff --git a/acpi_tables/src/lib.rs b/acpi_tables/src/lib.rs new file mode 100644 index 000000000..30297337f --- /dev/null +++ b/acpi_tables/src/lib.rs @@ -0,0 +1,11 @@ +// Copyright © 2019 Intel Corporation +// +// SPDX-License-Identifier: Apache-2.0 +// + +pub mod rsdp; +pub mod sdt; + +fn generate_checksum(data: &[u8]) -> u8 { + (255 - data.iter().fold(0u8, |acc, x| acc.wrapping_add(*x))).wrapping_add(1) +} diff --git a/acpi_tables/src/rsdp.rs b/acpi_tables/src/rsdp.rs new file mode 100644 index 000000000..776160000 --- /dev/null +++ b/acpi_tables/src/rsdp.rs @@ -0,0 +1,67 @@ +// Copyright © 2019 Intel Corporation +// +// SPDX-License-Identifier: Apache-2.0 +// + +use vm_memory::ByteValued; + +#[repr(packed)] +#[derive(Clone, Copy, Default)] +pub struct RSDP { + pub signature: [u8; 8], + pub checksum: u8, + pub oem_id: [u8; 6], + pub revision: u8, + _rsdt_addr: u32, + pub length: u32, + pub xsdt_addr: u64, + pub extended_checksum: u8, + _reserved: [u8; 3], +} + +unsafe impl ByteValued for RSDP {} + +impl RSDP { + pub fn new(oem_id: [u8; 6], xsdt_addr: u64) -> Self { + let mut rsdp = RSDP { + signature: *b"RSD PTR ", + checksum: 0, + oem_id, + revision: 2, + _rsdt_addr: 0, + length: std::mem::size_of::() as u32, + xsdt_addr, + extended_checksum: 0, + _reserved: [0; 3], + }; + + rsdp.checksum = super::generate_checksum(&rsdp.as_slice()[0..19]); + rsdp.extended_checksum = super::generate_checksum(&rsdp.as_slice());; + rsdp + } + + pub fn len() -> usize { + std::mem::size_of::() + } +} + +#[cfg(test)] +mod tests { + use super::RSDP; + use vm_memory::bytes::ByteValued; + + #[test] + fn test_rsdp() { + let rsdp = RSDP::new(*b"CHYPER", 0xdead_beef); + let sum = rsdp + .as_slice() + .iter() + .fold(0u8, |acc, x| acc.wrapping_add(*x)); + assert_eq!(sum, 0); + let sum: u8 = rsdp + .as_slice() + .iter() + .fold(0u8, |acc, x| acc.wrapping_add(*x)); + assert_eq!(sum, 0); + } +} diff --git a/acpi_tables/src/sdt.rs b/acpi_tables/src/sdt.rs new file mode 100644 index 000000000..e055f44d1 --- /dev/null +++ b/acpi_tables/src/sdt.rs @@ -0,0 +1,129 @@ +// Copyright © 2019 Intel Corporation +// +// SPDX-License-Identifier: Apache-2.0 +// + +#[repr(packed)] +pub struct GenericAddress { + pub address_space_id: u8, + pub register_bit_width: u8, + pub register_bit_offset: u8, + pub access_size: u8, + pub address: u64, +} + +impl GenericAddress { + pub fn io_port_address(address: u16) -> Self { + GenericAddress { + address_space_id: 1, + register_bit_width: 8, + register_bit_offset: 0, + access_size: 1, + address: u64::from(address), + } + } +} + +pub struct SDT { + data: Vec, +} + +#[allow(clippy::len_without_is_empty)] +impl SDT { + pub fn new( + signature: [u8; 4], + length: u32, + revision: u8, + oem_id: [u8; 6], + oem_table: [u8; 8], + oem_revision: u32, + ) -> Self { + assert!(length >= 36); + + let mut data = Vec::with_capacity(length as usize); + data.extend_from_slice(&signature); + data.extend_from_slice(&length.to_le_bytes()); + data.push(revision); + data.push(0); // checksum + data.extend_from_slice(&oem_id); + data.extend_from_slice(&oem_table); + data.extend_from_slice(&oem_revision.to_le_bytes()); + data.extend_from_slice(b"CLDH"); + data.extend_from_slice(&0u32.to_le_bytes()); + assert_eq!(data.len(), 36); + + data.resize(length as usize, 0); + let mut sdt = SDT { data }; + + sdt.update_checksum(); + sdt + } + + pub fn update_checksum(&mut self) { + self.data[9] = 0; + let checksum = super::generate_checksum(self.data.as_slice()); + self.data[9] = checksum + } + + pub fn as_slice(&self) -> &[u8] { + &self.data.as_slice() + } + + pub fn append(&mut self, value: T) { + let orig_length = self.data.len(); + let new_length = orig_length + std::mem::size_of::(); + self.data.resize(new_length, 0); + self.write_u32(4, new_length as u32); + self.write(orig_length, value); + } + + /// Write a value at the given offset + pub fn write(&mut self, offset: usize, value: T) { + assert!((offset + (std::mem::size_of::() - 1)) < self.data.len()); + unsafe { + *(((self.data.as_mut_ptr() as usize) + offset) as *mut T) = value; + } + self.update_checksum(); + } + + pub fn write_u8(&mut self, offset: usize, val: u8) { + self.write(offset, val); + } + + pub fn write_u16(&mut self, offset: usize, val: u16) { + self.write(offset, val); + } + + pub fn write_u32(&mut self, offset: usize, val: u32) { + self.write(offset, val); + } + + pub fn write_u64(&mut self, offset: usize, val: u64) { + self.write(offset, val); + } + + pub fn len(&self) -> usize { + self.data.len() + } +} + +#[cfg(test)] +mod tests { + use super::SDT; + + #[test] + fn test_sdt() { + let mut sdt = SDT::new(*b"TEST", 40, 1, *b"CLOUDH", *b"TESTTEST", 1); + let sum: u8 = sdt + .as_slice() + .iter() + .fold(0u8, |acc, x| acc.wrapping_add(*x)); + assert_eq!(sum, 0); + sdt.write_u32(36, 0x12345678); + let sum: u8 = sdt + .as_slice() + .iter() + .fold(0u8, |acc, x| acc.wrapping_add(*x)); + assert_eq!(sum, 0); + } +} diff --git a/vmm/Cargo.toml b/vmm/Cargo.toml index bb58c0d5f..de137cfd8 100644 --- a/vmm/Cargo.toml +++ b/vmm/Cargo.toml @@ -5,6 +5,7 @@ authors = ["The Cloud Hypervisor Authors"] edition = "2018" [dependencies] +acpi_tables = { path = "../acpi_tables" } arch = { path = "../arch" } devices = { path = "../devices" } epoll = "4.1.0"