Skip to content

Commit

Permalink
hv: support reference time enlightenment
Browse files Browse the repository at this point in the history
Two time related synthetic MSRs are implemented in this patch. Both of
them are partition wide MSR.
- HV_X64_MSR_TIME_REF_COUNT is read only and it is used to return the
  partition's reference counter value in 100ns units.
- HV_X64_MSR_REFERENCE_TSC is used to set/get the reference TSC page,
  a sequence number, an offset and a multiplier are defined in this
  page by hypervisor and guest OS can use them to calculate the
  normalized reference time since partition creation, in 100ns units.

Tracked-On: #3831
Signed-off-by: Jian Jun Chen <jian.jun.chen@intel.com>
Acked-by: Anthony Xu <anthony.xu@intel.com>
  • Loading branch information
chejianj authored and wenlingz committed Oct 22, 2019
1 parent 048155d commit 1d194ed
Show file tree
Hide file tree
Showing 3 changed files with 143 additions and 1 deletion.
125 changes: 124 additions & 1 deletion hypervisor/arch/x86/guest/hyperv.c
Expand Up @@ -10,14 +10,124 @@
#include <types.h>
#include <vm.h>
#include <logmsg.h>
#include <vmx.h>
#include <hyperv.h>

#define ACRN_DBG_HYPERV 6U

/* Partition Reference Counter (HV_X64_MSR_TIME_REF_COUNT) */
#define CPUID3A_TIME_REF_COUNT_MSR (1U << 1U)
/* Hypercall MSRs (HV_X64_MSR_GUEST_OS_ID and HV_X64_MSR_HYPERCALL) */
#define CPUID3A_HYPERCALL_MSR (1U << 5U)
/* Access virtual processor index MSR (HV_X64_MSR_VP_INDEX) */
#define CPUID3A_VP_INDEX_MSR (1U << 6U)
/* Partition reference TSC MSR (HV_X64_MSR_REFERENCE_TSC) */
#define CPUID3A_REFERENCE_TSC_MSR (1U << 9U)

struct HV_REFERENCE_TSC_PAGE {
uint32_t tsc_sequence;
uint32_t reserved1;
uint64_t tsc_scale;
uint64_t tsc_offset;
uint64_t reserved2[509];
};

static inline uint64_t
u64_shl64_div_u64(uint64_t a, uint64_t divisor)
{
uint64_t ret, tmp;

asm volatile ("divq %2" :
"=a" (ret), "=d" (tmp) :
"rm" (divisor), "0" (0U), "1" (a));

return ret;
}

