mirror of
https://github.com/projectacrn/acrn-hypervisor.git
synced 2025-06-20 20:53:46 +00:00
hv: TLFS: Add tsc_offset support for reference time
TLFS spec defines that when a VM is created, the value of HV_X64_MSR_TIME_REF_COUNT is set to zero. Now tsc_offset is not supported properly, so guest get a drifted reference time. This patch implements tsc_offset. tsc_scale and tsc_offset are calculated when a VM is launched and are saved in struct acrn_hyperv of struct acrn_vm. Tracked-On: #5956 Signed-off-by: Jian Jun Chen <jian.jun.chen@intel.com> Signed-off-by: Shuo A Liu <shuo.a.liu@intel.com> Acked-by: Eddie Dong <eddie.dong@intel.com>
This commit is contained in:
parent
b4312efbd7
commit
31b8b698ce
@ -56,41 +56,11 @@ u64_mul_u64_shr64(uint64_t a, uint64_t b)
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void
|
|
||||||
hyperv_get_tsc_scale_offset(struct acrn_vm *vm, uint64_t *scale, uint64_t *offset)
|
|
||||||
{
|
|
||||||
if (vm->arch_vm.hyperv.tsc_scale == 0UL) {
|
|
||||||
/*
|
|
||||||
* The partition reference time is computed by the following formula:
|
|
||||||
* ReferenceTime = ((VirtualTsc * TscScale) >> 64) + TscOffset
|
|
||||||
* ReferenceTime is in 100ns units
|
|
||||||
*
|
|
||||||
* ReferenceTime =
|
|
||||||
* VirtualTsc / (get_tsc_khz() * 1000) * 1000000000 / 100
|
|
||||||
* + TscOffset
|
|
||||||
*/
|
|
||||||
|
|
||||||
uint64_t ret, khz = get_tsc_khz();
|
|
||||||
|
|
||||||
/* ret = (10000U << 64U) / get_tsc_khz() */
|
|
||||||
ret = u64_shl64_div_u64(10000U, khz);
|
|
||||||
|
|
||||||
dev_dbg(DBG_LEVEL_HYPERV, "%s, ret = 0x%lx", __func__, ret);
|
|
||||||
|
|
||||||
vm->arch_vm.hyperv.tsc_scale = ret;
|
|
||||||
vm->arch_vm.hyperv.tsc_offset = 0UL;
|
|
||||||
}
|
|
||||||
|
|
||||||
*scale = vm->arch_vm.hyperv.tsc_scale;
|
|
||||||
*offset = vm->arch_vm.hyperv.tsc_offset;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
static void
|
||||||
hyperv_setup_tsc_page(const struct acrn_vcpu *vcpu, uint64_t val)
|
hyperv_setup_tsc_page(const struct acrn_vcpu *vcpu, uint64_t val)
|
||||||
{
|
{
|
||||||
union hyperv_ref_tsc_page_msr *ref_tsc_page = &vcpu->vm->arch_vm.hyperv.ref_tsc_page;
|
union hyperv_ref_tsc_page_msr *ref_tsc_page = &vcpu->vm->arch_vm.hyperv.ref_tsc_page;
|
||||||
struct HV_REFERENCE_TSC_PAGE *p;
|
struct HV_REFERENCE_TSC_PAGE *p;
|
||||||
uint64_t tsc_scale, tsc_offset;
|
|
||||||
uint32_t tsc_seq;
|
uint32_t tsc_seq;
|
||||||
|
|
||||||
ref_tsc_page->val64 = val;
|
ref_tsc_page->val64 = val;
|
||||||
@ -98,10 +68,9 @@ hyperv_setup_tsc_page(const struct acrn_vcpu *vcpu, uint64_t val)
|
|||||||
if (ref_tsc_page->enabled == 1U) {
|
if (ref_tsc_page->enabled == 1U) {
|
||||||
p = (struct HV_REFERENCE_TSC_PAGE *)gpa2hva(vcpu->vm, ref_tsc_page->gpfn << PAGE_SHIFT);
|
p = (struct HV_REFERENCE_TSC_PAGE *)gpa2hva(vcpu->vm, ref_tsc_page->gpfn << PAGE_SHIFT);
|
||||||
if (p != NULL) {
|
if (p != NULL) {
|
||||||
hyperv_get_tsc_scale_offset(vcpu->vm, &tsc_scale, &tsc_offset);
|
|
||||||
stac();
|
stac();
|
||||||
p->tsc_scale = tsc_scale;
|
p->tsc_scale = vcpu->vm->arch_vm.hyperv.tsc_scale;
|
||||||
p->tsc_offset = tsc_offset;
|
p->tsc_offset = vcpu->vm->arch_vm.hyperv.tsc_offset;
|
||||||
cpu_write_memory_barrier();
|
cpu_write_memory_barrier();
|
||||||
tsc_seq = p->tsc_sequence + 1U;
|
tsc_seq = p->tsc_sequence + 1U;
|
||||||
if ((tsc_seq == 0xFFFFFFFFU) || (tsc_seq == 0U)) {
|
if ((tsc_seq == 0xFFFFFFFFU) || (tsc_seq == 0U)) {
|
||||||
@ -114,19 +83,19 @@ hyperv_setup_tsc_page(const struct acrn_vcpu *vcpu, uint64_t val)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static inline uint64_t
|
static inline uint64_t
|
||||||
hyperv_get_ref_count(struct acrn_vm *vm)
|
hyperv_scale_tsc(uint64_t scale)
|
||||||
{
|
{
|
||||||
uint64_t tsc, tsc_scale, tsc_offset, ret;
|
uint64_t tsc;
|
||||||
|
|
||||||
/* currently only "use tsc offsetting" is set to 1 */
|
|
||||||
tsc = rdtsc() + exec_vmread64(VMX_TSC_OFFSET_FULL);
|
tsc = rdtsc() + exec_vmread64(VMX_TSC_OFFSET_FULL);
|
||||||
|
|
||||||
hyperv_get_tsc_scale_offset(vm, &tsc_scale, &tsc_offset);
|
return u64_mul_u64_shr64(tsc, scale);
|
||||||
|
}
|
||||||
|
|
||||||
/* ret = ((tsc * tsc_scale) >> 64) + tsc_offset */
|
static inline uint64_t
|
||||||
ret = u64_mul_u64_shr64(tsc, tsc_scale) + tsc_offset;
|
hyperv_get_ReferenceTime(struct acrn_vm *vm)
|
||||||
|
{
|
||||||
return ret;
|
return hyperv_scale_tsc(vm->arch_vm.hyperv.tsc_scale) - vm->arch_vm.hyperv.tsc_offset;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
@ -227,7 +196,7 @@ hyperv_rdmsr(struct acrn_vcpu *vcpu, uint32_t msr, uint64_t *rval)
|
|||||||
*rval = vcpu->vcpu_id;
|
*rval = vcpu->vcpu_id;
|
||||||
break;
|
break;
|
||||||
case HV_X64_MSR_TIME_REF_COUNT:
|
case HV_X64_MSR_TIME_REF_COUNT:
|
||||||
*rval = hyperv_get_ref_count(vcpu->vm);
|
*rval = hyperv_get_ReferenceTime(vcpu->vm);
|
||||||
break;
|
break;
|
||||||
case HV_X64_MSR_REFERENCE_TSC:
|
case HV_X64_MSR_REFERENCE_TSC:
|
||||||
*rval = vcpu->vm->arch_vm.hyperv.ref_tsc_page.val64;
|
*rval = vcpu->vm->arch_vm.hyperv.ref_tsc_page.val64;
|
||||||
@ -244,6 +213,33 @@ hyperv_rdmsr(struct acrn_vcpu *vcpu, uint32_t msr, uint64_t *rval)
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
hyperv_init_time(struct acrn_vm *vm)
|
||||||
|
{
|
||||||
|
uint64_t tsc_scale, tsc_khz = get_tsc_khz();
|
||||||
|
uint64_t tsc_offset;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The partition reference time is computed by the following formula:
|
||||||
|
* ReferenceTime = ((VirtualTsc * TscScale) >> 64) + TscOffset
|
||||||
|
* ReferenceTime is in 100ns units
|
||||||
|
*
|
||||||
|
* ReferenceTime =
|
||||||
|
* VirtualTsc / (get_tsc_khz() * 1000) * 1000000000 / 100
|
||||||
|
* + TscOffset
|
||||||
|
*
|
||||||
|
* TscScale = (10000U << 64U) / get_tsc_khz()
|
||||||
|
*/
|
||||||
|
tsc_scale = u64_shl64_div_u64(10000U, tsc_khz);
|
||||||
|
tsc_offset = hyperv_scale_tsc(tsc_scale);
|
||||||
|
|
||||||
|
vm->arch_vm.hyperv.tsc_scale = tsc_scale;
|
||||||
|
vm->arch_vm.hyperv.tsc_offset = tsc_offset;
|
||||||
|
|
||||||
|
dev_dbg(DBG_LEVEL_HYPERV, "%s, tsc_scale = 0x%lx, tsc_offset = %ld",
|
||||||
|
__func__, tsc_scale, tsc_offset);
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
hyperv_init_vcpuid_entry(uint32_t leaf, uint32_t subleaf, uint32_t flags,
|
hyperv_init_vcpuid_entry(uint32_t leaf, uint32_t subleaf, uint32_t flags,
|
||||||
struct vcpuid_entry *entry)
|
struct vcpuid_entry *entry)
|
||||||
|
@ -643,6 +643,12 @@ int32_t run_vcpu(struct acrn_vcpu *vcpu)
|
|||||||
*/
|
*/
|
||||||
flush_vpid_global();
|
flush_vpid_global();
|
||||||
|
|
||||||
|
#ifdef CONFIG_HYPERV_ENABLED
|
||||||
|
if (is_vcpu_bsp(vcpu)) {
|
||||||
|
hyperv_init_time(vcpu->vm);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
/* Set vcpu launched */
|
/* Set vcpu launched */
|
||||||
vcpu->launched = true;
|
vcpu->launched = true;
|
||||||
|
|
||||||
|
@ -59,7 +59,7 @@ struct acrn_hyperv {
|
|||||||
|
|
||||||
int32_t hyperv_wrmsr(struct acrn_vcpu *vcpu, uint32_t msr, uint64_t wval);
|
int32_t hyperv_wrmsr(struct acrn_vcpu *vcpu, uint32_t msr, uint64_t wval);
|
||||||
int32_t hyperv_rdmsr(struct acrn_vcpu *vcpu, uint32_t msr, uint64_t *rval);
|
int32_t hyperv_rdmsr(struct acrn_vcpu *vcpu, uint32_t msr, uint64_t *rval);
|
||||||
|
void hyperv_init_time(struct acrn_vm *vm);
|
||||||
void hyperv_init_vcpuid_entry(uint32_t leaf, uint32_t subleaf, uint32_t flags,
|
void hyperv_init_vcpuid_entry(uint32_t leaf, uint32_t subleaf, uint32_t flags,
|
||||||
struct vcpuid_entry *entry);
|
struct vcpuid_entry *entry);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
Loading…
Reference in New Issue
Block a user