Discussion:
[Xen-changelog] [xen staging] viridian: move the code into its own sub-directory
p***@xen.org
2018-10-31 17:11:46 UTC
Permalink
commit d5d30b394975f460a99a6313645a737acf16d4c7
Author: Paul Durrant <***@citrix.com>
AuthorDate: Wed Oct 31 13:44:00 2018 +0100
Commit: Jan Beulich <***@suse.com>
CommitDate: Wed Oct 31 17:59:53 2018 +0100

viridian: move the code into its own sub-directory

Subsequent patches will introduce support for more viridian enlightenments
which will make a single source module quite lengthy.

This patch therefore creates a new arch/x86/hvm/viridian sub-directory and
moves viridian.c into that.

The patch also fixes some bad whitespace whilst moving the code and
adjusts the MAINTAINERS file.

Signed-off-by: Paul Durrant <***@citrix.com>
Acked-by: Jan Beulich <***@suse.com>
---
MAINTAINERS | 2 +-
xen/arch/x86/hvm/Makefile | 2 +-
xen/arch/x86/hvm/viridian.c | 1121 ----------------------------------
xen/arch/x86/hvm/viridian/Makefile | 1 +
xen/arch/x86/hvm/viridian/viridian.c | 1121 ++++++++++++++++++++++++++++++++++
5 files changed, 1124 insertions(+), 1123 deletions(-)

diff --git a/MAINTAINERS b/MAINTAINERS
index 1970100b37..c2ba74d0c9 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -487,7 +487,7 @@ F: xen/arch/x86/mm/shadow/
X86 VIRIDIAN ENLIGHTENMENTS
M: Paul Durrant <***@citrix.com>
S: Supported
-F: xen/arch/x86/hvm/viridian.c
+F: xen/arch/x86/hvm/viridian/
F: xen/include/asm-x86/hvm/viridian.h

XENTRACE
diff --git a/xen/arch/x86/hvm/Makefile b/xen/arch/x86/hvm/Makefile
index 5e04bc1429..86b106f8e7 100644
--- a/xen/arch/x86/hvm/Makefile
+++ b/xen/arch/x86/hvm/Makefile
@@ -1,5 +1,6 @@
subdir-y += svm
subdir-y += vmx
+subdir-y += viridian

