[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Xen-changelog] [xen staging] viridian: move the code into its own sub-directory



commit d5d30b394975f460a99a6313645a737acf16d4c7
Author:     Paul Durrant <paul.durrant@xxxxxxxxxx>
AuthorDate: Wed Oct 31 13:44:00 2018 +0100
Commit:     Jan Beulich <jbeulich@xxxxxxxx>
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 <paul.durrant@xxxxxxxxxx>
    Acked-by: Jan Beulich <jbeulich@xxxxxxxx>
---
 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 <paul.durrant@xxxxxxxxxx>
 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

_______________________________________________
Xen-changelog mailing list
Xen-changelog@xxxxxxxxxxxxxxxxxxxx
https://lists.xenproject.org/xen-changelog

 


Rackspace

Lists.xenproject.org is hosted with RackSpace, monitoring our
servers 24x7x365 and backed by RackSpace's Fanatical Support®.