viridian: add implementation of synthetic timers

This patch introduces an implementation of the STIMER0-15_CONFIG/COUNT MSRs
and hence a the first SynIC message source.

The new (and documented) 'stimer' viridian enlightenment group may be
specified to enable this feature.

While in the neighbourhood, this patch adds a missing check for an
attempt to write the time reference count MSR, which should result in an
exception (but not be reported as an unimplemented MSR).

NOTE: It is necessary for correct operation that timer expiration and
      message delivery time-stamping use the same time source as the guest.
      The specification is ambiguous but testing with a Windows 10 1803
      guest has shown that using the partition reference counter as a
      source whilst the guest is using RDTSC and the reference tsc page
      does not work correctly. Therefore the time_now() function is used.
      This implements the algorithm for acquiring partition reference time
      that is documented in the specifiction.

Signed-off-by: Paul Durrant <paul.durrant@citrix.com>
Acked-by: Wei Liu <wei.liu2@citrix.com>
Reviewed-by: Jan Beulich <jbeulich@suse.com>
This commit is contained in:
Paul Durrant authored and Jan Beulich committed 2019-04-05 10:32:20 +02:00
1 parent a85089d0ef
commit 26fba3c855
11 files changed
+517 -13

No files matched your search

+10 -2
View File
@@ -2167,11 +2167,19 @@ This group incorporates the crash control MSRs. These enlightenments
allow Windows to write crash information such that it can be logged allow Windows to write crash information such that it can be logged
by Xen. by Xen.
=item B<stimer>
This set incorporates the SynIC and synthetic timer MSRs. Windows will
use synthetic timers in preference to emulated HPET for a source of
ticks and hence enabling this group will ensure that ticks will be
consistent with use of an enlightened time source (B<time_ref_count> or
B<reference_tsc>).
=item B<defaults> =item B<defaults>
This is a special value that enables the default set of groups, which This is a special value that enables the default set of groups, which
is currently the B<base>, B<freq>, B<time_ref_count>, B<apic_assist> is currently the B<base>, B<freq>, B<time_ref_count>, B<apic_assist>,
and B<crash_ctl> groups. B<crash_ctl> and B<stimer> groups.
=item B<all> =item B<all>
+6
View File
@@ -324,6 +324,12 @@
*/ */
#define LIBXL_HAVE_VIRIDIAN_SYNIC 1 #define LIBXL_HAVE_VIRIDIAN_SYNIC 1
/*
* LIBXL_HAVE_VIRIDIAN_STIMER indicates that the 'stimer' value
* is present in the viridian enlightenment enumeration.
*/
#define LIBXL_HAVE_VIRIDIAN_STIMER 1
/* /*
* LIBXL_HAVE_BUILDINFO_HVM_ACPI_LAPTOP_SLATE indicates that * LIBXL_HAVE_BUILDINFO_HVM_ACPI_LAPTOP_SLATE indicates that
* libxl_domain_build_info has the u.hvm.acpi_laptop_slate field. * libxl_domain_build_info has the u.hvm.acpi_laptop_slate field.
+4
View File
@@ -269,6 +269,7 @@ static int hvm_set_viridian_features(libxl__gc *gc, uint32_t domid,
libxl_bitmap_set(&enlightenments, LIBXL_VIRIDIAN_ENLIGHTENMENT_TIME_REF_COUNT); libxl_bitmap_set(&enlightenments, LIBXL_VIRIDIAN_ENLIGHTENMENT_TIME_REF_COUNT);
libxl_bitmap_set(&enlightenments, LIBXL_VIRIDIAN_ENLIGHTENMENT_APIC_ASSIST); libxl_bitmap_set(&enlightenments, LIBXL_VIRIDIAN_ENLIGHTENMENT_APIC_ASSIST);
libxl_bitmap_set(&enlightenments, LIBXL_VIRIDIAN_ENLIGHTENMENT_CRASH_CTL); libxl_bitmap_set(&enlightenments, LIBXL_VIRIDIAN_ENLIGHTENMENT_CRASH_CTL);
libxl_bitmap_set(&enlightenments, LIBXL_VIRIDIAN_ENLIGHTENMENT_STIMER);
} }
libxl_for_each_set_bit(v, info->u.hvm.viridian_enable) { libxl_for_each_set_bit(v, info->u.hvm.viridian_enable) {
@@ -320,6 +321,9 @@ static int hvm_set_viridian_features(libxl__gc *gc, uint32_t domid,
if (libxl_bitmap_test(&enlightenments, LIBXL_VIRIDIAN_ENLIGHTENMENT_SYNIC)) if (libxl_bitmap_test(&enlightenments, LIBXL_VIRIDIAN_ENLIGHTENMENT_SYNIC))
mask |= HVMPV_synic; mask |= HVMPV_synic;
if (libxl_bitmap_test(&enlightenments, LIBXL_VIRIDIAN_ENLIGHTENMENT_STIMER))
mask |= HVMPV_time_ref_count | HVMPV_synic | HVMPV_stimer;
if (mask != 0 && if (mask != 0 &&
xc_hvm_param_set(CTX->xch, xc_hvm_param_set(CTX->xch,
domid, domid,
+1
View File
@@ -236,6 +236,7 @@ libxl_viridian_enlightenment = Enumeration("viridian_enlightenment", [
(5, "apic_assist"), (5, "apic_assist"),
(6, "crash_ctl"), (6, "crash_ctl"),
(7, "synic"), (7, "synic"),
(8, "stimer"),
]) ])
libxl_hdtype = Enumeration("hdtype", [ libxl_hdtype = Enumeration("hdtype", [
+8 -1
View File
@@ -74,6 +74,11 @@
int viridian_synic_wrmsr(struct vcpu *v, uint32_t idx, uint64_t val); int viridian_synic_wrmsr(struct vcpu *v, uint32_t idx, uint64_t val);
int viridian_synic_rdmsr(const struct vcpu *v, uint32_t idx, uint64_t *val); int viridian_synic_rdmsr(const struct vcpu *v, uint32_t idx, uint64_t *val);
bool viridian_synic_deliver_timer_msg(struct vcpu *v, unsigned int sintx,
unsigned int index,
uint64_t expiration,
uint64_t delivery);
int viridian_synic_vcpu_init(const struct vcpu *v); int viridian_synic_vcpu_init(const struct vcpu *v);
int viridian_synic_domain_init(const struct domain *d); int viridian_synic_domain_init(const struct domain *d);
@@ -93,7 +98,9 @@ void viridian_synic_load_domain_ctxt(
int viridian_time_wrmsr(struct vcpu *v, uint32_t idx, uint64_t val); int viridian_time_wrmsr(struct vcpu *v, uint32_t idx, uint64_t val);
int viridian_time_rdmsr(const struct vcpu *v, uint32_t idx, uint64_t *val); int viridian_time_rdmsr(const struct vcpu *v, uint32_t idx, uint64_t *val);
int viridian_time_vcpu_init(const struct vcpu *v); void viridian_time_poll_timers(struct vcpu *v);
int viridian_time_vcpu_init(struct vcpu *v);
int viridian_time_domain_init(const struct domain *d); int viridian_time_domain_init(const struct domain *d);
void viridian_time_vcpu_deinit(const struct vcpu *v); void viridian_time_vcpu_deinit(const struct vcpu *v);
+53 -2
View File
@@ -346,9 +346,60 @@ void viridian_synic_domain_deinit(const struct domain *d)
{ {
} }
void viridian_synic_poll(const struct vcpu *v) void viridian_synic_poll(struct vcpu *v)
{ {
/* There are currently no message sources */ viridian_time_poll_timers(v);
}
bool viridian_synic_deliver_timer_msg(struct vcpu *v, unsigned int sintx,
unsigned int index,
uint64_t expiration,
uint64_t delivery)
{
struct viridian_vcpu *vv = v->arch.hvm.viridian;
const union viridian_sint_msr *vs = &vv->sint[sintx];
HV_MESSAGE *msg = vv->simp.ptr;
struct {
uint32_t TimerIndex;
uint32_t Reserved;
uint64_t ExpirationTime;
uint64_t DeliveryTime;
} payload = {
.TimerIndex = index,
.ExpirationTime = expiration,
.DeliveryTime = delivery,
};
if ( test_bit(sintx, &vv->msg_pending) )
return false;
/*
* To avoid using an atomic test-and-set, and barrier before calling
* vlapic_set_irq(), this function must be called in context of the
* vcpu receiving the message.
*/
ASSERT(v == current);
msg += sintx;
if ( msg->Header.MessageType != HvMessageTypeNone )
{
msg->Header.MessageFlags.MessagePending = 1;
__set_bit(sintx, &vv->msg_pending);
return false;
}
msg->Header.MessageType = HvMessageTimerExpired;
msg->Header.MessageFlags.MessagePending = 0;
msg->Header.PayloadSize = sizeof(payload);
BUILD_BUG_ON(sizeof(payload) > sizeof(msg->Payload));
memcpy(msg->Payload, &payload, sizeof(payload));
if ( !vs->mask )
vlapic_set_irq(vcpu_vlapic(v), vs->vector, 0);
return true;
} }
bool viridian_synic_is_auto_eoi_sint(const struct vcpu *v, bool viridian_synic_is_auto_eoi_sint(const struct vcpu *v,
+391 -6
View File
@@ -12,6 +12,7 @@
#include <xen/version.h> #include <xen/version.h>
#include <asm/apic.h> #include <asm/apic.h>
#include <asm/event.h>
#include <asm/hvm/support.h> #include <asm/hvm/support.h>
#include "private.h" #include "private.h"
@@ -27,8 +28,10 @@ typedef struct _HV_REFERENCE_TSC_PAGE
static void update_reference_tsc(struct domain *d, bool initialize) static void update_reference_tsc(struct domain *d, bool initialize)
{ {
const struct viridian_page *rt = &d->arch.hvm.viridian->reference_tsc; struct viridian_domain *vd = d->arch.hvm.viridian;
const struct viridian_page *rt = &vd->reference_tsc;
HV_REFERENCE_TSC_PAGE *p = rt->ptr; HV_REFERENCE_TSC_PAGE *p = rt->ptr;
uint32_t seq;
if ( initialize ) if ( initialize )
clear_page(p); clear_page(p);
@@ -59,6 +62,8 @@ static void update_reference_tsc(struct domain *d, bool initialize)
printk(XENLOG_G_INFO "d%d: VIRIDIAN REFERENCE_TSC: invalidated\n", printk(XENLOG_G_INFO "d%d: VIRIDIAN REFERENCE_TSC: invalidated\n",
d->domain_id); d->domain_id);
vd->reference_tsc_valid = false;
return; return;
} }
@@ -72,11 +77,14 @@ static void update_reference_tsc(struct domain *d, bool initialize)
* ticks per 100ns shifted left by 64. * ticks per 100ns shifted left by 64.
*/ */
p->TscScale = ((10000ul << 32) / d->arch.tsc_khz) << 32; p->TscScale = ((10000ul << 32) / d->arch.tsc_khz) << 32;
smp_wmb();
p->TscSequence++; seq = p->TscSequence + 1;
if ( p->TscSequence == 0xFFFFFFFF || if ( seq == 0xFFFFFFFF || seq == 0 ) /* Avoid both 'invalid' values */
p->TscSequence == 0 ) /* Avoid both 'invalid' values */ seq = 1;
p->TscSequence = 1;
p->TscSequence = seq;
vd->reference_tsc_valid = true;
} }
static int64_t raw_trc_val(const struct domain *d) static int64_t raw_trc_val(const struct domain *d)
@@ -118,18 +126,253 @@ static int64_t time_ref_count(const struct domain *d)
return raw_trc_val(d) + trc->off; return raw_trc_val(d) + trc->off;
} }
/*
* The specification says: "The partition reference time is computed
* by the following formula:
*
* ReferenceTime = ((VirtualTsc * TscScale) >> 64) + TscOffset
*
* The multiplication is a 64 bit multiplication, which results in a
* 128 bit number which is then shifted 64 times to the right to obtain
* the high 64 bits."
*/
static uint64_t scale_tsc(uint64_t tsc, uint64_t scale, uint64_t offset)
{
uint64_t result;
/*
* Quadword MUL takes an implicit operand in RAX, and puts the result
* in RDX:RAX. Because we only want the result of the multiplication
* after shifting right by 64 bits, we therefore only need the content
* of RDX.
*/
asm ( "mulq %[scale]"
: "+a" (tsc), "=d" (result)
: [scale] "rm" (scale) );
return result + offset;
}
static uint64_t time_now(struct domain *d)
{
uint64_t tsc, scale;
/*
* If the reference TSC page is not enabled, or has been invalidated
* fall back to the partition reference counter.
*/
if ( !d->arch.hvm.viridian->reference_tsc_valid )
return time_ref_count(d);
/* Otherwise compute reference time in the same way the guest would */
tsc = hvm_get_guest_tsc(pt_global_vcpu_target(d));
scale = ((10000ul << 32) / d->arch.tsc_khz) << 32;
return scale_tsc(tsc, scale, 0);
}
static void stop_stimer(struct viridian_stimer *vs)
{
if ( !vs->started )
return;
stop_timer(&vs->timer);
vs->started = false;
}
static void stimer_expire(void *data)
{
struct viridian_stimer *vs = data;
struct vcpu *v = vs->v;
struct viridian_vcpu *vv = v->arch.hvm.viridian;
unsigned int stimerx = vs - &vv->stimer[0];
set_bit(stimerx, &vv->stimer_pending);
vcpu_kick(v);
}
static void start_stimer(struct viridian_stimer *vs)
{
const struct vcpu *v = vs->v;
struct viridian_vcpu *vv = v->arch.hvm.viridian;
unsigned int stimerx = vs - &vv->stimer[0];
int64_t now = time_now(v->domain);
int64_t expiration;
s_time_t timeout;
if ( !test_and_set_bit(stimerx, &vv->stimer_enabled) )
printk(XENLOG_G_INFO "%pv: VIRIDIAN STIMER%u: enabled\n", v,
stimerx);
if ( vs->config.periodic )
{
/*
* The specification says that if the timer is lazy then we
* skip over any missed expirations so we can treat this case
* as the same as if the timer is currently stopped, i.e. we
* just schedule expiration to be 'count' ticks from now.
*/
if ( !vs->started || vs->config.lazy )
expiration = now + vs->count;
else
{
unsigned int missed = 0;
/*
* The timer is already started, so we're re-scheduling.
* Hence advance the timer expiration by one tick.
*/
expiration = vs->expiration + vs->count;
/* Now check to see if any expirations have been missed */
if ( expiration - now <= 0 )
missed = ((now - expiration) / vs->count) + 1;
/*
* The specification says that if the timer is not lazy then
* a non-zero missed count should be used to reduce the period
* of the timer until it catches up, unless the count has
* reached a 'significant number', in which case the timer
* should be treated as lazy. Unfortunately the specification
* does not state what that number is so the choice of number
* here is a pure guess.
*/
if ( missed > 3 )
expiration = now + vs->count;
else if ( missed )
expiration = now + (vs->count / missed);
}
}
else
{
expiration = vs->count;
if ( expiration - now <= 0 )
{
vs->expiration = expiration;
stimer_expire(vs);
return;
}
}
ASSERT(expiration - now > 0);
vs->expiration = expiration;
timeout = (expiration - now) * 100ull;
vs->started = true;
clear_bit(stimerx, &vv->stimer_pending);
migrate_timer(&vs->timer, v->processor);
set_timer(&vs->timer, timeout + NOW());
}
static void poll_stimer(struct vcpu *v, unsigned int stimerx)
{
struct viridian_vcpu *vv = v->arch.hvm.viridian;
struct viridian_stimer *vs = &vv->stimer[stimerx];
/*
* Timer expiry may race with the timer being disabled. If the timer
* is disabled make sure the pending bit is cleared to avoid re-
* polling.
*/
if ( !vs->config.enabled )
{
clear_bit(stimerx, &vv->stimer_pending);
return;
}
if ( !test_bit(stimerx, &vv->stimer_pending) )
return;
if ( !viridian_synic_deliver_timer_msg(v, vs->config.sintx,
stimerx, vs->expiration,
time_now(v->domain)) )
return;
clear_bit(stimerx, &vv->stimer_pending);
if ( vs->config.periodic )
start_stimer(vs);
else
vs->config.enabled = 0;
}
void viridian_time_poll_timers(struct vcpu *v)
{
struct viridian_vcpu *vv = v->arch.hvm.viridian;
unsigned int i;
if ( !vv->stimer_pending )
return;
for ( i = 0; i < ARRAY_SIZE(vv->stimer); i++ )
poll_stimer(v, i);
}
void viridian_time_vcpu_freeze(struct vcpu *v)
{
struct viridian_vcpu *vv = v->arch.hvm.viridian;
unsigned int i;
if ( !is_viridian_vcpu(v) ||
!(viridian_feature_mask(v->domain) & HVMPV_stimer) )
return;
for ( i = 0; i < ARRAY_SIZE(vv->stimer); i++ )
{
struct viridian_stimer *vs = &vv->stimer[i];
if ( vs->started )
stop_timer(&vs->timer);
}
}
void viridian_time_vcpu_thaw(struct vcpu *v)
{
struct viridian_vcpu *vv = v->arch.hvm.viridian;
unsigned int i;
if ( !is_viridian_vcpu(v) ||
!(viridian_feature_mask(v->domain) & HVMPV_stimer) )
return;
for ( i = 0; i < ARRAY_SIZE(vv->stimer); i++ )
{
struct viridian_stimer *vs = &vv->stimer[i];
if ( vs->config.enabled )
start_stimer(vs);
}
}
void viridian_time_domain_freeze(const struct domain *d) void viridian_time_domain_freeze(const struct domain *d)
{ {
struct vcpu *v;
if ( !is_viridian_domain(d) )
return;
for_each_vcpu ( d, v )
viridian_time_vcpu_freeze(v);
time_ref_count_freeze(d); time_ref_count_freeze(d);
} }
void viridian_time_domain_thaw(const struct domain *d) void viridian_time_domain_thaw(const struct domain *d)
{ {
struct vcpu *v;
if ( !is_viridian_domain(d) )
return;
time_ref_count_thaw(d); time_ref_count_thaw(d);
for_each_vcpu ( d, v )
viridian_time_vcpu_thaw(v);
} }
int viridian_time_wrmsr(struct vcpu *v, uint32_t idx, uint64_t val) int viridian_time_wrmsr(struct vcpu *v, uint32_t idx, uint64_t val)
{ {
struct viridian_vcpu *vv = v->arch.hvm.viridian;
struct domain *d = v->domain; struct domain *d = v->domain;
struct viridian_domain *vd = d->arch.hvm.viridian; struct viridian_domain *vd = d->arch.hvm.viridian;
@@ -149,6 +392,61 @@ int viridian_time_wrmsr(struct vcpu *v, uint32_t idx, uint64_t val)
} }
break; break;
case HV_X64_MSR_TIME_REF_COUNT:
return X86EMUL_EXCEPTION;
case HV_X64_MSR_STIMER0_CONFIG:
case HV_X64_MSR_STIMER1_CONFIG:
case HV_X64_MSR_STIMER2_CONFIG:
case HV_X64_MSR_STIMER3_CONFIG:
{
unsigned int stimerx = (idx - HV_X64_MSR_STIMER0_CONFIG) / 2;
struct viridian_stimer *vs =
&array_access_nospec(vv->stimer, stimerx);
if ( !(viridian_feature_mask(d) & HVMPV_stimer) )
return X86EMUL_EXCEPTION;
stop_stimer(vs);
vs->config.raw = val;
if ( !vs->config.sintx )
vs->config.enabled = 0;
if ( vs->config.enabled )
start_stimer(vs);
break;
}
case HV_X64_MSR_STIMER0_COUNT:
case HV_X64_MSR_STIMER1_COUNT:
case HV_X64_MSR_STIMER2_COUNT:
case HV_X64_MSR_STIMER3_COUNT:
{
unsigned int stimerx = (idx - HV_X64_MSR_STIMER0_CONFIG) / 2;
struct viridian_stimer *vs =
&array_access_nospec(vv->stimer, stimerx);
if ( !(viridian_feature_mask(d) & HVMPV_stimer) )
return X86EMUL_EXCEPTION;
stop_stimer(vs);
vs->count = val;
if ( !vs->count )
vs->config.enabled = 0;
else if ( vs->config.auto_enable )
vs->config.enabled = 1;
if ( vs->config.enabled )
start_stimer(vs);
break;
}
default: default:
gdprintk(XENLOG_INFO, "%s: unimplemented MSR %#x (%016"PRIx64")\n", gdprintk(XENLOG_INFO, "%s: unimplemented MSR %#x (%016"PRIx64")\n",
__func__, idx, val); __func__, idx, val);
@@ -160,6 +458,7 @@ int viridian_time_wrmsr(struct vcpu *v, uint32_t idx, uint64_t val)
int viridian_time_rdmsr(const struct vcpu *v, uint32_t idx, uint64_t *val) int viridian_time_rdmsr(const struct vcpu *v, uint32_t idx, uint64_t *val)
{ {
const struct viridian_vcpu *vv = v->arch.hvm.viridian;
const struct domain *d = v->domain; const struct domain *d = v->domain;
struct viridian_domain *vd = d->arch.hvm.viridian; struct viridian_domain *vd = d->arch.hvm.viridian;
@@ -201,6 +500,46 @@ int viridian_time_rdmsr(const struct vcpu *v, uint32_t idx, uint64_t *val)
break; break;
} }
case HV_X64_MSR_STIMER0_CONFIG:
case HV_X64_MSR_STIMER1_CONFIG:
case HV_X64_MSR_STIMER2_CONFIG:
case HV_X64_MSR_STIMER3_CONFIG:
{
unsigned int stimerx = (idx - HV_X64_MSR_STIMER0_CONFIG) / 2;
const struct viridian_stimer *vs =
&array_access_nospec(vv->stimer, stimerx);
union viridian_stimer_config_msr config = vs->config;
if ( !(viridian_feature_mask(d) & HVMPV_stimer) )
return X86EMUL_EXCEPTION;
/*
* If the timer is single-shot and it has expired, make sure
* the enabled flag is clear.
*/
if ( !config.periodic && test_bit(stimerx, &vv->stimer_pending) )
config.enabled = 0;
*val = config.raw;
break;
}
case HV_X64_MSR_STIMER0_COUNT:
case HV_X64_MSR_STIMER1_COUNT:
case HV_X64_MSR_STIMER2_COUNT:
case HV_X64_MSR_STIMER3_COUNT:
{
unsigned int stimerx = (idx - HV_X64_MSR_STIMER0_CONFIG) / 2;
const struct viridian_stimer *vs =
&array_access_nospec(vv->stimer, stimerx);
if ( !(viridian_feature_mask(d) & HVMPV_stimer) )
return X86EMUL_EXCEPTION;
*val = vs->count;
break;
}
default: default:
gdprintk(XENLOG_INFO, "%s: unimplemented MSR %#x\n", __func__, idx); gdprintk(XENLOG_INFO, "%s: unimplemented MSR %#x\n", __func__, idx);
return X86EMUL_EXCEPTION; return X86EMUL_EXCEPTION;
@@ -209,8 +548,19 @@ int viridian_time_rdmsr(const struct vcpu *v, uint32_t idx, uint64_t *val)
return X86EMUL_OKAY; return X86EMUL_OKAY;
} }
int viridian_time_vcpu_init(const struct vcpu *v) int viridian_time_vcpu_init(struct vcpu *v)
{ {
struct viridian_vcpu *vv = v->arch.hvm.viridian;
unsigned int i;
for ( i = 0; i < ARRAY_SIZE(vv->stimer); i++ )
{
struct viridian_stimer *vs = &vv->stimer[i];
vs->v = v;
init_timer(&vs->timer, stimer_expire, vs, v->processor);
}
return 0; return 0;
} }
@@ -221,6 +571,16 @@ int viridian_time_domain_init(const struct domain *d)
void viridian_time_vcpu_deinit(const struct vcpu *v) void viridian_time_vcpu_deinit(const struct vcpu *v)
{ {
struct viridian_vcpu *vv = v->arch.hvm.viridian;
unsigned int i;
for ( i = 0; i < ARRAY_SIZE(vv->stimer); i++ )
{
struct viridian_stimer *vs = &vv->stimer[i];
kill_timer(&vs->timer);
vs->v = NULL;
}
} }
void viridian_time_domain_deinit(const struct domain *d) void viridian_time_domain_deinit(const struct domain *d)
@@ -231,11 +591,36 @@ void viridian_time_domain_deinit(const struct domain *d)
void viridian_time_save_vcpu_ctxt( void viridian_time_save_vcpu_ctxt(
const struct vcpu *v, struct hvm_viridian_vcpu_context *ctxt) const struct vcpu *v, struct hvm_viridian_vcpu_context *ctxt)
{ {
const struct viridian_vcpu *vv = v->arch.hvm.viridian;
unsigned int i;
BUILD_BUG_ON(ARRAY_SIZE(vv->stimer) !=
ARRAY_SIZE(ctxt->stimer_config_msr));
BUILD_BUG_ON(ARRAY_SIZE(vv->stimer) !=
ARRAY_SIZE(ctxt->stimer_count_msr));
for ( i = 0; i < ARRAY_SIZE(vv->stimer); i++ )
{
const struct viridian_stimer *vs = &vv->stimer[i];
ctxt->stimer_config_msr[i] = vs->config.raw;
ctxt->stimer_count_msr[i] = vs->count;
}
} }
void viridian_time_load_vcpu_ctxt( void viridian_time_load_vcpu_ctxt(
struct vcpu *v, const struct hvm_viridian_vcpu_context *ctxt) struct vcpu *v, const struct hvm_viridian_vcpu_context *ctxt)
{ {
struct viridian_vcpu *vv = v->arch.hvm.viridian;
unsigned int i;
for ( i = 0; i < ARRAY_SIZE(vv->stimer); i++ )
{
struct viridian_stimer *vs = &vv->stimer[i];
vs->config.raw = ctxt->stimer_config_msr[i];
vs->count = ctxt->stimer_count_msr[i];
}
} }
void viridian_time_save_domain_ctxt( void viridian_time_save_domain_ctxt(
+5
View File
@@ -181,6 +181,8 @@ void cpuid_viridian_leaves(const struct vcpu *v, uint32_t leaf,
mask.AccessPartitionReferenceTsc = 1; mask.AccessPartitionReferenceTsc = 1;
if ( viridian_feature_mask(d) & HVMPV_synic ) if ( viridian_feature_mask(d) & HVMPV_synic )
mask.AccessSynicRegs = 1; mask.AccessSynicRegs = 1;
if ( viridian_feature_mask(d) & HVMPV_stimer )
mask.AccessSyntheticTimerRegs = 1;
u.mask = mask; u.mask = mask;
@@ -322,6 +324,8 @@ int guest_wrmsr_viridian(struct vcpu *v, uint32_t idx, uint64_t val)
case HV_X64_MSR_TSC_FREQUENCY: case HV_X64_MSR_TSC_FREQUENCY:
case HV_X64_MSR_APIC_FREQUENCY: case HV_X64_MSR_APIC_FREQUENCY:
case HV_X64_MSR_REFERENCE_TSC: case HV_X64_MSR_REFERENCE_TSC:
case HV_X64_MSR_TIME_REF_COUNT:
case HV_X64_MSR_STIMER0_CONFIG ... HV_X64_MSR_STIMER3_COUNT:
return viridian_time_wrmsr(v, idx, val); return viridian_time_wrmsr(v, idx, val);
case HV_X64_MSR_CRASH_P0: case HV_X64_MSR_CRASH_P0:
@@ -403,6 +407,7 @@ int guest_rdmsr_viridian(const struct vcpu *v, uint32_t idx, uint64_t *val)
case HV_X64_MSR_APIC_FREQUENCY: case HV_X64_MSR_APIC_FREQUENCY:
case HV_X64_MSR_REFERENCE_TSC: case HV_X64_MSR_REFERENCE_TSC:
case HV_X64_MSR_TIME_REF_COUNT: case HV_X64_MSR_TIME_REF_COUNT:
case HV_X64_MSR_STIMER0_CONFIG ... HV_X64_MSR_STIMER3_COUNT:
return viridian_time_rdmsr(v, idx, val); return viridian_time_rdmsr(v, idx, val);
case HV_X64_MSR_CRASH_P0: case HV_X64_MSR_CRASH_P0:
+31 -1
View File
@@ -40,6 +40,32 @@ union viridian_sint_msr
}; };
}; };
union viridian_stimer_config_msr
{
uint64_t raw;
struct
{
uint64_t enabled:1;
uint64_t periodic:1;
uint64_t lazy:1;
uint64_t auto_enable:1;
uint64_t vector:8;
uint64_t direct_mode:1;
uint64_t reserved_zero1:3;
uint64_t sintx:4;
uint64_t reserved_zero2:44;
};
};
struct viridian_stimer {
struct vcpu *v;
struct timer timer;
union viridian_stimer_config_msr config;
uint64_t count;
uint64_t expiration;
bool started;
};
struct viridian_vcpu struct viridian_vcpu
{ {
struct viridian_page vp_assist; struct viridian_page vp_assist;
@@ -51,6 +77,9 @@ struct viridian_vcpu
struct viridian_page simp; struct viridian_page simp;
union viridian_sint_msr sint[16]; union viridian_sint_msr sint[16];
uint8_t vector_to_sintx[256]; uint8_t vector_to_sintx[256];
struct viridian_stimer stimer[4];
unsigned int stimer_enabled;
unsigned int stimer_pending;
uint64_t crash_param[5]; uint64_t crash_param[5];
}; };
@@ -87,6 +116,7 @@ struct viridian_domain
union viridian_page_msr hypercall_gpa; union viridian_page_msr hypercall_gpa;
struct viridian_time_ref_count time_ref_count; struct viridian_time_ref_count time_ref_count;
struct viridian_page reference_tsc; struct viridian_page reference_tsc;
bool reference_tsc_valid;
}; };
void cpuid_viridian_leaves(const struct vcpu *v, uint32_t leaf, void cpuid_viridian_leaves(const struct vcpu *v, uint32_t leaf,
@@ -111,7 +141,7 @@ void viridian_apic_assist_set(const struct vcpu *v);
bool viridian_apic_assist_completed(const struct vcpu *v); bool viridian_apic_assist_completed(const struct vcpu *v);
void viridian_apic_assist_clear(const struct vcpu *v); void viridian_apic_assist_clear(const struct vcpu *v);
void viridian_synic_poll(const struct vcpu *v); void viridian_synic_poll(struct vcpu *v);
bool viridian_synic_is_auto_eoi_sint(const struct vcpu *v, bool viridian_synic_is_auto_eoi_sint(const struct vcpu *v,
unsigned int vector); unsigned int vector);
void viridian_synic_ack_sint(const struct vcpu *v, unsigned int vector); void viridian_synic_ack_sint(const struct vcpu *v, unsigned int vector);
+2
View File
@@ -604,6 +604,8 @@ struct hvm_viridian_vcpu_context {
uint8_t _pad[7]; uint8_t _pad[7];
uint64_t simp_msr; uint64_t simp_msr;
uint64_t sint_msr[16]; uint64_t sint_msr[16];
uint64_t stimer_config_msr[4];
uint64_t stimer_count_msr[4];
}; };
DECLARE_HVM_SAVE_TYPE(VIRIDIAN_VCPU, 17, struct hvm_viridian_vcpu_context); DECLARE_HVM_SAVE_TYPE(VIRIDIAN_VCPU, 17, struct hvm_viridian_vcpu_context);
+6 -1
View File
@@ -150,6 +150,10 @@
#define _HVMPV_synic 7 #define _HVMPV_synic 7
#define HVMPV_synic (1 << _HVMPV_synic) #define HVMPV_synic (1 << _HVMPV_synic)
/* Enable STIMER MSRs */
#define _HVMPV_stimer 8
#define HVMPV_stimer (1 << _HVMPV_stimer)
#define HVMPV_feature_mask \ #define HVMPV_feature_mask \
(HVMPV_base_freq | \ (HVMPV_base_freq | \
HVMPV_no_freq | \ HVMPV_no_freq | \
@@ -158,7 +162,8 @@
HVMPV_hcall_remote_tlb_flush | \ HVMPV_hcall_remote_tlb_flush | \
HVMPV_apic_assist | \ HVMPV_apic_assist | \
HVMPV_crash_ctl | \ HVMPV_crash_ctl | \
HVMPV_synic) HVMPV_synic | \
HVMPV_stimer)
#endif #endif
Reference in new issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.