mirror of
https://github.com/xen-project/xen.git
synced 2026-10-28 10:11:27 +01:00
x86, hvm: Hyper-V guest interface support with small set of enlightenments
A minimal implementation of the Viridian (Hyper-V) guest interface. The only enlightenments advertised and supported are vAPIC MSRs and long-spin-wait notifications. The set of enlightenments can easily be extended in future, as they are found to provide a performance win, and configured via an extended HVM_PARAM_VIRIDIAN hvm parameter. Signed-off-by: Peter Johnston <peter.johnston@citrix.com> Signed-off-by: Tim Deegan <tim.deegan@citrix.com> Signed-off-by: Keir Fraser <keir.fraser@citrix.com>
This commit is contained in:
1 parent
b525c05cf9
commit
39f97ffa29
22 files changed
+1330
-825
No files matched your search
@@ -101,30 +101,36 @@ asm (
|
||||
|
||||
static enum { VGA_none, VGA_std, VGA_cirrus } virtual_vga = VGA_none;
|
||||
|
||||
static void
|
||||
init_hypercalls(void)
|
||||
static void init_hypercalls(void)
|
||||
{
|
||||
uint32_t eax, ebx, ecx, edx;
|
||||
unsigned long i;
|
||||
char signature[13];
|
||||
xen_extraversion_t extraversion;
|
||||
uint32_t base;
|
||||
|
||||
cpuid(0x40000000, &eax, &ebx, &ecx, &edx);
|
||||
for ( base = 0x40000000; base < 0x40001000; base += 0x100 )
|
||||
{
|
||||
cpuid(base, &eax, &ebx, &ecx, &edx);
|
||||
|
||||
*(uint32_t *)(signature + 0) = ebx;
|
||||
*(uint32_t *)(signature + 4) = ecx;
|
||||
*(uint32_t *)(signature + 8) = edx;
|
||||
signature[12] = '\0';
|
||||
*(uint32_t *)(signature + 0) = ebx;
|
||||
*(uint32_t *)(signature + 4) = ecx;
|
||||
*(uint32_t *)(signature + 8) = edx;
|
||||
signature[12] = '\0';
|
||||
|
||||
BUG_ON(strcmp("XenVMMXenVMM", signature) || (eax < 0x40000002));
|
||||
if ( !strcmp("XenVMMXenVMM", signature) )
|
||||
break;
|
||||
}
|
||||
|
||||
BUG_ON(strcmp("XenVMMXenVMM", signature) || ((eax - base) < 2));
|
||||
|
||||
/* Fill in hypercall transfer pages. */
|
||||
cpuid(0x40000002, &eax, &ebx, &ecx, &edx);
|
||||
cpuid(base + 2, &eax, &ebx, &ecx, &edx);
|
||||
for ( i = 0; i < eax; i++ )
|
||||
wrmsr(ebx, HYPERCALL_PHYSICAL_ADDRESS + (i << 12) + i);
|
||||
|
||||
/* Print version information. */
|
||||
cpuid(0x40000001, &eax, &ebx, &ecx, &edx);
|
||||
cpuid(base + 1, &eax, &ebx, &ecx, &edx);
|
||||
hypercall_xen_version(XENVER_extraversion, extraversion);
|
||||
printf("Detected Xen v%u.%u%s\n", eax >> 16, eax & 0xffff, extraversion);
|
||||
}
|
||||
|
||||
@@ -155,6 +155,7 @@ XENAPI_PLATFORM_CFG_TYPES = {
|
||||
'vncdisplay': int,
|
||||
'vnclisten': str,
|
||||
'timer_mode': int,
|
||||
'viridian': int,
|
||||
'vncpasswd': str,
|
||||
'vncunused': int,
|
||||
'xauthority': str,
|
||||
@@ -442,6 +443,8 @@ class XendConfig(dict):
|
||||
if self.is_hvm():
|
||||
if 'timer_mode' not in self['platform']:
|
||||
self['platform']['timer_mode'] = 1
|
||||
if 'viridian' not in self['platform']:
|
||||
self['platform']['viridian'] = 0
|
||||
if 'rtc_timeoffset' not in self['platform']:
|
||||
self['platform']['rtc_timeoffset'] = 0
|
||||
if 'hpet' not in self['platform']:
|
||||
|
||||
@@ -43,9 +43,10 @@ HVM_PARAM_STORE_EVTCHN = 2
|
||||
HVM_PARAM_PAE_ENABLED = 4
|
||||
HVM_PARAM_IOREQ_PFN = 5
|
||||
HVM_PARAM_BUFIOREQ_PFN = 6
|
||||
HVM_PARAM_NVRAM_FD = 7
|
||||
HVM_PARAM_VHPT_SIZE = 8
|
||||
HVM_PARAM_BUFPIOREQ_PFN = 9
|
||||
HVM_PARAM_NVRAM_FD = 7 # ia64
|
||||
HVM_PARAM_VHPT_SIZE = 8 # ia64
|
||||
HVM_PARAM_BUFPIOREQ_PFN = 9 # ia64
|
||||
HVM_PARAM_VIRIDIAN = 9 # x86
|
||||
HVM_PARAM_TIMER_MODE = 10
|
||||
HVM_PARAM_HPET_ENABLED = 11
|
||||
HVM_PARAM_ACPI_S_STATE = 14
|
||||
|
||||
@@ -2078,6 +2078,11 @@ class XendDomainInfo:
|
||||
xc.hvm_set_param(self.domid, HVM_PARAM_TIMER_MODE,
|
||||
long(timer_mode))
|
||||
|
||||
# Set Viridian interface configuration of domain
|
||||
viridian = self.info["platform"].get("viridian")
|
||||
if arch.type == "x86" and hvm and viridian is not None:
|
||||
xc.hvm_set_param(self.domid, HVM_PARAM_VIRIDIAN, long(viridian))
|
||||
|
||||
# Optionally enable virtual HPET
|
||||
hpet = self.info["platform"].get("hpet")
|
||||
if hvm and hpet is not None:
|
||||
|
||||
@@ -218,6 +218,11 @@ gopts.var('timer_mode', val='TIMER_MODE',
|
||||
use="""Timer mode (0=delay virtual time when ticks are missed;
|
||||
1=virtual time is always wallclock time.""")
|
||||
|
||||
gopts.var('viridian', val='VIRIDIAN',
|
||||
fn=set_int, default=0,
|
||||
use="""Expose Viridian interface to x86 HVM guest?
|
||||
(Default is 0).""")
|
||||
|
||||
gopts.var('acpi', val='ACPI',
|
||||
fn=set_int, default=1,
|
||||
use="Disable or enable ACPI of HVM domain.")
|
||||
@@ -856,7 +861,8 @@ def configure_hvm(config_image, vals):
|
||||
'vnc', 'vncdisplay', 'vncunused', 'vncconsole', 'vnclisten',
|
||||
'sdl', 'display', 'xauthority', 'rtc_timeoffset', 'monitor',
|
||||
'acpi', 'apic', 'usb', 'usbdevice', 'keymap', 'pci', 'hpet',
|
||||
'guest_os_type', 'hap', 'opengl', 'cpuid', 'cpuid_check']
|
||||
'guest_os_type', 'hap', 'opengl', 'cpuid', 'cpuid_check',
|
||||
'viridian' ]
|
||||
|
||||
for a in args:
|
||||
if a in vals.__dict__ and vals.__dict__[a] is not None:
|
||||
|
||||
@@ -969,6 +969,7 @@ class sxp2xml:
|
||||
'usbdevice',
|
||||
'hpet',
|
||||
'timer_mode',
|
||||
'viridian',
|
||||
'vhpt',
|
||||
'guest_os_type',
|
||||
'hap',
|
||||
|
||||
@@ -111,26 +111,37 @@ unsigned long alloc_xen_mmio(unsigned long len)
|
||||
|
||||
#ifndef __ia64__
|
||||
|
||||
static int init_hypercall_stubs(void)
|
||||
static uint32_t xen_cpuid_base(void)
|
||||
{
|
||||
uint32_t eax, ebx, ecx, edx, pages, msr, i;
|
||||
uint32_t base, eax, ebx, ecx, edx;
|
||||
char signature[13];
|
||||
|
||||
cpuid(0x40000000, &eax, &ebx, &ecx, &edx);
|
||||
*(uint32_t*)(signature + 0) = ebx;
|
||||
*(uint32_t*)(signature + 4) = ecx;
|
||||
*(uint32_t*)(signature + 8) = edx;
|
||||
signature[12] = 0;
|
||||
for (base = 0x40000000; base < 0x40001000; base += 0x100) {
|
||||
cpuid(base, &eax, &ebx, &ecx, &edx);
|
||||
*(uint32_t*)(signature + 0) = ebx;
|
||||
*(uint32_t*)(signature + 4) = ecx;
|
||||
*(uint32_t*)(signature + 8) = edx;
|
||||
signature[12] = 0;
|
||||
|
||||
if (strcmp("XenVMMXenVMM", signature) || (eax < 0x40000002)) {
|
||||
if (!strcmp("XenVMMXenVMM", signature) && ((eax - base) >= 2))
|
||||
return base;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int init_hypercall_stubs(void)
|
||||
{
|
||||
uint32_t eax, ebx, ecx, edx, pages, msr, i, base;
|
||||
|
||||
base = xen_cpuid_base();
|
||||
if (base == 0) {
|
||||
printk(KERN_WARNING
|
||||
"Detected Xen platform device but not Xen VMM?"
|
||||
" (sig %s, eax %x)\n",
|
||||
signature, eax);
|
||||
"Detected Xen platform device but not Xen VMM?\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
cpuid(0x40000001, &eax, &ebx, &ecx, &edx);
|
||||
cpuid(base + 1, &eax, &ebx, &ecx, &edx);
|
||||
|
||||
printk(KERN_INFO "Xen version %d.%d.\n", eax >> 16, eax & 0xffff);
|
||||
|
||||
@@ -138,7 +149,7 @@ static int init_hypercall_stubs(void)
|
||||
* Find largest supported number of hypercall pages.
|
||||
* We'll create as many as possible up to this number.
|
||||
*/
|
||||
cpuid(0x40000002, &pages, &msr, &ecx, &edx);
|
||||
cpuid(base + 2, &pages, &msr, &ecx, &edx);
|
||||
|
||||
/*
|
||||
* Use __vmalloc() because vmalloc_exec() is not an exported symbol.
|
||||
@@ -174,18 +185,12 @@ static int init_hypercall_stubs(void)
|
||||
|
||||
static void resume_hypercall_stubs(void)
|
||||
{
|
||||
uint32_t eax, ebx, ecx, edx, pages, msr, i;
|
||||
char signature[13];
|
||||
uint32_t base, ecx, edx, pages, msr, i;
|
||||
|
||||
cpuid(0x40000000, &eax, &ebx, &ecx, &edx);
|
||||
*(uint32_t*)(signature + 0) = ebx;
|
||||
*(uint32_t*)(signature + 4) = ecx;
|
||||
*(uint32_t*)(signature + 8) = edx;
|
||||
signature[12] = 0;
|
||||
base = xen_cpuid_base();
|
||||
BUG_ON(base == 0);
|
||||
|
||||
BUG_ON(strcmp("XenVMMXenVMM", signature) || (eax < 0x40000002));
|
||||
|
||||
cpuid(0x40000002, &pages, &msr, &ecx, &edx);
|
||||
cpuid(base + 2, &pages, &msr, &ecx, &edx);
|
||||
|
||||
if (pages > max_hypercall_stub_pages)
|
||||
pages = max_hypercall_stub_pages;
|
||||
|
||||
@@ -18,3 +18,4 @@ obj-y += vpic.o
|
||||
obj-y += save.o
|
||||
obj-y += vmsi.o
|
||||
obj-y += stdvga.o
|
||||
obj-y += viridian.o
|
||||
@@ -1636,6 +1636,9 @@ void hvm_cpuid(unsigned int input, unsigned int *eax, unsigned int *ebx,
|
||||
{
|
||||
struct vcpu *v = current;
|
||||
|
||||
if ( cpuid_viridian_leaves(input, eax, ebx, ecx, edx) )
|
||||
return;
|
||||
|
||||
if ( cpuid_hypervisor_leaves(input, eax, ebx, ecx, edx) )
|
||||
return;
|
||||
|
||||
@@ -1954,6 +1957,9 @@ int hvm_do_hypercall(struct cpu_user_regs *regs)
|
||||
break;
|
||||
}
|
||||
|
||||
if ( (eax & 0x80000000) && is_viridian_domain(curr->domain) )
|
||||
return viridian_hypercall(regs);
|
||||
|
||||
if ( (eax >= NR_hypercalls) || !hvm_hypercall32_table[eax] )
|
||||
{
|
||||
regs->eax = -ENOSYS;
|
||||
@@ -2380,6 +2386,10 @@ long do_hvm_op(unsigned long op, XEN_GUEST_HANDLE(void) arg)
|
||||
if ( a.value > HVMPTM_one_missed_tick_pending )
|
||||
rc = -EINVAL;
|
||||
break;
|
||||
case HVM_PARAM_VIRIDIAN:
|
||||
if ( a.value > 1 )
|
||||
rc = -EINVAL;
|
||||
break;
|
||||
case HVM_PARAM_IDENT_PT:
|
||||
rc = -EPERM;
|
||||
if ( !IS_PRIV(current->domain) )
|
||||
|
||||
@@ -1005,7 +1005,8 @@ static int svm_msr_read_intercept(struct cpu_user_regs *regs)
|
||||
break;
|
||||
|
||||
default:
|
||||
if ( rdmsr_hypervisor_regs(ecx, &eax, &edx) ||
|
||||
if ( rdmsr_viridian_regs(ecx, &eax, &edx) ||
|
||||
rdmsr_hypervisor_regs(ecx, &eax, &edx) ||
|
||||
rdmsr_safe(ecx, eax, edx) == 0 )
|
||||
{
|
||||
regs->eax = eax;
|
||||
@@ -1073,6 +1074,9 @@ static int svm_msr_write_intercept(struct cpu_user_regs *regs)
|
||||
break;
|
||||
|
||||
default:
|
||||
if ( wrmsr_viridian_regs(ecx, regs->eax, regs->edx) )
|
||||
break;
|
||||
|
||||
switch ( long_mode_do_msr_write(regs) )
|
||||
{
|
||||
case HNDL_unhandled:
|
||||
|
||||
@@ -0,0 +1,350 @@
|
||||
/******************************************************************************
|
||||
* viridian.c
|
||||
*
|
||||
* An implementation of the Viridian hypercall interface.
|
||||
*/
|
||||
|
||||
#include <xen/sched.h>
|
||||
#include <xen/version.h>
|
||||
#include <xen/perfc.h>
|
||||
#include <xen/hypercall.h>
|
||||
#include <xen/domain_page.h>
|
||||
#include <asm/paging.h>
|
||||
#include <asm/p2m.h>
|
||||
#include <asm/hvm/support.h>
|
||||
#include <public/sched.h>
|
||||
#include <public/hvm/hvm_op.h>
|
||||
|
||||
/* Viridian MSR numbers. */
|
||||
#define VIRIDIAN_MSR_GUEST_OS_ID 0x40000000
|
||||
#define VIRIDIAN_MSR_HYPERCALL 0x40000001
|
||||
#define VIRIDIAN_MSR_VP_INDEX 0x40000002
|
||||
#define VIRIDIAN_MSR_EOI 0x40000070
|
||||
#define VIRIDIAN_MSR_ICR 0x40000071
|
||||
#define VIRIDIAN_MSR_TPR 0x40000072
|
||||
|
||||
/* Viridian Hypercall Status Codes. */
|
||||
#define HV_STATUS_SUCCESS 0x0000
|
||||
#define HV_STATUS_INVALID_HYPERCALL_CODE 0x0002
|
||||
|
||||
/* Viridian Hypercall Codes and Parameters. */
|
||||
#define HvNotifyLongSpinWait 8
|
||||
|
||||
/* Viridian CPUID 4000003, Viridian MSR availability. */
|
||||
#define CPUID3A_MSR_APIC_ACCESS (1 << 4)
|
||||
#define CPUID3A_MSR_HYPERCALL (1 << 5)
|
||||
#define CPUID3A_MSR_VP_INDEX (1 << 6)
|
||||
|
||||
/* Viridian CPUID 4000004, Implementation Recommendations. */
|
||||
#define CPUID4A_MSR_BASED_APIC (1 << 3)
|
||||
|
||||
int cpuid_viridian_leaves(unsigned int leaf, unsigned int *eax,
|
||||
unsigned int *ebx, unsigned int *ecx,
|
||||
unsigned int *edx)
|
||||
{
|
||||
struct domain *d = current->domain;
|
||||
|
||||
if ( !is_viridian_domain(d) )
|
||||
return 0;
|
||||
|
||||
leaf -= 0x40000000;
|
||||
if ( leaf > 5 )
|
||||
return 0;
|
||||
|
||||
*eax = *ebx = *ecx = *edx = 0;
|
||||
switch ( leaf )
|
||||
{
|
||||
case 0:
|
||||
*eax = 0x40000005; /* Maximum leaf */
|
||||
*ebx = 0x7263694d; /* Magic numbers */
|
||||
*ecx = 0x666F736F;
|
||||
*edx = 0x76482074;
|
||||
break;
|
||||
case 1:
|
||||
*eax = 0x31237648; /* Version number */
|
||||
break;
|
||||
case 2:
|
||||
/* Hypervisor information, but only if the guest has set its
|
||||
own version number. */
|
||||
if ( d->arch.hvm_domain.viridian.guest_os_id.raw == 0 )
|
||||
break;
|
||||
*eax = 1; /* Build number */
|
||||
*ebx = (xen_major_version() << 16) | xen_minor_version();
|
||||
*ecx = 0; /* SP */
|
||||
*edx = 0; /* Service branch and number */
|
||||
break;
|
||||
case 3:
|
||||
/* Which hypervisor MSRs are available to the guest */
|
||||
*eax = (CPUID3A_MSR_APIC_ACCESS |
|
||||
CPUID3A_MSR_HYPERCALL |
|
||||
CPUID3A_MSR_VP_INDEX);
|
||||
break;
|
||||
case 4:
|
||||
/* Recommended hypercall usage. */
|
||||
if ( (d->arch.hvm_domain.viridian.guest_os_id.raw == 0) ||
|
||||
(d->arch.hvm_domain.viridian.guest_os_id.fields.os < 4) )
|
||||
break;
|
||||
*eax = CPUID4A_MSR_BASED_APIC;
|
||||
*ebx = 2047; /* long spin count */
|
||||
break;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void enable_hypercall_page(void)
|
||||
{
|
||||
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;
|
||||
|
||||
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);
|
||||
|
||||
/*
|
||||
* 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) = ((boot_cpu_data.x86_vendor == X86_VENDOR_INTEL)
|
||||
? 0xc1 : 0xd9);
|
||||
*(u8 *)(p + 8) = 0xc3; /* ret */
|
||||
memset(p + 9, 0xcc, PAGE_SIZE - 9); /* int3, int3, ... */
|
||||
|
||||
unmap_domain_page(p);
|
||||
|
||||
put_page_and_type(mfn_to_page(mfn));
|
||||
}
|
||||
|
||||
int wrmsr_viridian_regs(uint32_t idx, uint32_t eax, uint32_t edx)
|
||||
{
|
||||
struct domain *d = current->domain;
|
||||
uint64_t val = ((uint64_t)edx << 32) | eax;
|
||||
|
||||
if ( !is_viridian_domain(d) )
|
||||
return 0;
|
||||
|
||||
switch ( idx )
|
||||
{
|
||||
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);
|
||||
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;
|
||||
if ( d->arch.hvm_domain.viridian.hypercall_gpa.fields.enabled )
|
||||
enable_hypercall_page();
|
||||
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));
|
||||
break;
|
||||
|
||||
case VIRIDIAN_MSR_ICR: {
|
||||
struct vlapic *vlapic = vcpu_vlapic(current);
|
||||
perfc_incr(mshv_wrmsr_icr);
|
||||
eax &= ~(1 << 12);
|
||||
edx &= 0xff000000;
|
||||
vlapic_set_reg(vlapic, APIC_ICR2, edx);
|
||||
if ( vlapic_ipi(vlapic, eax, edx) == X86EMUL_OKAY )
|
||||
vlapic_set_reg(vlapic, APIC_ICR, eax);
|
||||
break;
|
||||
}
|
||||
|
||||
case VIRIDIAN_MSR_TPR:
|
||||
perfc_incr(mshv_wrmsr_tpr);
|
||||
vlapic_set_reg(vcpu_vlapic(current), APIC_TASKPRI, eax & 0xff);
|
||||
break;
|
||||
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int rdmsr_viridian_regs(uint32_t idx, uint32_t *eax, uint32_t *edx)
|
||||
{
|
||||
uint64_t val;
|
||||
struct vcpu *v = current;
|
||||
|
||||
if ( !is_viridian_domain(v->domain) )
|
||||
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;
|
||||
break;
|
||||
|
||||
case VIRIDIAN_MSR_HYPERCALL:
|
||||
perfc_incr(mshv_rdmsr_hc_page);
|
||||
val = v->domain->arch.hvm_domain.viridian.hypercall_gpa.raw;
|
||||
break;
|
||||
|
||||
case VIRIDIAN_MSR_VP_INDEX:
|
||||
perfc_incr(mshv_rdmsr_vp_index);
|
||||
val = v->vcpu_id;
|
||||
break;
|
||||
|
||||
case VIRIDIAN_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 VIRIDIAN_MSR_TPR:
|
||||
perfc_incr(mshv_rdmsr_tpr);
|
||||
val = vlapic_get_reg(vcpu_vlapic(v), APIC_TASKPRI);
|
||||
break;
|
||||
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
|
||||
*eax = val;
|
||||
*edx = val >> 32;
|
||||
return 1;
|
||||
}
|
||||
|
||||
int viridian_hypercall(struct cpu_user_regs *regs)
|
||||
{
|
||||
struct domain *d = current->domain;
|
||||
int mode = hvm_guest_x86_mode(current);
|
||||
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 rsvd1;
|
||||
unsigned rep_count:12;
|
||||
unsigned rsvd2:4;
|
||||
unsigned rep_start:12;
|
||||
unsigned rsvd3:4;
|
||||
};
|
||||
} input;
|
||||
|
||||
union hypercall_output {
|
||||
uint64_t raw;
|
||||
struct {
|
||||
uint16_t result;
|
||||
uint16_t rsvd1;
|
||||
unsigned rep_complete:12;
|
||||
unsigned rsvd2:20;
|
||||
};
|
||||
} output = { 0 };
|
||||
|
||||
ASSERT(is_viridian_domain(d));
|
||||
|
||||
switch ( mode )
|
||||
{
|
||||
#ifdef __x86_64__
|
||||
case 8:
|
||||
input.raw = regs->rcx;
|
||||
input_params_gpa = regs->rdx;
|
||||
output_params_gpa = regs->r8;
|
||||
break;
|
||||
#endif
|
||||
case 4:
|
||||
input.raw = ((uint64_t)regs->edx << 32) | regs->eax;
|
||||
input_params_gpa = ((uint64_t)regs->ebx << 32) | regs->ecx;
|
||||
output_params_gpa = ((uint64_t)regs->edi << 32) | regs->esi;
|
||||
break;
|
||||
default:
|
||||
goto out;
|
||||
}
|
||||
|
||||
switch ( input.call_code )
|
||||
{
|
||||
case HvNotifyLongSpinWait:
|
||||
perfc_incr(mshv_call_long_wait);
|
||||
do_sched_op_compat(SCHEDOP_yield, 0);
|
||||
status = HV_STATUS_SUCCESS;
|
||||
break;
|
||||
default:
|
||||
status = HV_STATUS_INVALID_HYPERCALL_CODE;
|
||||
break;
|
||||
}
|
||||
|
||||
out:
|
||||
output.result = status;
|
||||
switch (mode) {
|
||||
#ifdef __x86_64__
|
||||
case 8:
|
||||
regs->rax = output.raw;
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
regs->edx = output.raw >> 32;
|
||||
regs->eax = output.raw;
|
||||
break;
|
||||
}
|
||||
|
||||
return HVM_HCALL_completed;
|
||||
}
|
||||
|
||||
static int viridian_save_cpu_ctxt(struct domain *d, hvm_domain_context_t *h)
|
||||
{
|
||||
struct hvm_viridian_context ctxt;
|
||||
|
||||
if ( !is_viridian_domain(d) )
|
||||
return 0;
|
||||
|
||||
ctxt.hypercall_gpa = d->arch.hvm_domain.viridian.hypercall_gpa.raw;
|
||||
ctxt.guest_os_id = d->arch.hvm_domain.viridian.guest_os_id.raw;
|
||||
|
||||
return (hvm_save_entry(VIRIDIAN, 0, h, &ctxt) != 0);
|
||||
}
|
||||
|
||||
static int viridian_load_cpu_ctxt(struct domain *d, hvm_domain_context_t *h)
|
||||
{
|
||||
struct hvm_viridian_context ctxt;
|
||||
|
||||
if ( hvm_load_entry(VIRIDIAN, h, &ctxt) != 0 )
|
||||
return -EINVAL;
|
||||
|
||||
d->arch.hvm_domain.viridian.hypercall_gpa.raw = ctxt.hypercall_gpa;
|
||||
d->arch.hvm_domain.viridian.guest_os_id.raw = ctxt.guest_os_id;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
HVM_REGISTER_SAVE_RESTORE(VIRIDIAN, viridian_save_cpu_ctxt,
|
||||
viridian_load_cpu_ctxt, 1, HVMSR_PER_DOM);
|
||||
@@ -417,7 +417,7 @@ void vlapic_EOI_set(struct vlapic *vlapic)
|
||||
hvm_dpci_msi_eoi(current->domain, vector);
|
||||
}
|
||||
|
||||
static int vlapic_ipi(
|
||||
int vlapic_ipi(
|
||||
struct vlapic *vlapic, uint32_t icr_low, uint32_t icr_high)
|
||||
{
|
||||
unsigned int dest = GET_xAPIC_DEST_FIELD(icr_high);
|
||||
|
||||
@@ -1677,7 +1677,8 @@ static int vmx_msr_read_intercept(struct cpu_user_regs *regs)
|
||||
break;
|
||||
}
|
||||
|
||||
if ( rdmsr_hypervisor_regs(ecx, &eax, &edx) ||
|
||||
if ( rdmsr_viridian_regs(ecx, &eax, &edx) ||
|
||||
rdmsr_hypervisor_regs(ecx, &eax, &edx) ||
|
||||
rdmsr_safe(ecx, eax, edx) == 0 )
|
||||
{
|
||||
regs->eax = eax;
|
||||
@@ -1852,6 +1853,10 @@ static int vmx_msr_write_intercept(struct cpu_user_regs *regs)
|
||||
default:
|
||||
if ( vpmu_do_wrmsr(regs) )
|
||||
return X86EMUL_OKAY;
|
||||
|
||||
if ( wrmsr_viridian_regs(ecx, regs->eax, regs->edx) )
|
||||
break;
|
||||
|
||||
switch ( long_mode_do_msr_write(regs) )
|
||||
{
|
||||
case HNDL_unhandled:
|
||||
|
||||
+18
-6
@@ -577,7 +577,11 @@ DO_ERROR_NOCODE(TRAP_simd_error, simd_coprocessor_error)
|
||||
int rdmsr_hypervisor_regs(
|
||||
uint32_t idx, uint32_t *eax, uint32_t *edx)
|
||||
{
|
||||
idx -= 0x40000000;
|
||||
struct domain *d = current->domain;
|
||||
/* Optionally shift out of the way of Viridian architectural MSRs. */
|
||||
uint32_t base = is_viridian_domain(d) ? 0x40000200 : 0x40000000;
|
||||
|
||||
idx -= base;
|
||||
if ( idx > 0 )
|
||||
return 0;
|
||||
|
||||
@@ -599,8 +603,10 @@ int wrmsr_hypervisor_regs(
|
||||
uint32_t idx, uint32_t eax, uint32_t edx)
|
||||
{
|
||||
struct domain *d = current->domain;
|
||||
/* Optionally shift out of the way of Viridian architectural MSRs. */
|
||||
uint32_t base = is_viridian_domain(d) ? 0x40000200 : 0x40000000;
|
||||
|
||||
idx -= 0x40000000;
|
||||
idx -= base;
|
||||
if ( idx > 0 )
|
||||
return 0;
|
||||
|
||||
@@ -628,7 +634,7 @@ int wrmsr_hypervisor_regs(
|
||||
{
|
||||
gdprintk(XENLOG_WARNING,
|
||||
"Bad GMFN %lx (MFN %lx) to MSR %08x\n",
|
||||
gmfn, mfn, 0x40000000);
|
||||
gmfn, mfn, base + idx);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -650,14 +656,18 @@ int wrmsr_hypervisor_regs(
|
||||
int cpuid_hypervisor_leaves(
|
||||
uint32_t idx, uint32_t *eax, uint32_t *ebx, uint32_t *ecx, uint32_t *edx)
|
||||
{
|
||||
idx -= 0x40000000;
|
||||
if ( idx > 2 )
|
||||
struct domain *d = current->domain;
|
||||
/* Optionally shift out of the way of Viridian architectural leaves. */
|
||||
uint32_t base = is_viridian_domain(d) ? 0x40000100 : 0x40000000;
|
||||
|
||||
idx -= base;
|
||||
if ( idx > 2 )
|
||||
return 0;
|
||||
|
||||
switch ( idx )
|
||||
{
|
||||
case 0:
|
||||
*eax = 0x40000002; /* Largest leaf */
|
||||
*eax = base + 2; /* Largest leaf */
|
||||
*ebx = XEN_CPUID_SIGNATURE_EBX;
|
||||
*ecx = XEN_CPUID_SIGNATURE_ECX;
|
||||
*edx = XEN_CPUID_SIGNATURE_EDX;
|
||||
@@ -673,6 +683,8 @@ int cpuid_hypervisor_leaves(
|
||||
case 2:
|
||||
*eax = 1; /* Number of hypercall-transfer pages */
|
||||
*ebx = 0x40000000; /* MSR base address */
|
||||
if ( is_viridian_domain(d) )
|
||||
*ebx = 0x40000200;
|
||||
*ecx = 0; /* Features 1 */
|
||||
*edx = 0; /* Features 2 */
|
||||
if ( !is_hvm_vcpu(current) )
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
@@ -28,6 +28,7 @@
|
||||
#include <asm/hvm/vioapic.h>
|
||||
#include <asm/hvm/io.h>
|
||||
#include <xen/hvm/iommu.h>
|
||||
#include <asm/hvm/viridian.h>
|
||||
#include <asm/hvm/vmx/vmcs.h>
|
||||
#include <asm/hvm/svm/vmcb.h>
|
||||
#include <public/hvm/params.h>
|
||||
@@ -74,6 +75,8 @@ struct hvm_domain {
|
||||
/* Pass-through */
|
||||
struct hvm_iommu hvm_iommu;
|
||||
|
||||
struct viridian_domain viridian;
|
||||
|
||||
bool_t hap_enabled;
|
||||
bool_t qemu_mapcache_invalidate;
|
||||
bool_t is_s3_suspended;
|
||||
|
||||
@@ -219,6 +219,9 @@ hvm_set_segment_register(struct vcpu *v, enum x86_segment seg,
|
||||
hvm_funcs.set_segment_register(v, seg, reg);
|
||||
}
|
||||
|
||||
#define is_viridian_domain(_d) \
|
||||
(is_hvm_domain(_d) && ((_d)->arch.hvm_domain.params[HVM_PARAM_VIRIDIAN]))
|
||||
|
||||
void hvm_cpuid(unsigned int input, unsigned int *eax, unsigned int *ebx,
|
||||
unsigned int *ecx, unsigned int *edx);
|
||||
void hvm_migrate_timers(struct vcpu *v);
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
/*****************************************************************************
|
||||
*
|
||||
* include/xen/viridian.h
|
||||
*
|
||||
* Copyright (c) 2008 Citrix Corp.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __ASM_X86_HVM_VIRIDIAN_H__
|
||||
#define __ASM_X86_HVM_VIRIDIAN_H__
|
||||
|
||||
union viridian_guest_os_id
|
||||
{
|
||||
uint64_t raw;
|
||||
struct
|
||||
{
|
||||
uint64_t build_number:16;
|
||||
uint64_t service_pack:8;
|
||||
uint64_t minor:8;
|
||||
uint64_t major:8;
|
||||
uint64_t os:8;
|
||||
uint64_t vendor:16;
|
||||
} fields;
|
||||
};
|
||||
|
||||
union viridian_hypercall_gpa
|
||||
{ uint64_t raw;
|
||||
struct
|
||||
{
|
||||
uint64_t enabled:1;
|
||||
uint64_t reserved_preserved:11;
|
||||
uint64_t pfn:48;
|
||||
} fields;
|
||||
};
|
||||
|
||||
struct viridian_domain
|
||||
{
|
||||
union viridian_guest_os_id guest_os_id;
|
||||
union viridian_hypercall_gpa hypercall_gpa;
|
||||
};
|
||||
|
||||
int
|
||||
cpuid_viridian_leaves(
|
||||
unsigned int leaf,
|
||||
unsigned int *eax,
|
||||
unsigned int *ebx,
|
||||
unsigned int *ecx,
|
||||
unsigned int *edx);
|
||||
|
||||
int
|
||||
wrmsr_viridian_regs(
|
||||
uint32_t idx,
|
||||
uint32_t eax,
|
||||
uint32_t edx);
|
||||
|
||||
int
|
||||
rdmsr_viridian_regs(
|
||||
uint32_t idx,
|
||||
uint32_t *eax,
|
||||
uint32_t *edx);
|
||||
|
||||
int
|
||||
viridian_hypercall(struct cpu_user_regs *regs);
|
||||
|
||||
#endif /* __ASM_X86_HVM_VIRIDIAN_H__ */
|
||||
@@ -98,4 +98,8 @@ struct vlapic *apic_round_robin(
|
||||
|
||||
int vlapic_match_logical_addr(struct vlapic *vlapic, uint8_t mda);
|
||||
|
||||
void vlapic_EOI_set(struct vlapic *vlapic);
|
||||
|
||||
int vlapic_ipi(struct vlapic *vlapic, uint32_t icr_low, uint32_t icr_high);
|
||||
|
||||
#endif /* __ASM_X86_HVM_VLAPIC_H__ */
|
||||
@@ -111,4 +111,20 @@ PERFCOUNTER(shadow_unsync, "shadow OOS unsyncs")
|
||||
PERFCOUNTER(shadow_unsync_evict, "shadow OOS evictions")
|
||||
PERFCOUNTER(shadow_resync, "shadow OOS resyncs")
|
||||
|
||||
PERFCOUNTER(mshv_call_sw_addr_space, "MS Hv Switch Address Space")
|
||||
PERFCOUNTER(mshv_call_flush_tlb_list, "MS Hv Flush TLB list")
|
||||
PERFCOUNTER(mshv_call_flush_tlb_all, "MS Hv Flush TLB all")
|
||||
PERFCOUNTER(mshv_call_long_wait, "MS Hv Notify long wait")
|
||||
PERFCOUNTER(mshv_rdmsr_osid, "MS Hv rdmsr Guest OS ID")
|
||||
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_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")
|
||||
|
||||
/*#endif*/ /* __XEN_PERFC_DEFN_H__ */
|
||||
@@ -421,9 +421,20 @@ struct hvm_hw_mtrr {
|
||||
|
||||
DECLARE_HVM_SAVE_TYPE(MTRR, 14, struct hvm_hw_mtrr);
|
||||
|
||||
/*
|
||||
* Viridian hypervisor context.
|
||||
*/
|
||||
|
||||
struct hvm_viridian_context {
|
||||
uint64_t hypercall_gpa;
|
||||
uint64_t guest_os_id;
|
||||
};
|
||||
|
||||
DECLARE_HVM_SAVE_TYPE(VIRIDIAN, 15, struct hvm_viridian_context);
|
||||
|
||||
/*
|
||||
* Largest type-code in use
|
||||
*/
|
||||
#define HVM_SAVE_CODE_MAX 14
|
||||
#define HVM_SAVE_CODE_MAX 15
|
||||
|
||||
#endif /* __XEN_PUBLIC_HVM_SAVE_X86_H__ */
|
||||
@@ -51,9 +51,16 @@
|
||||
#define HVM_PARAM_BUFIOREQ_PFN 6
|
||||
|
||||
#ifdef __ia64__
|
||||
|
||||
#define HVM_PARAM_NVRAM_FD 7
|
||||
#define HVM_PARAM_VHPT_SIZE 8
|
||||
#define HVM_PARAM_BUFPIOREQ_PFN 9
|
||||
|
||||
#elif defined(__i386__) || defined(__x86_64__)
|
||||
|
||||
/* Expose Viridian interfaces to this HVM guest? */
|
||||
#define HVM_PARAM_VIRIDIAN 9
|
||||
|
||||
#endif
|
||||
|
||||
/*
|
||||
|
||||