Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions Cargo.lock
Original file line number Diff line number Diff line change
Expand Up @@ -680,6 +680,7 @@ version = "0.0.0"
dependencies = [
"arbitrary",
"inspect",
"local_clock",
"mesh",
"vm_resource",
]
Expand Down Expand Up @@ -5445,6 +5446,7 @@ dependencies = [
"build_rs_guest_arch",
"chipset",
"chipset_legacy",
"chipset_resources",
"debug_worker",
"disk_striped",
"hyperv_ic",
Expand Down Expand Up @@ -5520,6 +5522,7 @@ dependencies = [
"build_rs_guest_arch",
"chipset",
"chipset_legacy",
"chipset_resources",
"debug_worker",
"disk_blob",
"disk_blockdevice",
Expand Down Expand Up @@ -8301,6 +8304,7 @@ dependencies = [
"chipset_device_worker",
"chipset_device_worker_defs",
"chipset_legacy",
"chipset_resources",
"closeable_mutex",
"crypto",
"cvm_tracing",
Expand Down
1 change: 1 addition & 0 deletions openhcl/openvmm_hcl_resources/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ uidevices = { workspace = true, optional = true }

chipset.workspace = true
chipset_legacy.workspace = true
chipset_resources.workspace = true
hyperv_ic.workspace = true
missing_dev.workspace = true
serial_16550.workspace = true
Expand Down
1 change: 1 addition & 0 deletions openhcl/openvmm_hcl_resources/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ vm_resource::register_static_resolvers! {
chipset::pit::resolver::PitResolver,
#[cfg(guest_arch = "x86_64")]
chipset::pic::resolver::PicResolver,
chipset_resources::cmos_rtc_time_source::SystemTimeClockResolver,
missing_dev::resolver::MissingDevResolver,
#[cfg(feature = "tpm")]
tpm_device::resolver::TpmDeviceResolver,
Expand Down
1 change: 1 addition & 0 deletions openhcl/underhill_core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ chipset_device.workspace = true
chipset_device_resources.workspace = true
chipset_device_worker.workspace = true
chipset_device_worker_defs.workspace = true
chipset_resources.workspace = true
closeable_mutex.workspace = true
profiler_worker = { workspace = true, optional = true }
chipset_legacy.workspace = true
Expand Down
30 changes: 27 additions & 3 deletions openhcl/underhill_core/src/worker.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,8 @@ use anyhow::Context;
use async_trait::async_trait;
use chipset_device::ChipsetDevice;
use chipset_device_worker_defs::RemoteChipsetDeviceHandle;
use chipset_resources::CmosRtcTimeSourceHandleKind;
use chipset_resources::ResolvedCmosRtcTimeSource;
use closeable_mutex::CloseableMutex;
use cvm_tracing::CVM_ALLOWED;
use debug_ptr::DebugPtr;
Expand Down Expand Up @@ -148,6 +150,8 @@ use virt_mshv_vtl::UhPartitionNewParams;
use virt_mshv_vtl::UhProtoPartition;
use vm_loader::initial_regs::initial_regs;
use vm_resource::IntoResource;
use vm_resource::PlatformResource;
use vm_resource::ResolveResource;
use vm_resource::Resource;
use vm_resource::ResourceResolver;
use vm_resource::kind::DiskHandleKind;
Expand Down Expand Up @@ -194,6 +198,23 @@ pub(crate) const PM_BASE: u16 = 0x400;
pub(crate) const SYSTEM_IRQ_ACPI: u32 = 9;
pub(crate) const WDAT_PORT: u16 = 0x30;

struct UnderhillCmosRtcTimeSourceResolver {
time_source: ArcMutexUnderhillLocalClock,
}

impl ResolveResource<CmosRtcTimeSourceHandleKind, PlatformResource>
for UnderhillCmosRtcTimeSourceResolver
{
type Output = ResolvedCmosRtcTimeSource;
type Error = std::convert::Infallible;

fn resolve(&self, _resource: PlatformResource, (): ()) -> Result<Self::Output, Self::Error> {
Ok(ResolvedCmosRtcTimeSource(Box::new(
self.time_source.new_linked_clock(),
)))
}
}

pub const UNDERHILL_WORKER: WorkerId<UnderhillWorkerParameters> = WorkerId::new("UnderhillWorker");

const MAX_SUBCHANNELS_PER_VNIC: u16 = 32;
Expand Down Expand Up @@ -2286,7 +2307,6 @@ async fn new_underhill_vm(
Some(n) => n.to_ne_bytes(),
};

// TODO: move to instantiate via a resource.
let rtc_time_source = ArcMutexUnderhillLocalClock(Arc::new(Mutex::new(
UnderhillLocalClock::new(
get_client.clone(),
Expand All @@ -2303,6 +2323,10 @@ async fn new_underhill_vm(
.context("failed to initialize UnderhillLocalClock emuplat")?,
)));

resolver.add_resolver(UnderhillCmosRtcTimeSourceResolver {
time_source: rtc_time_source.new_linked_clock(),
});

let mut serial_inputs = [None, None, None, None];

if dps.general.com1_vmbus_redirector {
Expand Down Expand Up @@ -2802,7 +2826,7 @@ async fn new_underhill_vm(

#[cfg(guest_arch = "x86_64")]
let deps_piix4_cmos_rtc = chipset.with_piix4_cmos_rtc.then(|| dev::Piix4CmosRtcDeps {
time_source: Box::new(rtc_time_source.new_linked_clock()),
time_source: PlatformResource.into_resource(),
initial_cmos: Some(firmware_pcat::default_cmos_values(&mem_layout)),
enlightened_interrupts: true, // As advertised by the PCAT BIOS.
});
Expand Down Expand Up @@ -2866,7 +2890,7 @@ async fn new_underhill_vm(
.with_generic_cmos_rtc
.then(|| dev::GenericCmosRtcDeps {
irq: 8,
time_source: Box::new(rtc_time_source.new_linked_clock()),
time_source: PlatformResource.into_resource(),
century_reg_idx: 0x32,
initial_cmos: None,
});
Expand Down
22 changes: 10 additions & 12 deletions openvmm/openvmm_core/src/worker/dispatch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ use anyhow::Context;
use cfg_if::cfg_if;
use chipset_device_resources::IRQ_LINE_SET;
use chipset_resources::LEGACY_CHIPSET_PCI_BUS_NAME;
use chipset_resources::cmos_rtc_time_source::SystemTimeClockHandle;
use debug_ptr::DebugPtr;
use disk_backend::Disk;
use disk_backend::resolve::ResolveDiskParameters;
Expand Down Expand Up @@ -99,6 +100,7 @@ use virtio::VirtioMmioDevice;
use virtio::VirtioPciDevice;
use virtio::resolve::VirtioResolveInput;
use vm_loader::initial_regs::initial_regs;
use vm_resource::IntoResource;
use vm_resource::Resource;
use vm_resource::ResourceResolver;
use vm_resource::kind::DiskHandleKind;
Expand Down Expand Up @@ -1419,14 +1421,12 @@ impl InitializedVm {
};

let deps_generic_cmos_rtc = (cfg.chipset.with_generic_cmos_rtc).then(|| {
// TODO: persist SystemTimeClock time across reboots.
// TODO: move to instantiate via a resource.
let time_source = Box::new(local_clock::SystemTimeClock::new(
LocalClockDelta::from_millis(cfg.rtc_delta_milliseconds),
));
dev::GenericCmosRtcDeps {
irq: 8,
time_source,
time_source: SystemTimeClockHandle {
delta_milliseconds: cfg.rtc_delta_milliseconds,
}
.into_resource(),
century_reg_idx: 0x32, // TODO: automatically sync with FADT
initial_cmos: initial_rtc_cmos,
}
Expand Down Expand Up @@ -1569,13 +1569,11 @@ impl InitializedVm {
});

let deps_piix4_cmos_rtc = (cfg.chipset.with_piix4_cmos_rtc).then(|| {
// TODO: persist SystemTimeClock time across reboots.
// TODO: move to instantiate via a resource.
let time_source = Box::new(local_clock::SystemTimeClock::new(
LocalClockDelta::from_millis(cfg.rtc_delta_milliseconds),
));
dev::Piix4CmosRtcDeps {
time_source,
time_source: SystemTimeClockHandle {
delta_milliseconds: cfg.rtc_delta_milliseconds,
}
.into_resource(),
initial_cmos: initial_rtc_cmos,
enlightened_interrupts: true, // As advertised by the PCAT BIOS.
}
Expand Down
1 change: 1 addition & 0 deletions openvmm/openvmm_resources/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ disklayer_sqlite = { workspace = true, optional = true }
# Chipset devices
chipset.workspace = true
chipset_legacy.workspace = true
chipset_resources.workspace = true
missing_dev.workspace = true
tpm_device = { workspace = true, optional = true, features = ["tpm"] }

Expand Down
1 change: 1 addition & 0 deletions openvmm/openvmm_resources/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ vm_resource::register_static_resolvers! {
chipset::pit::resolver::PitResolver,
#[cfg(guest_arch = "x86_64")]
chipset::pic::resolver::PicResolver,
chipset_resources::cmos_rtc_time_source::SystemTimeClockResolver,
missing_dev::resolver::MissingDevResolver,
#[cfg(feature = "tpm")]
tpm_device::resolver::TpmDeviceResolver,
Expand Down
1 change: 1 addition & 0 deletions vm/devices/chipset_resources/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ rust-version.workspace = true

[dependencies]
vm_resource.workspace = true
local_clock.workspace = true

inspect.workspace = true
mesh.workspace = true
Expand Down
68 changes: 68 additions & 0 deletions vm/devices/chipset_resources/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,77 @@

#![forbid(unsafe_code)]

use local_clock::InspectableLocalClock;
use vm_resource::CanResolveTo;
use vm_resource::ResourceKind;

/// The PCI bus name used by the Gen1 (i440BX + PIIX4) chipset.
pub const LEGACY_CHIPSET_PCI_BUS_NAME: &str = "i440bx";

/// Resource kind for CMOS RTC time-source handles.
pub enum CmosRtcTimeSourceHandleKind {}

impl ResourceKind for CmosRtcTimeSourceHandleKind {
const NAME: &'static str = "cmos_rtc_time_source";
}

/// Resolved runtime time source for CMOS RTC devices.
pub struct ResolvedCmosRtcTimeSource(pub Box<dyn InspectableLocalClock>);

impl CanResolveTo<ResolvedCmosRtcTimeSource> for CmosRtcTimeSourceHandleKind {
type Input<'a> = ();
}

pub mod cmos_rtc_time_source {
//! Resource definitions and resolvers for CMOS RTC time sources.

use super::CmosRtcTimeSourceHandleKind;
use super::ResolvedCmosRtcTimeSource;
use local_clock::LocalClockDelta;
use local_clock::SystemTimeClock;
use mesh::MeshPayload;
use vm_resource::ResolveResource;
use vm_resource::ResourceId;
use vm_resource::declare_static_resolver;

/// A time source backed by the host system clock with a configurable
/// millisecond delta.
#[derive(MeshPayload)]
pub struct SystemTimeClockHandle {
/// Offset from system time in milliseconds.
pub delta_milliseconds: i64,
}

impl ResourceId<CmosRtcTimeSourceHandleKind> for SystemTimeClockHandle {
const ID: &'static str = "system_time_clock";
}

/// Resolver for [`SystemTimeClockHandle`].
pub struct SystemTimeClockResolver;

declare_static_resolver! {
SystemTimeClockResolver,
(CmosRtcTimeSourceHandleKind, SystemTimeClockHandle),
}

impl ResolveResource<CmosRtcTimeSourceHandleKind, SystemTimeClockHandle>
for SystemTimeClockResolver
{
type Output = ResolvedCmosRtcTimeSource;
type Error = std::convert::Infallible;

fn resolve(
&self,
resource: SystemTimeClockHandle,
(): (),
) -> Result<Self::Output, Self::Error> {
Ok(ResolvedCmosRtcTimeSource(Box::new(SystemTimeClock::new(
LocalClockDelta::from_millis(resource.delta_milliseconds),
))))
}
}
}

pub mod i8042 {
//! Resource definitions for the i8042 PS2 keyboard/mouse controller.

Expand Down
22 changes: 16 additions & 6 deletions vmm_core/vmotherboard/src/base_chipset.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@ pub enum BaseChipsetBuilderError {
FeatureGatedDevice(&'static str),
#[error("no valid ISA DMA controller for floppy")]
NoDmaForFloppy,
#[error("failed to resolve resource")]
Copy link

Copilot AI Apr 21, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ResolveResource uses the generic message "failed to resolve resource" for CMOS RTC time-source resolution. Since this error can occur in multiple call sites (generic RTC vs PIIX4 RTC) and ResolveError typically only identifies kind/id, it’s hard to tell which device path failed from the top-level error. Consider making the variant/message RTC-specific (or splitting into separate variants per device) so the error context includes which RTC device was being built/resolved.

Suggested change
#[error("failed to resolve resource")]
#[error("failed to resolve CMOS RTC time-source resource")]

Copilot uses AI. Check for mistakes.
ResolveResource(#[source] vm_resource::ResolveError),
}

/// A grab-bag of device-specific interfaces that may need to be wired up into
Expand Down Expand Up @@ -448,9 +450,13 @@ impl<'a> BaseChipsetBuilder<'a> {
initial_cmos,
}) = deps_generic_cmos_rtc
{
let resolved = resolver
.resolve(time_source, ())
.await
.map_err(BaseChipsetBuilderError::ResolveResource)?;
builder.arc_mutex_device("rtc").add(|services| {
cmos_rtc::Rtc::new(
time_source,
resolved.0,
services.new_line(IRQ_LINE_SET, "interrupt", irq),
services.register_vmtime(),
century_reg_idx,
Expand All @@ -466,11 +472,15 @@ impl<'a> BaseChipsetBuilder<'a> {
enlightened_interrupts,
}) = deps_piix4_cmos_rtc
{
let resolved = resolver
.resolve(time_source, ())
.await
.map_err(BaseChipsetBuilderError::ResolveResource)?;
builder.arc_mutex_device("piix4-rtc").add(|services| {
// hard-coded to IRQ line 8, as per PIIX4 spec
let rtc_interrupt = services.new_line(IRQ_LINE_SET, "interrupt", 8);
chipset_legacy::piix4_cmos_rtc::Piix4CmosRtc::new(
time_source,
resolved.0,
rtc_interrupt,
services.register_vmtime(),
initial_cmos,
Expand Down Expand Up @@ -1340,8 +1350,8 @@ pub mod options {
pub struct GenericCmosRtcDeps {
/// IRQ line to signal RTC device events
pub irq: u32,
/// A source of "real time"
pub time_source: Box<dyn InspectableLocalClock>,
/// A time source resource, resolved at device build time.
pub time_source: vm_resource::Resource<chipset_resources::CmosRtcTimeSourceHandleKind>,
/// Which CMOS RAM register contains the century register
pub century_reg_idx: u8,
/// Initial state of CMOS RAM
Expand All @@ -1350,8 +1360,8 @@ pub mod options {

/// PIIX4 "flavored" MC146818A compatible RTC + CMOS device
pub struct Piix4CmosRtcDeps {
/// A source of "real time"
pub time_source: Box<dyn InspectableLocalClock>,
/// A time source resource, resolved at device build time.
pub time_source: vm_resource::Resource<chipset_resources::CmosRtcTimeSourceHandleKind>,
/// Initial state of CMOS RAM
pub initial_cmos: Option<[u8; 256]>,
/// Whether enlightened interrupts are enabled. Needed when
Expand Down
Loading