obj-y += asid.o
obj-y += dm.o
@@ -23,7 +24,6 @@ obj-y += rtc.o
obj-y += save.o
obj-y += stdvga.o
obj-y += vioapic.o
-obj-y += viridian.o
obj-y += vlapic.o
obj-y += vm_event.o
obj-y += vmsi.o
diff --git a/xen/arch/x86/hvm/viridian.c b/xen/arch/x86/hvm/viridian.c
deleted file mode 100644
index f42b1f063e..0000000000
--- a/xen/arch/x86/hvm/viridian.c
+++ /dev/null
@@ -1,1121 +0,0 @@
-/******************************************************************************
- * viridian.c
- *
- * An implementation of some Viridian enlightenments. See Microsoft's
- * Hypervisor Top Level Functional Specification (v5.0a) at:
- *
- * https://github.com/Microsoft/Virtualization-Documentation/raw/master/tlfs/Hypervisor%20Top%20Level%20Functional%20Specification%20v5.0.pdf
- *
- * for more information.
- */
-
-#include <xen/sched.h>
-#include <xen/version.h>
-#include <xen/perfc.h>
-#include <xen/hypercall.h>
-#include <xen/domain_page.h>
-#include <asm/guest_access.h>
-#include <asm/paging.h>
-#include <asm/p2m.h>
-#include <asm/apic.h>
-#include <asm/hvm/support.h>
-#include <public/sched.h>
-#include <public/hvm/hvm_op.h>
-
-/* Viridian MSR numbers. */
-#define HV_X64_MSR_GUEST_OS_ID 0x40000000
-#define HV_X64_MSR_HYPERCALL 0x40000001
-#define HV_X64_MSR_VP_INDEX 0x40000002
-#define HV_X64_MSR_RESET 0x40000003
-#define HV_X64_MSR_VP_RUNTIME 0x40000010
-#define HV_X64_MSR_TIME_REF_COUNT 0x40000020
-#define HV_X64_MSR_REFERENCE_TSC 0x40000021
-#define HV_X64_MSR_TSC_FREQUENCY 0x40000022
-#define HV_X64_MSR_APIC_FREQUENCY 0x40000023
-#define HV_X64_MSR_EOI 0x40000070
-#define HV_X64_MSR_ICR 0x40000071
-#define HV_X64_MSR_TPR 0x40000072
-#define HV_X64_MSR_VP_ASSIST_PAGE 0x40000073
-#define HV_X64_MSR_SCONTROL 0x40000080
-#define HV_X64_MSR_SVERSION 0x40000081
-#define HV_X64_MSR_SIEFP 0x40000082
-#define HV_X64_MSR_SIMP 0x40000083
-#define HV_X64_MSR_EOM 0x40000084
-#define HV_X64_MSR_SINT0 0x40000090
-#define HV_X64_MSR_SINT1 0x40000091
-#define HV_X64_MSR_SINT2 0x40000092
-#define HV_X64_MSR_SINT3 0x40000093
-#define HV_X64_MSR_SINT4 0x40000094
-#define HV_X64_MSR_SINT5 0x40000095
-#define HV_X64_MSR_SINT6 0x40000096
-#define HV_X64_MSR_SINT7 0x40000097
-#define HV_X64_MSR_SINT8 0x40000098
-#define HV_X64_MSR_SINT9 0x40000099
-#define HV_X64_MSR_SINT10 0x4000009A
-#define HV_X64_MSR_SINT11 0x4000009B
-#define HV_X64_MSR_SINT12 0x4000009C
-#define HV_X64_MSR_SINT13 0x4000009D
-#define HV_X64_MSR_SINT14 0x4000009E
-#define HV_X64_MSR_SINT15 0x4000009F
-#define HV_X64_MSR_STIMER0_CONFIG 0x400000B0
-#define HV_X64_MSR_STIMER0_COUNT 0x400000B1
-#define HV_X64_MSR_STIMER1_CONFIG 0x400000B2
-#define HV_X64_MSR_STIMER1_COUNT 0x400000B3
-#define HV_X64_MSR_STIMER2_CONFIG 0x400000B4
-#define HV_X64_MSR_STIMER2_COUNT 0x400000B5
-#define HV_X64_MSR_STIMER3_CONFIG 0x400000B6
-#define HV_X64_MSR_STIMER3_COUNT 0x400000B7
-#define HV_X64_MSR_POWER_STATE_TRIGGER_C1 0x400000C1
-#define HV_X64_MSR_POWER_STATE_TRIGGER_C2 0x400000C2
-#define HV_X64_MSR_POWER_STATE_TRIGGER_C3 0x400000C3
-#define HV_X64_MSR_POWER_STATE_CONFIG_C1 0x400000D1
-#define HV_X64_MSR_POWER_STATE_CONFIG_C2 0x400000D2
-#define HV_X64_MSR_POWER_STATE_CONFIG_C3 0x400000D3
-#define HV_X64_MSR_STATS_PARTITION_RETAIL_PAGE 0x400000E0
-#define HV_X64_MSR_STATS_PARTITION_INTERNAL_PAGE 0x400000E1
-#define HV_X64_MSR_STATS_VP_RETAIL_PAGE 0x400000E2
-#define HV_X64_MSR_STATS_VP_INTERNAL_PAGE 0x400000E3
-#define HV_X64_MSR_GUEST_IDLE 0x400000F0
-#define HV_X64_MSR_SYNTH_DEBUG_CONTROL 0x400000F1
-#define HV_X64_MSR_SYNTH_DEBUG_STATUS 0x400000F2
-#define HV_X64_MSR_SYNTH_DEBUG_SEND_BUFFER 0x400000F3
-#define HV_X64_MSR_SYNTH_DEBUG_RECEIVE_BUFFER 0x400000F4
-#define HV_X64_MSR_SYNTH_DEBUG_PENDING_BUFFER 0x400000F5
-#define HV_X64_MSR_CRASH_P0 0x40000100
-#define HV_X64_MSR_CRASH_P1 0x40000101
-#define HV_X64_MSR_CRASH_P2 0x40000102
-#define HV_X64_MSR_CRASH_P3 0x40000103
-#define HV_X64_MSR_CRASH_P4 0x40000104
-#define HV_X64_MSR_CRASH_CTL 0x40000105
-
-/* Viridian Hypercall Status Codes. */
-#define HV_STATUS_SUCCESS 0x0000
-#define HV_STATUS_INVALID_HYPERCALL_CODE 0x0002
-#define HV_STATUS_INVALID_PARAMETER 0x0005
-
-/* Viridian Hypercall Codes. */
-#define HvFlushVirtualAddressSpace 0x0002
-#define HvFlushVirtualAddressList 0x0003
-#define HvNotifyLongSpinWait 0x0008
-#define HvGetPartitionId 0x0046
-#define HvExtCallQueryCapabilities 0x8001
-
-/* Viridian Hypercall Flags. */
-#define HV_FLUSH_ALL_PROCESSORS 1
-
-/*
- * Viridian Partition Privilege Flags.
- *
- * This is taken from section 4.2.2 of the specification, and fixed for
- * style and correctness.
- */
-typedef struct {
- /* Access to virtual MSRs */
- uint64_t AccessVpRunTimeReg:1;
- uint64_t AccessPartitionReferenceCounter:1;
- uint64_t AccessSynicRegs:1;
- uint64_t AccessSyntheticTimerRegs:1;
- uint64_t AccessIntrCtrlRegs:1;
- uint64_t AccessHypercallMsrs:1;
- uint64_t AccessVpIndex:1;
- uint64_t AccessResetReg:1;
- uint64_t AccessStatsReg:1;
- uint64_t AccessPartitionReferenceTsc:1;
- uint64_t AccessGuestIdleReg:1;
- uint64_t AccessFrequencyRegs:1;
- uint64_t AccessDebugRegs:1;
- uint64_t Reserved1:19;
-
- /* Access to hypercalls */
- uint64_t CreatePartitions:1;
- uint64_t AccessPartitionId:1;
- uint64_t AccessMemoryPool:1;
- uint64_t AdjustMessageBuffers:1;
- uint64_t PostMessages:1;
- uint64_t SignalEvents:1;
- uint64_t CreatePort:1;
- uint64_t ConnectPort:1;
- uint64_t AccessStats:1;
- uint64_t Reserved2:2;
- uint64_t Debugging:1;
- uint64_t CpuManagement:1;
- uint64_t Reserved3:1;
- uint64_t Reserved4:1;
- uint64_t Reserved5:1;
- uint64_t AccessVSM:1;
- uint64_t AccessVpRegisters:1;
- uint64_t Reserved6:1;
- uint64_t Reserved7:1;
- uint64_t EnableExtendedHypercalls:1;
- uint64_t StartVirtualProcessor:1;
- uint64_t Reserved8:10;
-} HV_PARTITION_PRIVILEGE_MASK;
-
-typedef union _HV_CRASH_CTL_REG_CONTENTS
-{
- uint64_t AsUINT64;
- struct
- {
- uint64_t Reserved:63;
- uint64_t CrashNotify:1;
- } u;
-} HV_CRASH_CTL_REG_CONTENTS;
-
-/* Viridian CPUID leaf 3, Hypervisor Feature Indication */
-#define CPUID3D_CRASH_MSRS (1 << 10)
-
-/* Viridian CPUID leaf 4: Implementation Recommendations. */
-#define CPUID4A_HCALL_REMOTE_TLB_FLUSH (1 << 2)
-#define CPUID4A_MSR_BASED_APIC (1 << 3)
-#define CPUID4A_RELAX_TIMER_INT (1 << 5)
-
-/* Viridian CPUID leaf 6: Implementation HW features detected and in use. */
-#define CPUID6A_APIC_OVERLAY (1 << 0)
-#define CPUID6A_MSR_BITMAPS (1 << 1)
-#define CPUID6A_NESTED_PAGING (1 << 3)
-
-/*
- * Version and build number reported by CPUID leaf 2
- *
- * These numbers are chosen to match the version numbers reported by
- * Windows Server 2008.
- */
-static uint16_t __read_mostly viridian_major = 6;
-static uint16_t __read_mostly viridian_minor = 0;
-static uint32_t __read_mostly viridian_build = 0x1772;
-
-/*
- * Maximum number of retries before the guest will notify of failure
- * to acquire a spinlock.
- */
-static uint32_t __read_mostly viridian_spinlock_retry_count = 2047;
-integer_param("viridian-spinlock-retry-count",
- viridian_spinlock_retry_count);
-
-void cpuid_viridian_leaves(const struct vcpu *v, uint32_t leaf,
- uint32_t subleaf, struct cpuid_leaf *res)
-{
- const struct domain *d = v->domain;
-
- ASSERT(is_viridian_domain(d));
- ASSERT(leaf >= 0x40000000 && leaf < 0x40000100);
-
- leaf -= 0x40000000;
-
- switch ( leaf )
- {
- case 0:
- /* See section 2.4.1 of the specification */
- res->a = 0x40000006; /* Maximum leaf */
- memcpy(&res->b, "Micr", 4);
- memcpy(&res->c, "osof", 4);
- memcpy(&res->d, "t Hv", 4);
- break;
-
- case 1:
- /* See section 2.4.2 of the specification */
- memcpy(&res->a, "Hv#1", 4);
- break;
-
- case 2:
- /* Hypervisor information, but only if the guest has set its
- own version number. */
- if ( d->arch.hvm.viridian.guest_os_id.raw == 0 )
- break;
- res->a = viridian_build;
- res->b = ((uint32_t)viridian_major << 16) | viridian_minor;
- res->c = 0; /* SP */
- res->d = 0; /* Service branch and number */
- break;
-
- case 3:
- {
- /*
- * Section 2.4.4 details this leaf and states that EAX and EBX
- * are defined to be the low and high parts of the partition
- * privilege mask respectively.
- */
- HV_PARTITION_PRIVILEGE_MASK mask = {
- .AccessIntrCtrlRegs = 1,
- .AccessHypercallMsrs = 1,
- .AccessVpIndex = 1,
- };
- union {
- HV_PARTITION_PRIVILEGE_MASK mask;
- struct { uint32_t lo, hi; };
- } u;
-
- if ( !(viridian_feature_mask(d) & HVMPV_no_freq) )
- mask.AccessFrequencyRegs = 1;
- if ( viridian_feature_mask(d) & HVMPV_time_ref_count )
- mask.AccessPartitionReferenceCounter = 1;
- if ( viridian_feature_mask(d) & HVMPV_reference_tsc )
- mask.AccessPartitionReferenceTsc = 1;
-
- u.mask = mask;
-
- res->a = u.lo;
- res->b = u.hi;
-
- if ( viridian_feature_mask(d) & HVMPV_crash_ctl )
- res->d = CPUID3D_CRASH_MSRS;
-
- break;
- }
-
- case 4:
- /* Recommended hypercall usage. */
- if ( (d->arch.hvm.viridian.guest_os_id.raw == 0) ||
- (d->arch.hvm.viridian.guest_os_id.fields.os < 4) )
- break;
- res->a = CPUID4A_RELAX_TIMER_INT;
- if ( viridian_feature_mask(d) & HVMPV_hcall_remote_tlb_flush )
- res->a |= CPUID4A_HCALL_REMOTE_TLB_FLUSH;
- if ( !cpu_has_vmx_apic_reg_virt )
- res->a |= CPUID4A_MSR_BASED_APIC;
-
- /*
- * This value is the recommended number of attempts to try to
- * acquire a spinlock before notifying the hypervisor via the
- * HvNotifyLongSpinWait hypercall.
- */
- res->b = viridian_spinlock_retry_count;
- break;
-
- case 6:
- /* Detected and in use hardware features. */
- if ( cpu_has_vmx_virtualize_apic_accesses )
- res->a |= CPUID6A_APIC_OVERLAY;
- if ( cpu_has_vmx_msr_bitmap || (read_efer() & EFER_SVME) )
- res->a |= CPUID6A_MSR_BITMAPS;
- if ( hap_enabled(d) )
- res->a |= CPUID6A_NESTED_PAGING;
- break;
- }
-}
-
-static void dump_guest_os_id(const struct domain *d)
-{
- const union viridian_guest_os_id *goi;
-
- goi = &d->arch.hvm.viridian.guest_os_id;
-
- printk(XENLOG_G_INFO
- "d%d: VIRIDIAN GUEST_OS_ID: vendor: %x os: %x major: %x minor: %x sp: %x build: %x\n",
- d->domain_id,
- goi->fields.vendor, goi->fields.os,
- goi->fields.major, goi->fields.minor,
- goi->fields.service_pack, goi->fields.build_number);
-}
-
-static void dump_hypercall(const struct domain *d)
-{
- const union viridian_hypercall_gpa *hg;
-
- hg = &d->arch.hvm.viridian.hypercall_gpa;
-
- printk(XENLOG_G_INFO "d%d: VIRIDIAN HYPERCALL: enabled: %x pfn: %lx\n",
- d->domain_id,
- hg->fields.enabled, (unsigned long)hg->fields.pfn);
-}
-
-static void dump_vp_assist(const struct vcpu *v)
-{
- const union viridian_vp_assist *va;
-
- va = &v->arch.hvm.viridian.vp_assist.msr;
-
- printk(XENLOG_G_INFO "%pv: VIRIDIAN VP_ASSIST_PAGE: enabled: %x pfn: %lx\n",
- v, va->fields.enabled, (unsigned long)va->fields.pfn);
-}
-
-static void dump_reference_tsc(const struct domain *d)
-{
- const union viridian_reference_tsc *rt;
-
- rt = &d->arch.hvm.viridian.reference_tsc;
-
- printk(XENLOG_G_INFO "d%d: VIRIDIAN REFERENCE_TSC: enabled: %x pfn: %lx\n",
- d->domain_id,
- rt->fields.enabled, (unsigned long)rt->fields.pfn);
-}
-
-static void enable_hypercall_page(struct domain *d)
-{
- unsigned long gmfn = d->arch.hvm.viridian.hypercall_gpa.fields.pfn;
- struct page_info *page = get_page_from_gfn(d, gmfn, NULL, P2M_ALLOC);
- uint8_t *p;
-
- if ( !page || !get_page_type(page, PGT_writable_page) )
- {
- if ( page )
- put_page(page);
- gdprintk(XENLOG_WARNING, "Bad GMFN %#"PRI_gfn" (MFN %#"PRI_mfn")\n",
- gmfn, mfn_x(page ? page_to_mfn(page) : INVALID_MFN));
- return;
- }
-
- p = __map_domain_page(page);
-
- /*
- * We set the bit 31 in %eax (reserved field in the Viridian hypercall
- * calling convention) to differentiate Xen and Viridian hypercalls.
- */
- *(u8 *)(p + 0) = 0x0d; /* orl $0x80000000, %eax */
- *(u32 *)(p + 1) = 0x80000000;
- *(u8 *)(p + 5) = 0x0f; /* vmcall/vmmcall */
- *(u8 *)(p + 6) = 0x01;
- *(u8 *)(p + 7) = (cpu_has_vmx ? 0xc1 : 0xd9);
- *(u8 *)(p + 8) = 0xc3; /* ret */
- memset(p + 9, 0xcc, PAGE_SIZE - 9); /* int3, int3, ... */
-
- unmap_domain_page(p);
-
- put_page_and_type(page);
-}
-
-static void initialize_vp_assist(struct vcpu *v)
-{
- struct domain *d = v->domain;
- unsigned long gmfn = v->arch.hvm.viridian.vp_assist.msr.fields.pfn;
- struct page_info *page = get_page_from_gfn(d, gmfn, NULL, P2M_ALLOC);
- void *va;
-
- ASSERT(!v->arch.hvm.viridian.vp_assist.va);
-
- /*
- * See section 7.8.7 of the specification for details of this
- * enlightenment.
- */
-
- if ( !page )
- goto fail;
-
- if ( !get_page_type(page, PGT_writable_page) )
- {
- put_page(page);
- goto fail;
- }
-
- va = __map_domain_page_global(page);
- if ( !va )
- {
- put_page_and_type(page);
- goto fail;
- }
-
- clear_page(va);
-
- v->arch.hvm.viridian.vp_assist.va = va;
- return;
-
- fail:
- gdprintk(XENLOG_WARNING, "Bad GMFN %#"PRI_gfn" (MFN %#"PRI_mfn")\n", gmfn,
- mfn_x(page ? page_to_mfn(page) : INVALID_MFN));
-}
-
-static void teardown_vp_assist(struct vcpu *v)
-{
- void *va = v->arch.hvm.viridian.vp_assist.va;
- struct page_info *page;
-
- if ( !va )
- return;
-
- v->arch.hvm.viridian.vp_assist.va = NULL;
-
- page = mfn_to_page(domain_page_map_to_mfn(va));
-
- unmap_domain_page_global(va);
- put_page_and_type(page);
-}
-
-void viridian_apic_assist_set(struct vcpu *v)
-{
- uint32_t *va = v->arch.hvm.viridian.vp_assist.va;
-
- if ( !va )
- return;
-
- /*
- * If there is already an assist pending then something has gone
- * wrong and the VM will most likely hang so force a crash now
- * to make the problem clear.
- */
- if ( v->arch.hvm.viridian.vp_assist.pending )
- domain_crash(v->domain);
-
- v->arch.hvm.viridian.vp_assist.pending = true;
- *va |= 1u;
-}
-
-bool viridian_apic_assist_completed(struct vcpu *v)
-{
- uint32_t *va = v->arch.hvm.viridian.vp_assist.va;
-
- if ( !va )
- return false;
-
- if ( v->arch.hvm.viridian.vp_assist.pending &&
- !(*va & 1u) )
- {
- /* An EOI has been avoided */
- v->arch.hvm.viridian.vp_assist.pending = false;
- return true;
- }
-
- return false;
-}
-
-void viridian_apic_assist_clear(struct vcpu *v)
-{
- uint32_t *va = v->arch.hvm.viridian.vp_assist.va;
-
- if ( !va )
- return;
-
- *va &= ~1u;
- v->arch.hvm.viridian.vp_assist.pending = false;
-}
-
-static void update_reference_tsc(struct domain *d, bool_t initialize)
-{
- unsigned long gmfn = d->arch.hvm.viridian.reference_tsc.fields.pfn;
- struct page_info *page = get_page_from_gfn(d, gmfn, NULL, P2M_ALLOC);
- HV_REFERENCE_TSC_PAGE *p;
-
- if ( !page || !get_page_type(page, PGT_writable_page) )
- {
- if ( page )
- put_page(page);
- gdprintk(XENLOG_WARNING, "Bad GMFN %#"PRI_gfn" (MFN %#"PRI_mfn")\n",
- gmfn, mfn_x(page ? page_to_mfn(page) : INVALID_MFN));
- return;
- }
-
- p = __map_domain_page(page);
-
- if ( initialize )
- clear_page(p);
-
- /*
- * This enlightenment must be disabled is the host TSC is not invariant.
- * However it is also disabled if vtsc is true (which means rdtsc is being
- * emulated). This generally happens when guest TSC freq and host TSC freq
- * don't match. The TscScale value could be adjusted to cope with this,
- * allowing vtsc to be turned off, but support for this is not yet present
- * in the hypervisor. Thus is it is possible that migrating a Windows VM
- * between hosts of differing TSC frequencies may result in large
- * differences in guest performance.
- */
- if ( !host_tsc_is_safe() || d->arch.vtsc )
- {
- /*
- * The specification states that valid values of TscSequence range
- * from 0 to 0xFFFFFFFE. The value 0xFFFFFFFF is used to indicate
- * this mechanism is no longer a reliable source of time and that
- * the VM should fall back to a different source.
- *
- * Server 2012 (6.2 kernel) and 2012 R2 (6.3 kernel) actually violate
- * the spec. and rely on a value of 0 to indicate that this
- * enlightenment should no longer be used. These two kernel
- * versions are currently the only ones to make use of this
- * enlightenment, so just use 0 here.
- */
- p->TscSequence = 0;
-
- printk(XENLOG_G_INFO "d%d: VIRIDIAN REFERENCE_TSC: invalidated\n",
- d->domain_id);
- goto out;
- }
-
- /*
- * The guest will calculate reference time according to the following
- * formula:
- *
- * ReferenceTime = ((RDTSC() * TscScale) >> 64) + TscOffset
- *
- * Windows uses a 100ns tick, so we need a scale which is cpu
- * ticks per 100ns shifted left by 64.
- */
- p->TscScale = ((10000ul << 32) / d->arch.tsc_khz) << 32;
-
- p->TscSequence++;
- if ( p->TscSequence == 0xFFFFFFFF ||
- p->TscSequence == 0 ) /* Avoid both 'invalid' values */
- p->TscSequence = 1;
-
- out:
- unmap_domain_page(p);
-
- put_page_and_type(page);
-}
-
-int guest_wrmsr_viridian(struct vcpu *v, uint32_t idx, uint64_t val)
-{
- struct domain *d = v->domain;
-
- ASSERT(is_viridian_domain(d));
-
- switch ( idx )
- {
- case HV_X64_MSR_GUEST_OS_ID:
- perfc_incr(mshv_wrmsr_osid);
- d->arch.hvm.viridian.guest_os_id.raw = val;
- dump_guest_os_id(d);
- break;
-
- case HV_X64_MSR_HYPERCALL:
- perfc_incr(mshv_wrmsr_hc_page);
- d->arch.hvm.viridian.hypercall_gpa.raw = val;
- dump_hypercall(d);
- if ( d->arch.hvm.viridian.hypercall_gpa.fields.enabled )
- enable_hypercall_page(d);
- break;
-
- case HV_X64_MSR_VP_INDEX:
- perfc_incr(mshv_wrmsr_vp_index);
- break;
-
- case HV_X64_MSR_EOI:
- perfc_incr(mshv_wrmsr_eoi);
- vlapic_EOI_set(vcpu_vlapic(v));
- break;
-
- case HV_X64_MSR_ICR: {
- u32 eax = (u32)val, edx = (u32)(val >> 32);
- struct vlapic *vlapic = vcpu_vlapic(v);
- perfc_incr(mshv_wrmsr_icr);
- eax &= ~(1 << 12);
- edx &= 0xff000000;
- vlapic_set_reg(vlapic, APIC_ICR2, edx);
- vlapic_ipi(vlapic, eax, edx);
- vlapic_set_reg(vlapic, APIC_ICR, eax);
- break;
- }
-
- case HV_X64_MSR_TPR:
- perfc_incr(mshv_wrmsr_tpr);
- vlapic_set_reg(vcpu_vlapic(v), APIC_TASKPRI, (uint8_t)val);
- break;
-
- case HV_X64_MSR_VP_ASSIST_PAGE:
- perfc_incr(mshv_wrmsr_apic_msr);
- teardown_vp_assist(v); /* release any previous mapping */
- v->arch.hvm.viridian.vp_assist.msr.raw = val;
- dump_vp_assist(v);
- if ( v->arch.hvm.viridian.vp_assist.msr.fields.enabled )
- initialize_vp_assist(v);
- break;
-
- case HV_X64_MSR_REFERENCE_TSC:
- if ( !(viridian_feature_mask(d) & HVMPV_reference_tsc) )
- return X86EMUL_EXCEPTION;
-
- perfc_incr(mshv_wrmsr_tsc_msr);
- d->arch.hvm.viridian.reference_tsc.raw = val;
- dump_reference_tsc(d);
- if ( d->arch.hvm.viridian.reference_tsc.fields.enabled )
- update_reference_tsc(d, 1);
- break;
-
- case HV_X64_MSR_CRASH_P0:
- case HV_X64_MSR_CRASH_P1:
- case HV_X64_MSR_CRASH_P2:
- case HV_X64_MSR_CRASH_P3:
- case HV_X64_MSR_CRASH_P4:
- BUILD_BUG_ON(HV_X64_MSR_CRASH_P4 - HV_X64_MSR_CRASH_P0 >=
- ARRAY_SIZE(v->arch.hvm.viridian.crash_param));
-
- idx -= HV_X64_MSR_CRASH_P0;
- v->arch.hvm.viridian.crash_param[idx] = val;
- break;
-
- case HV_X64_MSR_CRASH_CTL:
- {
- HV_CRASH_CTL_REG_CONTENTS ctl;
-
- ctl.AsUINT64 = val;
-
- if ( !ctl.u.CrashNotify )
- break;
-
- spin_lock(&d->shutdown_lock);
- d->shutdown_code = SHUTDOWN_crash;
- spin_unlock(&d->shutdown_lock);
-
- gprintk(XENLOG_WARNING, "VIRIDIAN CRASH: %lx %lx %lx %lx %lx\n",
- v->arch.hvm.viridian.crash_param[0],
- v->arch.hvm.viridian.crash_param[1],
- v->arch.hvm.viridian.crash_param[2],
- v->arch.hvm.viridian.crash_param[3],
- v->arch.hvm.viridian.crash_param[4]);
- break;
- }
-
- default:
- gdprintk(XENLOG_INFO,
- "Write %016"PRIx64" to unimplemented MSR %#x\n", val, idx);
- return X86EMUL_EXCEPTION;
- }
-
- return X86EMUL_OKAY;
-}
-
-static int64_t raw_trc_val(struct domain *d)
-{
- uint64_t tsc;
- struct time_scale tsc_to_ns;
-
- tsc = hvm_get_guest_tsc(pt_global_vcpu_target(d));
-
- /* convert tsc to count of 100ns periods */
- set_time_scale(&tsc_to_ns, d->arch.tsc_khz * 1000ul);
- return scale_delta(tsc, &tsc_to_ns) / 100ul;
-}
-
-void viridian_time_ref_count_freeze(struct domain *d)
-{
- struct viridian_time_ref_count *trc;
-
- trc = &d->arch.hvm.viridian.time_ref_count;
-
- if ( test_and_clear_bit(_TRC_running, &trc->flags) )
- trc->val = raw_trc_val(d) + trc->off;
-}
-
-void viridian_time_ref_count_thaw(struct domain *d)
-{
- struct viridian_time_ref_count *trc;
-
- trc = &d->arch.hvm.viridian.time_ref_count;
-
- if ( !d->is_shutting_down &&
- !test_and_set_bit(_TRC_running, &trc->flags) )
- trc->off = (int64_t)trc->val - raw_trc_val(d);
-}
-
-int guest_rdmsr_viridian(const struct vcpu *v, uint32_t idx, uint64_t *val)
-{
- struct domain *d = v->domain;
-
- ASSERT(is_viridian_domain(d));
-
- switch ( idx )
- {
- case HV_X64_MSR_GUEST_OS_ID:
- perfc_incr(mshv_rdmsr_osid);
- *val = d->arch.hvm.viridian.guest_os_id.raw;
- break;
-
- case HV_X64_MSR_HYPERCALL:
- perfc_incr(mshv_rdmsr_hc_page);
- *val = d->arch.hvm.viridian.hypercall_gpa.raw;
- break;
-
- case HV_X64_MSR_VP_INDEX:
- perfc_incr(mshv_rdmsr_vp_index);
- *val = v->vcpu_id;
- break;
-
- case HV_X64_MSR_TSC_FREQUENCY:
- if ( viridian_feature_mask(d) & HVMPV_no_freq )
- return X86EMUL_EXCEPTION;
-
- perfc_incr(mshv_rdmsr_tsc_frequency);
- *val = (uint64_t)d->arch.tsc_khz * 1000ull;
- break;
-
- case HV_X64_MSR_APIC_FREQUENCY:
- if ( viridian_feature_mask(d) & HVMPV_no_freq )
- return X86EMUL_EXCEPTION;
-
- perfc_incr(mshv_rdmsr_apic_frequency);
- *val = 1000000000ull / APIC_BUS_CYCLE_NS;
- break;
-
- case HV_X64_MSR_ICR:
- perfc_incr(mshv_rdmsr_icr);
- *val = (((uint64_t)vlapic_get_reg(vcpu_vlapic(v), APIC_ICR2) << 32) |
- vlapic_get_reg(vcpu_vlapic(v), APIC_ICR));
- break;
-
- case HV_X64_MSR_TPR:
- perfc_incr(mshv_rdmsr_tpr);
- *val = vlapic_get_reg(vcpu_vlapic(v), APIC_TASKPRI);
- break;
-
- case HV_X64_MSR_VP_ASSIST_PAGE:
- perfc_incr(mshv_rdmsr_apic_msr);
- *val = v->arch.hvm.viridian.vp_assist.msr.raw;
- break;
-
- case HV_X64_MSR_REFERENCE_TSC:
- if ( !(viridian_feature_mask(d) & HVMPV_reference_tsc) )
- return X86EMUL_EXCEPTION;
-
- perfc_incr(mshv_rdmsr_tsc_msr);
- *val = d->arch.hvm.viridian.reference_tsc.raw;
- break;
-
- case HV_X64_MSR_TIME_REF_COUNT:
- {
- struct viridian_time_ref_count *trc;
-
- trc = &d->arch.hvm.viridian.time_ref_count;
-
- if ( !(viridian_feature_mask(d) & HVMPV_time_ref_count) )
- return X86EMUL_EXCEPTION;
-
- if ( !test_and_set_bit(_TRC_accessed, &trc->flags) )
- printk(XENLOG_G_INFO "d%d: VIRIDIAN MSR_TIME_REF_COUNT: accessed\n",
- d->domain_id);
-
- perfc_incr(mshv_rdmsr_time_ref_count);
- *val = raw_trc_val(d) + trc->off;
- break;
- }
-
- case HV_X64_MSR_CRASH_P0:
- case HV_X64_MSR_CRASH_P1:
- case HV_X64_MSR_CRASH_P2:
- case HV_X64_MSR_CRASH_P3:
- case HV_X64_MSR_CRASH_P4:
- BUILD_BUG_ON(HV_X64_MSR_CRASH_P4 - HV_X64_MSR_CRASH_P0 >=
- ARRAY_SIZE(v->arch.hvm.viridian.crash_param));
-
- idx -= HV_X64_MSR_CRASH_P0;
- *val = v->arch.hvm.viridian.crash_param[idx];
- break;
-
- case HV_X64_MSR_CRASH_CTL:
- {
- HV_CRASH_CTL_REG_CONTENTS ctl = {
- .u.CrashNotify = 1,
- };
-
- *val = ctl.AsUINT64;
- break;
- }
-
- default:
- gdprintk(XENLOG_INFO, "Read from unimplemented MSR %#x\n", idx);
- return X86EMUL_EXCEPTION;
- }
-
- return X86EMUL_OKAY;
-}
-
-void viridian_vcpu_deinit(struct vcpu *v)
-{
- teardown_vp_assist(v);
-}
-
-void viridian_domain_deinit(struct domain *d)
-{
- struct vcpu *v;
-
- for_each_vcpu ( d, v )
- teardown_vp_assist(v);
-}
-
-static DEFINE_PER_CPU(cpumask_t, ipi_cpumask);
-
-int viridian_hypercall(struct cpu_user_regs *regs)
-{
- struct vcpu *curr = current;
- struct domain *currd = curr->domain;
- int mode = hvm_guest_x86_mode(curr);
- unsigned long input_params_gpa, output_params_gpa;
- uint16_t status = HV_STATUS_SUCCESS;
-
- union hypercall_input {
- uint64_t raw;
- struct {
- uint16_t call_code;
- uint16_t fast:1;
- uint16_t rsvd1:15;
- uint16_t rep_count:12;
- uint16_t rsvd2:4;
- uint16_t rep_start:12;
- uint16_t rsvd3:4;
- };
- } input;
-
- union hypercall_output {
- uint64_t raw;
- struct {
- uint16_t result;
- uint16_t rsvd1;
- uint32_t rep_complete:12;
- uint32_t rsvd2:20;
- };
- } output = { 0 };
-
- ASSERT(is_viridian_domain(currd));
-
- switch ( mode )
- {
- case 8:
- input.raw = regs->rcx;
- input_params_gpa = regs->rdx;
- output_params_gpa = regs->r8;
- break;
- case 4:
- input.raw = (regs->rdx << 32) | regs->eax;
- input_params_gpa = (regs->rbx << 32) | regs->ecx;
- output_params_gpa = (regs->rdi << 32) | regs->esi;
- break;
- default:
- goto out;
- }
-
- switch ( input.call_code )
- {
- case HvNotifyLongSpinWait:
- /*
- * See section 14.5.1 of the specification.
- */
- perfc_incr(mshv_call_long_wait);
- do_sched_op(SCHEDOP_yield, guest_handle_from_ptr(NULL, void));
- status = HV_STATUS_SUCCESS;
- break;
-
- case HvFlushVirtualAddressSpace:
- case HvFlushVirtualAddressList:
- {
- cpumask_t *pcpu_mask;
- struct vcpu *v;
- struct {
- uint64_t address_space;
- uint64_t flags;
- uint64_t vcpu_mask;
- } input_params;
-
- /*
- * See sections 9.4.2 and 9.4.4 of the specification.
- */
- perfc_incr(mshv_call_flush);
-
- /* These hypercalls should never use the fast-call convention. */
- status = HV_STATUS_INVALID_PARAMETER;
- if ( input.fast )
- break;
-
- /* Get input parameters. */
- if ( hvm_copy_from_guest_phys(&input_params, input_params_gpa,
- sizeof(input_params)) != HVMTRANS_okay )
- break;
-
- /*
- * It is not clear from the spec. if we are supposed to
- * include current virtual CPU in the set or not in this case,
- * so err on the safe side.
- */
- if ( input_params.flags & HV_FLUSH_ALL_PROCESSORS )
- input_params.vcpu_mask = ~0ul;
-
- pcpu_mask = &this_cpu(ipi_cpumask);
- cpumask_clear(pcpu_mask);
-
- /*
- * For each specified virtual CPU flush all ASIDs to invalidate
- * TLB entries the next time it is scheduled and then, if it
- * is currently running, add its physical CPU to a mask of
- * those which need to be interrupted to force a flush.
- */
- for_each_vcpu ( currd, v )
- {
- if ( v->vcpu_id >= (sizeof(input_params.vcpu_mask) * 8) )
- break;
-
- if ( !(input_params.vcpu_mask & (1ul << v->vcpu_id)) )
- continue;
-
- hvm_asid_flush_vcpu(v);
- if ( v != curr && v->is_running )
- __cpumask_set_cpu(v->processor, pcpu_mask);
- }
-
- /*
- * Since ASIDs have now been flushed it just remains to
- * force any CPUs currently running target vCPUs out of non-
- * root mode. It's possible that re-scheduling has taken place
- * so we may unnecessarily IPI some CPUs.
- */
- if ( !cpumask_empty(pcpu_mask) )
- smp_send_event_check_mask(pcpu_mask);
-
- output.rep_complete = input.rep_count;
-
- status = HV_STATUS_SUCCESS;
- break;
- }
-
- default:
- gprintk(XENLOG_WARNING, "unimplemented hypercall %04x\n",
- input.call_code);
- /* Fallthrough. */
- case HvExtCallQueryCapabilities:
- /*
- * This hypercall seems to be erroneously issued by Windows
- * despite EnableExtendedHypercalls not being set in CPUID leaf 2.
- * Given that return a status of 'invalid code' has not so far
- * caused any problems it's not worth logging.
- */
- status = HV_STATUS_INVALID_HYPERCALL_CODE;
- break;
- }
-
-out:
- output.result = status;
- switch (mode) {
- case 8:
- regs->rax = output.raw;
- break;
- default:
- regs->rdx = output.raw >> 32;
- regs->rax = (uint32_t)output.raw;
- break;
- }
-
- return HVM_HCALL_completed;
-}
-
-static int viridian_save_domain_ctxt(struct vcpu *v, hvm_domain_context_t *h)
-{
- const struct domain *d = v->domain;
- struct hvm_viridian_domain_context ctxt = {
- .time_ref_count = d->arch.hvm.viridian.time_ref_count.val,
- .hypercall_gpa = d->arch.hvm.viridian.hypercall_gpa.raw,
- .guest_os_id = d->arch.hvm.viridian.guest_os_id.raw,
- .reference_tsc = d->arch.hvm.viridian.reference_tsc.raw,
- };
-
- if ( !is_viridian_domain(d) )
- return 0;
-
- return (hvm_save_entry(VIRIDIAN_DOMAIN, 0, h, &ctxt) != 0);
-}
-
-static int viridian_load_domain_ctxt(struct domain *d, hvm_domain_context_t *h)
-{
- struct hvm_viridian_domain_context ctxt;
-
- if ( hvm_load_entry_zeroextend(VIRIDIAN_DOMAIN, h, &ctxt) != 0 )
- return -EINVAL;
-
- d->arch.hvm.viridian.time_ref_count.val = ctxt.time_ref_count;
- d->arch.hvm.viridian.hypercall_gpa.raw = ctxt.hypercall_gpa;
- d->arch.hvm.viridian.guest_os_id.raw = ctxt.guest_os_id;
- d->arch.hvm.viridian.reference_tsc.raw = ctxt.reference_tsc;
-
- if ( d->arch.hvm.viridian.reference_tsc.fields.enabled )
- update_reference_tsc(d, 0);
-
- return 0;
-}
-
-HVM_REGISTER_SAVE_RESTORE(VIRIDIAN_DOMAIN, viridian_save_domain_ctxt,
- viridian_load_domain_ctxt, 1, HVMSR_PER_DOM);
-
-static int viridian_save_vcpu_ctxt(struct vcpu *v, hvm_domain_context_t *h)
-{
- struct hvm_viridian_vcpu_context ctxt = {
- .vp_assist_msr = v->arch.hvm.viridian.vp_assist.msr.raw,
- .vp_assist_pending = v->arch.hvm.viridian.vp_assist.pending,
- };
-
- if ( !is_viridian_domain(v->domain) )
- return 0;
-
- return hvm_save_entry(VIRIDIAN_VCPU, v->vcpu_id, h, &ctxt);
-}
-
-static int viridian_load_vcpu_ctxt(struct domain *d, hvm_domain_context_t *h)
-{
- unsigned int vcpuid = hvm_load_instance(h);
- struct vcpu *v;
- struct hvm_viridian_vcpu_context ctxt;
-
- if ( vcpuid >= d->max_vcpus || (v = d->vcpu[vcpuid]) == NULL )
- {
- dprintk(XENLOG_G_ERR, "HVM restore: dom%d has no vcpu%u\n",
- d->domain_id, vcpuid);
- return -EINVAL;
- }
-
- if ( hvm_load_entry_zeroextend(VIRIDIAN_VCPU, h, &ctxt) != 0 )
- return -EINVAL;
-
- if ( memcmp(&ctxt._pad, zero_page, sizeof(ctxt._pad)) )
- return -EINVAL;
-
- v->arch.hvm.viridian.vp_assist.msr.raw = ctxt.vp_assist_msr;
- if ( v->arch.hvm.viridian.vp_assist.msr.fields.enabled &&
- !v->arch.hvm.viridian.vp_assist.va )
- initialize_vp_assist(v);
-
- v->arch.hvm.viridian.vp_assist.pending = !!ctxt.vp_assist_pending;
-
- return 0;
-}
-
-HVM_REGISTER_SAVE_RESTORE(VIRIDIAN_VCPU, viridian_save_vcpu_ctxt,
- viridian_load_vcpu_ctxt, 1, HVMSR_PER_VCPU);
-
-static int __init parse_viridian_version(const char *arg)
-{
- const char *t;
- unsigned int n[3];
- unsigned int i = 0;
-
- n[0] = viridian_major;
- n[1] = viridian_minor;
- n[2] = viridian_build;
-
- do {
- const char *e;
-
- t = strchr(arg, ',');
- if ( !t )
- t = strchr(arg, '\0');
-
- if ( *arg && *arg != ',' && i < 3 )
- {
- n[i] = simple_strtoul(arg, &e, 0);
- if ( e != t )
- break;
- }
-
- i++;
- arg = t + 1;
- } while ( *t );
-
- if ( i != 3 )
- return -EINVAL;
-
- if ( ((typeof(viridian_major))n[0] != n[0]) ||
- ((typeof(viridian_minor))n[1] != n[1]) ||
- ((typeof(viridian_build))n[2] != n[2]) )
- return -EINVAL;
-
- viridian_major = n[0];
- viridian_minor = n[1];
- viridian_build = n[2];
-
- printk("viridian-version = %#x,%#x,%#x\n",
- viridian_major, viridian_minor, viridian_build);
- return 0;
-}
-custom_param("viridian-version", parse_viridian_version);
-
-/*
- * Local variables:
- * mode: C
- * c-file-style: "BSD"
- * c-basic-offset: 4
- * tab-width: 4
- * indent-tabs-mode: nil
- * End:
- */
diff --git a/xen/arch/x86/hvm/viridian/Makefile b/xen/arch/x86/hvm/viridian/Makefile
new file mode 100644
index 0000000000..09fd0a5f3c
--- /dev/null
+++ b/xen/arch/x86/hvm/viridian/Makefile
@@ -0,0 +1 @@
+obj-y += viridian.o
diff --git a/xen/arch/x86/hvm/viridian/viridian.c b/xen/arch/x86/hvm/viridian/viridian.c
new file mode 100644
index 0000000000..3e9beda831
--- /dev/null
+++ b/xen/arch/x86/hvm/viridian/viridian.c
@@ -0,0 +1,1121 @@
+/******************************************************************************
+ * viridian.c
+ *
+ * An implementation of some Viridian enlightenments. See Microsoft's
+ * Hypervisor Top Level Functional Specification (v5.0a) at:
+ *
+ * https://github.com/Microsoft/Virtualization-Documentation/raw/master/tlfs/Hypervisor%20Top%20Level%20Functional%20Specification%20v5.0.pdf
+ *
+ * for more information.
+ */
+
+#include <xen/sched.h>
+#include <xen/version.h>
+#include <xen/perfc.h>
+#include <xen/hypercall.h>
+#include <xen/domain_page.h>
+#include <asm/guest_access.h>
+#include <asm/paging.h>
+#include <asm/p2m.h>
+#include <asm/apic.h>
+#include <asm/hvm/support.h>
+#include <public/sched.h>
+#include <public/hvm/hvm_op.h>
+
+/* Viridian MSR numbers. */
+#define HV_X64_MSR_GUEST_OS_ID 0x40000000
+#define HV_X64_MSR_HYPERCALL 0x40000001
+#define HV_X64_MSR_VP_INDEX 0x40000002
+#define HV_X64_MSR_RESET 0x40000003
+#define HV_X64_MSR_VP_RUNTIME 0x40000010
+#define HV_X64_MSR_TIME_REF_COUNT 0x40000020
+#define HV_X64_MSR_REFERENCE_TSC 0x40000021
+#define HV_X64_MSR_TSC_FREQUENCY 0x40000022
+#define HV_X64_MSR_APIC_FREQUENCY 0x40000023
+#define HV_X64_MSR_EOI 0x40000070
+#define HV_X64_MSR_ICR 0x40000071
+#define HV_X64_MSR_TPR 0x40000072
+#define HV_X64_MSR_VP_ASSIST_PAGE 0x40000073
+#define HV_X64_MSR_SCONTROL 0x40000080
+#define HV_X64_MSR_SVERSION 0x40000081
+#define HV_X64_MSR_SIEFP 0x40000082
+#define HV_X64_MSR_SIMP 0x40000083
+#define HV_X64_MSR_EOM 0x40000084
+#define HV_X64_MSR_SINT0 0x40000090
+#define HV_X64_MSR_SINT1 0x40000091
+#define HV_X64_MSR_SINT2 0x40000092
+#define HV_X64_MSR_SINT3 0x40000093
+#define HV_X64_MSR_SINT4 0x40000094
+#define HV_X64_MSR_SINT5 0x40000095
+#define HV_X64_MSR_SINT6 0x40000096
+#define HV_X64_MSR_SINT7 0x40000097
+#define HV_X64_MSR_SINT8 0x40000098
+#define HV_X64_MSR_SINT9 0x40000099
+#define HV_X64_MSR_SINT10 0x4000009A
+#define HV_X64_MSR_SINT11 0x4000009B
+#define HV_X64_MSR_SINT12 0x4000009C
+#define HV_X64_MSR_SINT13 0x4000009D
+#define HV_X64_MSR_SINT14 0x4000009E
+#define HV_X64_MSR_SINT15 0x4000009F
+#define HV_X64_MSR_STIMER0_CONFIG 0x400000B0
+#define HV_X64_MSR_STIMER0_COUNT 0x400000B1
+#define HV_X64_MSR_STIMER1_CONFIG 0x400000B2
+#define HV_X64_MSR_STIMER1_COUNT 0x400000B3
+#define HV_X64_MSR_STIMER2_CONFIG 0x400000B4
+#define HV_X64_MSR_STIMER2_COUNT 0x400000B5
+#define HV_X64_MSR_STIMER3_CONFIG 0x400000B6
+#define HV_X64_MSR_STIMER3_COUNT 0x400000B7
+#define HV_X64_MSR_POWER_STATE_TRIGGER_C1 0x400000C1
+#define HV_X64_MSR_POWER_STATE_TRIGGER_C2 0x400000C2
+#define HV_X64_MSR_POWER_STATE_TRIGGER_C3 0x400000C3
+#define HV_X64_MSR_POWER_STATE_CONFIG_C1 0x400000D1
+#define HV_X64_MSR_POWER_STATE_CONFIG_C2 0x400000D2
+#define HV_X64_MSR_POWER_STATE_CONFIG_C3 0x400000D3
+#define HV_X64_MSR_STATS_PARTITION_RETAIL_PAGE 0x400000E0
+#define HV_X64_MSR_STATS_PARTITION_INTERNAL_PAGE 0x400000E1
+#define HV_X64_MSR_STATS_VP_RETAIL_PAGE 0x400000E2
+#define HV_X64_MSR_STATS_VP_INTERNAL_PAGE 0x400000E3
+#define HV_X64_MSR_GUEST_IDLE 0x400000F0
+#define HV_X64_MSR_SYNTH_DEBUG_CONTROL 0x400000F1
+#define HV_X64_MSR_SYNTH_DEBUG_STATUS 0x400000F2
+#define HV_X64_MSR_SYNTH_DEBUG_SEND_BUFFER 0x400000F3
+#define HV_X64_MSR_SYNTH_DEBUG_RECEIVE_BUFFER 0x400000F4
+#define HV_X64_MSR_SYNTH_DEBUG_PENDING_BUFFER 0x400000F5
+#define HV_X64_MSR_CRASH_P0 0x40000100
+#define HV_X64_MSR_CRASH_P1 0x40000101
+#define HV_X64_MSR_CRASH_P2 0x40000102
+#define HV_X64_MSR_CRASH_P3 0x40000103
+#define HV_X64_MSR_CRASH_P4 0x40000104
+#define HV_X64_MSR_CRASH_CTL 0x40000105
+
+/* Viridian Hypercall Status Codes. */
+#define HV_STATUS_SUCCESS 0x0000
+#define HV_STATUS_INVALID_HYPERCALL_CODE 0x0002
+#define HV_STATUS_INVALID_PARAMETER 0x0005
+
+/* Viridian Hypercall Codes. */
+#define HvFlushVirtualAddressSpace 0x0002
+#define HvFlushVirtualAddressList 0x0003
+#define HvNotifyLongSpinWait 0x0008
+#define HvGetPartitionId 0x0046
+#define HvExtCallQueryCapabilities 0x8001
+
+/* Viridian Hypercall Flags. */
+#define HV_FLUSH_ALL_PROCESSORS 1
+
+/*
+ * Viridian Partition Privilege Flags.
+ *
+ * This is taken from section 4.2.2 of the specification, and fixed for
+ * style and correctness.
+ */
+typedef struct {
+ /* Access to virtual MSRs */
+ uint64_t AccessVpRunTimeReg:1;
+ uint64_t AccessPartitionReferenceCounter:1;
+ uint64_t AccessSynicRegs:1;
+ uint64_t AccessSyntheticTimerRegs:1;
+ uint64_t AccessIntrCtrlRegs:1;
+ uint64_t AccessHypercallMsrs:1;
+ uint64_t AccessVpIndex:1;
+ uint64_t AccessResetReg:1;
+ uint64_t AccessStatsReg:1;
+ uint64_t AccessPartitionReferenceTsc:1;
+ uint64_t AccessGuestIdleReg:1;
+ uint64_t AccessFrequencyRegs:1;
+ uint64_t AccessDebugRegs:1;
+ uint64_t Reserved1:19;
+
+ /* Access to hypercalls */
+ uint64_t CreatePartitions:1;
+ uint64_t AccessPartitionId:1;
+ uint64_t AccessMemoryPool:1;
+ uint64_t AdjustMessageBuffers:1;
+ uint64_t PostMessages:1;
+ uint64_t SignalEvents:1;
+ uint64_t CreatePort:1;
+ uint64_t ConnectPort:1;
+ uint64_t AccessStats:1;
+ uint64_t Reserved2:2;
+ uint64_t Debugging:1;
+ uint64_t CpuManagement:1;
+ uint64_t Reserved3:1;
+ uint64_t Reserved4:1;
+ uint64_t Reserved5:1;
+ uint64_t AccessVSM:1;
+ uint64_t AccessVpRegisters:1;
+ uint64_t Reserved6:1;
+ uint64_t Reserved7:1;
+ uint64_t EnableExtendedHypercalls:1;
+ uint64_t StartVirtualProcessor:1;
+ uint64_t Reserved8:10;
+} HV_PARTITION_PRIVILEGE_MASK;
+
+typedef union _HV_CRASH_CTL_REG_CONTENTS
+{
+ uint64_t AsUINT64;
+ struct
+ {
+ uint64_t Reserved:63;
+ uint64_t CrashNotify:1;
+ } u;
+} HV_CRASH_CTL_REG_CONTENTS;
+
+/* Viridian CPUID leaf 3, Hypervisor Feature Indication */
+#define CPUID3D_CRASH_MSRS (1 << 10)
+
+/* Viridian CPUID leaf 4: Implementation Recommendations. */
+#define CPUID4A_HCALL_REMOTE_TLB_FLUSH (1 << 2)
+#define CPUID4A_MSR_BASED_APIC (1 << 3)
+#define CPUID4A_RELAX_TIMER_INT (1 << 5)
+
+/* Viridian CPUID leaf 6: Implementation HW features detected and in use. */
+#define CPUID6A_APIC_OVERLAY (1 << 0)
+#define CPUID6A_MSR_BITMAPS (1 << 1)
+#define CPUID6A_NESTED_PAGING (1 << 3)
+
+/*
+ * Version and build number reported by CPUID leaf 2
+ *
+ * These numbers are chosen to match the version numbers reported by
+ * Windows Server 2008.
+ */
+static uint16_t __read_mostly viridian_major = 6;
+static uint16_t __read_mostly viridian_minor = 0;
+static uint32_t __read_mostly viridian_build = 0x1772;
+
+/*
+ * Maximum number of retries before the guest will notify of failure
+ * to acquire a spinlock.
+ */
+static uint32_t __read_mostly viridian_spinlock_retry_count = 2047;
+integer_param("viridian-spinlock-retry-count",
+ viridian_spinlock_retry_count);
+
+void cpuid_viridian_leaves(const struct vcpu *v, uint32_t leaf,
+ uint32_t subleaf, struct cpuid_leaf *res)
+{
+ const struct domain *d = v->domain;
+
+ ASSERT(is_viridian_domain(d));
+ ASSERT(leaf >= 0x40000000 && leaf < 0x40000100);
+
+ leaf -= 0x40000000;
+
+ switch ( leaf )
+ {
+ case 0:
+ /* See section 2.4.1 of the specification */
+ res->a = 0x40000006; /* Maximum leaf */
+ memcpy(&res->b, "Micr", 4);
+ memcpy(&res->c, "osof", 4);
+ memcpy(&res->d, "t Hv", 4);
+ break;
+
+ case 1:
+ /* See section 2.4.2 of the specification */
+ memcpy(&res->a, "Hv#1", 4);
+ break;
+
+ case 2:
+ /* Hypervisor information, but only if the guest has set its
+ own version number. */
+ if ( d->arch.hvm.viridian.guest_os_id.raw == 0 )
+ break;
+ res->a = viridian_build;
+ res->b = ((uint32_t)viridian_major << 16) | viridian_minor;
+ res->c = 0; /* SP */
+ res->d = 0; /* Service branch and number */
+ break;
+
+ case 3:
+ {
+ /*
+ * Section 2.4.4 details this leaf and states that EAX and EBX
+ * are defined to be the low and high parts of the partition
+ * privilege mask respectively.
+ */
+ HV_PARTITION_PRIVILEGE_MASK mask = {
+ .AccessIntrCtrlRegs = 1,
+ .AccessHypercallMsrs = 1,
+ .AccessVpIndex = 1,
+ };
+ union {
+ HV_PARTITION_PRIVILEGE_MASK mask;
+ struct { uint32_t lo, hi; };
+ } u;
+
+ if ( !(viridian_feature_mask(d) & HVMPV_no_freq) )
+ mask.AccessFrequencyRegs = 1;
+ if ( viridian_feature_mask(d) & HVMPV_time_ref_count )
+ mask.AccessPartitionReferenceCounter = 1;
+ if ( viridian_feature_mask(d) & HVMPV_reference_tsc )
+ mask.AccessPartitionReferenceTsc = 1;
+
+ u.mask = mask;
+
+ res->a = u.lo;
+ res->b = u.hi;
+
+ if ( viridian_feature_mask(d) & HVMPV_crash_ctl )
+ res->d = CPUID3D_CRASH_MSRS;
+
+ break;
+ }
+
+ case 4:
+ /* Recommended hypercall usage. */
+ if ( (d->arch.hvm.viridian.guest_os_id.raw == 0) ||
+ (d->arch.hvm.viridian.guest_os_id.fields.os < 4) )
+ break;
+ res->a = CPUID4A_RELAX_TIMER_INT;
+ if ( viridian_feature_mask(d) & HVMPV_hcall_remote_tlb_flush )
+ res->a |= CPUID4A_HCALL_REMOTE_TLB_FLUSH;
+ if ( !cpu_has_vmx_apic_reg_virt )
+ res->a |= CPUID4A_MSR_BASED_APIC;
+
+ /*
+ * This value is the recommended number of attempts to try to
+ * acquire a spinlock before notifying the hypervisor via the
+ * HvNotifyLongSpinWait hypercall.
+ */
+ res->b = viridian_spinlock_retry_count;
+ break;
+
+ case 6:
+ /* Detected and in use hardware features. */
+ if ( cpu_has_vmx_virtualize_apic_accesses )
+ res->a |= CPUID6A_APIC_OVERLAY;
+ if ( cpu_has_vmx_msr_bitmap || (read_efer() & EFER_SVME) )
+ res->a |= CPUID6A_MSR_BITMAPS;
+ if ( hap_enabled(d) )
+ res->a |= CPUID6A_NESTED_PAGING;
+ break;
+ }
+}
+
+static void dump_guest_os_id(const struct domain *d)
+{
+ const union viridian_guest_os_id *goi;
+
+ goi = &d->arch.hvm.viridian.guest_os_id;
+
+ printk(XENLOG_G_INFO
+ "d%d: VIRIDIAN GUEST_OS_ID: vendor: %x os: %x major: %x minor: %x sp: %x build: %x\n",
+ d->domain_id,
+ goi->fields.vendor, goi->fields.os,
+ goi->fields.major, goi->fields.minor,
+ goi->fields.service_pack, goi->fields.build_number);
+}
+
+static void dump_hypercall(const struct domain *d)
+{
+ const union viridian_hypercall_gpa *hg;
+
+ hg = &d->arch.hvm.viridian.hypercall_gpa;
+
+ printk(XENLOG_G_INFO "d%d: VIRIDIAN HYPERCALL: enabled: %x pfn: %lx\n",
+ d->domain_id,
+ hg->fields.enabled, (unsigned long)hg->fields.pfn);
+}
+
+static void dump_vp_assist(const struct vcpu *v)
+{
+ const union viridian_vp_assist *va;
+
+ va = &v->arch.hvm.viridian.vp_assist.msr;
+
+ printk(XENLOG_G_INFO "%pv: VIRIDIAN VP_ASSIST_PAGE: enabled: %x pfn: %lx\n",
+ v, va->fields.enabled, (unsigned long)va->fields.pfn);
+}
+
+static void dump_reference_tsc(const struct domain *d)
+{
+ const union viridian_reference_tsc *rt;
+
+ rt = &d->arch.hvm.viridian.reference_tsc;
+
+ printk(XENLOG_G_INFO "d%d: VIRIDIAN REFERENCE_TSC: enabled: %x pfn: %lx\n",
+ d->domain_id,
+ rt->fields.enabled, (unsigned long)rt->fields.pfn);
+}
+
+static void enable_hypercall_page(struct domain *d)
+{
+ unsigned long gmfn = d->arch.hvm.viridian.hypercall_gpa.fields.pfn;
+ struct page_info *page = get_page_from_gfn(d, gmfn, NULL, P2M_ALLOC);
+ uint8_t *p;
+
+ if ( !page || !get_page_type(page, PGT_writable_page) )
+ {
+ if ( page )
+ put_page(page);
+ gdprintk(XENLOG_WARNING, "Bad GMFN %#"PRI_gfn" (MFN %#"PRI_mfn")\n",
+ gmfn, mfn_x(page ? page_to_mfn(page) : INVALID_MFN));
+ return;
+ }
+
+ p = __map_domain_page(page);
+
+ /*
+ * We set the bit 31 in %eax (reserved field in the Viridian hypercall
+ * calling convention) to differentiate Xen and Viridian hypercalls.
+ */
+ *(u8 *)(p + 0) = 0x0d; /* orl $0x80000000, %eax */
+ *(u32 *)(p + 1) = 0x80000000;
+ *(u8 *)(p + 5) = 0x0f; /* vmcall/vmmcall */
+ *(u8 *)(p + 6) = 0x01;
+ *(u8 *)(p + 7) = (cpu_has_vmx ? 0xc1 : 0xd9);
+ *(u8 *)(p + 8) = 0xc3; /* ret */
+ memset(p + 9, 0xcc, PAGE_SIZE - 9); /* int3, int3, ... */
+
+ unmap_domain_page(p);
+
+ put_page_and_type(page);
+}
+
+static void initialize_vp_assist(struct vcpu *v)
+{
+ struct domain *d = v->domain;
+ unsigned long gmfn = v->arch.hvm.viridian.vp_assist.msr.fields.pfn;
+ struct page_info *page = get_page_from_gfn(d, gmfn, NULL, P2M_ALLOC);
+ void *va;
+
+ ASSERT(!v->arch.hvm.viridian.vp_assist.va);
+
+ /*
+ * See section 7.8.7 of the specification for details of this
+ * enlightenment.
+ */
+
+ if ( !page )
+ goto fail;
+
+ if ( !get_page_type(page, PGT_writable_page) )
+ {
+ put_page(page);
+ goto fail;
+ }
+
+ va = __map_domain_page_global(page);
+ if ( !va )
+ {
+ put_page_and_type(page);
+ goto fail;
+ }
+
+ clear_page(va);
+
+ v->arch.hvm.viridian.vp_assist.va = va;
+ return;
+
+ fail:
+ gdprintk(XENLOG_WARNING, "Bad GMFN %#"PRI_gfn" (MFN %#"PRI_mfn")\n", gmfn,
+ mfn_x(page ? page_to_mfn(page) : INVALID_MFN));
+}
+
+static void teardown_vp_assist(struct vcpu *v)
+{
+ void *va = v->arch.hvm.viridian.vp_assist.va;
+ struct page_info *page;
+
+ if ( !va )
+ return;
+
+ v->arch.hvm.viridian.vp_assist.va = NULL;
+
+ page = mfn_to_page(domain_page_map_to_mfn(va));
+
+ unmap_domain_page_global(va);
+ put_page_and_type(page);
+}
+
+void viridian_apic_assist_set(struct vcpu *v)
+{
+ uint32_t *va = v->arch.hvm.viridian.vp_assist.va;
+
+ if ( !va )
+ return;
+
+ /*
+ * If there is already an assist pending then something has gone
+ * wrong and the VM will most likely hang so force a crash now
+ * to make the problem clear.
+ */
+ if ( v->arch.hvm.viridian.vp_assist.pending )
+ domain_crash(v->domain);
+
+ v->arch.hvm.viridian.vp_assist.pending = true;
+ *va |= 1u;
+}
+
+bool viridian_apic_assist_completed(struct vcpu *v)
+{
+ uint32_t *va = v->arch.hvm.viridian.vp_assist.va;
+
+ if ( !va )
+ return false;
+
+ if ( v->arch.hvm.viridian.vp_assist.pending &&
+ !(*va & 1u) )
+ {
+ /* An EOI has been avoided */
+ v->arch.hvm.viridian.vp_assist.pending = false;
+ return true;
+ }
+
+ return false;
+}
+
+void viridian_apic_assist_clear(struct vcpu *v)
+{
+ uint32_t *va = v->arch.hvm.viridian.vp_assist.va;
+
+ if ( !va )
+ return;
+
+ *va &= ~1u;
+ v->arch.hvm.viridian.vp_assist.pending = false;
+}
+
+static void update_reference_tsc(struct domain *d, bool_t initialize)
+{
+ unsigned long gmfn = d->arch.hvm.viridian.reference_tsc.fields.pfn;
+ struct page_info *page = get_page_from_gfn(d, gmfn, NULL, P2M_ALLOC);
+ HV_REFERENCE_TSC_PAGE *p;
+
+ if ( !page || !get_page_type(page, PGT_writable_page) )
+ {
+ if ( page )
+ put_page(page);
+ gdprintk(XENLOG_WARNING, "Bad GMFN %#"PRI_gfn" (MFN %#"PRI_mfn")\n",
+ gmfn, mfn_x(page ? page_to_mfn(page) : INVALID_MFN));
+ return;
+ }
+
+ p = __map_domain_page(page);
+
+ if ( initialize )
+ clear_page(p);
+
+ /*
+ * This enlightenment must be disabled is the host TSC is not invariant.
+ * However it is also disabled if vtsc is true (which means rdtsc is being
+ * emulated). This generally happens when guest TSC freq and host TSC freq
+ * don't match. The TscScale value could be adjusted to cope with this,
+ * allowing vtsc to be turned off, but support for this is not yet present
+ * in the hypervisor. Thus is it is possible that migrating a Windows VM
+ * between hosts of differing TSC frequencies may result in large
+ * differences in guest performance.
+ */
+ if ( !host_tsc_is_safe() || d->arch.vtsc )
+ {
+ /*
+ * The specification states that valid values of TscSequence range
+ * from 0 to 0xFFFFFFFE. The value 0xFFFFFFFF is used to indicate
+ * this mechanism is no longer a reliable source of time and that
+ * the VM should fall back to a different source.
+ *
+ * Server 2012 (6.2 kernel) and 2012 R2 (6.3 kernel) actually violate
+ * the spec. and rely on a value of 0 to indicate that this
+ * enlightenment should no longer be used. These two kernel
+ * versions are currently the only ones to make use of this
+ * enlightenment, so just use 0 here.
+ */
+ p->TscSequence = 0;
+
+ printk(XENLOG_G_INFO "d%d: VIRIDIAN REFERENCE_TSC: invalidated\n",
+ d->domain_id);
+ goto out;
+ }
+
+ /*
+ * The guest will calculate reference time according to the following
+ * formula:
+ *
+ * ReferenceTime = ((RDTSC() * TscScale) >> 64) + TscOffset
+ *
+ * Windows uses a 100ns tick, so we need a scale which is cpu
+ * ticks per 100ns shifted left by 64.
+ */
+ p->TscScale = ((10000ul << 32) / d->arch.tsc_khz) << 32;
+
+ p->TscSequence++;
+ if ( p->TscSequence == 0xFFFFFFFF ||
+ p->TscSequence == 0 ) /* Avoid both 'invalid' values */
+ p->TscSequence = 1;
+
+ out:
+ unmap_domain_page(p);
+
+ put_page_and_type(page);
+}
+
+int guest_wrmsr_viridian(struct vcpu *v, uint32_t idx, uint64_t val)
+{
+ struct domain *d = v->domain;
+
+ ASSERT(is_viridian_domain(d));
+
+ switch ( idx )
+ {
+ case HV_X64_MSR_GUEST_OS_ID:
+ perfc_incr(mshv_wrmsr_osid);
+ d->arch.hvm.viridian.guest_os_id.raw = val;
+ dump_guest_os_id(d);
+ break;
+
+ case HV_X64_MSR_HYPERCALL:
+ perfc_incr(mshv_wrmsr_hc_page);
+ d->arch.hvm.viridian.hypercall_gpa.raw = val;
+ dump_hypercall(d);
+ if ( d->arch.hvm.viridian.hypercall_gpa.fields.enabled )
+ enable_hypercall_page(d);
+ break;
+
+ case HV_X64_MSR_VP_INDEX:
+ perfc_incr(mshv_wrmsr_vp_index);
+ break;
+
+ case HV_X64_MSR_EOI:
+ perfc_incr(mshv_wrmsr_eoi);
+ vlapic_EOI_set(vcpu_vlapic(v));
+ break;
+
+ case HV_X64_MSR_ICR: {
+ u32 eax = (u32)val, edx = (u32)(val >> 32);
+ struct vlapic *vlapic = vcpu_vlapic(v);
+ perfc_incr(mshv_wrmsr_icr);
+ eax &= ~(1 << 12);
+ edx &= 0xff000000;
+ vlapic_set_reg(vlapic, APIC_ICR2, edx);
+ vlapic_ipi(vlapic, eax, edx);
+ vlapic_set_reg(vlapic, APIC_ICR, eax);
+ break;
+ }
+
+ case HV_X64_MSR_TPR:
+ perfc_incr(mshv_wrmsr_tpr);
+ vlapic_set_reg(vcpu_vlapic(v), APIC_TASKPRI, (uint8_t)val);
+ break;
+
+ case HV_X64_MSR_VP_ASSIST_PAGE:
+ perfc_incr(mshv_wrmsr_apic_msr);
+ teardown_vp_assist(v); /* release any previous mapping */
+ v->arch.hvm.viridian.vp_assist.msr.raw = val;
+ dump_vp_assist(v);
+ if ( v->arch.hvm.viridian.vp_assist.msr.fields.enabled )
+ initialize_vp_assist(v);
+ break;
+
+ case HV_X64_MSR_REFERENCE_TSC:
+ if ( !(viridian_feature_mask(d) & HVMPV_reference_tsc) )
+ return X86EMUL_EXCEPTION;
+
+ perfc_incr(mshv_wrmsr_tsc_msr);
+ d->arch.hvm.viridian.reference_tsc.raw = val;
+ dump_reference_tsc(d);
+ if ( d->arch.hvm.viridian.reference_tsc.fields.enabled )
+ update_reference_tsc(d, 1);
+ break;
+
+ case HV_X64_MSR_CRASH_P0:
+ case HV_X64_MSR_CRASH_P1:
+ case HV_X64_MSR_CRASH_P2:
+ case HV_X64_MSR_CRASH_P3:
+ case HV_X64_MSR_CRASH_P4:
+ BUILD_BUG_ON(HV_X64_MSR_CRASH_P4 - HV_X64_MSR_CRASH_P0 >=
+ ARRAY_SIZE(v->arch.hvm.viridian.crash_param));
+
+ idx -= HV_X64_MSR_CRASH_P0;
+ v->arch.hvm.viridian.crash_param[idx] = val;
+ break;
+
+ case HV_X64_MSR_CRASH_CTL:
+ {
+ HV_CRASH_CTL_REG_CONTENTS ctl;
+
+ ctl.AsUINT64 = val;
+
+ if ( !ctl.u.CrashNotify )
+ break;
+
+ spin_lock(&d->shutdown_lock);
+ d->shutdown_code = SHUTDOWN_crash;
+ spin_unlock(&d->shutdown_lock);
+
+ gprintk(XENLOG_WARNING, "VIRIDIAN CRASH: %lx %lx %lx %lx %lx\n",
+ v->arch.hvm.viridian.crash_param[0],
+ v->arch.hvm.viridian.crash_param[1],
+ v->arch.hvm.viridian.crash_param[2],
+ v->arch.hvm.viridian.crash_param[3],
+ v->arch.hvm.viridian.crash_param[4]);
+ break;
+ }
+
+ default:
+ gdprintk(XENLOG_INFO,
+ "Write %016"PRIx64" to unimplemented MSR %#x\n", val, idx);
+ return X86EMUL_EXCEPTION;
+ }
+
+ return X86EMUL_OKAY;
+}
+
+static int64_t raw_trc_val(struct domain *d)
+{
+ uint64_t tsc;
+ struct time_scale tsc_to_ns;
+
+ tsc = hvm_get_guest_tsc(pt_global_vcpu_target(d));
+
+ /* convert tsc to count of 100ns periods */
+ set_time_scale(&tsc_to_ns, d->arch.tsc_khz * 1000ul);
+ return scale_delta(tsc, &tsc_to_ns) / 100ul;
+}
+
+void viridian_time_ref_count_freeze(struct domain *d)
+{
+ struct viridian_time_ref_count *trc;
+
+ trc = &d->arch.hvm.viridian.time_ref_count;
+
+ if ( test_and_clear_bit(_TRC_running, &trc->flags) )
+ trc->val = raw_trc_val(d) + trc->off;
+}
+
+void viridian_time_ref_count_thaw(struct domain *d)
+{
+ struct viridian_time_ref_count *trc;
+
+ trc = &d->arch.hvm.viridian.time_ref_count;
+
+ if ( !d->is_shutting_down &&
+ !test_and_set_bit(_TRC_running, &trc->flags) )
+ trc->off = (int64_t)trc->val - raw_trc_val(d);
+}
+
+int guest_rdmsr_viridian(const struct vcpu *v, uint32_t idx, uint64_t *val)
+{
+ struct domain *d = v->domain;
+
+ ASSERT(is_viridian_domain(d));
+
+ switch ( idx )
+ {
+ case HV_X64_MSR_GUEST_OS_ID:
+ perfc_incr(mshv_rdmsr_osid);
+ *val = d->arch.hvm.viridian.guest_os_id.raw;
+ break;
+
+ case HV_X64_MSR_HYPERCALL:
+ perfc_incr(mshv_rdmsr_hc_page);
+ *val = d->arch.hvm.viridian.hypercall_gpa.raw;
+ break;
+
+ case HV_X64_MSR_VP_INDEX:
+ perfc_incr(mshv_rdmsr_vp_index);
+ *val = v->vcpu_id;
+ break;
+
+ case HV_X64_MSR_TSC_FREQUENCY:
+ if ( viridian_feature_mask(d) & HVMPV_no_freq )
+ return X86EMUL_EXCEPTION;
+
+ perfc_incr(mshv_rdmsr_tsc_frequency);
+ *val = (uint64_t)d->arch.tsc_khz * 1000ull;
+ break;
+
+ case HV_X64_MSR_APIC_FREQUENCY:
+ if ( viridian_feature_mask(d) & HVMPV_no_freq )
+ return X86EMUL_EXCEPTION;
+
+ perfc_incr(mshv_rdmsr_apic_frequency);
+ *val = 1000000000ull / APIC_BUS_CYCLE_NS;
+ break;
+
+ case HV_X64_MSR_ICR:
+ perfc_incr(mshv_rdmsr_icr);
+ *val = (((uint64_t)vlapic_get_reg(vcpu_vlapic(v), APIC_ICR2) << 32) |
+ vlapic_get_reg(vcpu_vlapic(v), APIC_ICR));
+ break;
+
+ case HV_X64_MSR_TPR:
+ perfc_incr(mshv_rdmsr_tpr);
+ *val = vlapic_get_reg(vcpu_vlapic(v), APIC_TASKPRI);
+ break;
+
+ case HV_X64_MSR_VP_ASSIST_PAGE:
+ perfc_incr(mshv_rdmsr_apic_msr);
+ *val = v->arch.hvm.viridian.vp_assist.msr.raw;
+ break;
+
+ case HV_X64_MSR_REFERENCE_TSC:
+ if ( !(viridian_feature_mask(d) & HVMPV_reference_tsc) )
+ return X86EMUL_EXCEPTION;
+
+ perfc_incr(mshv_rdmsr_tsc_msr);
+ *val = d->arch.hvm.viridian.reference_tsc.raw;
+ break;
+
+ case HV_X64_MSR_TIME_REF_COUNT:
+ {
+ struct viridian_time_ref_count *trc;
+
+ trc = &d->arch.hvm.viridian.time_ref_count;
+
+ if ( !(viridian_feature_mask(d) & HVMPV_time_ref_count) )
+ return X86EMUL_EXCEPTION;
+
+ if ( !test_and_set_bit(_TRC_accessed, &trc->flags) )
+ printk(XENLOG_G_INFO "d%d: VIRIDIAN MSR_TIME_REF_COUNT: accessed\n",
+ d->domain_id);
+
+ perfc_incr(mshv_rdmsr_time_ref_count);
+ *val = raw_trc_val(d) + trc->off;
+ break;
+ }
+
+ case HV_X64_MSR_CRASH_P0:
+ case HV_X64_MSR_CRASH_P1:
+ case HV_X64_MSR_CRASH_P2:
+ case HV_X64_MSR_CRASH_P3:
+ case HV_X64_MSR_CRASH_P4:
+ BUILD_BUG_ON(HV_X64_MSR_CRASH_P4 - HV_X64_MSR_CRASH_P0 >=
+ ARRAY_SIZE(v->arch.hvm.viridian.crash_param));
+
+ idx -= HV_X64_MSR_CRASH_P0;
+ *val = v->arch.hvm.viridian.crash_param[idx];
+ break;
+
+ case HV_X64_MSR_CRASH_CTL:
+ {
+ HV_CRASH_CTL_REG_CONTENTS ctl = {
+ .u.CrashNotify = 1,
+ };
+
+ *val = ctl.AsUINT64;
+ break;
+ }
+
+ default:
+ gdprintk(XENLOG_INFO, "Read from unimplemented MSR %#x\n", idx);
+ return X86EMUL_EXCEPTION;
+ }
+
+ return X86EMUL_OKAY;
+}
+
+void viridian_vcpu_deinit(struct vcpu *v)
+{
+ teardown_vp_assist(v);
+}
+
+void viridian_domain_deinit(struct domain *d)
+{
+ struct vcpu *v;
+
+ for_each_vcpu ( d, v )
+ teardown_vp_assist(v);
+}
+
+static DEFINE_PER_CPU(cpumask_t, ipi_cpumask);
+
+int viridian_hypercall(struct cpu_user_regs *regs)
+{
+ struct vcpu *curr = current;
+ struct domain *currd = curr->domain;
+ int mode = hvm_guest_x86_mode(curr);
+ unsigned long input_params_gpa, output_params_gpa;
+ uint16_t status = HV_STATUS_SUCCESS;
+
+ union hypercall_input {
+ uint64_t raw;
+ struct {
+ uint16_t call_code;
+ uint16_t fast:1;
+ uint16_t rsvd1:15;
+ uint16_t rep_count:12;
+ uint16_t rsvd2:4;
+ uint16_t rep_start:12;
+ uint16_t rsvd3:4;
+ };
+ } input;
+
+ union hypercall_output {
+ uint64_t raw;
+ struct {
+ uint16_t result;
+ uint16_t rsvd1;
+ uint32_t rep_complete:12;
+ uint32_t rsvd2:20;
+ };
+ } output = { 0 };
+
+ ASSERT(is_viridian_domain(currd));
+
+ switch ( mode )
+ {
+ case 8:
+ input.raw = regs->rcx;
+ input_params_gpa = regs->rdx;
+ output_params_gpa = regs->r8;
+ break;
+ case 4:
+ input.raw = (regs->rdx << 32) | regs->eax;
+ input_params_gpa = (regs->rbx << 32) | regs->ecx;
+ output_params_gpa = (regs->rdi << 32) | regs->esi;
+ break;
+ default:
+ goto out;
+ }
+
+ switch ( input.call_code )
+ {
+ case HvNotifyLongSpinWait:
+ /*
+ * See section 14.5.1 of the specification.
+ */
+ perfc_incr(mshv_call_long_wait);
+ do_sched_op(SCHEDOP_yield, guest_handle_from_ptr(NULL, void));
+ status = HV_STATUS_SUCCESS;
+ break;
+
+ case HvFlushVirtualAddressSpace:
+ case HvFlushVirtualAddressList:
+ {
+ cpumask_t *pcpu_mask;
+ struct vcpu *v;
+ struct {
+ uint64_t address_space;
+ uint64_t flags;
+ uint64_t vcpu_mask;
+ } input_params;
+
+ /*
+ * See sections 9.4.2 and 9.4.4 of the specification.
+ */
+ perfc_incr(mshv_call_flush);
+
+ /* These hypercalls should never use the fast-call convention. */
+ status = HV_STATUS_INVALID_PARAMETER;
+ if ( input.fast )
+ break;
+
+ /* Get input parameters. */
+ if ( hvm_copy_from_guest_phys(&input_params, input_params_gpa,
+ sizeof(input_params)) != HVMTRANS_okay )
+ break;
+
+ /*
+ * It is not clear from the spec. if we are supposed to
+ * include current virtual CPU in the set or not in this case,
+ * so err on the safe side.
+ */
+ if ( input_params.flags & HV_FLUSH_ALL_PROCESSORS )
+ input_params.vcpu_mask = ~0ul;
+
+ pcpu_mask = &this_cpu(ipi_cpumask);
+ cpumask_clear(pcpu_mask);
+
+ /*
+ * For each specified virtual CPU flush all ASIDs to invalidate
+ * TLB entries the next time it is scheduled and then, if it
+ * is currently running, add its physical CPU to a mask of
+ * those which need to be interrupted to force a flush.
+ */
+ for_each_vcpu ( currd, v )
+ {
+ if ( v->vcpu_id >= (sizeof(input_params.vcpu_mask) * 8) )
+ break;
+
+ if ( !(input_params.vcpu_mask & (1ul << v->vcpu_id)) )
+ continue;
+
+ hvm_asid_flush_vcpu(v);
+ if ( v != curr && v->is_running )
+ __cpumask_set_cpu(v->processor, pcpu_mask);
+ }
+
+ /*
+ * Since ASIDs have now been flushed it just remains to
+ * force any CPUs currently running target vCPUs out of non-
+ * root mode. It's possible that re-scheduling has taken place
+ * so we may unnecessarily IPI some CPUs.
+ */
+ if ( !cpumask_empty(pcpu_mask) )
+ smp_send_event_check_mask(pcpu_mask);
+
+ output.rep_complete = input.rep_count;
+
+ status = HV_STATUS_SUCCESS;
+ break;
+ }
+
+ default:
+ gprintk(XENLOG_WARNING, "unimplemented hypercall %04x\n",
+ input.call_code);
+ /* Fallthrough. */
+ case HvExtCallQueryCapabilities:
+ /*
+ * This hypercall seems to be erroneously issued by Windows
+ * despite EnableExtendedHypercalls not being set in CPUID leaf 2.
+ * Given that return a status of 'invalid code' has not so far
+ * caused any problems it's not worth logging.
+ */
+ status = HV_STATUS_INVALID_HYPERCALL_CODE;
+ break;
+ }
+
+out:
+ output.result = status;
+ switch (mode) {
+ case 8:
+ regs->rax = output.raw;
+ break;
+ default:
+ regs->rdx = output.raw >> 32;
+ regs->rax = (uint32_t)output.raw;
+ break;
+ }
+
+ return HVM_HCALL_completed;
+}
+
+static int viridian_save_domain_ctxt(struct vcpu *v, hvm_domain_context_t *h)
+{
+ const struct domain *d = v->domain;
+ struct hvm_viridian_domain_context ctxt = {
+ .time_ref_count = d->arch.hvm.viridian.time_ref_count.val,
+ .hypercall_gpa = d->arch.hvm.viridian.hypercall_gpa.raw,
+ .guest_os_id = d->arch.hvm.viridian.guest_os_id.raw,
+ .reference_tsc = d->arch.hvm.viridian.reference_tsc.raw,
+ };
+
+ if ( !is_viridian_domain(d) )
+ return 0;
+
+ return (hvm_save_entry(VIRIDIAN_DOMAIN, 0, h, &ctxt) != 0);
+}
+
+static int viridian_load_domain_ctxt(struct domain *d, hvm_domain_context_t *h)
+{
+ struct hvm_viridian_domain_context ctxt;
+
+ if ( hvm_load_entry_zeroextend(VIRIDIAN_DOMAIN, h, &ctxt) != 0 )
+ return -EINVAL;
+
+ d->arch.hvm.viridian.time_ref_count.val = ctxt.time_ref_count;
+ d->arch.hvm.viridian.hypercall_gpa.raw = ctxt.hypercall_gpa;
+ d->arch.hvm.viridian.guest_os_id.raw = ctxt.guest_os_id;
+ d->arch.hvm.viridian.reference_tsc.raw = ctxt.reference_tsc;
+
+ if ( d->arch.hvm.viridian.reference_tsc.fields.enabled )
+ update_reference_tsc(d, 0);
+
+ return 0;
+}
+
+HVM_REGISTER_SAVE_RESTORE(VIRIDIAN_DOMAIN, viridian_save_domain_ctxt,
+ viridian_load_domain_ctxt, 1, HVMSR_PER_DOM);
+
+static int viridian_save_vcpu_ctxt(struct vcpu *v, hvm_domain_context_t *h)
+{
+ struct hvm_viridian_vcpu_context ctxt = {
+ .vp_assist_msr = v->arch.hvm.viridian.vp_assist.msr.raw,
+ .vp_assist_pending = v->arch.hvm.viridian.vp_assist.pending,
+ };
+
+ if ( !is_viridian_domain(v->domain) )
+ return 0;
+
+ return hvm_save_entry(VIRIDIAN_VCPU, v->vcpu_id, h, &ctxt);
+}
+
+static int viridian_load_vcpu_ctxt(struct domain *d, hvm_domain_context_t *h)
+{
+ unsigned int vcpuid = hvm_load_instance(h);
+ struct vcpu *v;
+ struct hvm_viridian_vcpu_context ctxt;
+
+ if ( vcpuid >= d->max_vcpus || (v = d->vcpu[vcpuid]) == NULL )
+ {
+ dprintk(XENLOG_G_ERR, "HVM restore: dom%d has no vcpu%u\n",
+ d->domain_id, vcpuid);
+ return -EINVAL;
+ }
+
+ if ( hvm_load_entry_zeroextend(VIRIDIAN_VCPU, h, &ctxt) != 0 )
+ return -EINVAL;
+
+ if ( memcmp(&ctxt._pad, zero_page, sizeof(ctxt._pad)) )
+ return -EINVAL;
+
+ v->arch.hvm.viridian.vp_assist.msr.raw = ctxt.vp_assist_msr;
+ if ( v->arch.hvm.viridian.vp_assist.msr.fields.enabled &&
+ !v->arch.hvm.viridian.vp_assist.va )
+ initialize_vp_assist(v);
+
+ v->arch.hvm.viridian.vp_assist.pending = !!ctxt.vp_assist_pending;
+
+ return 0;
+}
+
+HVM_REGISTER_SAVE_RESTORE(VIRIDIAN_VCPU, viridian_save_vcpu_ctxt,
+ viridian_load_vcpu_ctxt, 1, HVMSR_PER_VCPU);
+
+static int __init parse_viridian_version(const char *arg)
+{
+ const char *t;
+ unsigned int n[3];
+ unsigned int i = 0;
+
+ n[0] = viridian_major;
+ n[1] = viridian_minor;
+ n[2] = viridian_build;
+
+ do {
+ const char *e;
+
+ t = strchr(arg, ',');
+ if ( !t )
+ t = strchr(arg, '\0');
+
+ if ( *arg && *arg != ',' && i < 3 )
+ {
+ n[i] = simple_strtoul(arg, &e, 0);
+ if ( e != t )
+ break;
+ }
+
+ i++;
+ arg = t + 1;
+ } while ( *t );
+
+ if ( i != 3 )
+ return -EINVAL;
+
+ if ( ((typeof(viridian_major))n[0] != n[0]) ||
+ ((typeof(viridian_minor))n[1] != n[1]) ||
+ ((typeof(viridian_build))n[2] != n[2]) )
+ return -EINVAL;
+
+ viridian_major = n[0];
+ viridian_minor = n[1];
+ viridian_build = n[2];
+
+ printk("viridian-version = %#x,%#x,%#x\n",
+ viridian_major, viridian_minor, viridian_build);
+ return 0;
+}
+custom_param("viridian-version", parse_viridian_version);
+
+/*
+ * Local variables:
+ * mode: C
+ * c-file-style: "BSD"
+ * c-basic-offset: 4
+ * tab-width: 4
+ * indent-tabs-mode: nil
+ * End:
+ */
--
generated by git-patchbot for /home/xen/git/xen.git#staging

Loading...