aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--xen/arch/x86/dom0_ops.c2
-rw-r--r--xen/arch/x86/domain.c2
-rw-r--r--xen/arch/x86/domain_build.c2
-rw-r--r--xen/arch/x86/hvm/hvm.c60
-rw-r--r--xen/arch/x86/hvm/svm/svm.c39
-rw-r--r--xen/arch/x86/hvm/vmx/vmx.c45
-rw-r--r--xen/arch/x86/traps.c97
-rw-r--r--xen/arch/x86/x86_32/traps.c6
-rw-r--r--xen/arch/x86/x86_64/traps.c10
-rw-r--r--xen/include/asm-x86/domain.h2
-rw-r--r--xen/include/asm-x86/hvm/hvm.h5
-rw-r--r--xen/include/asm-x86/hvm/support.h2
-rw-r--r--xen/include/asm-x86/processor.h4
13 files changed, 256 insertions, 20 deletions
diff --git a/xen/arch/x86/dom0_ops.c b/xen/arch/x86/dom0_ops.c
index 500c7f7ae6..e9c015fd0b 100644
--- a/xen/arch/x86/dom0_ops.c
+++ b/xen/arch/x86/dom0_ops.c
@@ -429,7 +429,7 @@ long arch_do_dom0_op(struct dom0_op *op, XEN_GUEST_HANDLE(dom0_op_t) u_dom0_op)
ret = 0;
hypercall_page = map_domain_page(mfn);
- hypercall_page_initialise(hypercall_page);
+ hypercall_page_initialise(d, hypercall_page);
unmap_domain_page(hypercall_page);
put_page_and_type(mfn_to_page(mfn));
diff --git a/xen/arch/x86/domain.c b/xen/arch/x86/domain.c
index 9eb1f53771..cdab497e43 100644
--- a/xen/arch/x86/domain.c
+++ b/xen/arch/x86/domain.c
@@ -819,7 +819,7 @@ unsigned long hypercall_create_continuation(
#if defined(__i386__)
regs->eax = op;
- if ( supervisor_mode_kernel )
+ if ( supervisor_mode_kernel || hvm_guest(current) )
regs->eip &= ~31; /* re-execute entire hypercall entry stub */
else
regs->eip -= 2; /* re-execute 'int 0x82' */
diff --git a/xen/arch/x86/domain_build.c b/xen/arch/x86/domain_build.c
index 78180898b7..8526d15365 100644
--- a/xen/arch/x86/domain_build.c
+++ b/xen/arch/x86/domain_build.c
@@ -704,7 +704,7 @@ int construct_dom0(struct domain *d,
return -1;
}
- hypercall_page_initialise((void *)hypercall_page);
+ hypercall_page_initialise(d, (void *)hypercall_page);
}
/* Copy the initial ramdisk. */
diff --git a/xen/arch/x86/hvm/hvm.c b/xen/arch/x86/hvm/hvm.c
index 6e0808e641..f4dee16ecc 100644
--- a/xen/arch/x86/hvm/hvm.c
+++ b/xen/arch/x86/hvm/hvm.c
@@ -27,6 +27,7 @@
#include <xen/softirq.h>
#include <xen/domain.h>
#include <xen/domain_page.h>
+#include <xen/hypercall.h>
#include <asm/current.h>
#include <asm/io.h>
#include <asm/shadow.h>
@@ -329,6 +330,65 @@ void hvm_print_line(struct vcpu *v, const char c)
pbuf[(*index)++] = c;
}
+#if defined(__i386__)
+
+typedef unsigned long hvm_hypercall_t(
+ unsigned long, unsigned long, unsigned long, unsigned long, unsigned long);
+#define HYPERCALL(x) [ __HYPERVISOR_ ## x ] = (hvm_hypercall_t *) do_ ## x
+static hvm_hypercall_handler *hvm_hypercall_table[] = {
+ HYPERCALL(mmu_update),
+ HYPERCALL(memory_op),
+ HYPERCALL(multicall),
+ HYPERCALL(update_va_mapping),
+ HYPERCALL(event_channel_op_compat),
+ HYPERCALL(xen_version),
+ HYPERCALL(grant_table_op),
+ HYPERCALL(event_channel_op),
+ HYPERCALL(hvm_op)
+};
+#undef HYPERCALL
+
+void hvm_do_hypercall(struct cpu_user_regs *pregs)
+{
+ if ( ring_3(pregs) )
+ {
+ pregs->eax = -EPERM;
+ return;
+ }
+
+ if ( pregs->eax > ARRAY_SIZE(hvm_hypercall_table) ||
+ !hvm_hypercall_table[pregs->eax] )
+ {
+ DPRINTK("HVM vcpu %d:%d did a bad hypercall %d.\n",
+ current->domain->domain_id, current->vcpu_id,
+ pregs->eax);
+ pregs->eax = -ENOSYS;
+ }
+ else
+ {
+ pregs->eax = hvm_hypercall_table[pregs->eax](
+ pregs->ebx, pregs->ecx, pregs->edx, pregs->esi, pregs->edi);
+ }
+}
+
+#else /* __x86_64__ */
+
+void hvm_do_hypercall(struct cpu_user_regs *pregs)
+{
+ printk("not supported yet!\n");
+}
+
+#endif
+
+/* Initialise a hypercall transfer page for a VMX domain using
+ paravirtualised drivers. */
+void hvm_hypercall_page_initialise(struct domain *d,
+ void *hypercall_page)
+{
+ hvm_funcs.init_hypercall_page(d, hypercall_page);
+}
+
+
/*
* only called in HVM domain BSP context
* when booting, vcpuid is always equal to apic_id
diff --git a/xen/arch/x86/hvm/svm/svm.c b/xen/arch/x86/hvm/svm/svm.c
index 998611220e..9ee9ee5bf8 100644
--- a/xen/arch/x86/hvm/svm/svm.c
+++ b/xen/arch/x86/hvm/svm/svm.c
@@ -456,6 +456,28 @@ void svm_init_ap_context(struct vcpu_guest_context *ctxt,
ctxt->flags = VGCF_HVM_GUEST;
}
+static void svm_init_hypercall_page(struct domain *d, void *hypercall_page)
+{
+ char *p;
+ int i;
+
+ memset(hypercall_page, 0, PAGE_SIZE);
+
+ for ( i = 0; i < (PAGE_SIZE / 32); i++ )
+ {
+ p = (char *)(hypercall_page + (i * 32));
+ *(u8 *)(p + 0) = 0xb8; /* mov imm32, %eax */
+ *(u32 *)(p + 1) = i;
+ *(u8 *)(p + 5) = 0x0f; /* vmmcall */
+ *(u8 *)(p + 6) = 0x01;
+ *(u8 *)(p + 7) = 0xd9;
+ *(u8 *)(p + 8) = 0xc3; /* ret */
+ }
+
+ /* Don't support HYPERVISOR_iret at the moment */
+ *(u16 *)(hypercall_page + (__HYPERVISOR_iret * 32)) = 0x0b0f; /* ud2 */
+}
+
int start_svm(void)
{
u32 eax, ecx, edx;
@@ -504,6 +526,8 @@ int start_svm(void)
hvm_funcs.get_guest_ctrl_reg = svm_get_ctrl_reg;
hvm_funcs.init_ap_context = svm_init_ap_context;
+ hvm_funcs.init_hypercall_page = svm_init_hypercall_page;
+
hvm_enabled = 1;
return 1;
@@ -1980,11 +2004,13 @@ static int svm_cr_access(struct vcpu *v, unsigned int cr, unsigned int type,
return result;
}
-static inline void svm_do_msr_access(struct vcpu *v, struct cpu_user_regs *regs)
+static inline void svm_do_msr_access(
+ struct vcpu *v, struct cpu_user_regs *regs)
{
struct vmcb_struct *vmcb = v->arch.hvm_svm.vmcb;
int inst_len;
u64 msr_content=0;
+ u32 eax, edx;
ASSERT(vmcb);
@@ -2018,6 +2044,14 @@ static inline void svm_do_msr_access(struct vcpu *v, struct cpu_user_regs *regs)
default:
if (long_mode_do_msr_read(regs))
goto done;
+
+ if ( rdmsr_hypervisor_regs(regs->ecx, &eax, &edx) )
+ {
+ regs->eax = eax;
+ regs->edx = edx;
+ goto done;
+ }
+
rdmsr_safe(regs->ecx, regs->eax, regs->edx);
break;
}
@@ -2047,7 +2081,8 @@ static inline void svm_do_msr_access(struct vcpu *v, struct cpu_user_regs *regs)
vlapic_msr_set(VLAPIC(v), msr_content);
break;
default:
- long_mode_do_msr_write(regs);
+ if ( !long_mode_do_msr_write(regs) )
+ wrmsr_hypervisor_regs(regs->ecx, regs->eax, regs->edx);
break;
}
}
diff --git a/xen/arch/x86/hvm/vmx/vmx.c b/xen/arch/x86/hvm/vmx/vmx.c
index fe00954a0a..f8df7bbcf8 100644
--- a/xen/arch/x86/hvm/vmx/vmx.c
+++ b/xen/arch/x86/hvm/vmx/vmx.c
@@ -669,6 +669,28 @@ static int check_vmx_controls(u32 ctrls, u32 msr)
return 1;
}
+static void vmx_init_hypercall_page(struct domain *d, void *hypercall_page)
+{
+ char *p;
+ int i;
+
+ memset(hypercall_page, 0, PAGE_SIZE);
+
+ for ( i = 0; i < (PAGE_SIZE / 32); i++ )
+ {
+ p = (char *)(hypercall_page + (i * 32));
+ *(u8 *)(p + 0) = 0xb8; /* mov imm32, %eax */
+ *(u32 *)(p + 1) = i;
+ *(u8 *)(p + 5) = 0x0f; /* vmcall */
+ *(u8 *)(p + 6) = 0x01;
+ *(u8 *)(p + 7) = 0xc1;
+ *(u8 *)(p + 8) = 0xc3; /* ret */
+ }
+
+ /* Don't support HYPERVISOR_iret at the moment */
+ *(u16 *)(hypercall_page + (__HYPERVISOR_iret * 32)) = 0x0b0f; /* ud2 */
+}
+
int start_vmx(void)
{
u32 eax, edx;
@@ -749,6 +771,8 @@ int start_vmx(void)
hvm_funcs.init_ap_context = vmx_init_ap_context;
+ hvm_funcs.init_hypercall_page = vmx_init_hypercall_page;
+
hvm_enabled = 1;
return 1;
@@ -1877,6 +1901,7 @@ static int vmx_cr_access(unsigned long exit_qualification, struct cpu_user_regs
static inline void vmx_do_msr_read(struct cpu_user_regs *regs)
{
u64 msr_content = 0;
+ u32 eax, edx;
struct vcpu *v = current;
HVM_DBG_LOG(DBG_LEVEL_1, "vmx_do_msr_read: ecx=%lx, eax=%lx, edx=%lx",
@@ -1899,8 +1924,16 @@ static inline void vmx_do_msr_read(struct cpu_user_regs *regs)
msr_content = VLAPIC(v) ? VLAPIC(v)->apic_base_msr : 0;
break;
default:
- if(long_mode_do_msr_read(regs))
+ if (long_mode_do_msr_read(regs))
return;
+
+ if ( rdmsr_hypervisor_regs(regs->ecx, &eax, &edx) )
+ {
+ regs->eax = eax;
+ regs->edx = edx;
+ return;
+ }
+
rdmsr_safe(regs->ecx, regs->eax, regs->edx);
break;
}
@@ -1942,7 +1975,8 @@ static inline void vmx_do_msr_write(struct cpu_user_regs *regs)
vlapic_msr_set(VLAPIC(v), msr_content);
break;
default:
- long_mode_do_msr_write(regs);
+ if ( !long_mode_do_msr_write(regs) )
+ wrmsr_hypervisor_regs(regs->ecx, regs->eax, regs->edx);
break;
}
@@ -2273,16 +2307,16 @@ asmlinkage void vmx_vmexit_handler(struct cpu_user_regs regs)
__update_guest_eip(inst_len);
break;
}
-#if 0 /* keep this for debugging */
case EXIT_REASON_VMCALL:
+ {
__get_instruction_length(inst_len);
__vmread(GUEST_RIP, &eip);
__vmread(EXIT_QUALIFICATION, &exit_qualification);
- hvm_print_line(v, regs.eax); /* provides the current domain */
+ hvm_do_hypercall(&regs);
__update_guest_eip(inst_len);
break;
-#endif
+ }
case EXIT_REASON_CR_ACCESS:
{
__vmread(GUEST_RIP, &eip);
@@ -2323,7 +2357,6 @@ asmlinkage void vmx_vmexit_handler(struct cpu_user_regs regs)
case EXIT_REASON_MWAIT_INSTRUCTION:
__hvm_bug(&regs);
break;
- case EXIT_REASON_VMCALL:
case EXIT_REASON_VMCLEAR:
case EXIT_REASON_VMLAUNCH:
case EXIT_REASON_VMPTRLD:
diff --git a/xen/arch/x86/traps.c b/xen/arch/x86/traps.c
index 44913b2f42..45d73bc511 100644
--- a/xen/arch/x86/traps.c
+++ b/xen/arch/x86/traps.c
@@ -44,6 +44,7 @@
#include <xen/symbols.h>
#include <xen/iocap.h>
#include <xen/nmi.h>
+#include <xen/version.h>
#include <asm/shadow.h>
#include <asm/system.h>
#include <asm/io.h>
@@ -429,18 +430,95 @@ DO_ERROR_NOCODE(16, "fpu error", coprocessor_error)
DO_ERROR(17, "alignment check", alignment_check)
DO_ERROR_NOCODE(19, "simd error", simd_coprocessor_error)
+int rdmsr_hypervisor_regs(
+ uint32_t idx, uint32_t *eax, uint32_t *edx)
+{
+ idx -= 0x40000000;
+ if ( idx > 0 )
+ return 0;
+
+ *eax = *edx = 0;
+ return 1;
+}
+
+int wrmsr_hypervisor_regs(
+ uint32_t idx, uint32_t eax, uint32_t edx)
+{
+ struct domain *d = current->domain;
+
+ idx -= 0x40000000;
+ if ( idx > 0 )
+ return 0;
+
+ switch ( idx )
+ {
+ case 0:
+ {
+ void *hypercall_page;
+ unsigned long mfn;
+ unsigned long gmfn = ((unsigned long)edx << 20) | (eax >> 12);
+ unsigned int idx = eax & 0xfff;
+
+ if ( idx > 0 )
+ {
+ DPRINTK("Dom%d: Out of range index %u to MSR %08x\n",
+ d->domain_id, idx, 0x40000000);
+ return 0;
+ }
+
+ mfn = gmfn_to_mfn(d, gmfn);
+
+ if ( !mfn_valid(mfn) ||
+ !get_page_and_type(mfn_to_page(mfn), d, PGT_writable_page) )
+ {
+ DPRINTK("Dom%d: Bad GMFN %lx (MFN %lx) to MSR %08x\n",
+ d->domain_id, gmfn, mfn, 0x40000000);
+ return 0;
+ }
+
+ hypercall_page = map_domain_page(mfn);
+ hypercall_page_initialise(d, hypercall_page);
+ unmap_domain_page(hypercall_page);
+
+ put_page_and_type(mfn_to_page(mfn));
+ break;
+ }
+
+ default:
+ BUG();
+ }
+
+ return 1;
+}
+
int cpuid_hypervisor_leaves(
uint32_t idx, uint32_t *eax, uint32_t *ebx, uint32_t *ecx, uint32_t *edx)
{
- if ( (idx < 0x40000000) || (idx > 0x40000000) )
+ idx -= 0x40000000;
+ if ( idx > 2 )
return 0;
- switch ( idx - 0x40000000 )
+ switch ( idx )
{
case 0:
- *eax = 0x40000000;
- *ebx = 0x006e6558; /* "Xen\0" */
- *ecx = *edx = 0;
+ *eax = 0x40000002; /* Largest leaf */
+ *ebx = 0x566e6558; /* Signature 1: "XenV" */
+ *ecx = 0x65584d4d; /* Signature 2: "MMXe" */
+ *edx = 0x4d4d566e; /* Signature 3: "nVMM" */
+ break;
+
+ case 1:
+ *eax = (xen_major_version() << 16) | xen_minor_version();
+ *ebx = 0; /* Reserved */
+ *ecx = 0; /* Reserved */
+ *edx = 0; /* Reserved */
+ break;
+
+ case 2:
+ *eax = 1; /* Number of hypercall-transfer pages */
+ *ebx = 0x40000000; /* MSR base address */
+ *ecx = 0; /* Features 1 */
+ *edx = 0; /* Features 2 */
break;
default:
@@ -1297,6 +1375,9 @@ static int emulate_privileged_op(struct cpu_user_regs *regs)
break;
#endif
default:
+ if ( wrmsr_hypervisor_regs(regs->ecx, regs->eax, regs->edx) )
+ break;
+
if ( (rdmsr_safe(regs->ecx, l, h) != 0) ||
(regs->eax != l) || (regs->edx != h) )
DPRINTK("Domain attempted WRMSR %p from "
@@ -1328,6 +1409,12 @@ static int emulate_privileged_op(struct cpu_user_regs *regs)
goto fail;
break;
default:
+ if ( rdmsr_hypervisor_regs(regs->ecx, &l, &h) )
+ {
+ regs->eax = l;
+ regs->edx = h;
+ break;
+ }
/* Everyone can read the MSR space. */
/*DPRINTK("Domain attempted RDMSR %p.\n", _p(regs->ecx));*/
if ( rdmsr_safe(regs->ecx, regs->eax, regs->edx) )
diff --git a/xen/arch/x86/x86_32/traps.c b/xen/arch/x86/x86_32/traps.c
index ae18f654ad..3c6d81f21d 100644
--- a/xen/arch/x86/x86_32/traps.c
+++ b/xen/arch/x86/x86_32/traps.c
@@ -490,9 +490,11 @@ static void hypercall_page_initialise_ring1_kernel(void *hypercall_page)
*(u16 *)(p+ 6) = 0x82cd; /* int $0x82 */
}
-void hypercall_page_initialise(void *hypercall_page)
+void hypercall_page_initialise(struct domain *d, void *hypercall_page)
{
- if ( supervisor_mode_kernel )
+ if ( hvm_guest(d->vcpu[0]) )
+ hvm_hypercall_page_initialise(d, hypercall_page);
+ else if ( supervisor_mode_kernel )
hypercall_page_initialise_ring0_kernel(hypercall_page);
else
hypercall_page_initialise_ring1_kernel(hypercall_page);
diff --git a/xen/arch/x86/x86_64/traps.c b/xen/arch/x86/x86_64/traps.c
index 3cae3e3fc3..6b674a5b53 100644
--- a/xen/arch/x86/x86_64/traps.c
+++ b/xen/arch/x86/x86_64/traps.c
@@ -432,7 +432,7 @@ long do_set_callbacks(unsigned long event_address,
return 0;
}
-void hypercall_page_initialise(void *hypercall_page)
+static void hypercall_page_initialise_ring3_kernel(void *hypercall_page)
{
char *p;
int i;
@@ -465,6 +465,14 @@ void hypercall_page_initialise(void *hypercall_page)
*(u16 *)(p+ 9) = 0x050f; /* syscall */
}
+void hypercall_page_initialise(struct domain *d, void *hypercall_page)
+{
+ if ( hvm_guest(d->vcpu[0]) )
+ hvm_hypercall_page_initialise(d, hypercall_page);
+ else
+ hypercall_page_initialise_ring3_kernel(hypercall_page);
+}
+
/*
* Local variables:
* mode: C
diff --git a/xen/include/asm-x86/domain.h b/xen/include/asm-x86/domain.h
index 962fa7954b..bdba98ba03 100644
--- a/xen/include/asm-x86/domain.h
+++ b/xen/include/asm-x86/domain.h
@@ -55,7 +55,7 @@ extern void toggle_guest_mode(struct vcpu *);
* Initialise a hypercall-transfer page. The given pointer must be mapped
* in Xen virtual address space (accesses are not validated or checked).
*/
-extern void hypercall_page_initialise(void *);
+extern void hypercall_page_initialise(struct domain *d, void *);
struct arch_domain
{
diff --git a/xen/include/asm-x86/hvm/hvm.h b/xen/include/asm-x86/hvm/hvm.h
index 8e830977cc..04faa8971c 100644
--- a/xen/include/asm-x86/hvm/hvm.h
+++ b/xen/include/asm-x86/hvm/hvm.h
@@ -61,6 +61,8 @@ struct hvm_function_table {
void (*init_ap_context)(struct vcpu_guest_context *ctxt,
int vcpuid, int trampoline_vector);
+
+ void (*init_hypercall_page)(struct domain *d, void *hypercall_page);
};
extern struct hvm_function_table hvm_funcs;
@@ -121,6 +123,9 @@ hvm_instruction_length(struct vcpu *v)
return hvm_funcs.instruction_length(v);
}
+void hvm_hypercall_page_initialise(struct domain *d,
+ void *hypercall_page);
+
static inline unsigned long
hvm_get_guest_ctrl_reg(struct vcpu *v, unsigned int num)
{
diff --git a/xen/include/asm-x86/hvm/support.h b/xen/include/asm-x86/hvm/support.h
index 61ad981399..4534e66680 100644
--- a/xen/include/asm-x86/hvm/support.h
+++ b/xen/include/asm-x86/hvm/support.h
@@ -148,4 +148,6 @@ extern void hvm_hooks_assist(struct vcpu *v);
extern void hvm_print_line(struct vcpu *v, const char c);
extern void hlt_timer_fn(void *data);
+void hvm_do_hypercall(struct cpu_user_regs *pregs);
+
#endif /* __ASM_X86_HVM_SUPPORT_H__ */
diff --git a/xen/include/asm-x86/processor.h b/xen/include/asm-x86/processor.h
index 38c8330446..bc8d95b2c1 100644
--- a/xen/include/asm-x86/processor.h
+++ b/xen/include/asm-x86/processor.h
@@ -547,6 +547,10 @@ extern void mcheck_init(struct cpuinfo_x86 *c);
int cpuid_hypervisor_leaves(
uint32_t idx, uint32_t *eax, uint32_t *ebx, uint32_t *ecx, uint32_t *edx);
+int rdmsr_hypervisor_regs(
+ uint32_t idx, uint32_t *eax, uint32_t *edx);
+int wrmsr_hypervisor_regs(
+ uint32_t idx, uint32_t eax, uint32_t edx);
#endif /* !__ASSEMBLY__ */