mirror of
https://github.com/xen-project/xen.git
synced 2026-10-28 02:01:24 +01:00
Implement SVM specific part for Nested Virtualization
Signed-off-by: Christoph Egger <Christoph.Egger@amd.com> Acked-by: Tim Deegan <Tim.Deegan@citrix.com> Committed-by: Tim Deegan <Tim.Deegan@citrix.com>
This commit is contained in:
1 parent
5120a76fad
commit
9a779e4fc1
13 files changed
+1970
-147
No files matched your search
@@ -2,6 +2,8 @@ obj-y += asid.o
|
||||
obj-y += emulate.o
|
||||
obj-bin-y += entry.o
|
||||
obj-y += intr.o
|
||||
obj-y += nestedsvm.o
|
||||
obj-y += svm.o
|
||||
obj-y += svmdebug.o
|
||||
obj-y += vmcb.o
|
||||
obj-y += vpmu.o
|
||||
@@ -102,6 +102,11 @@ MAKE_INSTR(INT3, 1, 0xcc);
|
||||
MAKE_INSTR(RDTSC, 2, 0x0f, 0x31);
|
||||
MAKE_INSTR(PAUSE, 1, 0x90);
|
||||
MAKE_INSTR(XSETBV, 3, 0x0f, 0x01, 0xd1);
|
||||
MAKE_INSTR(VMRUN, 3, 0x0f, 0x01, 0xd8);
|
||||
MAKE_INSTR(VMLOAD, 3, 0x0f, 0x01, 0xda);
|
||||
MAKE_INSTR(VMSAVE, 3, 0x0f, 0x01, 0xdb);
|
||||
MAKE_INSTR(STGI, 3, 0x0f, 0x01, 0xdc);
|
||||
MAKE_INSTR(CLGI, 3, 0x0f, 0x01, 0xdd);
|
||||
|
||||
static const u8 *opc_bytes[INSTR_MAX_COUNT] =
|
||||
{
|
||||
@@ -116,6 +121,11 @@ static const u8 *opc_bytes[INSTR_MAX_COUNT] =
|
||||
[INSTR_RDTSC] = OPCODE_RDTSC,
|
||||
[INSTR_PAUSE] = OPCODE_PAUSE,
|
||||
[INSTR_XSETBV] = OPCODE_XSETBV,
|
||||
[INSTR_VMRUN] = OPCODE_VMRUN,
|
||||
[INSTR_VMLOAD] = OPCODE_VMLOAD,
|
||||
[INSTR_VMSAVE] = OPCODE_VMSAVE,
|
||||
[INSTR_STGI] = OPCODE_STGI,
|
||||
[INSTR_CLGI] = OPCODE_CLGI,
|
||||
};
|
||||
|
||||
static int fetch(struct vcpu *v, u8 *buf, unsigned long addr, int len)
|
||||
|
||||
@@ -54,6 +54,7 @@
|
||||
|
||||
ENTRY(svm_asm_do_resume)
|
||||
call svm_intr_assist
|
||||
call_with_regs(nsvm_vcpu_switch)
|
||||
|
||||
get_current(bx)
|
||||
CLGI
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
+297
-29
@@ -49,6 +49,9 @@
|
||||
#include <asm/hvm/svm/vmcb.h>
|
||||
#include <asm/hvm/svm/emulate.h>
|
||||
#include <asm/hvm/svm/intr.h>
|
||||
#include <asm/hvm/svm/svmdebug.h>
|
||||
#include <asm/hvm/svm/nestedsvm.h>
|
||||
#include <asm/hvm/nestedhvm.h>
|
||||
#include <asm/x86_emulate.h>
|
||||
#include <public/sched.h>
|
||||
#include <asm/hvm/vpt.h>
|
||||
@@ -106,6 +109,44 @@ static void svm_cpu_down(void)
|
||||
write_efer(read_efer() & ~EFER_SVME);
|
||||
}
|
||||
|
||||
unsigned long *
|
||||
svm_msrbit(unsigned long *msr_bitmap, uint32_t msr)
|
||||
{
|
||||
unsigned long *msr_bit = NULL;
|
||||
|
||||
/*
|
||||
* See AMD64 Programmers Manual, Vol 2, Section 15.10 (MSR-Bitmap Address).
|
||||
*/
|
||||
if ( msr <= 0x1fff )
|
||||
msr_bit = msr_bitmap + 0x0000 / BYTES_PER_LONG;
|
||||
else if ( (msr >= 0xc0000000) && (msr <= 0xc0001fff) )
|
||||
msr_bit = msr_bitmap + 0x0800 / BYTES_PER_LONG;
|
||||
else if ( (msr >= 0xc0010000) && (msr <= 0xc0011fff) )
|
||||
msr_bit = msr_bitmap + 0x1000 / BYTES_PER_LONG;
|
||||
|
||||
return msr_bit;
|
||||
}
|
||||
|
||||
void svm_intercept_msr(struct vcpu *v, uint32_t msr, int enable)
|
||||
{
|
||||
unsigned long *msr_bit;
|
||||
|
||||
msr_bit = svm_msrbit(v->arch.hvm_svm.msrpm, msr);
|
||||
BUG_ON(msr_bit == NULL);
|
||||
msr &= 0x1fff;
|
||||
|
||||
if ( enable )
|
||||
{
|
||||
__set_bit(msr * 2, msr_bit);
|
||||
__set_bit(msr * 2 + 1, msr_bit);
|
||||
}
|
||||
else
|
||||
{
|
||||
__clear_bit(msr * 2, msr_bit);
|
||||
__clear_bit(msr * 2 + 1, msr_bit);
|
||||
}
|
||||
}
|
||||
|
||||
static void svm_save_dr(struct vcpu *v)
|
||||
{
|
||||
struct vmcb_struct *vmcb = v->arch.hvm_svm.vmcb;
|
||||
@@ -296,7 +337,7 @@ static int svm_load_vmcb_ctxt(struct vcpu *v, struct hvm_hw_cpu *ctxt)
|
||||
{
|
||||
svm_load_cpu_state(v, ctxt);
|
||||
if (svm_vmcb_restore(v, ctxt)) {
|
||||
printk("svm_vmcb restore failed!\n");
|
||||
gdprintk(XENLOG_ERR, "svm_vmcb restore failed!\n");
|
||||
domain_crash(v->domain);
|
||||
return -EINVAL;
|
||||
}
|
||||
@@ -588,7 +629,24 @@ static void svm_set_segment_register(struct vcpu *v, enum x86_segment seg,
|
||||
static void svm_set_tsc_offset(struct vcpu *v, u64 offset)
|
||||
{
|
||||
struct vmcb_struct *vmcb = v->arch.hvm_svm.vmcb;
|
||||
vmcb_set_tsc_offset(vmcb, offset);
|
||||
struct vmcb_struct *n1vmcb, *n2vmcb;
|
||||
uint64_t n2_tsc_offset = 0;
|
||||
|
||||
if ( !nestedhvm_enabled(v->domain) ) {
|
||||
vmcb_set_tsc_offset(vmcb, offset);
|
||||
return;
|
||||
}
|
||||
|
||||
n1vmcb = vcpu_nestedhvm(v).nv_n1vmcx;
|
||||
n2vmcb = vcpu_nestedhvm(v).nv_n2vmcx;
|
||||
|
||||
if ( nestedhvm_vcpu_in_guestmode(v) ) {
|
||||
n2_tsc_offset = vmcb_get_tsc_offset(n2vmcb) -
|
||||
vmcb_get_tsc_offset(n1vmcb);
|
||||
vmcb_set_tsc_offset(n1vmcb, offset);
|
||||
}
|
||||
|
||||
vmcb_set_tsc_offset(vmcb, offset + n2_tsc_offset);
|
||||
}
|
||||
|
||||
static void svm_set_rdtsc_exiting(struct vcpu *v, bool_t enable)
|
||||
@@ -683,9 +741,13 @@ static void svm_do_resume(struct vcpu *v)
|
||||
{
|
||||
struct vmcb_struct *vmcb = v->arch.hvm_svm.vmcb;
|
||||
bool_t debug_state = v->domain->debugger_attached;
|
||||
vintr_t intr;
|
||||
bool_t vcpu_guestmode = 0;
|
||||
|
||||
if ( unlikely(v->arch.hvm_vcpu.debug_state_latch != debug_state) )
|
||||
if ( nestedhvm_enabled(v->domain) && nestedhvm_vcpu_in_guestmode(v) )
|
||||
vcpu_guestmode = 1;
|
||||
|
||||
if ( !vcpu_guestmode &&
|
||||
unlikely(v->arch.hvm_vcpu.debug_state_latch != debug_state) )
|
||||
{
|
||||
uint32_t intercepts = vmcb_get_exception_intercepts(vmcb);
|
||||
uint32_t mask = (1U << TRAP_debug) | (1U << TRAP_int3);
|
||||
@@ -703,13 +765,19 @@ static void svm_do_resume(struct vcpu *v)
|
||||
hvm_asid_flush_vcpu(v);
|
||||
}
|
||||
|
||||
/* Reflect the vlapic's TPR in the hardware vtpr */
|
||||
intr = vmcb_get_vintr(vmcb);
|
||||
intr.fields.tpr =
|
||||
(vlapic_get_reg(vcpu_vlapic(v), APIC_TASKPRI) & 0xFF) >> 4;
|
||||
vmcb_set_vintr(vmcb, intr);
|
||||
if ( !vcpu_guestmode )
|
||||
{
|
||||
vintr_t intr;
|
||||
|
||||
/* Reflect the vlapic's TPR in the hardware vtpr */
|
||||
intr = vmcb_get_vintr(vmcb);
|
||||
intr.fields.tpr =
|
||||
(vlapic_get_reg(vcpu_vlapic(v), APIC_TASKPRI) & 0xFF) >> 4;
|
||||
vmcb_set_vintr(vmcb, intr);
|
||||
}
|
||||
|
||||
hvm_do_resume(v);
|
||||
|
||||
reset_stack_and_jump(svm_asm_do_resume);
|
||||
}
|
||||
|
||||
@@ -961,8 +1029,8 @@ static void svm_do_nested_pgfault(paddr_t gpa)
|
||||
struct {
|
||||
uint64_t gpa;
|
||||
uint64_t mfn;
|
||||
u32 qualification;
|
||||
u32 p2mt;
|
||||
uint32_t qualification;
|
||||
uint32_t p2mt;
|
||||
} _d;
|
||||
|
||||
_d.gpa = gpa;
|
||||
@@ -984,12 +1052,21 @@ static void svm_do_nested_pgfault(paddr_t gpa)
|
||||
|
||||
static void svm_fpu_dirty_intercept(void)
|
||||
{
|
||||
struct vcpu *curr = current;
|
||||
struct vmcb_struct *vmcb = curr->arch.hvm_svm.vmcb;
|
||||
struct vcpu *v = current;
|
||||
struct vmcb_struct *vmcb = v->arch.hvm_svm.vmcb;
|
||||
|
||||
svm_fpu_enter(curr);
|
||||
svm_fpu_enter(v);
|
||||
|
||||
if ( !(curr->arch.hvm_vcpu.guest_cr[0] & X86_CR0_TS) )
|
||||
if ( nestedhvm_enabled(v->domain) && nestedhvm_vcpu_in_guestmode(v) ) {
|
||||
/* Check if guest must make FPU ready for the nested guest */
|
||||
if ( v->arch.hvm_vcpu.guest_cr[0] & X86_CR0_TS )
|
||||
hvm_inject_exception(TRAP_no_device, HVM_DELIVER_NO_ERROR_CODE, 0);
|
||||
else
|
||||
vmcb_set_cr0(vmcb, vmcb_get_cr0(vmcb) & ~X86_CR0_TS);
|
||||
return;
|
||||
}
|
||||
|
||||
if ( !(v->arch.hvm_vcpu.guest_cr[0] & X86_CR0_TS) )
|
||||
vmcb_set_cr0(vmcb, vmcb_get_cr0(vmcb) & ~X86_CR0_TS);
|
||||
}
|
||||
|
||||
@@ -1003,11 +1080,14 @@ static void svm_cpuid_intercept(
|
||||
|
||||
hvm_cpuid(input, eax, ebx, ecx, edx);
|
||||
|
||||
if ( input == 0x80000001 )
|
||||
{
|
||||
switch (input) {
|
||||
case 0x80000001:
|
||||
/* Fix up VLAPIC details. */
|
||||
if ( vlapic_hw_disabled(vcpu_vlapic(v)) )
|
||||
__clear_bit(X86_FEATURE_APIC & 31, edx);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
HVMTRACE_5D (CPUID, input, *eax, *ebx, *ecx, *edx);
|
||||
@@ -1043,6 +1123,7 @@ static void svm_dr_access(struct vcpu *v, struct cpu_user_regs *regs)
|
||||
|
||||
static int svm_msr_read_intercept(unsigned int msr, uint64_t *msr_content)
|
||||
{
|
||||
int ret;
|
||||
struct vcpu *v = current;
|
||||
struct vmcb_struct *vmcb = v->arch.hvm_svm.vmcb;
|
||||
|
||||
@@ -1076,9 +1157,6 @@ static int svm_msr_read_intercept(unsigned int msr, uint64_t *msr_content)
|
||||
*msr_content = 0;
|
||||
break;
|
||||
|
||||
case MSR_K8_VM_HSAVE_PA:
|
||||
goto gpf;
|
||||
|
||||
case MSR_IA32_DEBUGCTLMSR:
|
||||
*msr_content = vmcb_get_debugctlmsr(vmcb);
|
||||
break;
|
||||
@@ -1111,6 +1189,11 @@ static int svm_msr_read_intercept(unsigned int msr, uint64_t *msr_content)
|
||||
break;
|
||||
|
||||
default:
|
||||
ret = nsvm_rdmsr(v, msr, msr_content);
|
||||
if ( ret < 0 )
|
||||
goto gpf;
|
||||
else if ( ret )
|
||||
break;
|
||||
|
||||
if ( rdmsr_viridian_regs(msr, msr_content) ||
|
||||
rdmsr_hypervisor_regs(msr, msr_content) )
|
||||
@@ -1133,6 +1216,7 @@ static int svm_msr_read_intercept(unsigned int msr, uint64_t *msr_content)
|
||||
|
||||
static int svm_msr_write_intercept(unsigned int msr, uint64_t msr_content)
|
||||
{
|
||||
int ret;
|
||||
struct vcpu *v = current;
|
||||
struct vmcb_struct *vmcb = v->arch.hvm_svm.vmcb;
|
||||
int sync = 0;
|
||||
@@ -1153,9 +1237,6 @@ static int svm_msr_write_intercept(unsigned int msr, uint64_t msr_content)
|
||||
|
||||
switch ( msr )
|
||||
{
|
||||
case MSR_K8_VM_HSAVE_PA:
|
||||
goto gpf;
|
||||
|
||||
case MSR_IA32_SYSENTER_CS:
|
||||
vmcb->sysenter_cs = v->arch.hvm_svm.guest_sysenter_cs = msr_content;
|
||||
break;
|
||||
@@ -1215,6 +1296,12 @@ static int svm_msr_write_intercept(unsigned int msr, uint64_t msr_content)
|
||||
break;
|
||||
|
||||
default:
|
||||
ret = nsvm_wrmsr(v, msr, msr_content);
|
||||
if ( ret < 0 )
|
||||
goto gpf;
|
||||
else if ( ret )
|
||||
break;
|
||||
|
||||
if ( wrmsr_viridian_regs(msr, msr_content) )
|
||||
break;
|
||||
|
||||
@@ -1298,6 +1385,96 @@ static void svm_vmexit_do_pause(struct cpu_user_regs *regs)
|
||||
do_sched_op_compat(SCHEDOP_yield, 0);
|
||||
}
|
||||
|
||||
static void
|
||||
svm_vmexit_do_vmrun(struct cpu_user_regs *regs,
|
||||
struct vcpu *v, uint64_t vmcbaddr)
|
||||
{
|
||||
if (!nestedhvm_enabled(v->domain)) {
|
||||
gdprintk(XENLOG_ERR, "VMRUN: nestedhvm disabled, injecting #UD\n");
|
||||
hvm_inject_exception(TRAP_invalid_op, HVM_DELIVER_NO_ERROR_CODE, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!nestedsvm_vmcb_map(v, vmcbaddr)) {
|
||||
gdprintk(XENLOG_ERR, "VMRUN: mapping vmcb failed, injecting #UD\n");
|
||||
hvm_inject_exception(TRAP_invalid_op, HVM_DELIVER_NO_ERROR_CODE, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
vcpu_nestedhvm(v).nv_vmentry_pending = 1;
|
||||
return;
|
||||
}
|
||||
|
||||
static void
|
||||
svm_vmexit_do_vmload(struct vmcb_struct *vmcb,
|
||||
struct cpu_user_regs *regs,
|
||||
struct vcpu *v, uint64_t vmcbaddr)
|
||||
{
|
||||
int ret;
|
||||
unsigned int inst_len;
|
||||
struct nestedvcpu *nv = &vcpu_nestedhvm(v);
|
||||
|
||||
if ( (inst_len = __get_instruction_length(v, INSTR_VMLOAD)) == 0 )
|
||||
return;
|
||||
|
||||
if (!nestedhvm_enabled(v->domain)) {
|
||||
gdprintk(XENLOG_ERR, "VMLOAD: nestedhvm disabled, injecting #UD\n");
|
||||
ret = TRAP_invalid_op;
|
||||
goto inject;
|
||||
}
|
||||
|
||||
if (!nestedsvm_vmcb_map(v, vmcbaddr)) {
|
||||
gdprintk(XENLOG_ERR, "VMLOAD: mapping vmcb failed, injecting #UD\n");
|
||||
ret = TRAP_invalid_op;
|
||||
goto inject;
|
||||
}
|
||||
|
||||
svm_vmload(nv->nv_vvmcx);
|
||||
/* State in L1 VMCB is stale now */
|
||||
v->arch.hvm_svm.vmcb_in_sync = 0;
|
||||
|
||||
__update_guest_eip(regs, inst_len);
|
||||
return;
|
||||
|
||||
inject:
|
||||
hvm_inject_exception(ret, HVM_DELIVER_NO_ERROR_CODE, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
static void
|
||||
svm_vmexit_do_vmsave(struct vmcb_struct *vmcb,
|
||||
struct cpu_user_regs *regs,
|
||||
struct vcpu *v, uint64_t vmcbaddr)
|
||||
{
|
||||
int ret;
|
||||
unsigned int inst_len;
|
||||
struct nestedvcpu *nv = &vcpu_nestedhvm(v);
|
||||
|
||||
if ( (inst_len = __get_instruction_length(v, INSTR_VMSAVE)) == 0 )
|
||||
return;
|
||||
|
||||
if (!nestedhvm_enabled(v->domain)) {
|
||||
gdprintk(XENLOG_ERR, "VMSAVE: nestedhvm disabled, injecting #UD\n");
|
||||
ret = TRAP_invalid_op;
|
||||
goto inject;
|
||||
}
|
||||
|
||||
if (!nestedsvm_vmcb_map(v, vmcbaddr)) {
|
||||
gdprintk(XENLOG_ERR, "VMSAVE: mapping vmcb failed, injecting #UD\n");
|
||||
ret = TRAP_invalid_op;
|
||||
goto inject;
|
||||
}
|
||||
|
||||
svm_vmsave(nv->nv_vvmcx);
|
||||
|
||||
__update_guest_eip(regs, inst_len);
|
||||
return;
|
||||
|
||||
inject:
|
||||
hvm_inject_exception(ret, HVM_DELIVER_NO_ERROR_CODE, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
static void svm_vmexit_ud_intercept(struct cpu_user_regs *regs)
|
||||
{
|
||||
struct hvm_emulate_ctxt ctxt;
|
||||
@@ -1428,22 +1605,38 @@ static struct hvm_function_table __read_mostly svm_function_table = {
|
||||
.msr_read_intercept = svm_msr_read_intercept,
|
||||
.msr_write_intercept = svm_msr_write_intercept,
|
||||
.invlpg_intercept = svm_invlpg_intercept,
|
||||
.set_rdtsc_exiting = svm_set_rdtsc_exiting
|
||||
.set_rdtsc_exiting = svm_set_rdtsc_exiting,
|
||||
|
||||
.nhvm_vcpu_initialise = nsvm_vcpu_initialise,
|
||||
.nhvm_vcpu_destroy = nsvm_vcpu_destroy,
|
||||
.nhvm_vcpu_reset = nsvm_vcpu_reset,
|
||||
.nhvm_vcpu_hostrestore = nsvm_vcpu_hostrestore,
|
||||
.nhvm_vcpu_vmexit = nsvm_vcpu_vmexit_inject,
|
||||
.nhvm_vcpu_vmexit_trap = nsvm_vcpu_vmexit_trap,
|
||||
.nhvm_vcpu_guestcr3 = nsvm_vcpu_guestcr3,
|
||||
.nhvm_vcpu_hostcr3 = nsvm_vcpu_hostcr3,
|
||||
.nhvm_vcpu_asid = nsvm_vcpu_asid,
|
||||
.nhvm_vmcx_guest_intercepts_trap = nsvm_vmcb_guest_intercepts_trap,
|
||||
.nhvm_vmcx_hap_enabled = nsvm_vmcb_hap_enabled,
|
||||
};
|
||||
|
||||
asmlinkage void svm_vmexit_handler(struct cpu_user_regs *regs)
|
||||
{
|
||||
unsigned int exit_reason;
|
||||
uint64_t exit_reason;
|
||||
struct vcpu *v = current;
|
||||
struct vmcb_struct *vmcb = v->arch.hvm_svm.vmcb;
|
||||
eventinj_t eventinj;
|
||||
int inst_len, rc;
|
||||
vintr_t intr;
|
||||
bool_t vcpu_guestmode = 0;
|
||||
|
||||
if ( paging_mode_hap(v->domain) )
|
||||
v->arch.hvm_vcpu.guest_cr[3] = v->arch.hvm_vcpu.hw_cr[3] =
|
||||
vmcb_get_cr3(vmcb);
|
||||
|
||||
if ( nestedhvm_enabled(v->domain) && nestedhvm_vcpu_in_guestmode(v) )
|
||||
vcpu_guestmode = 1;
|
||||
|
||||
/*
|
||||
* Before doing anything else, we need to sync up the VLAPIC's TPR with
|
||||
* SVM's vTPR. It's OK if the guest doesn't touch CR8 (e.g. 32-bit Windows)
|
||||
@@ -1451,13 +1644,73 @@ asmlinkage void svm_vmexit_handler(struct cpu_user_regs *regs)
|
||||
* NB. We need to preserve the low bits of the TPR to make checked builds
|
||||
* of Windows work, even though they don't actually do anything.
|
||||
*/
|
||||
intr = vmcb_get_vintr(vmcb);
|
||||
vlapic_set_reg(vcpu_vlapic(v), APIC_TASKPRI,
|
||||
if ( !vcpu_guestmode ) {
|
||||
intr = vmcb_get_vintr(vmcb);
|
||||
vlapic_set_reg(vcpu_vlapic(v), APIC_TASKPRI,
|
||||
((intr.fields.tpr & 0x0F) << 4) |
|
||||
(vlapic_get_reg(vcpu_vlapic(v), APIC_TASKPRI) & 0x0F));
|
||||
}
|
||||
|
||||
exit_reason = vmcb->exitcode;
|
||||
|
||||
if ( vcpu_guestmode ) {
|
||||
enum nestedhvm_vmexits nsret;
|
||||
struct nestedvcpu *nv = &vcpu_nestedhvm(v);
|
||||
struct vmcb_struct *ns_vmcb = nv->nv_vvmcx;
|
||||
uint64_t exitinfo1, exitinfo2;
|
||||
|
||||
/* Write real exitinfo1 back into virtual vmcb.
|
||||
* nestedsvm_check_intercepts() expects to have the correct
|
||||
* exitinfo1 value there.
|
||||
*/
|
||||
exitinfo1 = ns_vmcb->exitinfo1;
|
||||
ns_vmcb->exitinfo1 = vmcb->exitinfo1;
|
||||
nsret = nestedsvm_check_intercepts(v, regs, exit_reason);
|
||||
switch (nsret) {
|
||||
case NESTEDHVM_VMEXIT_CONTINUE:
|
||||
BUG();
|
||||
break;
|
||||
case NESTEDHVM_VMEXIT_HOST:
|
||||
break;
|
||||
case NESTEDHVM_VMEXIT_INJECT:
|
||||
/* Switch vcpu from l2 to l1 guest. We must perform
|
||||
* the switch here to have svm_do_resume() working
|
||||
* as intended.
|
||||
*/
|
||||
exitinfo1 = vmcb->exitinfo1;
|
||||
exitinfo2 = vmcb->exitinfo2;
|
||||
nv->nv_vmswitch_in_progress = 1;
|
||||
nsret = nestedsvm_vmexit_n2n1(v, regs);
|
||||
nv->nv_vmswitch_in_progress = 0;
|
||||
switch (nsret) {
|
||||
case NESTEDHVM_VMEXIT_DONE:
|
||||
/* defer VMEXIT injection */
|
||||
nestedsvm_vmexit_defer(v, exit_reason, exitinfo1, exitinfo2);
|
||||
goto out;
|
||||
case NESTEDHVM_VMEXIT_FATALERROR:
|
||||
gdprintk(XENLOG_ERR, "unexpected nestedsvm_vmexit() error\n");
|
||||
goto exit_and_crash;
|
||||
|
||||
default:
|
||||
BUG();
|
||||
case NESTEDHVM_VMEXIT_ERROR:
|
||||
break;
|
||||
}
|
||||
case NESTEDHVM_VMEXIT_ERROR:
|
||||
gdprintk(XENLOG_ERR,
|
||||
"nestedsvm_check_intercepts() returned NESTEDHVM_VMEXIT_ERROR\n");
|
||||
goto out;
|
||||
case NESTEDHVM_VMEXIT_FATALERROR:
|
||||
gdprintk(XENLOG_ERR,
|
||||
"unexpected nestedsvm_check_intercepts() error\n");
|
||||
goto exit_and_crash;
|
||||
default:
|
||||
gdprintk(XENLOG_INFO, "nestedsvm_check_intercepts() returned %i\n",
|
||||
nsret);
|
||||
goto exit_and_crash;
|
||||
}
|
||||
}
|
||||
|
||||
if ( hvm_long_mode_enabled(v) )
|
||||
HVMTRACE_ND(VMEXIT64, 1/*cycles*/, 3, exit_reason,
|
||||
(uint32_t)regs->eip, (uint32_t)((uint64_t)regs->eip >> 32),
|
||||
@@ -1469,7 +1722,7 @@ asmlinkage void svm_vmexit_handler(struct cpu_user_regs *regs)
|
||||
|
||||
if ( unlikely(exit_reason == VMEXIT_INVALID) )
|
||||
{
|
||||
svm_dump_vmcb(__func__, vmcb);
|
||||
svm_vmcb_dump(__func__, vmcb);
|
||||
goto exit_and_crash;
|
||||
}
|
||||
|
||||
@@ -1630,6 +1883,7 @@ asmlinkage void svm_vmexit_handler(struct cpu_user_regs *regs)
|
||||
case VMEXIT_VMMCALL:
|
||||
if ( (inst_len = __get_instruction_length(v, INSTR_VMCALL)) == 0 )
|
||||
break;
|
||||
BUG_ON(vcpu_guestmode);
|
||||
HVMTRACE_1D(VMMCALL, regs->eax);
|
||||
rc = hvm_do_hypercall(regs);
|
||||
if ( rc != HVM_HCALL_preempted )
|
||||
@@ -1662,9 +1916,18 @@ asmlinkage void svm_vmexit_handler(struct cpu_user_regs *regs)
|
||||
|
||||
case VMEXIT_MONITOR:
|
||||
case VMEXIT_MWAIT:
|
||||
hvm_inject_exception(TRAP_invalid_op, HVM_DELIVER_NO_ERROR_CODE, 0);
|
||||
break;
|
||||
|
||||
case VMEXIT_VMRUN:
|
||||
svm_vmexit_do_vmrun(regs, v, regs->eax);
|
||||
break;
|
||||
case VMEXIT_VMLOAD:
|
||||
svm_vmexit_do_vmload(vmcb, regs, v, regs->eax);
|
||||
break;
|
||||
case VMEXIT_VMSAVE:
|
||||
svm_vmexit_do_vmsave(vmcb, regs, v, regs->eax);
|
||||
break;
|
||||
case VMEXIT_STGI:
|
||||
case VMEXIT_CLGI:
|
||||
case VMEXIT_SKINIT:
|
||||
@@ -1708,7 +1971,7 @@ asmlinkage void svm_vmexit_handler(struct cpu_user_regs *regs)
|
||||
|
||||
default:
|
||||
exit_and_crash:
|
||||
gdprintk(XENLOG_ERR, "unexpected VMEXIT: exit reason = 0x%x, "
|
||||
gdprintk(XENLOG_ERR, "unexpected VMEXIT: exit reason = 0x%"PRIx64", "
|
||||
"exitinfo1 = %"PRIx64", exitinfo2 = %"PRIx64"\n",
|
||||
exit_reason,
|
||||
(u64)vmcb->exitinfo1, (u64)vmcb->exitinfo2);
|
||||
@@ -1716,6 +1979,11 @@ asmlinkage void svm_vmexit_handler(struct cpu_user_regs *regs)
|
||||
break;
|
||||
}
|
||||
|
||||
out:
|
||||
if ( vcpu_guestmode )
|
||||
/* Don't clobber TPR of the nested guest. */
|
||||
return;
|
||||
|
||||
/* The exit may have updated the TPR: reflect this in the hardware vtpr */
|
||||
intr = vmcb_get_vintr(vmcb);
|
||||
intr.fields.tpr =
|
||||
|
||||
@@ -0,0 +1,191 @@
|
||||
/*
|
||||
* svmdebug.c: debug functions
|
||||
* Copyright (c) 2011, Advanced Micro Devices, Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program; if not, write to the Free Software Foundation, Inc., 59 Temple
|
||||
* Place - Suite 330, Boston, MA 02111-1307 USA.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <asm/processor.h>
|
||||
#include <asm/msr-index.h>
|
||||
#include <asm/hvm/svm/svmdebug.h>
|
||||
|
||||
static void svm_dump_sel(const char *name, svm_segment_register_t *s)
|
||||
{
|
||||
printk("%s: sel=0x%04x, attr=0x%04x, limit=0x%08x, base=0x%016llx\n",
|
||||
name, s->sel, s->attr.bytes, s->limit,
|
||||
(unsigned long long)s->base);
|
||||
}
|
||||
|
||||
/* This function can directly access fields which are covered by clean bits. */
|
||||
void svm_vmcb_dump(const char *from, struct vmcb_struct *vmcb)
|
||||
{
|
||||
printk("Dumping guest's current state at %s...\n", from);
|
||||
printk("Size of VMCB = %d, paddr = 0x%016lx, vaddr = %p\n",
|
||||
(int) sizeof(struct vmcb_struct), virt_to_maddr(vmcb), vmcb);
|
||||
|
||||
printk("cr_intercepts = 0x%08x dr_intercepts = 0x%08x "
|
||||
"exception_intercepts = 0x%08x\n",
|
||||
vmcb->_cr_intercepts, vmcb->_dr_intercepts,
|
||||
vmcb->_exception_intercepts);
|
||||
printk("general1_intercepts = 0x%08x general2_intercepts = 0x%08x\n",
|
||||
vmcb->_general1_intercepts, vmcb->_general2_intercepts);
|
||||
printk("iopm_base_pa = 0x%016llx msrpm_base_pa = 0x%016llx tsc_offset = "
|
||||
"0x%016llx\n",
|
||||
(unsigned long long)vmcb->_iopm_base_pa,
|
||||
(unsigned long long)vmcb->_msrpm_base_pa,
|
||||
(unsigned long long)vmcb->_tsc_offset);
|
||||
printk("tlb_control = 0x%08x vintr = 0x%016llx interrupt_shadow = "
|
||||
"0x%016llx\n", vmcb->tlb_control,
|
||||
(unsigned long long)vmcb->_vintr.bytes,
|
||||
(unsigned long long)vmcb->interrupt_shadow);
|
||||
printk("exitcode = 0x%016llx exitintinfo = 0x%016llx\n",
|
||||
(unsigned long long)vmcb->exitcode,
|
||||
(unsigned long long)vmcb->exitintinfo.bytes);
|
||||
printk("exitinfo1 = 0x%016llx exitinfo2 = 0x%016llx \n",
|
||||
(unsigned long long)vmcb->exitinfo1,
|
||||
(unsigned long long)vmcb->exitinfo2);
|
||||
printk("np_enable = 0x%016llx guest_asid = 0x%03x\n",
|
||||
(unsigned long long)vmcb->_np_enable, vmcb->_guest_asid);
|
||||
printk("cpl = %d efer = 0x%016llx star = 0x%016llx lstar = 0x%016llx\n",
|
||||
vmcb->_cpl, (unsigned long long)vmcb->_efer,
|
||||
(unsigned long long)vmcb->star, (unsigned long long)vmcb->lstar);
|
||||
printk("CR0 = 0x%016llx CR2 = 0x%016llx\n",
|
||||
(unsigned long long)vmcb->_cr0, (unsigned long long)vmcb->_cr2);
|
||||
printk("CR3 = 0x%016llx CR4 = 0x%016llx\n",
|
||||
(unsigned long long)vmcb->_cr3, (unsigned long long)vmcb->_cr4);
|
||||
printk("RSP = 0x%016llx RIP = 0x%016llx\n",
|
||||
(unsigned long long)vmcb->rsp, (unsigned long long)vmcb->rip);
|
||||
printk("RAX = 0x%016llx RFLAGS=0x%016llx\n",
|
||||
(unsigned long long)vmcb->rax, (unsigned long long)vmcb->rflags);
|
||||
printk("DR6 = 0x%016llx, DR7 = 0x%016llx\n",
|
||||
(unsigned long long)vmcb->_dr6, (unsigned long long)vmcb->_dr7);
|
||||
printk("CSTAR = 0x%016llx SFMask = 0x%016llx\n",
|
||||
(unsigned long long)vmcb->cstar,
|
||||
(unsigned long long)vmcb->sfmask);
|
||||
printk("KernGSBase = 0x%016llx PAT = 0x%016llx \n",
|
||||
(unsigned long long)vmcb->kerngsbase,
|
||||
(unsigned long long)vmcb->_g_pat);
|
||||
printk("H_CR3 = 0x%016llx CleanBits = 0x%08x\n",
|
||||
(unsigned long long)vmcb->_h_cr3, vmcb->cleanbits.bytes);
|
||||
|
||||
/* print out all the selectors */
|
||||
svm_dump_sel("CS", &vmcb->cs);
|
||||
svm_dump_sel("DS", &vmcb->ds);
|
||||
svm_dump_sel("SS", &vmcb->ss);
|
||||
svm_dump_sel("ES", &vmcb->es);
|
||||
svm_dump_sel("FS", &vmcb->fs);
|
||||
svm_dump_sel("GS", &vmcb->gs);
|
||||
svm_dump_sel("GDTR", &vmcb->gdtr);
|
||||
svm_dump_sel("LDTR", &vmcb->ldtr);
|
||||
svm_dump_sel("IDTR", &vmcb->idtr);
|
||||
svm_dump_sel("TR", &vmcb->tr);
|
||||
}
|
||||
|
||||
bool_t
|
||||
svm_vmcb_isvalid(const char *from, struct vmcb_struct *vmcb,
|
||||
bool_t verbose)
|
||||
{
|
||||
bool_t ret = 0; /* ok */
|
||||
|
||||
#define PRINTF(...) \
|
||||
if (verbose) { ret = 1; printk("%s: ", from); printk(__VA_ARGS__); \
|
||||
} else return 1;
|
||||
|
||||
if ((vmcb->_efer & EFER_SVME) == 0) {
|
||||
PRINTF("EFER: SVME bit not set (0x%"PRIx64")\n", vmcb->_efer);
|
||||
}
|
||||
|
||||
if ((vmcb->_cr0 & X86_CR0_CD) == 0 && (vmcb->_cr0 & X86_CR0_NW) != 0) {
|
||||
PRINTF("CR0: CD bit is zero and NW bit set (0x%"PRIx64")\n",
|
||||
vmcb->_cr0);
|
||||
}
|
||||
|
||||
if ((vmcb->_cr0 >> 32U) != 0) {
|
||||
PRINTF("CR0: bits [63:32] are not zero (0x%"PRIx64")\n",
|
||||
vmcb->_cr0);
|
||||
}
|
||||
|
||||
if ((vmcb->_cr3 & 0x7) != 0) {
|
||||
PRINTF("CR3: MBZ bits are set (0x%"PRIx64")\n", vmcb->_cr3);
|
||||
}
|
||||
if ((vmcb->_efer & EFER_LMA) && (vmcb->_cr3 & 0xfe) != 0) {
|
||||
PRINTF("CR3: MBZ bits are set (0x%"PRIx64")\n", vmcb->_cr3);
|
||||
}
|
||||
|
||||
if ((vmcb->_cr4 >> 11U) != 0) {
|
||||
PRINTF("CR4: bits [63:11] are not zero (0x%"PRIx64")\n",
|
||||
vmcb->_cr4);
|
||||
}
|
||||
|
||||
if ((vmcb->_dr6 >> 32U) != 0) {
|
||||
PRINTF("DR6: bits [63:32] are not zero (0x%"PRIx64")\n",
|
||||
vmcb->_dr6);
|
||||
}
|
||||
|
||||
if ((vmcb->_dr7 >> 32U) != 0) {
|
||||
PRINTF("DR7: bits [63:32] are not zero (0x%"PRIx64")\n",
|
||||
vmcb->_dr7);
|
||||
}
|
||||
|
||||
if ((vmcb->_efer >> 15U) != 0) {
|
||||
PRINTF("EFER: bits [63:15] are not zero (0x%"PRIx64")\n",
|
||||
vmcb->_efer);
|
||||
}
|
||||
|
||||
if ((vmcb->_efer & EFER_LME) != 0 && ((vmcb->_cr0 & X86_CR0_PG) != 0)) {
|
||||
if ((vmcb->_cr4 & X86_CR4_PAE) == 0) {
|
||||
PRINTF("EFER_LME and CR0.PG are both set and CR4.PAE is zero.\n");
|
||||
}
|
||||
if ((vmcb->_cr0 & X86_CR0_PE) == 0) {
|
||||
PRINTF("EFER_LME and CR0.PG are both set and CR0.PE is zero.\n");
|
||||
}
|
||||
}
|
||||
|
||||
if ((vmcb->_efer & EFER_LME) != 0
|
||||
&& (vmcb->_cr0 & X86_CR0_PG) != 0
|
||||
&& (vmcb->_cr4 & X86_CR4_PAE) != 0
|
||||
&& (vmcb->cs.attr.fields.l != 0)
|
||||
&& (vmcb->cs.attr.fields.db != 0))
|
||||
{
|
||||
PRINTF("EFER_LME, CR0.PG, CR4.PAE, CS.L and CS.D are all non-zero.\n");
|
||||
}
|
||||
|
||||
if ((vmcb->_general2_intercepts & GENERAL2_INTERCEPT_VMRUN) == 0) {
|
||||
PRINTF("GENERAL2_INTERCEPT: VMRUN intercept bit is clear (0x%"PRIx32")\n",
|
||||
vmcb->_general2_intercepts);
|
||||
}
|
||||
|
||||
if (vmcb->eventinj.fields.resvd1 != 0) {
|
||||
PRINTF("eventinj: MBZ bits are set (0x%"PRIx64")\n",
|
||||
vmcb->eventinj.bytes);
|
||||
}
|
||||
|
||||
if (vmcb->_np_enable && vmcb->_h_cr3 == 0) {
|
||||
PRINTF("nested paging enabled but host cr3 is 0\n");
|
||||
}
|
||||
|
||||
#undef PRINTF
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Local variables:
|
||||
* mode: C
|
||||
* c-set-style: "BSD"
|
||||
* c-basic-offset: 4
|
||||
* tab-width: 4
|
||||
* indent-tabs-mode: nil
|
||||
* End:
|
||||
*/
|
||||
+17
-116
@@ -33,6 +33,7 @@
|
||||
#include <asm/hvm/svm/svm.h>
|
||||
#include <asm/hvm/svm/intr.h>
|
||||
#include <asm/hvm/svm/asid.h>
|
||||
#include <asm/hvm/svm/svmdebug.h>
|
||||
#include <xen/event.h>
|
||||
#include <xen/kernel.h>
|
||||
#include <xen/domain_page.h>
|
||||
@@ -40,9 +41,6 @@
|
||||
|
||||
extern int svm_dbg_on;
|
||||
|
||||
#define IOPM_SIZE (12 * 1024)
|
||||
#define MSRPM_SIZE (8 * 1024)
|
||||
|
||||
struct vmcb_struct *alloc_vmcb(void)
|
||||
{
|
||||
struct vmcb_struct *vmcb;
|
||||
@@ -78,37 +76,6 @@ struct host_save_area *alloc_host_save_area(void)
|
||||
return hsa;
|
||||
}
|
||||
|
||||
void svm_intercept_msr(struct vcpu *v, uint32_t msr, int enable)
|
||||
{
|
||||
unsigned long *msr_bitmap = v->arch.hvm_svm.msrpm;
|
||||
unsigned long *msr_bit = NULL;
|
||||
|
||||
/*
|
||||
* See AMD64 Programmers Manual, Vol 2, Section 15.10 (MSR-Bitmap Address).
|
||||
*/
|
||||
if ( msr <= 0x1fff )
|
||||
msr_bit = msr_bitmap + 0x0000 / BYTES_PER_LONG;
|
||||
else if ( (msr >= 0xc0000000) && (msr <= 0xc0001fff) )
|
||||
msr_bit = msr_bitmap + 0x0800 / BYTES_PER_LONG;
|
||||
else if ( (msr >= 0xc0010000) && (msr <= 0xc0011fff) )
|
||||
msr_bit = msr_bitmap + 0x1000 / BYTES_PER_LONG;
|
||||
|
||||
BUG_ON(msr_bit == NULL);
|
||||
|
||||
msr &= 0x1fff;
|
||||
|
||||
if ( enable )
|
||||
{
|
||||
__set_bit(msr * 2, msr_bit);
|
||||
__set_bit(msr * 2 + 1, msr_bit);
|
||||
}
|
||||
else
|
||||
{
|
||||
__clear_bit(msr * 2, msr_bit);
|
||||
__clear_bit(msr * 2 + 1, msr_bit);
|
||||
}
|
||||
}
|
||||
|
||||
/* This function can directly access fields which are covered by clean bits. */
|
||||
static int construct_vmcb(struct vcpu *v)
|
||||
{
|
||||
@@ -257,7 +224,7 @@ static int construct_vmcb(struct vcpu *v)
|
||||
|
||||
if ( cpu_has_pause_filter )
|
||||
{
|
||||
vmcb->_pause_filter_count = 3000;
|
||||
vmcb->_pause_filter_count = SVM_PAUSEFILTER_INIT;
|
||||
vmcb->_general1_intercepts |= GENERAL1_INTERCEPT_PAUSE;
|
||||
}
|
||||
|
||||
@@ -268,34 +235,38 @@ static int construct_vmcb(struct vcpu *v)
|
||||
|
||||
int svm_create_vmcb(struct vcpu *v)
|
||||
{
|
||||
struct nestedvcpu *nv = &vcpu_nestedhvm(v);
|
||||
struct arch_svm_struct *arch_svm = &v->arch.hvm_svm;
|
||||
int rc;
|
||||
|
||||
if ( (arch_svm->vmcb == NULL) &&
|
||||
(arch_svm->vmcb = alloc_vmcb()) == NULL )
|
||||
if ( (nv->nv_n1vmcx == NULL) &&
|
||||
(nv->nv_n1vmcx = alloc_vmcb()) == NULL )
|
||||
{
|
||||
printk("Failed to create a new VMCB\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
if ( (rc = construct_vmcb(v)) != 0 )
|
||||
arch_svm->vmcb = nv->nv_n1vmcx;
|
||||
rc = construct_vmcb(v);
|
||||
if ( rc != 0 )
|
||||
{
|
||||
free_vmcb(arch_svm->vmcb);
|
||||
free_vmcb(nv->nv_n1vmcx);
|
||||
nv->nv_n1vmcx = NULL;
|
||||
arch_svm->vmcb = NULL;
|
||||
return rc;
|
||||
}
|
||||
|
||||
arch_svm->vmcb_pa = virt_to_maddr(arch_svm->vmcb);
|
||||
|
||||
arch_svm->vmcb_pa = nv->nv_n1vmcx_pa = virt_to_maddr(arch_svm->vmcb);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void svm_destroy_vmcb(struct vcpu *v)
|
||||
{
|
||||
struct nestedvcpu *nv = &vcpu_nestedhvm(v);
|
||||
struct arch_svm_struct *arch_svm = &v->arch.hvm_svm;
|
||||
|
||||
if ( arch_svm->vmcb != NULL )
|
||||
free_vmcb(arch_svm->vmcb);
|
||||
if ( nv->nv_n1vmcx != NULL )
|
||||
free_vmcb(nv->nv_n1vmcx);
|
||||
|
||||
if ( arch_svm->msrpm != NULL )
|
||||
{
|
||||
@@ -304,81 +275,11 @@ void svm_destroy_vmcb(struct vcpu *v)
|
||||
arch_svm->msrpm = NULL;
|
||||
}
|
||||
|
||||
nv->nv_n1vmcx = NULL;
|
||||
nv->nv_n1vmcx_pa = VMCX_EADDR;
|
||||
arch_svm->vmcb = NULL;
|
||||
}
|
||||
|
||||
static void svm_dump_sel(char *name, svm_segment_register_t *s)
|
||||
{
|
||||
printk("%s: sel=0x%04x, attr=0x%04x, limit=0x%08x, base=0x%016llx\n",
|
||||
name, s->sel, s->attr.bytes, s->limit,
|
||||
(unsigned long long)s->base);
|
||||
}
|
||||
|
||||
/* This function can directly access fields which are covered by clean bits. */
|
||||
void svm_dump_vmcb(const char *from, struct vmcb_struct *vmcb)
|
||||
{
|
||||
printk("Dumping guest's current state at %s...\n", from);
|
||||
printk("Size of VMCB = %d, paddr = 0x%016lx, vaddr = %p\n",
|
||||
(int) sizeof(struct vmcb_struct), virt_to_maddr(vmcb), vmcb);
|
||||
|
||||
printk("cr_intercepts = 0x%08x dr_intercepts = 0x%08x "
|
||||
"exception_intercepts = 0x%08x\n",
|
||||
vmcb->_cr_intercepts, vmcb->_dr_intercepts,
|
||||
vmcb->_exception_intercepts);
|
||||
printk("general1_intercepts = 0x%08x general2_intercepts = 0x%08x\n",
|
||||
vmcb->_general1_intercepts, vmcb->_general2_intercepts);
|
||||
printk("iopm_base_pa = 0x%016llx msrpm_base_pa = 0x%016llx tsc_offset = "
|
||||
"0x%016llx\n",
|
||||
(unsigned long long)vmcb->_iopm_base_pa,
|
||||
(unsigned long long)vmcb->_msrpm_base_pa,
|
||||
(unsigned long long)vmcb->_tsc_offset);
|
||||
printk("tlb_control = 0x%08x vintr = 0x%016llx interrupt_shadow = "
|
||||
"0x%016llx\n", vmcb->tlb_control,
|
||||
(unsigned long long)vmcb->_vintr.bytes,
|
||||
(unsigned long long)vmcb->interrupt_shadow);
|
||||
printk("exitcode = 0x%016llx exitintinfo = 0x%016llx\n",
|
||||
(unsigned long long)vmcb->exitcode,
|
||||
(unsigned long long)vmcb->exitintinfo.bytes);
|
||||
printk("exitinfo1 = 0x%016llx exitinfo2 = 0x%016llx \n",
|
||||
(unsigned long long)vmcb->exitinfo1,
|
||||
(unsigned long long)vmcb->exitinfo2);
|
||||
printk("np_enable = 0x%016llx guest_asid = 0x%03x\n",
|
||||
(unsigned long long)vmcb->_np_enable, vmcb->_guest_asid);
|
||||
printk("cpl = %d efer = 0x%016llx star = 0x%016llx lstar = 0x%016llx\n",
|
||||
vmcb->_cpl, (unsigned long long)vmcb->_efer,
|
||||
(unsigned long long)vmcb->star, (unsigned long long)vmcb->lstar);
|
||||
printk("CR0 = 0x%016llx CR2 = 0x%016llx\n",
|
||||
(unsigned long long)vmcb->_cr0, (unsigned long long)vmcb->_cr2);
|
||||
printk("CR3 = 0x%016llx CR4 = 0x%016llx\n",
|
||||
(unsigned long long)vmcb->_cr3, (unsigned long long)vmcb->_cr4);
|
||||
printk("RSP = 0x%016llx RIP = 0x%016llx\n",
|
||||
(unsigned long long)vmcb->rsp, (unsigned long long)vmcb->rip);
|
||||
printk("RAX = 0x%016llx RFLAGS=0x%016llx\n",
|
||||
(unsigned long long)vmcb->rax, (unsigned long long)vmcb->rflags);
|
||||
printk("DR6 = 0x%016llx, DR7 = 0x%016llx\n",
|
||||
(unsigned long long)vmcb->_dr6, (unsigned long long)vmcb->_dr7);
|
||||
printk("CSTAR = 0x%016llx SFMask = 0x%016llx\n",
|
||||
(unsigned long long)vmcb->cstar,
|
||||
(unsigned long long)vmcb->sfmask);
|
||||
printk("KernGSBase = 0x%016llx PAT = 0x%016llx \n",
|
||||
(unsigned long long)vmcb->kerngsbase,
|
||||
(unsigned long long)vmcb->_g_pat);
|
||||
printk("H_CR3 = 0x%016llx CleanBits = 0x%08x\n",
|
||||
(unsigned long long)vmcb->_h_cr3, vmcb->cleanbits.bytes);
|
||||
|
||||
/* print out all the selectors */
|
||||
svm_dump_sel("CS", &vmcb->cs);
|
||||
svm_dump_sel("DS", &vmcb->ds);
|
||||
svm_dump_sel("SS", &vmcb->ss);
|
||||
svm_dump_sel("ES", &vmcb->es);
|
||||
svm_dump_sel("FS", &vmcb->fs);
|
||||
svm_dump_sel("GS", &vmcb->gs);
|
||||
svm_dump_sel("GDTR", &vmcb->gdtr);
|
||||
svm_dump_sel("LDTR", &vmcb->ldtr);
|
||||
svm_dump_sel("IDTR", &vmcb->idtr);
|
||||
svm_dump_sel("TR", &vmcb->tr);
|
||||
}
|
||||
|
||||
static void vmcb_dump(unsigned char ch)
|
||||
{
|
||||
struct domain *d;
|
||||
@@ -396,7 +297,7 @@ static void vmcb_dump(unsigned char ch)
|
||||
for_each_vcpu ( d, v )
|
||||
{
|
||||
printk("\tVCPU %d\n", v->vcpu_id);
|
||||
svm_dump_vmcb("key_handler", v->arch.hvm_svm.vmcb);
|
||||
svm_vmcb_dump("key_handler", v->arch.hvm_svm.vmcb);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -33,6 +33,11 @@ enum instruction_index {
|
||||
INSTR_RDTSC,
|
||||
INSTR_PAUSE,
|
||||
INSTR_XSETBV,
|
||||
INSTR_VMRUN,
|
||||
INSTR_VMLOAD,
|
||||
INSTR_VMSAVE,
|
||||
INSTR_STGI,
|
||||
INSTR_CLGI,
|
||||
INSTR_MAX_COUNT /* Must be last - Number of instructions supported */
|
||||
};
|
||||
|
||||
|
||||
@@ -0,0 +1,129 @@
|
||||
/*
|
||||
* nestedsvm.h: Nested Virtualization
|
||||
* Copyright (c) 2011, Advanced Micro Devices, Inc
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program; if not, write to the Free Software Foundation, Inc., 59 Temple
|
||||
* Place - Suite 330, Boston, MA 02111-1307 USA.
|
||||
*
|
||||
*/
|
||||
#ifndef __ASM_X86_HVM_SVM_NESTEDSVM_H__
|
||||
#define __ASM_X86_HVM_SVM_NESTEDSVM_H__
|
||||
|
||||
#include <asm/config.h>
|
||||
#include <asm/hvm/hvm.h>
|
||||
#include <asm/hvm/svm/vmcb.h>
|
||||
|
||||
struct nestedsvm {
|
||||
uint64_t ns_msr_hsavepa; /* MSR HSAVE_PA value */
|
||||
|
||||
/* l1 guest physical address of virtual vmcb used by prior VMRUN.
|
||||
* Needed for VMCB Cleanbit emulation.
|
||||
*/
|
||||
uint64_t ns_ovvmcb_pa;
|
||||
|
||||
/* Cached real intercepts of the l2 guest */
|
||||
uint32_t ns_cr_intercepts;
|
||||
uint32_t ns_dr_intercepts;
|
||||
uint32_t ns_exception_intercepts;
|
||||
uint32_t ns_general1_intercepts;
|
||||
uint32_t ns_general2_intercepts;
|
||||
|
||||
/* Cached real lbr of the l2 guest */
|
||||
lbrctrl_t ns_lbr_control;
|
||||
|
||||
/* Cached real MSR permission bitmaps of the l2 guest */
|
||||
unsigned long *ns_cached_msrpm;
|
||||
/* Merged MSR permission bitmap */
|
||||
unsigned long *ns_merged_msrpm;
|
||||
|
||||
/* guest physical address of virtual io permission map */
|
||||
paddr_t ns_iomap_pa, ns_oiomap_pa;
|
||||
/* Shadow io permission map */
|
||||
unsigned long *ns_iomap;
|
||||
|
||||
/* Cache guest cr3/host cr3 the guest sets up for the l2 guest.
|
||||
* Used by Shadow-on-Shadow and Nested-on-Nested.
|
||||
* ns_vmcb_guestcr3: in l2 guest physical address space and points to
|
||||
* the l2 guest page table
|
||||
* ns_vmcb_hostcr3: in l1 guest physical address space and points to
|
||||
* the l1 guest nested page table
|
||||
*/
|
||||
uint64_t ns_vmcb_guestcr3, ns_vmcb_hostcr3;
|
||||
uint32_t ns_guest_asid;
|
||||
|
||||
bool_t ns_hap_enabled;
|
||||
|
||||
/* Only meaningful when vmexit_pending flag is set */
|
||||
struct {
|
||||
uint64_t exitcode; /* native exitcode to inject into l1 guest */
|
||||
uint64_t exitinfo1; /* additional information to the exitcode */
|
||||
uint64_t exitinfo2; /* additional information to the exitcode */
|
||||
} ns_vmexit;
|
||||
union {
|
||||
uint32_t bytes;
|
||||
struct {
|
||||
uint32_t rflagsif: 1;
|
||||
uint32_t vintrmask: 1;
|
||||
uint32_t reserved: 30;
|
||||
} fields;
|
||||
} ns_hostflags;
|
||||
};
|
||||
|
||||
#define vcpu_nestedsvm(v) (vcpu_nestedhvm(v).u.nsvm)
|
||||
|
||||
/* True when l1 guest enabled SVM in EFER */
|
||||
#define hvm_svm_enabled(v) \
|
||||
(!!((v)->arch.hvm_vcpu.guest_efer & EFER_SVME))
|
||||
|
||||
int nestedsvm_vmcb_map(struct vcpu *v, uint64_t vmcbaddr);
|
||||
void nestedsvm_vmexit_defer(struct vcpu *v,
|
||||
uint64_t exitcode, uint64_t exitinfo1, uint64_t exitinfo2);
|
||||
enum nestedhvm_vmexits
|
||||
nestedsvm_vmexit_n2n1(struct vcpu *v, struct cpu_user_regs *regs);
|
||||
enum nestedhvm_vmexits
|
||||
nestedsvm_check_intercepts(struct vcpu *v, struct cpu_user_regs *regs,
|
||||
uint64_t exitcode);
|
||||
|
||||
/* Interface methods */
|
||||
int nsvm_vcpu_destroy(struct vcpu *v);
|
||||
int nsvm_vcpu_initialise(struct vcpu *v);
|
||||
int nsvm_vcpu_reset(struct vcpu *v);
|
||||
int nsvm_vcpu_hostrestore(struct vcpu *v, struct cpu_user_regs *regs);
|
||||
int nsvm_vcpu_vmrun(struct vcpu *v, struct cpu_user_regs *regs);
|
||||
int nsvm_vcpu_vmexit_inject(struct vcpu *v, struct cpu_user_regs *regs,
|
||||
uint64_t exitcode);
|
||||
int nsvm_vcpu_vmexit_trap(struct vcpu *v, unsigned int trapnr,
|
||||
int errcode, unsigned long cr2);
|
||||
uint64_t nsvm_vcpu_guestcr3(struct vcpu *v);
|
||||
uint64_t nsvm_vcpu_hostcr3(struct vcpu *v);
|
||||
uint32_t nsvm_vcpu_asid(struct vcpu *v);
|
||||
int nsvm_vmcb_guest_intercepts_exitcode(struct vcpu *v,
|
||||
struct cpu_user_regs *regs, uint64_t exitcode);
|
||||
int nsvm_vmcb_guest_intercepts_trap(struct vcpu *v, unsigned int trapnr);
|
||||
bool_t nsvm_vmcb_hap_enabled(struct vcpu *v);
|
||||
|
||||
/* MSRs */
|
||||
int nsvm_rdmsr(struct vcpu *v, unsigned int msr, uint64_t *msr_content);
|
||||
int nsvm_wrmsr(struct vcpu *v, unsigned int msr, uint64_t msr_content);
|
||||
|
||||
#endif /* ASM_X86_HVM_SVM_NESTEDSVM_H__ */
|
||||
|
||||
/*
|
||||
* Local variables:
|
||||
* mode: C
|
||||
* c-set-style: "BSD"
|
||||
* c-basic-offset: 4
|
||||
* tab-width: 4
|
||||
* indent-tabs-mode: nil
|
||||
* End:
|
||||
*/
|
||||
@@ -29,8 +29,6 @@
|
||||
#include <asm/i387.h>
|
||||
#include <asm/hvm/vpmu.h>
|
||||
|
||||
void svm_dump_vmcb(const char *from, struct vmcb_struct *vmcb);
|
||||
|
||||
#define SVM_REG_EAX (0)
|
||||
#define SVM_REG_ECX (1)
|
||||
#define SVM_REG_EDX (2)
|
||||
@@ -62,6 +60,8 @@ static inline void svm_vmsave(void *vmcb)
|
||||
: : "a" (__pa(vmcb)) : "memory" );
|
||||
}
|
||||
|
||||
unsigned long *svm_msrbit(unsigned long *msr_bitmap, uint32_t msr);
|
||||
|
||||
extern u32 svm_feature_flags;
|
||||
|
||||
#define SVM_FEATURE_NPT 0 /* Nested page table support */
|
||||
@@ -82,4 +82,6 @@ extern u32 svm_feature_flags;
|
||||
#define cpu_has_svm_cleanbits cpu_has_svm_feature(SVM_FEATURE_VMCBCLEAN)
|
||||
#define cpu_has_pause_filter cpu_has_svm_feature(SVM_FEATURE_PAUSEFILTER)
|
||||
|
||||
#define SVM_PAUSEFILTER_INIT 3000
|
||||
|
||||
#endif /* __ASM_X86_HVM_SVM_H__ */
|
||||
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* svmdebug.h: SVM related debug defintions
|
||||
* Copyright (c) 2011, AMD Corporation.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program; if not, write to the Free Software Foundation, Inc., 59 Temple
|
||||
* Place - Suite 330, Boston, MA 02111-1307 USA.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __ASM_X86_HVM_SVM_SVMDEBUG_H__
|
||||
#define __ASM_X86_HVM_SVM_SVMDEBUG_H__
|
||||
|
||||
#include <asm/types.h>
|
||||
#include <asm/hvm/svm/vmcb.h>
|
||||
|
||||
void svm_vmcb_dump(const char *from, struct vmcb_struct *vmcb);
|
||||
bool_t svm_vmcb_isvalid(const char *from, struct vmcb_struct *vmcb,
|
||||
bool_t verbose);
|
||||
|
||||
#endif /* __ASM_X86_HVM_SVM_SVMDEBUG_H__ */
|
||||
@@ -398,6 +398,9 @@ typedef union
|
||||
} fields;
|
||||
} __attribute__ ((packed)) vmcbcleanbits_t;
|
||||
|
||||
#define IOPM_SIZE (12 * 1024)
|
||||
#define MSRPM_SIZE (8 * 1024)
|
||||
|
||||
struct vmcb_struct {
|
||||
u32 _cr_intercepts; /* offset 0x00 - cleanbit 0 */
|
||||
u32 _dr_intercepts; /* offset 0x04 - cleanbit 0 */
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
#include <asm/hvm/vlapic.h>
|
||||
#include <asm/hvm/vmx/vmcs.h>
|
||||
#include <asm/hvm/svm/vmcb.h>
|
||||
#include <asm/hvm/svm/nestedsvm.h>
|
||||
#include <asm/mtrr.h>
|
||||
|
||||
enum hvm_io_state {
|
||||
@@ -50,6 +51,7 @@ struct nestedvcpu {
|
||||
|
||||
/* SVM/VMX arch specific */
|
||||
union {
|
||||
struct nestedsvm nsvm;
|
||||
} u;
|
||||
|
||||
bool_t nv_flushp2m; /* True, when p2m table must be flushed */
|
||||
|
||||