Skip to content

Commit 6c1c2f4

Browse files
authoredFeb 25, 2025··
lib: implement reference TSC enlightenment (#856)
Add a Hyper-V reference time enlightenment to the Hyper-V stack. See the new tsc module's doc comment for details about how the enlightenment operates. Add PHD tests that try to verify both that the enlightenment is present (by querying the current clocksource) and that it keeps time roughly correctly (by comparing host and guest time readings). The latter test also takes readings over a migration to help get some coverage of the time data phase of live migration (though the migration is not currently cross-machine, so this is a little less interesting than it might otherwise be).
1 parent 35ff586 commit 6c1c2f4

File tree

15 files changed

+1001
-45
lines changed

15 files changed

+1001
-45
lines changed
 

‎Cargo.lock

+1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎bin/propolis-cli/src/main.rs

+4-2
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ use newtype_uuid::{GenericUuid, TypedUuid, TypedUuidKind, TypedUuidTag};
1919
use propolis_client::support::nvme_serial_from_str;
2020
use propolis_client::types::{
2121
BlobStorageBackend, Board, Chipset, ComponentV0, CrucibleStorageBackend,
22-
GuestHypervisorInterface, I440Fx, InstanceEnsureRequest,
22+
GuestHypervisorInterface, HyperVFeatureFlag, I440Fx, InstanceEnsureRequest,
2323
InstanceInitializationMethod, InstanceMetadata, InstanceSpecGetResponse,
2424
InstanceSpecV0, NvmeDisk, QemuPvpanic, ReplacementComponent, SerialPort,
2525
SerialPortNumber, VirtioDisk,
@@ -298,7 +298,9 @@ impl VmConfig {
298298
cpus: self.vcpus,
299299
memory_mb: self.memory,
300300
guest_hv_interface: if self.hyperv {
301-
Some(GuestHypervisorInterface::HyperV { features: vec![] })
301+
Some(GuestHypervisorInterface::HyperV {
302+
features: vec![HyperVFeatureFlag::ReferenceTsc],
303+
})
302304
} else {
303305
None
304306
},

‎bin/propolis-server/src/lib/vm/ensure.rs

+18-5
Original file line numberDiff line numberDiff line change
@@ -94,10 +94,14 @@ use std::sync::Arc;
9494
use oximeter::types::ProducerRegistry;
9595
use oximeter_instruments::kstat::KstatSampler;
9696
use propolis::enlightenment::{
97-
bhyve::BhyveGuestInterface, hyperv::HyperV, Enlightenment,
97+
bhyve::BhyveGuestInterface,
98+
hyperv::{Features as HyperVFeatures, HyperV},
99+
Enlightenment,
98100
};
99101
use propolis_api_types::{
100-
instance_spec::components::board::GuestHypervisorInterface,
102+
instance_spec::components::board::{
103+
GuestHypervisorInterface, HyperVFeatureFlag,
104+
},
101105
InstanceEnsureResponse, InstanceMigrateInitiateResponse,
102106
InstanceProperties, InstanceState,
103107
};
@@ -492,14 +496,23 @@ async fn initialize_vm_objects(
492496
let vmm_log = log.new(slog::o!("component" => "vmm"));
493497

494498
let (guest_hv_interface, guest_hv_lifecycle) =
495-
match spec.board.guest_hv_interface {
499+
match &spec.board.guest_hv_interface {
496500
GuestHypervisorInterface::Bhyve => {
497501
let bhyve = Arc::new(BhyveGuestInterface);
498502
let lifecycle = bhyve.clone();
499503
(bhyve as Arc<dyn Enlightenment>, lifecycle.as_lifecycle())
500504
}
501-
GuestHypervisorInterface::HyperV { .. } => {
502-
let hyperv = Arc::new(HyperV::new(&vmm_log));
505+
GuestHypervisorInterface::HyperV { features } => {
506+
let mut hv_features = HyperVFeatures::default();
507+
for f in features {
508+
match f {
509+
HyperVFeatureFlag::ReferenceTsc => {
510+
hv_features.reference_tsc = true
511+
}
512+
}
513+
}
514+
515+
let hyperv = Arc::new(HyperV::new(&vmm_log, hv_features));
503516
let lifecycle = hyperv.clone();
504517
(hyperv as Arc<dyn Enlightenment>, lifecycle.as_lifecycle())
505518
}

‎crates/propolis-api-types/src/instance_spec/components/board.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,9 @@ pub struct CpuidEntry {
115115
PartialEq,
116116
)]
117117
#[serde(deny_unknown_fields)]
118-
pub enum HyperVFeatureFlag {}
118+
pub enum HyperVFeatureFlag {
119+
ReferenceTsc,
120+
}
119121

120122
/// A hypervisor interface to expose to the guest.
121123
#[derive(Clone, Deserialize, Serialize, Debug, JsonSchema, Default)]

‎lib/propolis/src/enlightenment/bhyve.rs

+4-1
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77
//! This interface supplies no special enlightenments; it merely identifies
88
//! itself as a bhyve hypervisor in CPUID leaf 0x4000_0000.
99
10+
use std::sync::Arc;
11+
1012
use cpuid_utils::{
1113
bits::HYPERVISOR_BASE_LEAF, CpuidIdent, CpuidSet, CpuidValues,
1214
};
@@ -16,6 +18,7 @@ use crate::{
1618
common::{Lifecycle, VcpuId},
1719
enlightenment::{AddCpuidError, Enlightenment},
1820
msr::{MsrId, RdmsrOutcome, WrmsrOutcome},
21+
vmm::VmmHdl,
1922
};
2023

2124
/// An implementation of the bhyve guest-hypervisor interface. This interface
@@ -59,5 +62,5 @@ impl Enlightenment for BhyveGuestInterface {
5962
WrmsrOutcome::NotHandled
6063
}
6164

62-
fn attach(&self, _parent: &MemAccessor) {}
65+
fn attach(&self, _parent: &MemAccessor, _vmm_hdl: Arc<VmmHdl>) {}
6366
}

‎lib/propolis/src/enlightenment/hyperv/bits.rs

+18-1
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,7 @@ pub(super) const HYPERV_LEAF_5_VALUES: CpuidValues =
108108
pub(super) const HV_X64_MSR_GUEST_OS_ID: u32 = 0x4000_0000;
109109

110110
/// Specifies the guest physical address at which the guest would like to place
111-
/// the hypercall page. See TLFS section 3.13 and the [`MsrHypercalLValue`]
111+
/// the hypercall page. See TLFS section 3.13 and the [`MsrHypercallValue`]
112112
/// struct.
113113
///
114114
/// Read-write; requires the [`HyperVLeaf3Eax::HYPERCALL`] privilege.
@@ -121,3 +121,20 @@ pub(super) const HV_X64_MSR_HYPERCALL: u32 = 0x4000_0001;
121121
///
122122
/// Read-only; requires the [`HyperVLeaf3Eax::VP_INDEX`] privilege.
123123
pub(super) const HV_X64_MSR_VP_INDEX: u32 = 0x4000_0002;
124+
125+
/// Guests may read this register to obtain the time since this VM was created,
126+
/// in 100-nanosecond units.
127+
///
128+
/// Read-only; requires the [`HyperVLeaf3Eax::PARTITION_REFERENCE_COUNTER`]
129+
/// privilege.
130+
pub(super) const HV_X64_MSR_TIME_REF_COUNT: u32 = 0x4000_0020;
131+
132+
/// Specifies the guest physical address at which the guest would like to place
133+
/// the reference TSC page. See TLFS section 12.7 and the
134+
/// [`MsrReferenceTscValue`] struct.
135+
///
136+
/// Read-write; requires the [`HyperVLeaf3Eax::PARTITION_REFERENCE_TSC`]
137+
/// privilege.
138+
///
139+
/// [`MsrReferenceTscValue`]: super::tsc::MsrReferenceTscValue
140+
pub(super) const HV_X64_MSR_REFERENCE_TSC: u32 = 0x4000_0021;

0 commit comments

Comments
 (0)
Please sign in to comment.