diff --git a/hypervisor/Makefile b/hypervisor/Makefile index 98988a8a1..e125d92e2 100644 --- a/hypervisor/Makefile +++ b/hypervisor/Makefile @@ -250,6 +250,9 @@ VP_BASE_C_SRCS += arch/x86/guest/virtual_cr.c VP_BASE_C_SRCS += arch/x86/guest/vmexit.c VP_BASE_C_SRCS += arch/x86/guest/ept.c VP_BASE_C_SRCS += arch/x86/guest/ucode.c +ifeq ($(CONFIG_HYPERV_ENABLED),y) +VP_BASE_C_SRCS += arch/x86/guest/hyperv.c +endif VP_BASE_C_SRCS += boot/guest/vboot_info.c VP_BASE_C_SRCS += common/hv_main.c VP_BASE_C_SRCS += common/vm_load.c diff --git a/hypervisor/arch/x86/Kconfig b/hypervisor/arch/x86/Kconfig index e3f1d9064..f59f414c3 100644 --- a/hypervisor/arch/x86/Kconfig +++ b/hypervisor/arch/x86/Kconfig @@ -226,6 +226,13 @@ config ACPI_PARSE_ENABLED help Whether to parse the ACPI tables at runtime. +config HYPERV_ENABLED + bool "Enable Hyper-V enlightenment" + default y + help + When set, the minimum set of TLFS functionality together with some + performance enlightenments are enabled. + config GPU_SBDF hex "Segment, Bus, Device, and function of the GPU" depends on ACPI_PARSE_ENABLED diff --git a/hypervisor/arch/x86/guest/hyperv.c b/hypervisor/arch/x86/guest/hyperv.c new file mode 100644 index 000000000..442433f4b --- /dev/null +++ b/hypervisor/arch/x86/guest/hyperv.c @@ -0,0 +1,160 @@ +/* + * Microsoft Hyper-V emulation. See Microsoft's + * Hypervisor Top Level Functional Specification for more information. + * + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include +#include +#include +#include + +#define ACRN_DBG_HYPERV 6U + +/* 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) + +static void +hyperv_setup_hypercall_page(const struct acrn_vcpu *vcpu, uint64_t val) +{ + union hyperv_hypercall_msr hypercall; + uint64_t page_gpa; + void *page_hva; + + /* asm volatile ("mov $2,%%eax; mov $0,%%edx; ret":::"eax","edx"); */ + const uint8_t inst[11] = {0xb8U, 0x02U, 0x0U, 0x0U, 0x0U, 0xbaU, 0x0U, 0x0U, 0x0U, 0x0U, 0xc3U}; + + hypercall.val64 = val; + + if (hypercall.enabled != 0UL) { + page_gpa = hypercall.gpfn << PAGE_SHIFT; + page_hva = gpa2hva(vcpu->vm, page_gpa); + if (page_hva != NULL) { + stac(); + (void)memset(page_hva, 0U, PAGE_SIZE); + (void)memcpy_s(page_hva, 11U, inst, 11U); + clac(); + } + } +} + +int32_t +hyperv_wrmsr(struct acrn_vcpu *vcpu, uint32_t msr, uint64_t wval) +{ + int32_t ret = 0; + + switch (msr) { + case HV_X64_MSR_GUEST_OS_ID: + vcpu->vm->arch_vm.hyperv.guest_os_id.val64 = wval; + if (wval == 0UL) { + vcpu->vm->arch_vm.hyperv.hypercall_page.enabled = 0UL; + } + break; + case HV_X64_MSR_HYPERCALL: + if (vcpu->vm->arch_vm.hyperv.guest_os_id.val64 == 0UL) { + pr_warn("hv: %s: guest_os_id is 0", __func__); + break; + } + vcpu->vm->arch_vm.hyperv.hypercall_page.val64 = wval; + hyperv_setup_hypercall_page(vcpu, wval); + break; + case HV_X64_MSR_VP_INDEX: + /* read only */ + break; + default: + pr_err("hv: %s: unexpected MSR[0x%x] write", __func__, msr); + ret = -1; + break; + } + + dev_dbg(ACRN_DBG_HYPERV, "hv: %s: MSR=0x%x wval=0x%llx vcpuid=%d vmid=%d", + __func__, msr, wval, vcpu->vcpu_id, vcpu->vm->vm_id); + + return ret; +} + +int32_t +hyperv_rdmsr(struct acrn_vcpu *vcpu, uint32_t msr, uint64_t *rval) +{ + int32_t ret = 0; + + switch (msr) { + case HV_X64_MSR_GUEST_OS_ID: + *rval = vcpu->vm->arch_vm.hyperv.guest_os_id.val64; + break; + case HV_X64_MSR_HYPERCALL: + *rval = vcpu->vm->arch_vm.hyperv.hypercall_page.val64; + break; + case HV_X64_MSR_VP_INDEX: + *rval = vcpu->vcpu_id; + break; + default: + pr_err("hv: %s: unexpected MSR[0x%x] read", __func__, msr); + ret = -1; + break; + } + + dev_dbg(ACRN_DBG_HYPERV, "hv: %s: MSR=0x%x rval=0x%llx vcpuid=%d vmid=%d", + __func__, msr, *rval, vcpu->vcpu_id, vcpu->vm->vm_id); + + return ret; +} + +void +hyperv_init_vcpuid_entry(uint32_t leaf, uint32_t subleaf, uint32_t flags, + struct vcpuid_entry *entry) +{ + entry->leaf = leaf; + entry->subleaf = subleaf; + entry->flags = flags; + + switch (leaf) { + case 0x40000001U: /* HV interface version */ + entry->eax = 0x31237648U; /* "Hv#1" */ + entry->ebx = 0U; + entry->ecx = 0U; + entry->edx = 0U; + break; + case 0x40000002U: /* HV system identity */ + entry->eax = 0U; + entry->ebx = 0U; + entry->ecx = 0U; + entry->edx = 0U; + break; + case 0x40000003U: /* HV supported feature */ + entry->eax = CPUID3A_HYPERCALL_MSR | CPUID3A_VP_INDEX_MSR; + entry->ebx = 0U; + entry->ecx = 0U; + entry->edx = 0U; + break; + case 0x40000004U: /* HV Recommended hypercall usage */ + entry->eax = 0U; + entry->ebx = 0U; + entry->ecx = 0U; + entry->edx = 0U; + break; + case 0x40000005U: /* HV Maximum Supported Virtual & logical Processors */ + entry->eax = CONFIG_MAX_VCPUS_PER_VM; + entry->ebx = 0U; + entry->ecx = 0U; + entry->edx = 0U; + break; + case 0x40000006U: /* Implementation Hardware Features */ + entry->eax = 0U; + entry->ebx = 0U; + entry->ecx = 0U; + entry->edx = 0U; + break; + default: + /* do nothing */ + break; + } + + dev_dbg(ACRN_DBG_HYPERV, "hv: %s: leaf=%x subleaf=%x flags=%x eax=%x ebx=%x ecx=%x edx=%x", + __func__, leaf, subleaf, flags, entry->eax, entry->ebx, entry->ecx, entry->edx); +} diff --git a/hypervisor/arch/x86/guest/vcpuid.c b/hypervisor/arch/x86/guest/vcpuid.c index 6c3caa84c..0fbce948f 100644 --- a/hypervisor/arch/x86/guest/vcpuid.c +++ b/hypervisor/arch/x86/guest/vcpuid.c @@ -263,9 +263,26 @@ static int32_t set_vcpuid_extended_function(struct acrn_vm *vm) if (is_sos_vm(vm)) { entry.eax |= GUEST_CAPS_PRIVILEGE_VM; } +#ifdef CONFIG_HYPERV_ENABLED + else { + hyperv_init_vcpuid_entry(0x40000001U, 0U, 0U, &entry); + } +#endif result = set_vcpuid_entry(vm, &entry); } +#ifdef CONFIG_HYPERV_ENABLED + if (result == 0) { + for (i = 0x40000002U; i <= 0x40000006U; i++) { + hyperv_init_vcpuid_entry(i, 0U, 0U, &entry); + result = set_vcpuid_entry(vm, &entry); + if (result != 0) { + break; + } + } + } +#endif + if (result == 0) { init_vcpuid_entry(0x40000010U, 0U, 0U, &entry); result = set_vcpuid_entry(vm, &entry); diff --git a/hypervisor/arch/x86/guest/vmsr.c b/hypervisor/arch/x86/guest/vmsr.c index 34250a819..f2b069ffd 100644 --- a/hypervisor/arch/x86/guest/vmsr.c +++ b/hypervisor/arch/x86/guest/vmsr.c @@ -397,6 +397,15 @@ int32_t rdmsr_vmexit_handler(struct acrn_vcpu *vcpu) /* Do the required processing for each msr case */ switch (msr) { +#ifdef CONFIG_HYPERV_ENABLED + case HV_X64_MSR_GUEST_OS_ID: + case HV_X64_MSR_HYPERCALL: + case HV_X64_MSR_VP_INDEX: + { + err = hyperv_rdmsr(vcpu, msr, &v); + break; + } +#endif case MSR_IA32_TSC_DEADLINE: { v = vlapic_get_tsc_deadline_msr(vcpu_vlapic(vcpu)); @@ -676,6 +685,15 @@ int32_t wrmsr_vmexit_handler(struct acrn_vcpu *vcpu) /* Do the required processing for each msr case */ switch (msr) { +#ifdef CONFIG_HYPERV_ENABLED + case HV_X64_MSR_GUEST_OS_ID: + case HV_X64_MSR_HYPERCALL: + case HV_X64_MSR_VP_INDEX: + { + err = hyperv_wrmsr(vcpu, msr, v); + break; + } +#endif case MSR_IA32_TSC_DEADLINE: { vlapic_set_tsc_deadline_msr(vcpu_vlapic(vcpu), v); diff --git a/hypervisor/include/arch/x86/guest/hyperv.h b/hypervisor/include/arch/x86/guest/hyperv.h new file mode 100644 index 000000000..7dfc35c7f --- /dev/null +++ b/hypervisor/include/arch/x86/guest/hyperv.h @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2019 Intel Corporation. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef HYPERV_H +#define HYPERV_H + +#include + +/* Hyper-V MSR numbers */ +#define HV_X64_MSR_GUEST_OS_ID 0x40000000U +#define HV_X64_MSR_HYPERCALL 0x40000001U +#define HV_X64_MSR_VP_INDEX 0x40000002U + +union hyperv_hypercall_msr { + uint64_t val64; + struct { + uint64_t enabled:1; + uint64_t locked:1; + uint64_t rsvdp:10; + uint64_t gpfn:52; + }; +}; + +union hyperv_guest_os_id_msr { + uint64_t val64; + struct { + uint64_t build_number:16; + uint64_t service_version:8; + uint64_t minor_version:8; + uint64_t major_version:8; + uint64_t os_id:8; + uint64_t vendor_id:15; + uint64_t os_type:1; + }; +}; + +struct acrn_hyperv { + union hyperv_hypercall_msr hypercall_page; + union hyperv_guest_os_id_msr guest_os_id; +}; + +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); +void hyperv_init_vcpuid_entry(uint32_t leaf, uint32_t subleaf, uint32_t flags, + struct vcpuid_entry *entry); + +#endif diff --git a/hypervisor/include/arch/x86/guest/vm.h b/hypervisor/include/arch/x86/guest/vm.h index 442f97a66..bd81500f7 100644 --- a/hypervisor/include/arch/x86/guest/vm.h +++ b/hypervisor/include/arch/x86/guest/vm.h @@ -26,6 +26,9 @@ #include #include #include +#ifdef CONFIG_HYPERV_ENABLED +#include +#endif struct vm_hw_info { /* vcpu array of this VM */ @@ -100,6 +103,9 @@ struct vm_arch { void *tmp_pg_array; /* Page array for tmp guest paging struct */ struct acrn_vioapic vioapic; /* Virtual IOAPIC base address */ struct acrn_vpic vpic; /* Virtual PIC */ +#ifdef CONFIG_HYPERV_ENABLED + struct acrn_hyperv hyperv; +#endif enum vm_vlapic_state vlapic_state; /* Represents vLAPIC state across vCPUs*/ /* reference to virtual platform to come here (as needed) */