summaryrefslogtreecommitdiffstats
path: root/sys/amd64/vmm/vmm_lapic.c
diff options
context:
space:
mode:
authorneel <neel@FreeBSD.org>2012-09-25 22:31:35 +0000
committerneel <neel@FreeBSD.org>2012-09-25 22:31:35 +0000
commitbc269b51afe43aab28df7ea0d543c167bb7c7d2e (patch)
treebcd31b7f03fe25c622c8a7edf72d8163a4023b8c /sys/amd64/vmm/vmm_lapic.c
parentebdd69568d7fa97153aa47a86afe367476a0a1de (diff)
downloadFreeBSD-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/vmm_lapic.c')
-rw-r--r--sys/amd64/vmm/vmm_lapic.c71
1 files changed, 71 insertions, 0 deletions
diff --git a/sys/amd64/vmm/vmm_lapic.c b/sys/amd64/vmm/vmm_lapic.c
index 13550b4..0d797e6 100644
--- a/sys/amd64/vmm/vmm_lapic.c
+++ b/sys/amd64/vmm/vmm_lapic.c
@@ -39,6 +39,7 @@ __FBSDID("$FreeBSD$");
#include "vmm_ipi.h"
#include "vmm_lapic.h"
#include "vlapic.h"
+#include "vmm_instruction_emul.h"
static int
lapic_write(struct vlapic *vlapic, u_int offset, uint64_t val)
@@ -174,3 +175,73 @@ lapic_wrmsr(struct vm *vm, int cpu, u_int msr, uint64_t val)
return (handled);
}
+
+int
+lapic_mmio(struct vm *vm, int cpu, u_int offset, int read,
+ uint64_t rip, uint64_t cr3)
+{
+ int handled, error;
+ uint64_t val;
+ struct vie vie;
+ struct vlapic *vlapic;
+
+ const int UNHANDLED = 0;
+
+ vlapic = vm_lapic(vm, cpu);
+
+ vmm_fetch_instruction(vm, rip, cr3, &vie);
+
+ if (vmm_decode_instruction(&vie) != 0)
+ return (UNHANDLED);
+
+ /* Only 32-bit accesses to local apic */
+ if (vie.op_size != VIE_OP_SIZE_32BIT)
+ return (UNHANDLED);
+
+ /*
+ * XXX
+ * The operand register in which we store the result of the
+ * read must be a GPR that we can modify even if the vcpu
+ * is "running". All the GPRs qualify except for %rsp.
+ *
+ * This is a limitation of the vm_set_register() API
+ * and can be fixed if necessary.
+ */
+ if (vie.operand_register == VM_REG_GUEST_RSP)
+ return (UNHANDLED);
+
+ if (read) {
+ if ((vie.opcode_flags & VIE_F_TO_REG) == 0)
+ return (UNHANDLED);
+
+ if (vie.operand_register >= VM_REG_LAST)
+ return (UNHANDLED);
+
+ handled = lapic_read(vlapic, offset, &val);
+ if (handled) {
+ error = vm_set_register(vm, cpu, vie.operand_register,
+ val);
+ if (error)
+ panic("lapic_mmio: error %d setting gpr %d",
+ error, vie.operand_register);
+ }
+ } else {
+ if ((vie.opcode_flags & VIE_F_FROM_REG) &&
+ (vie.operand_register < VM_REG_LAST)) {
+ error = vm_get_register(vm, cpu, vie.operand_register,
+ &val);
+ if (error) {
+ panic("lapic_mmio: error %d getting gpr %d",
+ error, vie.operand_register);
+ }
+ } else if (vie.opcode_flags & VIE_F_FROM_IMM) {
+ val = vie.immediate;
+ } else {
+ return (UNHANDLED);
+ }
+
+ handled = lapic_write(vlapic, offset, val);
+ }
+
+ return (handled);
+}
OpenPOWER on IntegriCloud