aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPaul Durrant <paul.durrant@citrix.com>2011-09-17 16:22:13 +0100
committerPaul Durrant <paul.durrant@citrix.com>2011-09-17 16:22:13 +0100
commit251d889009995640af7c2d53eb018639d77ae98a (patch)
tree87f8c965469dd26ea241459bf6811c7c440ec604
parentd285a773d6f5d7fb4e654291b5fce90bb8ec8852 (diff)
downloadxen-251d889009995640af7c2d53eb018639d77ae98a.tar.gz
xen-251d889009995640af7c2d53eb018639d77ae98a.tar.bz2
xen-251d889009995640af7c2d53eb018639d77ae98a.zip
x86/hvm: Tidy up the viridian code a little and flesh out the APIC
assist MSR handling code. We don't say we that handle that MSR but Windows assumes it. In Windows 7 it just wrote to the MSR and we used to handle that ok. Windows 8 also reads from the MSR so we need to keep a record of the contents. Signed-off-by: Paul Durrant <paul.durrant@citrix.com>
-rw-r--r--xen/arch/x86/hvm/viridian.c139
-rw-r--r--xen/include/asm-x86/hvm/vcpu.h3
-rw-r--r--xen/include/asm-x86/hvm/viridian.h15
-rw-r--r--xen/include/asm-x86/perfc_defn.h2
4 files changed, 111 insertions, 48 deletions
diff --git a/xen/arch/x86/hvm/viridian.c b/xen/arch/x86/hvm/viridian.c
index 2e1b4ecc08..191eeb0c23 100644
--- a/xen/arch/x86/hvm/viridian.c
+++ b/xen/arch/x86/hvm/viridian.c
@@ -96,9 +96,43 @@ int cpuid_viridian_leaves(unsigned int leaf, unsigned int *eax,
return 1;
}
-static void enable_hypercall_page(void)
+void dump_guest_os_id(struct domain *d)
+{
+ gdprintk(XENLOG_INFO, "GUEST_OS_ID:\n");
+ gdprintk(XENLOG_INFO, "\tvendor: %x\n",
+ d->arch.hvm_domain.viridian.guest_os_id.fields.vendor);
+ gdprintk(XENLOG_INFO, "\tos: %x\n",
+ d->arch.hvm_domain.viridian.guest_os_id.fields.os);
+ gdprintk(XENLOG_INFO, "\tmajor: %x\n",
+ d->arch.hvm_domain.viridian.guest_os_id.fields.major);
+ gdprintk(XENLOG_INFO, "\tminor: %x\n",
+ d->arch.hvm_domain.viridian.guest_os_id.fields.minor);
+ gdprintk(XENLOG_INFO, "\tsp: %x\n",
+ d->arch.hvm_domain.viridian.guest_os_id.fields.service_pack);
+ gdprintk(XENLOG_INFO, "\tbuild: %x\n",
+ d->arch.hvm_domain.viridian.guest_os_id.fields.build_number);
+}
+
+void dump_hypercall(struct domain *d)
+{
+ gdprintk(XENLOG_INFO, "HYPERCALL:\n");
+ gdprintk(XENLOG_INFO, "\tenabled: %x\n",
+ d->arch.hvm_domain.viridian.hypercall_gpa.fields.enabled);
+ gdprintk(XENLOG_INFO, "\tpfn: %lx\n",
+ (unsigned long)d->arch.hvm_domain.viridian.hypercall_gpa.fields.pfn);
+}
+
+void dump_apic_assist(struct vcpu *v)
+{
+ gdprintk(XENLOG_INFO, "APIC_ASSIST[%d]:\n", v->vcpu_id);
+ gdprintk(XENLOG_INFO, "\tenabled: %x\n",
+ v->arch.hvm_vcpu.viridian.apic_assist.fields.enabled);
+ gdprintk(XENLOG_INFO, "\tpfn: %lx\n",
+ (unsigned long)v->arch.hvm_vcpu.viridian.apic_assist.fields.pfn);
+}
+
+static void enable_hypercall_page(struct domain *d)
{
- struct domain *d = current->domain;
unsigned long gmfn = d->arch.hvm_domain.viridian.hypercall_gpa.fields.pfn;
unsigned long mfn = gmfn_to_mfn(d, gmfn);
uint8_t *p;
@@ -130,9 +164,43 @@ static void enable_hypercall_page(void)
put_page_and_type(mfn_to_page(mfn));
}
+void initialize_apic_assist(struct vcpu *v)
+{
+ struct domain *d = v->domain;
+ unsigned long gmfn = v->arch.hvm_vcpu.viridian.apic_assist.fields.pfn;
+ unsigned long mfn = gmfn_to_mfn(d, gmfn);
+ uint8_t *p;
+
+ /*
+ * We don't support the APIC assist page, and that fact is reflected in
+ * our CPUID flags. However, Newer versions of Windows have a bug which
+ * means that they don't recognise that, and tries to use the page
+ * anyway. We therefore have to fake up just enough to keep windows happy.
+ *
+ * See http://msdn.microsoft.com/en-us/library/ff538657%28VS.85%29.aspx for
+ * details of how Windows uses the page.
+ */
+
+ if ( !mfn_valid(mfn) ||
+ !get_page_and_type(mfn_to_page(mfn), d, PGT_writable_page) )
+ {
+ gdprintk(XENLOG_WARNING, "Bad GMFN %lx (MFN %lx)\n", gmfn, mfn);
+ return;
+ }
+
+ p = map_domain_page(mfn);
+
+ *(u32 *)p = 0;
+
+ unmap_domain_page(p);
+
+ put_page_and_type(mfn_to_page(mfn));
+}
+
int wrmsr_viridian_regs(uint32_t idx, uint64_t val)
{
- struct domain *d = current->domain;
+ struct vcpu *v = current;
+ struct domain *d = v->domain;
if ( !is_viridian_domain(d) )
return 0;
@@ -142,44 +210,29 @@ int wrmsr_viridian_regs(uint32_t idx, uint64_t val)
case VIRIDIAN_MSR_GUEST_OS_ID:
perfc_incr(mshv_wrmsr_osid);
d->arch.hvm_domain.viridian.guest_os_id.raw = val;
- gdprintk(XENLOG_INFO, "Guest os:\n");
- gdprintk(XENLOG_INFO, "\tvendor: %x\n",
- d->arch.hvm_domain.viridian.guest_os_id.fields.vendor);
- gdprintk(XENLOG_INFO, "\tos: %x\n",
- d->arch.hvm_domain.viridian.guest_os_id.fields.os);
- gdprintk(XENLOG_INFO, "\tmajor: %x\n",
- d->arch.hvm_domain.viridian.guest_os_id.fields.major);
- gdprintk(XENLOG_INFO, "\tminor: %x\n",
- d->arch.hvm_domain.viridian.guest_os_id.fields.minor);
- gdprintk(XENLOG_INFO, "\tsp: %x\n",
- d->arch.hvm_domain.viridian.guest_os_id.fields.service_pack);
- gdprintk(XENLOG_INFO, "\tbuild: %x\n",
- d->arch.hvm_domain.viridian.guest_os_id.fields.build_number);
+ dump_guest_os_id(d);
break;
case VIRIDIAN_MSR_HYPERCALL:
perfc_incr(mshv_wrmsr_hc_page);
- gdprintk(XENLOG_INFO, "Set hypercall page %"PRIx64".\n", val);
- if ( d->arch.hvm_domain.viridian.guest_os_id.raw == 0 )
- break;
d->arch.hvm_domain.viridian.hypercall_gpa.raw = val;
+ dump_hypercall(d);
if ( d->arch.hvm_domain.viridian.hypercall_gpa.fields.enabled )
- enable_hypercall_page();
+ enable_hypercall_page(d);
break;
case VIRIDIAN_MSR_VP_INDEX:
perfc_incr(mshv_wrmsr_vp_index);
- gdprintk(XENLOG_INFO, "Set VP index %"PRIu64".\n", val);
break;
case VIRIDIAN_MSR_EOI:
perfc_incr(mshv_wrmsr_eoi);
- vlapic_EOI_set(vcpu_vlapic(current));
+ vlapic_EOI_set(vcpu_vlapic(v));
break;
case VIRIDIAN_MSR_ICR: {
u32 eax = (u32)val, edx = (u32)(val >> 32);
- struct vlapic *vlapic = vcpu_vlapic(current);
+ struct vlapic *vlapic = vcpu_vlapic(v);
perfc_incr(mshv_wrmsr_icr);
eax &= ~(1 << 12);
edx &= 0xff000000;
@@ -191,31 +244,15 @@ int wrmsr_viridian_regs(uint32_t idx, uint64_t val)
case VIRIDIAN_MSR_TPR:
perfc_incr(mshv_wrmsr_tpr);
- vlapic_set_reg(vcpu_vlapic(current), APIC_TASKPRI, (uint8_t)val);
+ vlapic_set_reg(vcpu_vlapic(v), APIC_TASKPRI, (uint8_t)val);
break;
case VIRIDIAN_MSR_APIC_ASSIST:
- /*
- * We don't support the APIC assist page, and that fact is reflected in
- * our CPUID flags. However, Windows 7 build 7000 has a bug which means
- * that it doesn't recognise that, and tries to use the page anyway. We
- * therefore have to fake up just enough to keep win7 happy.
- * Fortunately, that's really easy: just setting the first four bytes
- * in the page to zero effectively disables the page again, so that's
- * what we do. Semantically, the first four bytes are supposed to be a
- * flag saying whether the guest really needs to issue an EOI. Setting
- * that flag to zero means that it must always issue one, which is what
- * we want. Once a page has been repurposed as an APIC assist page the
- * guest isn't allowed to set anything in it, so the flag remains zero
- * and all is fine. The guest is allowed to clear flags in the page,
- * but that doesn't cause us any problems.
- */
- if ( val & 1 ) /* APIC assist page enabled? */
- {
- uint32_t word = 0;
- paddr_t page_start = val & ~1ul;
- (void)hvm_copy_to_guest_phys(page_start, &word, sizeof(word));
- }
+ perfc_incr(mshv_wrmsr_apic_msr);
+ v->arch.hvm_vcpu.viridian.apic_assist.raw = val;
+ dump_apic_assist(v);
+ if (v->arch.hvm_vcpu.viridian.apic_assist.fields.enabled)
+ initialize_apic_assist(v);
break;
default:
@@ -228,20 +265,21 @@ int wrmsr_viridian_regs(uint32_t idx, uint64_t val)
int rdmsr_viridian_regs(uint32_t idx, uint64_t *val)
{
struct vcpu *v = current;
+ struct domain *d = v->domain;
- if ( !is_viridian_domain(v->domain) )
+ if ( !is_viridian_domain(d) )
return 0;
switch ( idx )
{
case VIRIDIAN_MSR_GUEST_OS_ID:
perfc_incr(mshv_rdmsr_osid);
- *val = v->domain->arch.hvm_domain.viridian.guest_os_id.raw;
+ *val = d->arch.hvm_domain.viridian.guest_os_id.raw;
break;
case VIRIDIAN_MSR_HYPERCALL:
perfc_incr(mshv_rdmsr_hc_page);
- *val = v->domain->arch.hvm_domain.viridian.hypercall_gpa.raw;
+ *val = d->arch.hvm_domain.viridian.hypercall_gpa.raw;
break;
case VIRIDIAN_MSR_VP_INDEX:
@@ -260,6 +298,11 @@ int rdmsr_viridian_regs(uint32_t idx, uint64_t *val)
*val = vlapic_get_reg(vcpu_vlapic(v), APIC_TASKPRI);
break;
+ case VIRIDIAN_MSR_APIC_ASSIST:
+ perfc_incr(mshv_rdmsr_apic_msr);
+ *val = v->arch.hvm_vcpu.viridian.apic_assist.raw;
+ break;
+
default:
return 0;
}
diff --git a/xen/include/asm-x86/hvm/vcpu.h b/xen/include/asm-x86/hvm/vcpu.h
index 8c225a51eb..d899c9a713 100644
--- a/xen/include/asm-x86/hvm/vcpu.h
+++ b/xen/include/asm-x86/hvm/vcpu.h
@@ -23,6 +23,7 @@
#include <xen/tasklet.h>
#include <asm/hvm/io.h>
#include <asm/hvm/vlapic.h>
+#include <asm/hvm/viridian.h>
#include <asm/hvm/vmx/vmcs.h>
#include <asm/hvm/vmx/vvmx.h>
#include <asm/hvm/svm/vmcb.h>
@@ -163,6 +164,8 @@ struct hvm_vcpu {
int inject_trap; /* -1 for nothing to inject */
int inject_error_code;
unsigned long inject_cr2;
+
+ struct viridian_vcpu viridian;
};
#endif /* __ASM_X86_HVM_VCPU_H__ */
diff --git a/xen/include/asm-x86/hvm/viridian.h b/xen/include/asm-x86/hvm/viridian.h
index d8509cb081..496da33ed5 100644
--- a/xen/include/asm-x86/hvm/viridian.h
+++ b/xen/include/asm-x86/hvm/viridian.h
@@ -9,6 +9,21 @@
#ifndef __ASM_X86_HVM_VIRIDIAN_H__
#define __ASM_X86_HVM_VIRIDIAN_H__
+union viridian_apic_assist
+{ uint64_t raw;
+ struct
+ {
+ uint64_t enabled:1;
+ uint64_t reserved_preserved:11;
+ uint64_t pfn:48;
+ } fields;
+};
+
+struct viridian_vcpu
+{
+ union viridian_apic_assist apic_assist;
+};
+
union viridian_guest_os_id
{
uint64_t raw;
diff --git a/xen/include/asm-x86/perfc_defn.h b/xen/include/asm-x86/perfc_defn.h
index 274533e4f3..05bad415d4 100644
--- a/xen/include/asm-x86/perfc_defn.h
+++ b/xen/include/asm-x86/perfc_defn.h
@@ -120,12 +120,14 @@ PERFCOUNTER(mshv_rdmsr_hc_page, "MS Hv rdmsr hypercall page")
PERFCOUNTER(mshv_rdmsr_vp_index, "MS Hv rdmsr vp index")
PERFCOUNTER(mshv_rdmsr_icr, "MS Hv rdmsr icr")
PERFCOUNTER(mshv_rdmsr_tpr, "MS Hv rdmsr tpr")
+PERFCOUNTER(mshv_rdmsr_apic_assist, "MS Hv rdmsr APIC assist")
PERFCOUNTER(mshv_wrmsr_osid, "MS Hv wrmsr Guest OS ID")
PERFCOUNTER(mshv_wrmsr_hc_page, "MS Hv wrmsr hypercall page")
PERFCOUNTER(mshv_wrmsr_vp_index, "MS Hv wrmsr vp index")
PERFCOUNTER(mshv_wrmsr_icr, "MS Hv wrmsr icr")
PERFCOUNTER(mshv_wrmsr_tpr, "MS Hv wrmsr tpr")
PERFCOUNTER(mshv_wrmsr_eoi, "MS Hv wrmsr eoi")
+PERFCOUNTER(mshv_wrmsr_apic_assist, "MS Hv wrmsr APIC assist")
PERFCOUNTER(realmode_emulations, "realmode instructions emulated")
PERFCOUNTER(realmode_exits, "vmexits from realmode")