diff options
author | neel <neel@FreeBSD.org> | 2012-09-25 22:31:35 +0000 |
---|---|---|
committer | neel <neel@FreeBSD.org> | 2012-09-25 22:31:35 +0000 |
commit | bc269b51afe43aab28df7ea0d543c167bb7c7d2e (patch) | |
tree | bcd31b7f03fe25c622c8a7edf72d8163a4023b8c /sys/amd64/vmm/intel | |
parent | ebdd69568d7fa97153aa47a86afe367476a0a1de (diff) | |
download | FreeBSD-src-bc269b51afe43aab28df7ea0d543c167bb7c7d2e.zip FreeBSD-src-bc269b51afe43aab28df7ea0d543c167bb7c7d2e.tar.gz |
Add support for trapping MMIO writes to local apic registers and emulating them.
The default behavior is still to present the local apic to the guest in the
x2apic mode.
Diffstat (limited to 'sys/amd64/vmm/intel')
-rw-r--r-- | sys/amd64/vmm/intel/vmcs.h | 10 | ||||
-rw-r--r-- | sys/amd64/vmm/intel/vmx.c | 74 |
2 files changed, 70 insertions, 14 deletions
diff --git a/sys/amd64/vmm/intel/vmcs.h b/sys/amd64/vmm/intel/vmcs.h index a7cf4f6..84532f4 100644 --- a/sys/amd64/vmm/intel/vmcs.h +++ b/sys/amd64/vmm/intel/vmcs.h @@ -66,6 +66,7 @@ uint64_t vmcs_read(uint32_t encoding); #define vmcs_exit_reason() (vmcs_read(VMCS_EXIT_REASON) & 0xffff) #define vmcs_exit_qualification() vmcs_read(VMCS_EXIT_QUALIFICATION) #define vmcs_guest_cr3() vmcs_read(VMCS_GUEST_CR3) +#define vmcs_gpa() vmcs_read(VMCS_GUEST_PHYSICAL_ADDRESS) #endif /* _KERNEL */ @@ -324,4 +325,13 @@ uint64_t vmcs_read(uint32_t encoding); */ #define EXIT_QUAL_NMI_WHILE_STI_BLOCKING 3 +/* + * Exit qualification for EPT violation + */ +#define EPT_VIOLATION_DATA_READ (1UL << 0) +#define EPT_VIOLATION_DATA_WRITE (1UL << 1) +#define EPT_VIOLATION_INST_FETCH (1UL << 2) +#define EPT_VIOLATION_GLA_VALID (1UL << 7) +#define EPT_VIOLATION_XLAT_VALID (1UL << 8) + #endif diff --git a/sys/amd64/vmm/intel/vmx.c b/sys/amd64/vmm/intel/vmx.c index 6689013..ed0996e 100644 --- a/sys/amd64/vmm/intel/vmx.c +++ b/sys/amd64/vmm/intel/vmx.c @@ -48,6 +48,8 @@ __FBSDID("$FreeBSD$"); #include <machine/specialreg.h> #include <machine/vmparam.h> +#include <x86/apicreg.h> + #include <machine/vmm.h> #include "vmm_lapic.h" #include "vmm_msr.h" @@ -60,6 +62,7 @@ __FBSDID("$FreeBSD$"); #include "vmx.h" #include "x86.h" #include "vmx_controls.h" +#include "vmm_instruction_emul.h" #define CR4_VMXE (1UL << 13) @@ -771,21 +774,17 @@ vmx_vminit(struct vm *vm) } static int -vmx_handle_cpuid(int vcpu, struct vmxctx *vmxctx) +vmx_handle_cpuid(struct vm *vm, int vcpu, struct vmxctx *vmxctx) { int handled, func; func = vmxctx->guest_rax; - handled = x86_emulate_cpuid((uint32_t*)(&vmxctx->guest_rax), - (uint32_t*)(&vmxctx->guest_rbx), (uint32_t*)(&vmxctx->guest_rcx), - (uint32_t*)(&vmxctx->guest_rdx), vcpu); -#if 0 - printf("%s: func %x rax %lx rbx %lx rcx %lx rdx %lx handled %d\n", - __func__, func, vmxctx->guest_rax, vmxctx->guest_rbx, - vmxctx->guest_rcx, vmxctx->guest_rdx, handled); -#endif - + handled = x86_emulate_cpuid(vm, vcpu, + (uint32_t*)(&vmxctx->guest_rax), + (uint32_t*)(&vmxctx->guest_rbx), + (uint32_t*)(&vmxctx->guest_rcx), + (uint32_t*)(&vmxctx->guest_rdx)); return (handled); } @@ -1146,13 +1145,54 @@ vmx_emulate_cr_access(struct vmx *vmx, int vcpu, uint64_t exitqual) } static int +vmx_lapic_fault(struct vm *vm, int cpu, + uint64_t gpa, uint64_t rip, uint64_t cr3, uint64_t ept_qual) +{ + int read, write, handled; + + /* + * For this to be a legitimate access to the local apic: + * - the GPA in the local apic page + * - the GPA must be aligned on a 16 byte boundary + */ + if (gpa < DEFAULT_APIC_BASE || gpa >= DEFAULT_APIC_BASE + PAGE_SIZE) + return (UNHANDLED); + + if ((gpa & 0xF) != 0) + return (UNHANDLED); + + /* EPT violation on an instruction fetch doesn't make sense here */ + if (ept_qual & EPT_VIOLATION_INST_FETCH) + return (UNHANDLED); + + /* EPT violation must be a read fault or a write fault but not both */ + read = ept_qual & EPT_VIOLATION_DATA_READ ? 1 : 0; + write = ept_qual & EPT_VIOLATION_DATA_WRITE ? 1 : 0; + if ((read ^ write) == 0) + return (UNHANDLED); + + /* + * The EPT violation must have been caused by accessing a guest-physical + * address that is a translation of a guest-linear address. + */ + if ((ept_qual & EPT_VIOLATION_GLA_VALID) == 0 || + (ept_qual & EPT_VIOLATION_XLAT_VALID) == 0) { + return (UNHANDLED); + } + + handled = lapic_mmio(vm, cpu, gpa - DEFAULT_APIC_BASE, read, rip, cr3); + + return (handled); +} + +static int vmx_exit_process(struct vmx *vmx, int vcpu, struct vm_exit *vmexit) { int handled; struct vmcs *vmcs; struct vmxctx *vmxctx; uint32_t eax, ecx, edx; - uint64_t qual; + uint64_t qual, gpa, cr3; handled = 0; vmcs = &vmx->vmcs[vcpu]; @@ -1229,11 +1269,17 @@ vmx_exit_process(struct vmx *vmx, int vcpu, struct vm_exit *vmexit) vmexit->u.inout.eax = (uint32_t)(vmxctx->guest_rax); break; case EXIT_REASON_CPUID: - handled = vmx_handle_cpuid(vcpu, vmxctx); + handled = vmx_handle_cpuid(vmx->vm, vcpu, vmxctx); break; case EXIT_REASON_EPT_FAULT: - vmexit->exitcode = VM_EXITCODE_PAGING; - vmexit->u.paging.cr3 = vmcs_guest_cr3(); + gpa = vmcs_gpa(); + cr3 = vmcs_guest_cr3(); + handled = vmx_lapic_fault(vmx->vm, vcpu, + gpa, vmexit->rip, cr3, qual); + if (!handled) { + vmexit->exitcode = VM_EXITCODE_PAGING; + vmexit->u.paging.cr3 = cr3; + } break; default: break; |