diff options
author | Keir Fraser <keir@xen.org> | 2010-12-15 11:01:59 +0000 |
---|---|---|
committer | Keir Fraser <keir@xen.org> | 2010-12-15 11:01:59 +0000 |
commit | 464242cd16463dde8745450e9b199cb3dc6e6a06 (patch) | |
tree | d12e7e352bd7eeeee3f58508cd43c74b2af56a83 | |
parent | 3c5da143be896254ef8e86e2a0e45dae6322b3d0 (diff) | |
download | xen-464242cd16463dde8745450e9b199cb3dc6e6a06.tar.gz xen-464242cd16463dde8745450e9b199cb3dc6e6a06.tar.bz2 xen-464242cd16463dde8745450e9b199cb3dc6e6a06.zip |
x86 hvm: Emulate MSR_IA32_TSC_DEADLINE
Accesses to MSR_IA32_TSC_DEADLINE are trapped, with value stored in a
new field vlapic->hw.tdt_msr. vlapic->pt is reused in one shot mode
for vtdt to trigger expire events.
For details, please refer to the Intel Architectures Software
Developer's Manual 3A, 10.5.4.1 TSC-Deadline Mode.
Signed-off-by: Wei Gang <gang.wei@intel.com>
-rw-r--r-- | xen/arch/x86/hvm/hvm.c | 8 | ||||
-rw-r--r-- | xen/arch/x86/hvm/vlapic.c | 120 | ||||
-rw-r--r-- | xen/include/asm-x86/hvm/vlapic.h | 2 | ||||
-rw-r--r-- | xen/include/public/arch-x86/hvm/save.h | 1 |
4 files changed, 127 insertions, 4 deletions
diff --git a/xen/arch/x86/hvm/hvm.c b/xen/arch/x86/hvm/hvm.c index 91bcb92997..93b724ee05 100644 --- a/xen/arch/x86/hvm/hvm.c +++ b/xen/arch/x86/hvm/hvm.c @@ -2235,6 +2235,10 @@ int hvm_msr_read_intercept(unsigned int msr, uint64_t *msr_content) goto gp_fault; break; + case MSR_IA32_TSC_DEADLINE: + *msr_content = vlapic_tdt_msr_get(vcpu_vlapic(v)); + break; + case MSR_IA32_CR_PAT: *msr_content = v->arch.hvm_vcpu.pat_cr; break; @@ -2342,6 +2346,10 @@ int hvm_msr_write_intercept(unsigned int msr, uint64_t msr_content) vlapic_msr_set(vcpu_vlapic(v), msr_content); break; + case MSR_IA32_TSC_DEADLINE: + vlapic_tdt_msr_set(vcpu_vlapic(v), msr_content); + break; + case MSR_IA32_APICBASE_MSR ... MSR_IA32_APICBASE_MSR + 0x3ff: if ( hvm_x2apic_msr_write(v, msr, msr_content) ) goto gp_fault; diff --git a/xen/arch/x86/hvm/vlapic.c b/xen/arch/x86/hvm/vlapic.c index 034dfd64a7..880693f49c 100644 --- a/xen/arch/x86/hvm/vlapic.c +++ b/xen/arch/x86/hvm/vlapic.c @@ -79,7 +79,16 @@ static unsigned int vlapic_lvt_mask[VLAPIC_LVT_NUM] = (vlapic_get_reg(vlapic, lvt_type) & APIC_MODE_MASK) #define vlapic_lvtt_period(vlapic) \ - (vlapic_get_reg(vlapic, APIC_LVTT) & APIC_TIMER_MODE_PERIODIC) + ((vlapic_get_reg(vlapic, APIC_LVTT) & APIC_TIMER_MODE_MASK) \ + == APIC_TIMER_MODE_PERIODIC) + +#define vlapic_lvtt_oneshot(vlapic) \ + ((vlapic_get_reg(vlapic, APIC_LVTT) & APIC_TIMER_MODE_MASK) \ + == APIC_TIMER_MODE_ONESHOT) + +#define vlapic_lvtt_tdt(vlapic) \ + ((vlapic_get_reg(vlapic, APIC_LVTT) & APIC_TIMER_MODE_MASK) \ + == APIC_TIMER_MODE_TSC_DEADLINE) /* @@ -481,9 +490,20 @@ static void vlapic_read_aligned( break; case APIC_TMCCT: /* Timer CCR */ + if ( !vlapic_lvtt_oneshot(vlapic) && !vlapic_lvtt_period(vlapic) ) + { + *result = 0; + break; + } *result = vlapic_get_tmcct(vlapic); break; + case APIC_TMICT: /* Timer ICR */ + if ( !vlapic_lvtt_oneshot(vlapic) && !vlapic_lvtt_period(vlapic) ) + { + *result = 0; + break; + } default: *result = vlapic_get_reg(vlapic, offset); break; @@ -566,6 +586,12 @@ static void vlapic_pt_cb(struct vcpu *v, void *data) *(s_time_t *)data = hvm_get_guest_time(v); } +static void vlapic_tdt_pt_cb(struct vcpu *v, void *data) +{ + *(s_time_t *)data = hvm_get_guest_time(v); + vcpu_vlapic(v)->hw.tdt_msr = 0; +} + static int vlapic_reg_write(struct vcpu *v, unsigned int offset, unsigned long val) { @@ -657,6 +683,14 @@ static int vlapic_reg_write(struct vcpu *v, break; case APIC_LVTT: /* LVT Timer Reg */ + if ( (vlapic_get_reg(vlapic, offset) & APIC_TIMER_MODE_MASK) != + (val & APIC_TIMER_MODE_MASK) ) + { + destroy_periodic_time(&vlapic->pt); + vlapic_set_reg(vlapic, APIC_TMICT, 0); + vlapic_set_reg(vlapic, APIC_TMCCT, 0); + vlapic->hw.tdt_msr = 0; + } vlapic->pt.irq = val & APIC_VECTOR_MASK; case APIC_LVTTHMR: /* LVT Thermal Monitor */ case APIC_LVTPC: /* LVT Performance Counter */ @@ -680,6 +714,9 @@ static int vlapic_reg_write(struct vcpu *v, { uint64_t period; + if ( !vlapic_lvtt_oneshot(vlapic) && !vlapic_lvtt_period(vlapic) ) + break; + vlapic_set_reg(vlapic, APIC_TMICT, val); if ( val == 0 ) { @@ -844,6 +881,73 @@ void vlapic_msr_set(struct vlapic *vlapic, uint64_t value) "apic base msr is 0x%016"PRIx64, vlapic->hw.apic_base_msr); } +uint64_t vlapic_tdt_msr_get(struct vlapic *vlapic) +{ + if ( !vlapic_lvtt_tdt(vlapic) ) + return 0; + + return vlapic->hw.tdt_msr; +} + +void vlapic_tdt_msr_set(struct vlapic *vlapic, uint64_t value) +{ + uint64_t guest_tsc; + uint64_t guest_time; + struct vcpu *v = vlapic_vcpu(vlapic); + + /* may need to exclude some other conditions like vlapic->hw.disabled */ + if ( !vlapic_lvtt_tdt(vlapic) ) + { + HVM_DBG_LOG(DBG_LEVEL_VLAPIC_TIMER, "ignore tsc deadline msr write"); + return; + } + + /* new_value = 0, >0 && <= now, > now */ + guest_tsc = hvm_get_guest_tsc(v); + guest_time = hvm_get_guest_time(v); + if ( value > guest_tsc ) + { + uint64_t delta = value - v->arch.hvm_vcpu.cache_tsc_offset; + delta = gtsc_to_gtime(v->domain, delta); + delta = max_t(s64, delta - guest_time, 0); + + HVM_DBG_LOG(DBG_LEVEL_VLAPIC_TIMER, "delta[0x%016"PRIx64"]", delta); + + vlapic->hw.tdt_msr = value; + /* .... reprogram tdt timer */ + create_periodic_time(v, &vlapic->pt, delta, 0, + vlapic->pt.irq, vlapic_tdt_pt_cb, + &vlapic->timer_last_update); + vlapic->timer_last_update = vlapic->pt.last_plt_gtime; + } + else + { + vlapic->hw.tdt_msr = 0; + + /* trigger a timer event if needed */ + if ( value > 0 ) + { + create_periodic_time(v, &vlapic->pt, 0, 0, + vlapic->pt.irq, vlapic_tdt_pt_cb, + &vlapic->timer_last_update); + vlapic->timer_last_update = vlapic->pt.last_plt_gtime; + } + else + { + /* .... stop tdt timer */ + destroy_periodic_time(&vlapic->pt); + } + + HVM_DBG_LOG(DBG_LEVEL_VLAPIC_TIMER, "value[0x%016"PRIx64"]", value); + } + + HVM_DBG_LOG(DBG_LEVEL_VLAPIC_TIMER, + "tdt_msr[0x%016"PRIx64"]," + " gtsc[0x%016"PRIx64"]," + " gtime[0x%016"PRIx64"]", + vlapic->hw.tdt_msr, guest_tsc, guest_time); +} + static int __vlapic_accept_pic_intr(struct vcpu *v) { struct domain *d = v->domain; @@ -959,10 +1063,18 @@ void vlapic_reset(struct vlapic *vlapic) /* rearm the actimer if needed, after a HVM restore */ static void lapic_rearm(struct vlapic *s) { - unsigned long tmict = vlapic_get_reg(s, APIC_TMICT); - uint64_t period; + unsigned long tmict; + uint64_t period, tdt_msr; s->pt.irq = vlapic_get_reg(s, APIC_LVTT) & APIC_VECTOR_MASK; + + if ( vlapic_lvtt_tdt(s) ) + { + if ( (tdt_msr = vlapic_tdt_msr_get(s)) != 0 ) + vlapic_tdt_msr_set(s, tdt_msr); + return; + } + if ( (tmict = vlapic_get_reg(s, APIC_TMICT)) == 0 ) return; @@ -1023,7 +1135,7 @@ static int lapic_load_hidden(struct domain *d, hvm_domain_context_t *h) } s = vcpu_vlapic(v); - if ( hvm_load_entry(LAPIC, h, &s->hw) != 0 ) + if ( hvm_load_entry_zeroextend(LAPIC, h, &s->hw) != 0 ) return -EINVAL; vmx_vlapic_msr_changed(v); diff --git a/xen/include/asm-x86/hvm/vlapic.h b/xen/include/asm-x86/hvm/vlapic.h index 702ba54276..2ec6482015 100644 --- a/xen/include/asm-x86/hvm/vlapic.h +++ b/xen/include/asm-x86/hvm/vlapic.h @@ -92,6 +92,8 @@ void vlapic_destroy(struct vcpu *v); void vlapic_reset(struct vlapic *vlapic); void vlapic_msr_set(struct vlapic *vlapic, uint64_t value); +void vlapic_tdt_msr_set(struct vlapic *vlapic, uint64_t value); +uint64_t vlapic_tdt_msr_get(struct vlapic *vlapic); int vlapic_accept_pic_intr(struct vcpu *v); diff --git a/xen/include/public/arch-x86/hvm/save.h b/xen/include/public/arch-x86/hvm/save.h index ddf2a63e7c..17a0164209 100644 --- a/xen/include/public/arch-x86/hvm/save.h +++ b/xen/include/public/arch-x86/hvm/save.h @@ -265,6 +265,7 @@ struct hvm_hw_lapic { uint64_t apic_base_msr; uint32_t disabled; /* VLAPIC_xx_DISABLED */ uint32_t timer_divisor; + uint64_t tdt_msr; }; DECLARE_HVM_SAVE_TYPE(LAPIC, 5, struct hvm_hw_lapic); |