mirror of
https://chromium.googlesource.com/crosvm/crosvm
synced 2024-11-24 20:48:55 +00:00
devices: Introduce virt PMC device support allowing to notify about s2idle
Adding Victual PMC device allows to trap on MMIO access caused by its Linux driver counterpart upon entering s2idle state. Linux Virtual PMC driver registers notify() hook, which is triggered before system actually enters s2idle state and triggers _DSM method which in turn triggers MMIO access causing mentioned trap. More info can be found in relevant linux kernel mailing list thread which implements kernel counterpart: https://patchwork.kernel.org/project/linux-pm/patch/20230213100921.268770-2-jaz@semihalf.com/ Upon Virtual PMC BusDevice write() handling, trigger functionality responsible for handling s2idle notification, which is: wakeup blocked thread awaiting guest suspension to finish. Old functionality for handling s2idle request based on non-accepted by Linux community, hypercall based solution - is removed as separate patch CL:4507305 BUG=b:194391015 TEST=Make sure that S2Idle notification from guest are seen by crosvm when --s2idle parameter is used. In such case the guest suspension is detected quite fast and 15s timeout is not reached. Change-Id: I79e1755cd344c46e7fa0dabc211cf7e354583204 Reviewed-on: https://chromium-review.googlesource.com/c/crosvm/crosvm/+/3780642 Reviewed-by: Daniel Verkamp <dverkamp@chromium.org> Commit-Queue: Grzegorz Jaszczyk <jaszczyk@google.com>
This commit is contained in:
parent
24bbacfda0
commit
769c452925
9 changed files with 197 additions and 3 deletions
|
@ -59,6 +59,8 @@ use remain::sorted;
|
|||
use resources::AddressRange;
|
||||
use resources::SystemAllocator;
|
||||
use resources::SystemAllocatorConfig;
|
||||
#[cfg(unix)]
|
||||
use sync::Condvar;
|
||||
use sync::Mutex;
|
||||
use thiserror::Error;
|
||||
use vm_control::BatControl;
|
||||
|
@ -379,6 +381,7 @@ impl arch::LinuxArch for AArch64 {
|
|||
dump_device_tree_blob: Option<PathBuf>,
|
||||
_debugcon_jail: Option<Minijail>,
|
||||
#[cfg(feature = "swap")] swap_controller: Option<&swap::SwapController>,
|
||||
#[cfg(unix)] _guest_suspended_cvar: Option<Arc<(Mutex<bool>, Condvar)>>,
|
||||
) -> std::result::Result<RunnableLinuxVm<V, Vcpu>, Self::Error>
|
||||
where
|
||||
V: VmAArch64,
|
||||
|
|
|
@ -76,6 +76,8 @@ pub use serial::get_serial_cmdline;
|
|||
pub use serial::set_default_serial_parameters;
|
||||
pub use serial::GetSerialCmdlineError;
|
||||
pub use serial::SERIAL_ADDR;
|
||||
#[cfg(unix)]
|
||||
use sync::Condvar;
|
||||
use sync::Mutex;
|
||||
use thiserror::Error;
|
||||
use vm_control::BatControl;
|
||||
|
@ -454,6 +456,7 @@ pub trait LinuxArch {
|
|||
debugcon_jail: Option<Minijail>,
|
||||
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] pflash_jail: Option<Minijail>,
|
||||
#[cfg(feature = "swap")] swap_controller: Option<&swap::SwapController>,
|
||||
#[cfg(unix)] guest_suspended_cvar: Option<Arc<(Mutex<bool>, Condvar)>>,
|
||||
) -> std::result::Result<RunnableLinuxVm<V, Vcpu>, Self::Error>
|
||||
where
|
||||
V: VmArch,
|
||||
|
|
|
@ -25,6 +25,7 @@ pub mod irqchip;
|
|||
mod pci;
|
||||
mod pflash;
|
||||
pub mod pl030;
|
||||
pub mod pmc_virt;
|
||||
mod serial;
|
||||
pub mod serial_device;
|
||||
#[cfg(feature = "tpm")]
|
||||
|
@ -153,6 +154,7 @@ cfg_if::cfg_if! {
|
|||
};
|
||||
pub use self::platform::VfioPlatformDevice;
|
||||
pub use self::ac_adapter::AcAdapter;
|
||||
pub use self::pmc_virt::VirtualPmc;
|
||||
pub use self::proxy::Error as ProxyError;
|
||||
pub use self::proxy::ProxyDevice;
|
||||
#[cfg(feature = "usb")]
|
||||
|
|
|
@ -136,6 +136,7 @@ pub enum CrosvmDeviceId {
|
|||
Pflash = 18,
|
||||
VirtioMmio = 19,
|
||||
AcAdapter = 20,
|
||||
VirtualPmc = 21,
|
||||
}
|
||||
|
||||
impl TryFrom<u16> for CrosvmDeviceId {
|
||||
|
@ -163,6 +164,7 @@ impl TryFrom<u16> for CrosvmDeviceId {
|
|||
18 => Ok(CrosvmDeviceId::Pflash),
|
||||
19 => Ok(CrosvmDeviceId::VirtioMmio),
|
||||
20 => Ok(CrosvmDeviceId::AcAdapter),
|
||||
21 => Ok(CrosvmDeviceId::VirtualPmc),
|
||||
_ => Err(base::Error::new(EINVAL)),
|
||||
}
|
||||
}
|
||||
|
|
140
devices/src/pmc_virt.rs
Normal file
140
devices/src/pmc_virt.rs
Normal file
|
@ -0,0 +1,140 @@
|
|||
// Copyright 2023 The ChromiumOS Authors
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
use crate::{pci::CrosvmDeviceId, BusAccessInfo, BusDevice, DeviceId};
|
||||
use acpi_tables::aml;
|
||||
use acpi_tables::aml::Aml;
|
||||
use base::warn;
|
||||
use std::sync::Arc;
|
||||
use sync::{Condvar, Mutex};
|
||||
|
||||
use crate::Suspendable;
|
||||
|
||||
/// PMC Virt MMIO offset
|
||||
const PMC_RESERVED1: u32 = 0;
|
||||
const _PMC_RESERVED2: u32 = 0x4;
|
||||
const _PMC_RESERVED3: u32 = 0x8;
|
||||
const _PMC_RESERVED4: u32 = 0xc;
|
||||
|
||||
pub const VPMC_VIRT_MMIO_SIZE: u64 = 0x10;
|
||||
|
||||
pub struct VirtualPmc {
|
||||
mmio_base: u64,
|
||||
guest_suspended_cvar: Arc<(Mutex<bool>, Condvar)>,
|
||||
}
|
||||
|
||||
impl VirtualPmc {
|
||||
pub fn new(mmio_base: u64, guest_suspended_cvar: Arc<(Mutex<bool>, Condvar)>) -> Self {
|
||||
VirtualPmc {
|
||||
mmio_base,
|
||||
guest_suspended_cvar,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_s2idle_request(guest_suspended_cvar: &Arc<(Mutex<bool>, Condvar)>) {
|
||||
// Wake up blocked thread on condvar, which is awaiting non-privileged guest suspension to
|
||||
// finish.
|
||||
let (lock, cvar) = &**guest_suspended_cvar;
|
||||
let mut guest_suspended = lock.lock();
|
||||
*guest_suspended = true;
|
||||
|
||||
cvar.notify_one();
|
||||
}
|
||||
|
||||
impl BusDevice for VirtualPmc {
|
||||
fn device_id(&self) -> DeviceId {
|
||||
CrosvmDeviceId::VirtualPmc.into()
|
||||
}
|
||||
fn debug_label(&self) -> String {
|
||||
"PmcVirt".to_owned()
|
||||
}
|
||||
|
||||
fn read(&mut self, info: BusAccessInfo, data: &mut [u8]) {
|
||||
if data.len() != std::mem::size_of::<u32>() {
|
||||
warn!(
|
||||
"{}: unsupported read length {}, only support 4bytes read",
|
||||
self.debug_label(),
|
||||
data.len()
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
warn!("{}: unsupported read address {}", self.debug_label(), info);
|
||||
}
|
||||
|
||||
fn write(&mut self, info: BusAccessInfo, data: &[u8]) {
|
||||
if data.len() != std::mem::size_of::<u32>() {
|
||||
warn!(
|
||||
"{}: unsupported write length {}, only support 4bytes write",
|
||||
self.debug_label(),
|
||||
data.len()
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
match info.offset as u32 {
|
||||
PMC_RESERVED1 => {
|
||||
handle_s2idle_request(&self.guest_suspended_cvar);
|
||||
}
|
||||
_ => {
|
||||
warn!("{}: Bad write to address {}", self.debug_label(), info);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
impl Aml for VirtualPmc {
|
||||
fn to_aml_bytes(&self, bytes: &mut Vec<u8>) {
|
||||
let vpmc_uuid = "9ea49ba3-434a-49a6-be30-37cc55c4d397";
|
||||
aml::Device::new(
|
||||
"VPMC".into(),
|
||||
vec![
|
||||
&aml::Name::new("_CID".into(), &aml::EISAName::new("PNP0D80")),
|
||||
&aml::Name::new("_HID".into(), &"HYPE0001"),
|
||||
&aml::Name::new("UUID".into(), &aml::Uuid::new(vpmc_uuid)),
|
||||
&aml::OpRegion::new(
|
||||
"VREG".into(),
|
||||
aml::OpRegionSpace::SystemMemory,
|
||||
&self.mmio_base,
|
||||
&(16_u32),
|
||||
),
|
||||
&aml::Field::new(
|
||||
"VREG".into(),
|
||||
aml::FieldAccessType::DWord,
|
||||
aml::FieldLockRule::Lock,
|
||||
aml::FieldUpdateRule::Preserve,
|
||||
vec![aml::FieldEntry::Named(*b"RSV1", 32)],
|
||||
),
|
||||
&aml::Method::new(
|
||||
"_DSM".into(),
|
||||
4,
|
||||
true,
|
||||
vec![
|
||||
&aml::If::new(
|
||||
&aml::Equal::new(&aml::Arg(0), &aml::Name::new_field_name("UUID")),
|
||||
vec![
|
||||
&aml::If::new(
|
||||
&aml::Equal::new(&aml::Arg(2), &aml::ZERO),
|
||||
vec![&aml::Return::new(&aml::BufferData::new(vec![3]))],
|
||||
),
|
||||
&aml::If::new(
|
||||
&aml::Equal::new(&aml::Arg(2), &aml::ONE),
|
||||
vec![&aml::Store::new(
|
||||
&aml::Name::new_field_name("RSV1"),
|
||||
&0x3_usize,
|
||||
)],
|
||||
),
|
||||
],
|
||||
),
|
||||
&aml::Return::new(&aml::BufferData::new(vec![3])),
|
||||
],
|
||||
),
|
||||
],
|
||||
)
|
||||
.to_aml_bytes(bytes);
|
||||
}
|
||||
}
|
||||
|
||||
impl Suspendable for VirtualPmc {}
|
|
@ -51,6 +51,8 @@ use remain::sorted;
|
|||
use resources::AddressRange;
|
||||
use resources::SystemAllocator;
|
||||
use resources::SystemAllocatorConfig;
|
||||
#[cfg(unix)]
|
||||
use sync::Condvar;
|
||||
use sync::Mutex;
|
||||
use thiserror::Error;
|
||||
use vm_control::BatteryType;
|
||||
|
@ -186,6 +188,7 @@ impl arch::LinuxArch for Riscv64 {
|
|||
_dump_device_tree_blob: Option<PathBuf>,
|
||||
_debugcon_jail: Option<Minijail>,
|
||||
#[cfg(feature = "swap")] swap_controller: Option<&swap::SwapController>,
|
||||
#[cfg(unix)] _guest_suspended_cvar: Option<Arc<(Mutex<bool>, Condvar)>>,
|
||||
) -> std::result::Result<RunnableLinuxVm<V, Vcpu>, Self::Error>
|
||||
where
|
||||
V: VmRiscv64,
|
||||
|
|
|
@ -1961,6 +1961,12 @@ where
|
|||
// KVM_CREATE_VCPU uses apic id for x86 and uses cpu id for others.
|
||||
let mut vcpu_ids = Vec::new();
|
||||
|
||||
let guest_suspended_cvar = if cfg.force_s2idle {
|
||||
Some(Arc::new((Mutex::new(false), Condvar::new())))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
#[cfg_attr(not(feature = "direct"), allow(unused_mut))]
|
||||
let mut linux = Arch::build_vm::<V, Vcpu>(
|
||||
components,
|
||||
|
@ -1980,6 +1986,7 @@ where
|
|||
simple_jail(&cfg.jail_config, "block_device")?,
|
||||
#[cfg(feature = "swap")]
|
||||
swap_controller.as_ref(),
|
||||
guest_suspended_cvar.clone(),
|
||||
)
|
||||
.context("the architecture failed to build the vm")?;
|
||||
|
||||
|
@ -2068,6 +2075,7 @@ where
|
|||
#[cfg(feature = "swap")]
|
||||
swap_controller,
|
||||
reg_evt_rdtube,
|
||||
guest_suspended_cvar,
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -2509,6 +2517,7 @@ fn run_control<V: VmArch + 'static, Vcpu: VcpuArch + 'static>(
|
|||
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] hp_thread: std::thread::JoinHandle<()>,
|
||||
#[cfg(feature = "swap")] swap_controller: Option<SwapController>,
|
||||
reg_evt_rdtube: RecvTube,
|
||||
guest_suspended_cvar: Option<Arc<(Mutex<bool>, Condvar)>>,
|
||||
) -> Result<ExitState> {
|
||||
#[derive(EventToken)]
|
||||
enum Token {
|
||||
|
@ -2708,8 +2717,6 @@ fn run_control<V: VmArch + 'static, Vcpu: VcpuArch + 'static>(
|
|||
#[cfg(target_os = "android")]
|
||||
android::set_process_profiles(&cfg.task_profiles)?;
|
||||
|
||||
let guest_suspended_cvar = Arc::new((Mutex::new(false), Condvar::new()));
|
||||
|
||||
#[allow(unused_mut)]
|
||||
let mut run_mode = VmRunMode::Running;
|
||||
#[cfg(feature = "gdb")]
|
||||
|
@ -3157,7 +3164,7 @@ fn run_control<V: VmArch + 'static, Vcpu: VcpuArch + 'static>(
|
|||
.name("s2idle_wait".to_owned())
|
||||
.spawn(move || {
|
||||
trigger_vm_suspend_and_wait_for_entry(
|
||||
guest_suspended_cvar,
|
||||
guest_suspended_cvar.unwrap(),
|
||||
&send_tube,
|
||||
delayed_response,
|
||||
suspend_evt,
|
||||
|
|
|
@ -97,6 +97,8 @@ use devices::ProxyDevice;
|
|||
use devices::Serial;
|
||||
use devices::SerialHardware;
|
||||
use devices::SerialParameters;
|
||||
#[cfg(unix)]
|
||||
use devices::VirtualPmc;
|
||||
#[cfg(feature = "gdb")]
|
||||
use gdbstub_arch::x86::reg::id::X86_64CoreRegId;
|
||||
#[cfg(feature = "gdb")]
|
||||
|
@ -131,6 +133,8 @@ use remain::sorted;
|
|||
use resources::AddressRange;
|
||||
use resources::SystemAllocator;
|
||||
use resources::SystemAllocatorConfig;
|
||||
#[cfg(unix)]
|
||||
use sync::Condvar;
|
||||
use sync::Mutex;
|
||||
use thiserror::Error;
|
||||
use vm_control::BatControl;
|
||||
|
@ -694,6 +698,7 @@ impl arch::LinuxArch for X8664arch {
|
|||
debugcon_jail: Option<Minijail>,
|
||||
pflash_jail: Option<Minijail>,
|
||||
#[cfg(feature = "swap")] swap_controller: Option<&swap::SwapController>,
|
||||
#[cfg(unix)] guest_suspended_cvar: Option<Arc<(Mutex<bool>, Condvar)>>,
|
||||
) -> std::result::Result<RunnableLinuxVm<V, Vcpu>, Self::Error>
|
||||
where
|
||||
V: VmX86_64,
|
||||
|
@ -893,6 +898,8 @@ impl arch::LinuxArch for X8664arch {
|
|||
swap_controller,
|
||||
#[cfg(unix)]
|
||||
components.ac_adapter,
|
||||
#[cfg(unix)]
|
||||
guest_suspended_cvar,
|
||||
)?;
|
||||
|
||||
// Create customized SSDT table
|
||||
|
@ -1803,6 +1810,7 @@ impl X8664arch {
|
|||
resume_notify_devices: &mut Vec<Arc<Mutex<dyn BusResumeDevice>>>,
|
||||
#[cfg(feature = "swap")] swap_controller: Option<&swap::SwapController>,
|
||||
#[cfg(unix)] ac_adapter: bool,
|
||||
#[cfg(unix)] guest_suspended_cvar: Option<Arc<(Mutex<bool>, Condvar)>>,
|
||||
) -> Result<(acpi::AcpiDevResource, Option<BatControl>)> {
|
||||
// The AML data for the acpi devices
|
||||
let mut amls = Vec::new();
|
||||
|
@ -1917,6 +1925,31 @@ impl X8664arch {
|
|||
#[cfg(windows)]
|
||||
let acdc = None;
|
||||
|
||||
//Virtual PMC
|
||||
#[cfg(unix)]
|
||||
if let Some(guest_suspended_cvar) = guest_suspended_cvar {
|
||||
let alloc = resources.get_anon_alloc();
|
||||
let mmio_base = resources
|
||||
.allocate_mmio(
|
||||
devices::pmc_virt::VPMC_VIRT_MMIO_SIZE,
|
||||
alloc,
|
||||
"VirtualPmc".to_string(),
|
||||
resources::AllocOptions::new().align(devices::pmc_virt::VPMC_VIRT_MMIO_SIZE),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let pmc_virtio_mmio =
|
||||
Arc::new(Mutex::new(VirtualPmc::new(mmio_base, guest_suspended_cvar)));
|
||||
mmio_bus
|
||||
.insert(
|
||||
pmc_virtio_mmio.clone(),
|
||||
mmio_base,
|
||||
devices::pmc_virt::VPMC_VIRT_MMIO_SIZE,
|
||||
)
|
||||
.unwrap();
|
||||
pmc_virtio_mmio.lock().to_aml_bytes(&mut amls);
|
||||
}
|
||||
|
||||
let mut pmresource = devices::ACPIPMResource::new(
|
||||
pm_sci_evt.try_clone().map_err(Error::CloneEvent)?,
|
||||
#[cfg(feature = "direct")]
|
||||
|
|
|
@ -222,6 +222,7 @@ where
|
|||
None,
|
||||
#[cfg(unix)]
|
||||
false,
|
||||
Default::default(),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
|
|
Loading…
Reference in a new issue