summary refs log tree commit diff
diff options
context:
space:
mode:
authorTomasz Jeznach <tjeznach@chromium.org>2020-05-20 23:27:59 -0700
committerCommit Bot <commit-bot@chromium.org>2020-06-01 23:25:39 +0000
commit4264464153a7a788ef73c5015ac8bbde5f8ebe1c (patch)
tree38d1e2b38304f9fc81b9627a037fa11146a291fb
parent2705264dc649a60077dfefc9924b8689d8f7d994 (diff)
downloadcrosvm-4264464153a7a788ef73c5015ac8bbde5f8ebe1c.tar
crosvm-4264464153a7a788ef73c5015ac8bbde5f8ebe1c.tar.gz
crosvm-4264464153a7a788ef73c5015ac8bbde5f8ebe1c.tar.bz2
crosvm-4264464153a7a788ef73c5015ac8bbde5f8ebe1c.tar.lz
crosvm-4264464153a7a788ef73c5015ac8bbde5f8ebe1c.tar.xz
crosvm-4264464153a7a788ef73c5015ac8bbde5f8ebe1c.tar.zst
crosvm-4264464153a7a788ef73c5015ac8bbde5f8ebe1c.zip
acpi: support user provided ACPI SDTs.
Enable support for user provided ACPI tables with supplementary system
description. Argument --acpi-table shall point to exsting file or
pseudo-file with valid ACPI table content.

BUG=None
TEST=boot Linux kernel with generated SSDT tables.

Change-Id: I8eac21da070dcc325884ed888cc7bcb01bc086ce
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/crosvm/+/2212501
Reviewed-by: Daniel Verkamp <dverkamp@chromium.org>
Tested-by: Tomasz Jeznach <tjeznach@chromium.org>
Commit-Queue: Tomasz Jeznach <tjeznach@chromium.org>
-rw-r--r--Cargo.lock1
-rw-r--r--acpi_tables/src/sdt.rs21
-rw-r--r--arch/Cargo.toml1
-rw-r--r--arch/src/lib.rs2
-rw-r--r--src/crosvm.rs2
-rw-r--r--src/linux.rs9
-rw-r--r--src/main.rs18
-rw-r--r--x86_64/src/acpi.rs61
-rw-r--r--x86_64/src/lib.rs17
9 files changed, 103 insertions, 29 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 124d045..7a9a5c4 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 0ea61a0..96f4d0f 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<Self> {
+        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 6b4e529..68b40a3 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 ba09fa2..5aa8c67 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<File>,
     pub extra_kernel_params: Vec<String>,
     pub wayland_dmabuf: bool,
+    pub acpi_sdts: Vec<SDT>,
 }
 
 /// Holds the elements needed to run a Linux VM. Created by `build_vm`.
diff --git a/src/crosvm.rs b/src/crosvm.rs
index 33ed236..1ea7e0d 100644
--- a/src/crosvm.rs
+++ b/src/crosvm.rs
@@ -204,6 +204,7 @@ pub struct Config {
     pub vfio: Vec<PathBuf>,
     pub video_dec: bool,
     pub video_enc: bool,
+    pub acpi_tables: Vec<PathBuf>,
 }
 
 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 e95f372..fb463c2 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<dyn StdError>),
     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::<Result<Vec<SDT>>>()?,
     };
 
     let control_server_socket = match &cfg.socket_path {
diff --git a/src/main.rs b/src/main.rs
index 61fcd48..e8a6e6c 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 c1d92ee..e6fc15a 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<u8>,
     pub pm_iobase: u64,
+    /// Additional system descriptor tables.
+    pub sdts: Vec<SDT>,
 }
 
 #[repr(C)]
@@ -102,17 +104,35 @@ pub fn create_acpi_tables(
     num_cpus: u8,
     sci_irq: u32,
     acpi_dev_resource: ACPIDevResource,
-) -> GuestAddress {
+) -> Option<GuestAddress> {
     // 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<u64> = Vec::new();
+    let mut dsdt_offset: Option<GuestAddress> = 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 b4c4aa7..9714638 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<SDT>,
     ) -> Result<acpi::ACPIDevResource> {
         // 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