In direct mode, the timer asserts an interrupt on expiration rather than
using a SynIC message. It is useful to implement this since Windows 11's
Hyper-V can only use synthetic timers in direct mode.
Signed-off-by: Ross Lagerwall <ross.lagerwall@xxxxxxxxxx>
---
In v2:
* Handle migration from older Xen by introducing a new Viridian flag.
* Added more sanity checks during MSR write and vCPU context load.
xen/arch/x86/hvm/viridian/time.c | 46 ++++++++++++++++++++++++----
xen/arch/x86/hvm/viridian/viridian.c | 3 ++
xen/include/public/hvm/params.h | 7 ++++-
3 files changed, 49 insertions(+), 7 deletions(-)
diff --git a/xen/arch/x86/hvm/viridian/time.c b/xen/arch/x86/hvm/viridian/time.c
index 082528dc9416..4c8352612b19 100644
--- a/xen/arch/x86/hvm/viridian/time.c
+++ b/xen/arch/x86/hvm/viridian/time.c
@@ -223,6 +223,14 @@ static void start_stimer(struct viridian_stimer *vs)
set_timer(&vs->timer, timeout + NOW());
}
+static void stimer_deliver_direct(struct vcpu *v, const struct viridian_stimer *vs)
+{
+ struct vlapic *vlapic = vcpu_vlapic(v);
+
+ if ( vlapic_enabled(vlapic) )
+ vlapic_set_irq(vlapic, vs->config.apic_vector, 0);
+}
+
static void poll_stimer(struct vcpu *v, unsigned int stimerx)
{
struct viridian_vcpu *vv = v->arch.hvm.viridian;
@@ -242,9 +250,11 @@ static void poll_stimer(struct vcpu *v, unsigned int
stimerx)
if ( !test_bit(stimerx, &vv->stimer_pending) )
return;
- if ( !viridian_synic_deliver_timer_msg(v, vs->config.sintx,
- stimerx, vs->expiration,
- time_ref_count(v->domain)) )
+ if ( vs->config.direct_mode )
+ stimer_deliver_direct(v, vs);
+ else if ( !viridian_synic_deliver_timer_msg(v, vs->config.sintx,
+ stimerx, vs->expiration,
+ time_ref_count(v->domain)) )
return;
clear_bit(stimerx, &vv->stimer_pending);
@@ -361,6 +371,7 @@ int viridian_time_wrmsr(struct vcpu *v, uint32_t idx,
uint64_t val)
case HV_X64_MSR_STIMER2_CONFIG:
case HV_X64_MSR_STIMER3_CONFIG:
{
+ union hv_stimer_config new;
unsigned int stimerx = (idx - HV_X64_MSR_STIMER0_CONFIG) / 2;
struct viridian_stimer *vs =
&array_access_nospec(vv->stimer, stimerx);
@@ -368,11 +379,18 @@ int viridian_time_wrmsr(struct vcpu *v, uint32_t idx,
uint64_t val)
if ( !(viridian_feature_mask(d) & HVMPV_stimer) )
return X86EMUL_EXCEPTION;
+ new.as_uint64 = val;
+ if ( new.direct_mode &&
+ !(viridian_feature_mask(d) & HVMPV_stimer_direct) )
+ return X86EMUL_EXCEPTION;