diff --git a/Cargo.lock b/Cargo.lock index 124d0451b3..7a9a5c4c1a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -30,6 +30,7 @@ dependencies = [ name = "arch" version = "0.1.0" dependencies = [ + "acpi_tables 0.1.0", "devices 0.1.0", "io_jail 0.1.0", "kernel_cmdline 0.1.0", diff --git a/acpi_tables/src/sdt.rs b/acpi_tables/src/sdt.rs index 0ea61a0554..96f4d0fde2 100644 --- a/acpi_tables/src/sdt.rs +++ b/acpi_tables/src/sdt.rs @@ -2,6 +2,10 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +use std::fs::File; +use std::io::{ErrorKind, Read, Result}; +use std::path::PathBuf; + use data_model::DataInit; /// SDT represents for System Description Table. The structure SDT is a @@ -54,6 +58,23 @@ impl SDT { sdt } + /// Set up the ACPI table from file content. Verify file checksum. + pub fn from_file(path: &PathBuf) -> Result { + let mut file = File::open(path)?; + let mut data = Vec::new(); + file.read_to_end(&mut data)?; + let checksum = super::generate_checksum(data.as_slice()); + if checksum == 0 { + Ok(SDT { data }) + } else { + Err(ErrorKind::InvalidData.into()) + } + } + + pub fn is_signature(&self, signature: &[u8; 4]) -> bool { + self.data[0..4] == *signature + } + fn update_checksum(&mut self) { self.data[CHECKSUM_OFFSET] = 0; let checksum = super::generate_checksum(self.data.as_slice()); diff --git a/arch/Cargo.toml b/arch/Cargo.toml index 6b4e529ebf..68b40a3d68 100644 --- a/arch/Cargo.toml +++ b/arch/Cargo.toml @@ -5,6 +5,7 @@ authors = ["The Chromium OS Authors"] edition = "2018" [dependencies] +acpi_tables = { path = "../acpi_tables" } devices = { path = "../devices" } io_jail = { path = "../io_jail" } kernel_cmdline = { path = "../kernel_cmdline" } diff --git a/arch/src/lib.rs b/arch/src/lib.rs index ba09fa2907..5aa8c67872 100644 --- a/arch/src/lib.rs +++ b/arch/src/lib.rs @@ -16,6 +16,7 @@ use std::os::unix::io::AsRawFd; use std::path::PathBuf; use std::sync::Arc; +use acpi_tables::sdt::SDT; use devices::split_irqchip_common::GsiRelay; use devices::virtio::VirtioDevice; use devices::{ @@ -57,6 +58,7 @@ pub struct VmComponents { pub initrd_image: Option, pub extra_kernel_params: Vec, pub wayland_dmabuf: bool, + pub acpi_sdts: Vec, } /// Holds the elements needed to run a Linux VM. Created by `build_vm`. diff --git a/src/crosvm.rs b/src/crosvm.rs index 33ed23659e..1ea7e0d0c3 100644 --- a/src/crosvm.rs +++ b/src/crosvm.rs @@ -204,6 +204,7 @@ pub struct Config { pub vfio: Vec, pub video_dec: bool, pub video_enc: bool, + pub acpi_tables: Vec, } impl Default for Config { @@ -254,6 +255,7 @@ impl Default for Config { vfio: Vec::new(), video_dec: false, video_enc: false, + acpi_tables: Vec::new(), } } } diff --git a/src/linux.rs b/src/linux.rs index e95f3720ac..fb463c2b2f 100644 --- a/src/linux.rs +++ b/src/linux.rs @@ -26,6 +26,8 @@ use std::time::Duration; use libc::{self, c_int, gid_t, uid_t}; +use acpi_tables::sdt::SDT; + #[cfg(feature = "gpu")] use devices::virtio::EventDevice; use devices::virtio::{self, Console, VirtioDevice}; @@ -109,6 +111,7 @@ pub enum Error { LoadKernel(Box), MemoryTooLarge, NetDeviceNew(virtio::NetError), + OpenAcpiTable(PathBuf, io::Error), OpenAndroidFstab(PathBuf, io::Error), OpenBios(PathBuf, io::Error), OpenInitrd(PathBuf, io::Error), @@ -196,6 +199,7 @@ impl Display for Error { LoadKernel(e) => write!(f, "failed to load kernel: {}", e), MemoryTooLarge => write!(f, "requested memory size too large"), NetDeviceNew(e) => write!(f, "failed to set up virtio networking: {}", e), + OpenAcpiTable(p, e) => write!(f, "failed to open ACPI file {}: {}", p.display(), e), OpenAndroidFstab(p, e) => write!( f, "failed to open android fstab file {}: {}", @@ -1712,6 +1716,11 @@ pub fn run_config(cfg: Config) -> Result<()> { initrd_image, extra_kernel_params: cfg.params.clone(), wayland_dmabuf: cfg.wayland_dmabuf, + acpi_sdts: cfg + .acpi_tables + .iter() + .map(|path| SDT::from_file(path).map_err(|e| Error::OpenAcpiTable(path.clone(), e))) + .collect::>>()?, }; let control_server_socket = match &cfg.socket_path { diff --git a/src/main.rs b/src/main.rs index 61fcd48e90..e8a6e6c2b6 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1232,6 +1232,23 @@ fn set_argument(cfg: &mut Config, name: &str, value: Option<&str>) -> argument:: "video-encoder" => { cfg.video_enc = true; } + "acpi-table" => { + let acpi_table = PathBuf::from(value.unwrap()); + if !acpi_table.exists() { + return Err(argument::Error::InvalidValue { + value: value.unwrap().to_owned(), + expected: String::from("the acpi-table path does not exist"), + }); + } + if !acpi_table.is_file() { + return Err(argument::Error::InvalidValue { + value: value.unwrap().to_owned(), + expected: String::from("the acpi-table path should be a file"), + }); + } + + cfg.acpi_tables.push(acpi_table); + } "help" => return Err(argument::Error::PrintHelp), _ => unreachable!(), @@ -1406,6 +1423,7 @@ writeback=BOOL - Indicates whether the VM can use writeback caching (default: fa Argument::flag("video-decoder", "(EXPERIMENTAL) enable virtio-video decoder device"), #[cfg(feature = "video-encoder")] Argument::flag("video-encoder", "(EXPERIMENTAL) enable virtio-video encoder device"), + Argument::value("acpi-table", "PATH", "Path to user provided ACPI table"), Argument::short_flag('h', "help", "Print help message.")]; let mut cfg = Config::default(); diff --git a/x86_64/src/acpi.rs b/x86_64/src/acpi.rs index c1d92eec14..e6fc15af99 100644 --- a/x86_64/src/acpi.rs +++ b/x86_64/src/acpi.rs @@ -8,6 +8,8 @@ use sys_util::{GuestAddress, GuestMemory}; pub struct ACPIDevResource { pub amls: Vec, pub pm_iobase: u64, + /// Additional system descriptor tables. + pub sdts: Vec, } #[repr(C)] @@ -102,17 +104,35 @@ pub fn create_acpi_tables( num_cpus: u8, sci_irq: u32, acpi_dev_resource: ACPIDevResource, -) -> GuestAddress { +) -> Option { // RSDP is at the HI RSDP WINDOW let rsdp_offset = GuestAddress(super::ACPI_HI_RSDP_WINDOW_BASE); + let mut offset = rsdp_offset.checked_add(RSDP::len() as u64)?; let mut tables: Vec = Vec::new(); + let mut dsdt_offset: Option = None; + + // User supplied System Description Tables, e.g. SSDT. + for sdt in acpi_dev_resource.sdts.iter() { + guest_mem.write_at_addr(sdt.as_slice(), offset).ok()?; + if sdt.is_signature(b"DSDT") { + dsdt_offset = Some(offset); + } else { + tables.push(offset.0); + } + offset = offset.checked_add(sdt.len() as u64)?; + } // DSDT - let dsdt = create_dsdt_table(acpi_dev_resource.amls); - let dsdt_offset = rsdp_offset.checked_add(RSDP::len() as u64).unwrap(); - guest_mem - .write_at_addr(dsdt.as_slice(), dsdt_offset) - .expect("Error writing DSDT table"); + let dsdt_offset = match dsdt_offset { + Some(dsdt_offset) => dsdt_offset, + None => { + let dsdt_offset = offset; + let dsdt = create_dsdt_table(acpi_dev_resource.amls); + guest_mem.write_at_addr(dsdt.as_slice(), offset).ok()?; + offset = offset.checked_add(dsdt.len() as u64)?; + dsdt_offset + } + }; // FACP aka FADT // Revision 6 of the ACPI FADT table is 276 bytes long @@ -160,11 +180,9 @@ pub fn create_acpi_tables( facp.write(FADT_FIELD_HYPERVISOR_ID, *b"CROSVM"); // Hypervisor Vendor Identity - let facp_offset = dsdt_offset.checked_add(dsdt.len() as u64).unwrap(); - guest_mem - .write_at_addr(facp.as_slice(), facp_offset) - .expect("Error writing FACP table"); - tables.push(facp_offset.0); + guest_mem.write_at_addr(facp.as_slice(), offset).ok()?; + tables.push(offset.0); + offset = offset.checked_add(facp.len() as u64)?; // MADT let mut madt = SDT::new( @@ -198,11 +216,9 @@ pub fn create_acpi_tables( ..Default::default() }); - let madt_offset = facp_offset.checked_add(facp.len() as u64).unwrap(); - guest_mem - .write_at_addr(madt.as_slice(), madt_offset) - .expect("Error writing MADT table"); - tables.push(madt_offset.0); + guest_mem.write_at_addr(madt.as_slice(), offset).ok()?; + tables.push(offset.0); + offset = offset.checked_add(madt.len() as u64)?; // XSDT let mut xsdt = SDT::new( @@ -217,16 +233,11 @@ pub fn create_acpi_tables( xsdt.append(table); } - let xsdt_offset = madt_offset.checked_add(madt.len() as u64).unwrap(); - guest_mem - .write_at_addr(xsdt.as_slice(), xsdt_offset) - .expect("Error writing XSDT table"); + guest_mem.write_at_addr(xsdt.as_slice(), offset).ok()?; // RSDP - let rsdp = RSDP::new(*b"CROSVM", xsdt_offset.0); - guest_mem - .write_at_addr(rsdp.as_slice(), rsdp_offset) - .expect("Error writing RSDP"); + let rsdp = RSDP::new(*b"CROSVM", offset.0); + guest_mem.write_at_addr(rsdp.as_slice(), rsdp_offset).ok()?; - rsdp_offset + Some(rsdp_offset) } diff --git a/x86_64/src/lib.rs b/x86_64/src/lib.rs index b4c4aa79c7..971463882e 100644 --- a/x86_64/src/lib.rs +++ b/x86_64/src/lib.rs @@ -55,6 +55,7 @@ use std::sync::Arc; use crate::bootparam::boot_params; use acpi_tables::aml::Aml; +use acpi_tables::sdt::SDT; use arch::{ get_serial_cmdline, GetSerialCmdlineError, RunnableLinuxVm, SerialHardware, SerialParameters, VmComponents, VmImage, @@ -283,9 +284,11 @@ fn configure_system( .write_obj_at_addr(params, zero_page_addr) .map_err(|_| Error::ZeroPageSetup)?; - let rsdp_addr = - acpi::create_acpi_tables(guest_mem, num_cpus, X86_64_SCI_IRQ, acpi_dev_resource); - params.acpi_rsdp_addr = rsdp_addr.0; + if let Some(rsdp_addr) = + acpi::create_acpi_tables(guest_mem, num_cpus, X86_64_SCI_IRQ, acpi_dev_resource) + { + params.acpi_rsdp_addr = rsdp_addr.0; + } Ok(()) } @@ -442,6 +445,7 @@ impl arch::LinuxArch for X8664arch { &mut io_bus, &mut resources, suspend_evt.try_clone().map_err(Error::CloneEventFd)?, + components.acpi_sdts, )?; let ramoops_region = match components.pstore { @@ -853,6 +857,7 @@ impl X8664arch { io_bus: &mut devices::Bus, resources: &mut SystemAllocator, suspend_evt: EventFd, + sdts: Vec, ) -> Result { // The AML data for the acpi devices let mut amls = Vec::new(); @@ -883,7 +888,11 @@ impl X8664arch { .unwrap(); io_bus.notify_on_resume(pm); - Ok(acpi::ACPIDevResource { amls, pm_iobase }) + Ok(acpi::ACPIDevResource { + amls, + pm_iobase, + sdts, + }) } /// Sets up the serial devices for this platform. Returns the serial port number and serial