diff options
Diffstat (limited to 'sys/amd64/vmm/vmm_lapic.c')
-rw-r--r-- | sys/amd64/vmm/vmm_lapic.c | 71 |
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); +} |