summary refs log tree commit diff
diff options
context:
space:
mode:
authorChuanxiao Dong <chuanxiao.dong@intel.corp-partner.google.com>2020-04-27 16:32:13 +0800
committerCommit Bot <commit-bot@chromium.org>2020-05-11 13:43:59 +0000
commit6db9f9f58a095ee9a70c8edb6563aaf7027cb278 (patch)
tree283b384f5bb40f1a16ebf1ee3a7304f71601b499
parente1f8d9187d3f16e31d0c1304030ad412cf43b2bf (diff)
downloadcrosvm-6db9f9f58a095ee9a70c8edb6563aaf7027cb278.tar
crosvm-6db9f9f58a095ee9a70c8edb6563aaf7027cb278.tar.gz
crosvm-6db9f9f58a095ee9a70c8edb6563aaf7027cb278.tar.bz2
crosvm-6db9f9f58a095ee9a70c8edb6563aaf7027cb278.tar.lz
crosvm-6db9f9f58a095ee9a70c8edb6563aaf7027cb278.tar.xz
crosvm-6db9f9f58a095ee9a70c8edb6563aaf7027cb278.tar.zst
crosvm-6db9f9f58a095ee9a70c8edb6563aaf7027cb278.zip
acpi: refactor the ACPI PM device
Add the AML support to generate the S1 table instead of hard coding.
Also use the IO allocater to allocate the IO resouce for ACPI PM.

BUG=None
TEST=boot crosvm by command "crosvm run -s crosvm.sock -m 4096 --cpus 4
--rwdisk rootfs.img -p "root=/dev/vda rootfstype=ext4" vmlinux".
Check the S1 capability by "#echo standby > /sys/power/state" from guest
side. Linux guest is suspended. And resume linux guest by "#crosvm resume
crosvm.sock" from host side.

