diff --git a/hypervisor/arch/x86/guest/vlapic.c b/hypervisor/arch/x86/guest/vlapic.c index 73ebd7a62..159873eb1 100644 --- a/hypervisor/arch/x86/guest/vlapic.c +++ b/hypervisor/arch/x86/guest/vlapic.c @@ -107,6 +107,8 @@ apicv_batch_set_tmr(struct vlapic *vlapic); */ static void vlapic_set_error(struct vlapic *vlapic, uint32_t mask); +static int vlapic_timer_expired(void *data); + static struct vlapic * vm_lapic_from_vcpu_id(struct vm *vm, int vcpu_id) { @@ -255,6 +257,58 @@ vlapic_lvtt_masked(struct vlapic *vlapic) return !!(vlapic->apic_page->lvt_timer & APIC_LVTT_M); } +static void vlapic_create_timer(struct vlapic *vlapic) +{ + struct vlapic_timer *vlapic_timer; + + if (vlapic == NULL) + return; + + vlapic_timer = &vlapic->vlapic_timer; + memset(vlapic_timer, 0, sizeof(struct vlapic_timer)); + + initialize_timer(&vlapic_timer->timer, + vlapic_timer_expired, vlapic->vcpu, + 0, 0, 0); +} + +static void vlapic_reset_timer(struct vlapic *vlapic) +{ + struct timer *timer; + + if (vlapic == NULL) + return; + + timer = &vlapic->vlapic_timer.timer; + del_timer(timer); + timer->mode = 0; + timer->fire_tsc = 0; + timer->period_in_cycle = 0; +} + +static void vlapic_update_lvtt(struct vlapic *vlapic, + uint32_t val) +{ + uint32_t timer_mode = val & APIC_LVTT_TM; + struct vlapic_timer *vlapic_timer = &vlapic->vlapic_timer; + + if (vlapic_timer->mode != timer_mode) { + struct timer *timer = &vlapic_timer->timer; + + /* + * A write to the LVT Timer Register that changes + * the timer mode disarms the local APIC timer. + */ + del_timer(timer); + timer->mode = (timer_mode == APIC_LVTT_TM_PERIODIC) ? + TICK_MODE_PERIODIC: TICK_MODE_ONESHOT; + timer->fire_tsc = 0; + timer->period_in_cycle = 0; + + vlapic_timer->mode = timer_mode; + } +} + static uint32_t vlapic_get_ccr(__unused struct vlapic *vlapic) { return 0; @@ -264,6 +318,46 @@ static void vlapic_dcr_write_handler(__unused struct vlapic *vlapic) { } +static void vlapic_icrtmr_write_handler(__unused struct vlapic *vlapic) +{ +} + + +static uint64_t vlapic_get_tsc_deadline_msr(struct vlapic *vlapic) +{ + if (!vlapic_lvtt_tsc_deadline(vlapic)) + return 0; + + return (vlapic->vlapic_timer.timer.fire_tsc == 0) ? 0 : + vlapic->vcpu->guest_msrs[IDX_TSC_DEADLINE]; + +} + +static void vlapic_set_tsc_deadline_msr(struct vlapic *vlapic, + uint64_t val) +{ + struct timer *timer; + + if (!vlapic_lvtt_tsc_deadline(vlapic)) + return; + + vlapic->vcpu->guest_msrs[IDX_TSC_DEADLINE] = val; + + timer = &vlapic->vlapic_timer.timer; + del_timer(timer); + + if (val != 0UL) { + struct vcpu_arch *arch = &vlapic->vcpu->arch_vcpu; + + /* transfer guest tsc to host tsc */ + val -= arch->contexts[arch->cur_context].tsc_offset; + timer->fire_tsc = val; + + add_timer(timer); + } else + timer->fire_tsc = 0; +} + static void vlapic_esr_write_handler(struct vlapic *vlapic) { @@ -451,7 +545,9 @@ vlapic_lvt_write_handler(struct vlapic *vlapic, uint32_t offset) "vpic wire mode -> NULL"); } } - } + } else if (offset == APIC_OFFSET_TIMER_LVT) + vlapic_update_lvtt(vlapic, val); + *lvtptr = val; atomic_store_rel_32(&vlapic->lvt_last[idx], val); } @@ -703,10 +799,6 @@ vlapic_trigger_lvt(struct vlapic *vlapic, int vector) return 0; } -static void vlapic_icrtmr_write_handler(__unused struct vlapic *vlapic) -{ -} - /* * This function populates 'dmask' with the set of vcpus that match the * addressing specified by the (dest, phys, lowprio) tuple. @@ -1332,12 +1424,11 @@ vlapic_reset(struct vlapic *vlapic) vlapic_mask_lvts(vlapic); vlapic_reset_tmr(vlapic); + lapic->icr_timer = 0; lapic->dcr_timer = 0; - vlapic_dcr_write_handler(vlapic); + vlapic_reset_timer(vlapic); vlapic->svr_last = lapic->svr; - - initialize_timer(&vlapic->timer, NULL, NULL, 0, 0, 0); } void @@ -1359,6 +1450,8 @@ vlapic_init(struct vlapic *vlapic) if (vlapic->vcpu->vcpu_id == 0) vlapic->msr_apicbase |= APICBASE_BSP; + vlapic_create_timer(vlapic); + vlapic_reset(vlapic); } @@ -1655,7 +1748,7 @@ vlapic_msr(uint32_t msr) } /* interrupt context */ -static int tsc_periodic_time(void *data) +static int vlapic_timer_expired(void *data) { struct vcpu *vcpu = (struct vcpu *)data; struct vlapic *vlapic; @@ -1668,6 +1761,9 @@ static int tsc_periodic_time(void *data) if (!vlapic_lvtt_masked(vlapic)) vlapic_intr_edge(vcpu, lapic->lvt_timer & APIC_LVTT_VECTOR); + if (!vlapic_lvtt_period(vlapic)) + vlapic->vlapic_timer.timer.fire_tsc = 0; + return 0; } @@ -1686,6 +1782,10 @@ vlapic_rdmsr(struct vcpu *vcpu, uint32_t msr, uint64_t *rval) *rval = vlapic_get_apicbase(vlapic); break; + case MSR_IA32_TSC_DEADLINE: + *rval = vlapic_get_tsc_deadline_msr(vlapic); + break; + default: offset = x2apic_msr_to_regoff(msr); error = vlapic_read(vlapic, 0, offset, rval); @@ -1698,7 +1798,7 @@ vlapic_rdmsr(struct vcpu *vcpu, uint32_t msr, uint64_t *rval) int vlapic_wrmsr(struct vcpu *vcpu, uint32_t msr, uint64_t val) { - int error; + int error = 0; uint32_t offset; struct vlapic *vlapic; @@ -1710,26 +1810,7 @@ vlapic_wrmsr(struct vcpu *vcpu, uint32_t msr, uint64_t val) break; case MSR_IA32_TSC_DEADLINE: - error = 0; - if (!vlapic_lvtt_tsc_deadline(vlapic)) - return error; - - del_timer(&vlapic->timer); - if (val != 0UL) { - /* transfer guest tsc to host tsc */ - val -= vcpu->arch_vcpu.contexts[vcpu-> - arch_vcpu.cur_context].tsc_offset; - - initialize_timer(&vlapic->timer, - tsc_periodic_time, (void *)vcpu, - val, TICK_MODE_ONESHOT, 0); - - if (add_timer(&vlapic->timer) != 0) { - pr_err("failed to add timer on VM %d", - vcpu->vm->attr.id); - error = -1; - } - } + vlapic_set_tsc_deadline_msr(vlapic, val); break; default: @@ -1881,7 +1962,7 @@ void vlapic_free(struct vcpu *vcpu) if (vlapic == NULL) return; - del_timer(&vlapic->timer); + del_timer(&vlapic->vlapic_timer.timer); if (!is_vapic_supported()) { unregister_mmio_emulation_handler(vcpu->vm, diff --git a/hypervisor/arch/x86/guest/vlapic_priv.h b/hypervisor/arch/x86/guest/vlapic_priv.h index 3110cdd66..22586933a 100644 --- a/hypervisor/arch/x86/guest/vlapic_priv.h +++ b/hypervisor/arch/x86/guest/vlapic_priv.h @@ -108,6 +108,13 @@ struct vlapic_ops { void (*enable_x2apic_mode)(struct vlapic *vlapic); }; +struct vlapic_timer { + struct timer timer; + uint32_t mode; + uint32_t tmicr; + uint32_t divisor; +}; + struct vlapic { struct vm *vm; struct vcpu *vcpu; @@ -118,7 +125,7 @@ struct vlapic { uint32_t esr_pending; int esr_firing; - struct timer timer; + struct vlapic_timer vlapic_timer; /* * The 'isrvec_stk' is a stack of vectors injected by the local apic. diff --git a/hypervisor/arch/x86/guest/vmsr.c b/hypervisor/arch/x86/guest/vmsr.c index b3d0593b5..9b61f2cdb 100644 --- a/hypervisor/arch/x86/guest/vmsr.c +++ b/hypervisor/arch/x86/guest/vmsr.c @@ -51,16 +51,6 @@ static const uint32_t emulated_msrs[] = { */ }; -/* the index is matched with emulated msrs array*/ -enum { - IDX_TSC_DEADLINE, - IDX_BIOS_UPDT_TRIG, - IDX_BIOS_SIGN_ID, - IDX_TSC, - - IDX_MAX_MSR -}; - static void enable_msr_interception(uint8_t *bitmap, uint32_t msr) { uint8_t *read_map; @@ -185,7 +175,7 @@ int rdmsr_vmexit_handler(struct vcpu *vcpu) switch (msr) { case MSR_IA32_TSC_DEADLINE: { - v = vcpu->guest_msrs[IDX_TSC_DEADLINE]; + vlapic_rdmsr(vcpu, msr, &v); break; } case MSR_IA32_TIME_STAMP_COUNTER: @@ -280,7 +270,6 @@ int wrmsr_vmexit_handler(struct vcpu *vcpu) case MSR_IA32_TSC_DEADLINE: { vlapic_wrmsr(vcpu, msr, v); - vcpu->guest_msrs[IDX_TSC_DEADLINE] = v; break; } case MSR_IA32_TIME_STAMP_COUNTER: diff --git a/hypervisor/include/arch/x86/guest/guest.h b/hypervisor/include/arch/x86/guest/guest.h index f7199a991..f9e6fb944 100644 --- a/hypervisor/include/arch/x86/guest/guest.h +++ b/hypervisor/include/arch/x86/guest/guest.h @@ -45,6 +45,17 @@ (idx < vm->hw.num_vcpus) & (vcpu != NULL); \ idx++, vcpu = vm->hw.vcpu_array[idx]) + +/* the index is matched with emulated msrs array*/ +enum { + IDX_TSC_DEADLINE, + IDX_BIOS_UPDT_TRIG, + IDX_BIOS_SIGN_ID, + IDX_TSC, + + IDX_MAX_MSR +}; + struct vhm_request; /*