devices: vmm: Add ACPI PM timer

This is a counter exposed via an I/O port that runs at 3.579545MHz. Here
we use a hardcoded I/O and expose the details through the FADT table.

TEST=Boot Linux kernel and see the following in dmesg:

[    0.506198] clocksource: acpi_pm: mask: 0xffffff max_cycles: 0xffffff, max_idle_ns: 2085701024 ns

Signed-off-by: Rob Bradford <robert.bradford@intel.com>
This commit is contained in:
Rob Bradford 2020-07-23 10:17:07 +01:00
parent 4fc7eb3daa
commit aae5d988e1
4 changed files with 64 additions and 3 deletions

View File

@ -5,6 +5,7 @@
use acpi_tables::{aml, aml::Aml};
use std::sync::Arc;
use std::time::Instant;
use vm_device::interrupt::InterruptSourceGroup;
use vmm_sys_util::eventfd::EventFd;
use BusDevice;
@ -149,3 +150,39 @@ impl Aml for AcpiGEDDevice {
.to_aml_bytes()
}
}
pub struct AcpiPMTimerDevice {
start: Instant,
}
impl AcpiPMTimerDevice {
pub fn new() -> Self {
Self {
start: Instant::now(),
}
}
}
impl Default for AcpiPMTimerDevice {
fn default() -> Self {
Self::new()
}
}
impl BusDevice for AcpiPMTimerDevice {
fn read(&mut self, _base: u64, _offset: u64, data: &mut [u8]) {
let now = Instant::now();
let since = now.duration_since(self.start);
let nanos = since.as_nanos();
const PM_TIMER_FREQUENCY_HZ: u128 = 3_579_545;
const NANOS_PER_SECOND: u128 = 1_000_000_000;
let counter = (nanos * PM_TIMER_FREQUENCY_HZ) / NANOS_PER_SECOND;
let counter: u32 = (counter & 0xffff_ffff) as u32;
data.copy_from_slice(&counter.to_le_bytes());
}
fn write(&mut self, _base: u64, _offset: u64, _data: &[u8]) {}
}

View File

@ -39,7 +39,7 @@ pub mod ioapic;
pub mod legacy;
#[cfg(feature = "acpi")]
pub use self::acpi::{AcpiGEDDevice, AcpiShutdownDevice};
pub use self::acpi::{AcpiGEDDevice, AcpiPMTimerDevice, AcpiShutdownDevice};
pub use self::bus::{Bus, BusDevice, Error as BusError};
pub type DeviceEventT = u16;

View File

@ -64,8 +64,11 @@ pub fn create_acpi_tables(
// Revision 6 of the ACPI FADT table is 276 bytes long
let mut facp = SDT::new(*b"FACP", 276, 6, *b"CLOUDH", *b"CHFACP ", 1);
// HW_REDUCED_ACPI and RESET_REG_SUP
let fadt_flags: u32 = 1 << 20 | 1 << 10;
// PM_TMR_BLK I/O port
facp.write(76, 0xb008);
// HW_REDUCED_ACPI, RESET_REG_SUP, TMR_VAL_EXT
let fadt_flags: u32 = 1 << 20 | 1 << 10 | 1 << 8;
facp.write(112, fadt_flags);
// RESET_REG
@ -76,6 +79,9 @@ pub fn create_acpi_tables(
facp.write(131, 3u8); // FADT minor version
facp.write(140, dsdt_offset.0); // X_DSDT
// X_PM_TMR_BLK
facp.write(208, GenericAddress::io_port_address(0xb008));
// SLEEP_CONTROL_REG
facp.write(244, GenericAddress::io_port_address(0x3c0));
// SLEEP_STATUS_REG

View File

@ -1224,6 +1224,24 @@ impl DeviceManager {
.io_bus
.insert(ged_device.clone(), 0xb000, 0x1)
.map_err(DeviceManagerError::BusError)?;
let pm_timer_device = Arc::new(Mutex::new(devices::AcpiPMTimerDevice::new()));
self.bus_devices
.push(Arc::clone(&pm_timer_device) as Arc<Mutex<dyn BusDevice>>);
self.address_manager
.allocator
.lock()
.unwrap()
.allocate_io_addresses(Some(GuestAddress(0xb008)), 0x4, None)
.ok_or(DeviceManagerError::AllocateIOPort)?;
self.address_manager
.io_bus
.insert(pm_timer_device, 0xb008, 0x4)
.map_err(DeviceManagerError::BusError)?;
Ok(Some(ged_device))
}