Change-Id: I75b484c44db05f98d49557ba694a1531b57871c1
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/crosvm/+/2119571
Reviewed-by: Tomasz Jeznach <tjeznach@chromium.org>
Tested-by: kokoro <noreply+kokoro@google.com>
Commit-Queue: Chuanxiao Dong <chuanxiao.dong@intel.corp-partner.google.com>
-rw-r--r--Cargo.lock1
-rw-r--r--acpi_tables/src/sdt.rs5
-rw-r--r--devices/Cargo.toml1
-rw-r--r--devices/src/acpi.rs15
-rw-r--r--x86_64/src/acpi.rs41
-rw-r--r--x86_64/src/lib.rs60
6 files changed, 93 insertions, 30 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 41843ee..88a1881 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -167,6 +167,7 @@ dependencies = [
 name = "devices"
 version = "0.1.0"
 dependencies = [
+ "acpi_tables 0.1.0",
  "audio_streams 0.1.0",
  "bit_field 0.1.0",
  "bitflags 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
diff --git a/acpi_tables/src/sdt.rs b/acpi_tables/src/sdt.rs
index e8a9ea2..0ea61a0 100644
--- a/acpi_tables/src/sdt.rs
+++ b/acpi_tables/src/sdt.rs
@@ -69,6 +69,11 @@ impl SDT {
         self.write(LENGTH_OFFSET, self.data.len() as u32);
     }
 
+    pub fn append_slice(&mut self, value: &[u8]) {
+        self.data.extend_from_slice(value);
+        self.write(LENGTH_OFFSET, self.data.len() as u32);
+    }
+
     /// Write a value at the given offset
     pub fn write<T: DataInit>(&mut self, offset: usize, value: T) {
         let value_len = std::mem::size_of::<T>();
diff --git a/devices/Cargo.toml b/devices/Cargo.toml
index 629f29b..0ffab50 100644
--- a/devices/Cargo.toml
+++ b/devices/Cargo.toml
@@ -12,6 +12,7 @@ x = ["gpu_display/x"]
 gfxstream = ["gpu"]
 
 [dependencies]
+acpi_tables = {path = "../acpi_tables" }
 audio_streams = "*"
 bit_field = { path = "../bit_field" }
 bitflags = "1"
diff --git a/devices/src/acpi.rs b/devices/src/acpi.rs
index 990a782..1cfab35 100644
--- a/devices/src/acpi.rs
+++ b/devices/src/acpi.rs
@@ -3,6 +3,7 @@
 // found in the LICENSE file.
 
 use crate::{BusDevice, BusResumeDevice};
+use acpi_tables::{aml, aml::Aml};
 use sys_util::{error, warn, EventFd};
 
 /// ACPI PM resource for handling OS suspend/resume request
@@ -29,8 +30,7 @@ impl ACPIPMResource {
     }
 }
 
-/// the ACPI PM register's base and length.
-pub const ACPIPM_RESOURCE_BASE: u64 = 0x600;
+/// the ACPI PM register length.
 pub const ACPIPM_RESOURCE_LEN: u8 = 8;
 pub const ACPIPM_RESOURCE_EVENTBLK_LEN: u8 = 4;
 pub const ACPIPM_RESOURCE_CONTROLBLK_LEN: u8 = 2;
@@ -127,3 +127,14 @@ impl BusResumeDevice for ACPIPMResource {
         self.sleep_status = val | BITMASK_SLEEPCNT_WAKE_STATUS;
     }
 }
+
+impl Aml for ACPIPMResource {
+    fn to_aml_bytes(&self, bytes: &mut Vec<u8>) {
+        // S1
+        aml::Name::new(
+            "_S1_".into(),
+            &aml::Package::new(vec![&aml::ONE, &aml::ONE, &aml::ZERO, &aml::ZERO]),
+        )
+        .to_aml_bytes(bytes);
+    }
+}
diff --git a/x86_64/src/acpi.rs b/x86_64/src/acpi.rs
index 14b21a7..c1d92ee 100644
--- a/x86_64/src/acpi.rs
+++ b/x86_64/src/acpi.rs
@@ -5,6 +5,11 @@ use acpi_tables::{rsdp::RSDP, sdt::SDT};
 use data_model::DataInit;
 use sys_util::{GuestAddress, GuestMemory};
 
+pub struct ACPIDevResource {
+    pub amls: Vec<u8>,
+    pub pm_iobase: u64,
+}
+
 #[repr(C)]
 #[derive(Clone, Copy, Default)]
 struct LocalAPIC {
@@ -65,21 +70,7 @@ const MADT_ENABLED: u32 = 1;
 // XSDT
 const XSDT_REVISION: u8 = 1;
 
-fn create_dsdt_table() -> SDT {
-    // The hex tables in this file are generated from the ASL below with:
-    // "iasl -tc <dsdt.asl>"
-    // Below is the tables represents by the pm_dsdt_data
-    // Name (_S1, Package (0x04)  // _S1_: S1 System State
-    // {
-    //     One,
-    //     One,
-    //     Zero,
-    //     Zero
-    // })
-    let pm_dsdt_data = [
-        0x08u8, 0x5F, 0x53, 0x31, 0x5f, 0x12, 0x06, 0x04, 0x01, 0x01, 0x00, 0x00,
-    ];
-
+fn create_dsdt_table(amls: Vec<u8>) -> SDT {
     let mut dsdt = SDT::new(
         *b"DSDT",
         acpi_tables::HEADER_LEN,
@@ -88,7 +79,10 @@ fn create_dsdt_table() -> SDT {
         *b"CROSVMDT",
         OEM_REVISION,
     );
-    dsdt.append(pm_dsdt_data);
+
+    if amls.len() != 0 {
+        dsdt.append_slice(amls.as_slice());
+    }
 
     dsdt
 }
@@ -102,13 +96,19 @@ fn create_dsdt_table() -> SDT {
 /// * `sci_irq` - Used to fill the FACP SCI_INTERRUPT field, which
 ///               is going to be used by the ACPI drivers to register
 ///               sci handler.
-pub fn create_acpi_tables(guest_mem: &GuestMemory, num_cpus: u8, sci_irq: u32) -> GuestAddress {
+/// * `acpi_dev_resource` - resouces needed by the ACPI devices for creating tables
+pub fn create_acpi_tables(
+    guest_mem: &GuestMemory,
+    num_cpus: u8,
+    sci_irq: u32,
+    acpi_dev_resource: ACPIDevResource,
+) -> GuestAddress {
     // RSDP is at the HI RSDP WINDOW
     let rsdp_offset = GuestAddress(super::ACPI_HI_RSDP_WINDOW_BASE);
     let mut tables: Vec<u64> = Vec::new();
 
     // DSDT
-    let dsdt = create_dsdt_table();
+    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)
@@ -134,14 +134,13 @@ pub fn create_acpi_tables(guest_mem: &GuestMemory, num_cpus: u8, sci_irq: u32) -
     // PM1A Event Block Address
     facp.write(
         FADT_FIELD_PM1A_EVENT_BLK_ADDR,
-        devices::acpi::ACPIPM_RESOURCE_BASE as u32,
+        acpi_dev_resource.pm_iobase as u32,
     );
 
     // PM1A Control Block Address
     facp.write(
         FADT_FIELD_PM1A_CONTROL_BLK_ADDR,
-        devices::acpi::ACPIPM_RESOURCE_BASE as u32
-            + devices::acpi::ACPIPM_RESOURCE_EVENTBLK_LEN as u32,
+        acpi_dev_resource.pm_iobase as u32 + devices::acpi::ACPIPM_RESOURCE_EVENTBLK_LEN as u32,
     );
 
     // PM1 Event Block Length
diff --git a/x86_64/src/lib.rs b/x86_64/src/lib.rs
index a6a02bc..b4c4aa7 100644
--- a/x86_64/src/lib.rs
+++ b/x86_64/src/lib.rs
@@ -54,6 +54,7 @@ use std::mem;
 use std::sync::Arc;
 
 use crate::bootparam::boot_params;
+use acpi_tables::aml::Aml;
 use arch::{
     get_serial_cmdline, GetSerialCmdlineError, RunnableLinuxVm, SerialHardware, SerialParameters,
     VmComponents, VmImage,
@@ -74,6 +75,7 @@ use vm_control::VmIrqRequestSocket;
 #[sorted]
 #[derive(Debug)]
 pub enum Error {
+    AllocateIOResouce(resources::Error),
     AllocateIrq,
     CloneEventFd(sys_util::Error),
     Cmdline(kernel_cmdline::Error),
@@ -124,6 +126,7 @@ impl Display for Error {
 
         #[sorted]
         match self {
+            AllocateIOResouce(e) => write!(f, "error allocating IO resource: {}", e),
             AllocateIrq => write!(f, "error allocating a single irq"),
             CloneEventFd(e) => write!(f, "unable to clone an EventFd: {}", e),
             Cmdline(e) => write!(f, "the given kernel command line was invalid: {}", e),
@@ -216,6 +219,7 @@ fn configure_system(
     setup_data: Option<GuestAddress>,
     initrd: Option<(GuestAddress, usize)>,
     mut params: boot_params,
+    acpi_dev_resource: acpi::ACPIDevResource,
 ) -> Result<()> {
     const EBDA_START: u64 = 0x0009fc00;
     const KERNEL_BOOT_FLAG_MAGIC: u16 = 0xaa55;
@@ -279,7 +283,8 @@ 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);
+    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;
 
     Ok(())
@@ -423,7 +428,6 @@ impl arch::LinuxArch for X8664arch {
             exit_evt.try_clone().map_err(Error::CloneEventFd)?,
             Some(pci_bus.clone()),
             components.memory_size,
-            suspend_evt.try_clone().map_err(Error::CloneEventFd)?,
         )?;
 
         Self::setup_serial_devices(
@@ -434,6 +438,12 @@ impl arch::LinuxArch for X8664arch {
             serial_jail,
         )?;
 
+        let acpi_dev_resource = Self::setup_acpi_devices(
+            &mut io_bus,
+            &mut resources,
+            suspend_evt.try_clone().map_err(Error::CloneEventFd)?,
+        )?;
+
         let ramoops_region = match components.pstore {
             Some(pstore) => Some(
                 arch::pstore::create_memory_region(&mut vm, &mut resources, &pstore)
@@ -506,6 +516,7 @@ impl arch::LinuxArch for X8664arch {
                     components.android_fstab,
                     kernel_end,
                     params,
+                    acpi_dev_resource,
                 )?;
             }
         }
@@ -592,6 +603,7 @@ impl X8664arch {
         android_fstab: Option<File>,
         kernel_end: u64,
         params: boot_params,
+        acpi_dev_resource: acpi::ACPIDevResource,
     ) -> Result<()> {
         kernel_loader::load_cmdline(mem, GuestAddress(CMDLINE_OFFSET), cmdline)
             .map_err(Error::LoadCmdline)?;
@@ -653,6 +665,7 @@ impl X8664arch {
             setup_data,
             initrd,
             params,
+            acpi_dev_resource,
         )?;
         Ok(())
     }
@@ -754,14 +767,12 @@ impl X8664arch {
     /// * - `gsi_relay`: only valid for split IRQ chip (i.e. userspace PIT/PIC/IOAPIC)
     /// * - `exit_evt` - the event fd object which should receive exit events
     /// * - `mem_size` - the size in bytes of physical ram for the guest
-    /// * - `suspend_evt` - the event fd object which used to suspend the vm
     fn setup_io_bus(
         _vm: &mut Vm,
         gsi_relay: &mut Option<GsiRelay>,
         exit_evt: EventFd,
         pci: Option<Arc<Mutex<devices::PciConfigIo>>>,
         mem_size: u64,
-        suspend_evt: EventFd,
     ) -> Result<devices::Bus> {
         struct NoDevice;
         impl devices::BusDevice for NoDevice {
@@ -826,18 +837,53 @@ impl X8664arch {
                 .unwrap();
         }
 
-        let pm = Arc::new(Mutex::new(devices::ACPIPMResource::new(suspend_evt)));
+        Ok(io_bus)
+    }
+
+    /// Sets up the acpi devices for this platform and
+    /// return the resources which is used to set the ACPI tables.
+    ///
+    /// # Arguments
+    ///
+    /// * - `io_bus` the I/O bus to add the devices to
+    /// * - `resources` the SystemAllocator to allocate IO and MMIO for acpi
+    ///                devices.
+    /// * - `suspend_evt` - the event fd object which used to suspend the vm
+    fn setup_acpi_devices(
+        io_bus: &mut devices::Bus,
+        resources: &mut SystemAllocator,
+        suspend_evt: EventFd,
+    ) -> Result<acpi::ACPIDevResource> {
+        // The AML data for the acpi devices
+        let mut amls = Vec::new();
+
+        let pm_alloc = resources.get_anon_alloc();
+        let pm_iobase = match resources.io_allocator() {
+            Some(io) => io
+                .allocate_with_align(
+                    devices::acpi::ACPIPM_RESOURCE_LEN as u64,
+                    pm_alloc,
+                    "ACPIPM".to_string(),
+                    devices::acpi::ACPIPM_RESOURCE_LEN as u64,
+                )
+                .map_err(Error::AllocateIOResouce)?,
+            None => 0x600,
+        };
+
+        let pmresource = devices::ACPIPMResource::new(suspend_evt);
+        Aml::to_aml_bytes(&pmresource, &mut amls);
+        let pm = Arc::new(Mutex::new(pmresource));
         io_bus
             .insert(
                 pm.clone(),
-                devices::acpi::ACPIPM_RESOURCE_BASE,
+                pm_iobase as u64,
                 devices::acpi::ACPIPM_RESOURCE_LEN as u64,
                 false,
             )
             .unwrap();
         io_bus.notify_on_resume(pm);
 
-        Ok(io_bus)
+        Ok(acpi::ACPIDevResource { amls, pm_iobase })
     }
 
     /// Sets up the serial devices for this platform. Returns the serial port number and serial