static inline uint64_t
u64_mul_u64_shr64(uint64_t a, uint64_t b)
{
uint64_t ret, disc;

asm volatile ("mulq %3" :
"=d" (ret), "=a" (disc) :
"a" (a), "r" (b));

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(ACRN_DBG_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
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;
struct HV_REFERENCE_TSC_PAGE *p;
uint64_t tsc_scale, tsc_offset;
uint32_t tsc_seq;

ref_tsc_page->val64 = val;

if (ref_tsc_page->enabled == 1U) {
p = (struct HV_REFERENCE_TSC_PAGE *)gpa2hva(vcpu->vm, ref_tsc_page->gpfn << PAGE_SHIFT);
if (p != NULL) {
hyperv_get_tsc_scale_offset(vcpu->vm, &tsc_scale, &tsc_offset);
stac();
p->tsc_scale = tsc_scale;
p->tsc_offset = tsc_offset;
cpu_write_memory_barrier();
tsc_seq = p->tsc_sequence + 1U;
if ((tsc_seq == 0xFFFFFFFFU) || (tsc_seq == 0U)) {
tsc_seq = 1U;
}
p->tsc_sequence = tsc_seq;
clac();
}
}
}

static inline uint64_t
hyperv_get_ref_count(struct acrn_vm *vm)
{
uint64_t tsc, tsc_scale, tsc_offset, ret;

/* currently only "use tsc offsetting" is set to 1 */
tsc = rdtsc() + exec_vmread64(VMX_TSC_OFFSET_FULL);

hyperv_get_tsc_scale_offset(vm, &tsc_scale, &tsc_offset);

/* ret = ((tsc * tsc_scale) >> 64) + tsc_offset */
ret = u64_mul_u64_shr64(tsc, tsc_scale) + tsc_offset;

return ret;
}

static void
hyperv_setup_hypercall_page(const struct acrn_vcpu *vcpu, uint64_t val)
Expand Down Expand Up @@ -66,6 +176,12 @@ hyperv_wrmsr(struct acrn_vcpu *vcpu, uint32_t msr, uint64_t wval)
case HV_X64_MSR_VP_INDEX:
/* read only */
break;
case HV_X64_MSR_TIME_REF_COUNT:
/* read only */
break;
case HV_X64_MSR_REFERENCE_TSC:
hyperv_setup_tsc_page(vcpu, wval);
break;
default:
pr_err("hv: %s: unexpected MSR[0x%x] write", __func__, msr);
ret = -1;
Expand Down Expand Up @@ -93,6 +209,12 @@ hyperv_rdmsr(struct acrn_vcpu *vcpu, uint32_t msr, uint64_t *rval)
case HV_X64_MSR_VP_INDEX:
*rval = vcpu->vcpu_id;
break;
case HV_X64_MSR_TIME_REF_COUNT:
*rval = hyperv_get_ref_count(vcpu->vm);
break;
case HV_X64_MSR_REFERENCE_TSC:
*rval = vcpu->vm->arch_vm.hyperv.ref_tsc_page.val64;
break;
default:
pr_err("hv: %s: unexpected MSR[0x%x] read", __func__, msr);
ret = -1;
Expand Down Expand Up @@ -127,7 +249,8 @@ hyperv_init_vcpuid_entry(uint32_t leaf, uint32_t subleaf, uint32_t flags,
entry->edx = 0U;
break;
case 0x40000003U: /* HV supported feature */
entry->eax = CPUID3A_HYPERCALL_MSR | CPUID3A_VP_INDEX_MSR;
entry->eax = CPUID3A_HYPERCALL_MSR | CPUID3A_VP_INDEX_MSR |
CPUID3A_TIME_REF_COUNT_MSR | CPUID3A_REFERENCE_TSC_MSR;
entry->ebx = 0U;
entry->ecx = 0U;
entry->edx = 0U;
Expand Down
4 changes: 4 additions & 0 deletions hypervisor/arch/x86/guest/vmsr.c
Expand Up @@ -401,6 +401,8 @@ int32_t rdmsr_vmexit_handler(struct acrn_vcpu *vcpu)
case HV_X64_MSR_GUEST_OS_ID:
case HV_X64_MSR_HYPERCALL:
case HV_X64_MSR_VP_INDEX:
case HV_X64_MSR_REFERENCE_TSC:
case HV_X64_MSR_TIME_REF_COUNT:
{
err = hyperv_rdmsr(vcpu, msr, &v);
break;
Expand Down Expand Up @@ -689,6 +691,8 @@ int32_t wrmsr_vmexit_handler(struct acrn_vcpu *vcpu)
case HV_X64_MSR_GUEST_OS_ID:
case HV_X64_MSR_HYPERCALL:
case HV_X64_MSR_VP_INDEX:
case HV_X64_MSR_REFERENCE_TSC:
case HV_X64_MSR_TIME_REF_COUNT:
{
err = hyperv_wrmsr(vcpu, msr, v);
break;
Expand Down
15 changes: 15 additions & 0 deletions hypervisor/include/arch/x86/guest/hyperv.h
Expand Up @@ -14,6 +14,18 @@
#define HV_X64_MSR_HYPERCALL 0x40000001U
#define HV_X64_MSR_VP_INDEX 0x40000002U

#define HV_X64_MSR_TIME_REF_COUNT 0x40000020U
#define HV_X64_MSR_REFERENCE_TSC 0x40000021U

union hyperv_ref_tsc_page_msr {
uint64_t val64;
struct {
uint64_t enabled:1;
uint64_t rsvdp:11;
uint64_t gpfn:52;
};
};

union hyperv_hypercall_msr {
uint64_t val64;
struct {
Expand All @@ -40,6 +52,9 @@ union hyperv_guest_os_id_msr {
struct acrn_hyperv {
union hyperv_hypercall_msr hypercall_page;
union hyperv_guest_os_id_msr guest_os_id;
union hyperv_ref_tsc_page_msr ref_tsc_page;
uint64_t tsc_scale;
uint64_t tsc_offset;
};

int32_t hyperv_wrmsr(struct acrn_vcpu *vcpu, uint32_t msr, uint64_t wval);
Expand Down

0 comments on commit 1d194ed

Please sign in to comment.