summaryrefslogtreecommitdiffstats
path: root/sys/amd64
diff options
context:
space:
mode:
authorneel <neel@FreeBSD.org>2012-09-27 00:27:58 +0000
committerneel <neel@FreeBSD.org>2012-09-27 00:27:58 +0000
commitb65259b285734eec4d40fe639b4e84a6f4bf9f02 (patch)
tree011ad438ff80f1edd6963871c40761b08d136f4f /sys/amd64
parent5dbc1ca26acaa3175dae7b9d0c45151fba0275ab (diff)
downloadFreeBSD-src-b65259b285734eec4d40fe639b4e84a6f4bf9f02.zip
FreeBSD-src-b65259b285734eec4d40fe639b4e84a6f4bf9f02.tar.gz
Intel VT-x provides the length of the instruction at the time of the nested
page table fault. Use this when fetching the instruction bytes from the guest memory. Also modify the lapic_mmio() API so that a decoded instruction is fed into it instead of having it fetch the instruction bytes from the guest. This is useful for hardware assists like SVM that provide the faulting instruction as part of the vmexit.
Diffstat (limited to 'sys/amd64')
-rw-r--r--sys/amd64/vmm/intel/vmx.c16
-rw-r--r--sys/amd64/vmm/vmm_instruction_emul.c22
-rw-r--r--sys/amd64/vmm/vmm_instruction_emul.h4
-rw-r--r--sys/amd64/vmm/vmm_lapic.c33
-rw-r--r--sys/amd64/vmm/vmm_lapic.h4
5 files changed, 44 insertions, 35 deletions
diff --git a/sys/amd64/vmm/intel/vmx.c b/sys/amd64/vmm/intel/vmx.c
index ed0996e..a2c8e76 100644
--- a/sys/amd64/vmm/intel/vmx.c
+++ b/sys/amd64/vmm/intel/vmx.c
@@ -1146,9 +1146,11 @@ 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)
+ uint64_t gpa, uint64_t rip, int inst_length,
+ uint64_t cr3, uint64_t ept_qual)
{
int read, write, handled;
+ struct vie vie;
/*
* For this to be a legitimate access to the local apic:
@@ -1180,7 +1182,14 @@ vmx_lapic_fault(struct vm *vm, int cpu,
return (UNHANDLED);
}
- handled = lapic_mmio(vm, cpu, gpa - DEFAULT_APIC_BASE, read, rip, cr3);
+ /* Fetch, decode and emulate the faulting instruction */
+ if (vmm_fetch_instruction(vm, rip, inst_length, cr3, &vie) != 0)
+ return (UNHANDLED);
+
+ if (vmm_decode_instruction(&vie) != 0)
+ return (UNHANDLED);
+
+ handled = lapic_mmio(vm, cpu, gpa - DEFAULT_APIC_BASE, read, &vie);
return (handled);
}
@@ -1275,7 +1284,8 @@ vmx_exit_process(struct vmx *vmx, int vcpu, struct vm_exit *vmexit)
gpa = vmcs_gpa();
cr3 = vmcs_guest_cr3();
handled = vmx_lapic_fault(vmx->vm, vcpu,
- gpa, vmexit->rip, cr3, qual);
+ gpa, vmexit->rip, vmexit->inst_length,
+ cr3, qual);
if (!handled) {
vmexit->exitcode = VM_EXITCODE_PAGING;
vmexit->u.paging.cr3 = cr3;
diff --git a/sys/amd64/vmm/vmm_instruction_emul.c b/sys/amd64/vmm/vmm_instruction_emul.c
index fe01d69..66af72c 100644
--- a/sys/amd64/vmm/vmm_instruction_emul.c
+++ b/sys/amd64/vmm/vmm_instruction_emul.c
@@ -128,9 +128,9 @@ error:
return (-1);
}
-void
-vmm_fetch_instruction(struct vm *vm, uint64_t rip, uint64_t cr3,
- struct vie *vie)
+int
+vmm_fetch_instruction(struct vm *vm, uint64_t rip, int inst_length,
+ uint64_t cr3, struct vie *vie)
{
int n, err;
uint64_t hpa, gpa, gpaend;
@@ -139,17 +139,18 @@ vmm_fetch_instruction(struct vm *vm, uint64_t rip, uint64_t cr3,
* XXX cache previously fetched instructions using 'rip' as the tag
*/
+ if (inst_length > VIE_INST_SIZE)
+ panic("vmm_fetch_instruction: invalid length %d", inst_length);
+
vie_init(vie);
- /*
- * Copy up to 15 bytes of the instruction stream into 'vie'
- */
- while (vie->num_valid < VIE_INST_SIZE) {
+ /* Copy the instruction into 'vie' */
+ while (vie->num_valid < inst_length) {
err = gla2gpa(vm, rip, cr3, &gpa, &gpaend);
if (err)
break;
- n = min(VIE_INST_SIZE - vie->num_valid, gpaend - gpa);
+ n = min(inst_length - vie->num_valid, gpaend - gpa);
hpa = vm_gpa2hpa(vm, gpa, n);
if (hpa == -1)
@@ -160,6 +161,11 @@ vmm_fetch_instruction(struct vm *vm, uint64_t rip, uint64_t cr3,
rip += n;
vie->num_valid += n;
}
+
+ if (vie->num_valid == inst_length)
+ return (0);
+ else
+ return (-1);
}
static int
diff --git a/sys/amd64/vmm/vmm_instruction_emul.h b/sys/amd64/vmm/vmm_instruction_emul.h
index 94937f2..1fa9e2b 100644
--- a/sys/amd64/vmm/vmm_instruction_emul.h
+++ b/sys/amd64/vmm/vmm_instruction_emul.h
@@ -83,8 +83,8 @@ struct vie {
struct vm;
-void vmm_fetch_instruction(struct vm *vm, uint64_t rip, uint64_t cr3,
- struct vie *vie);
+int vmm_fetch_instruction(struct vm *vm, uint64_t rip, int inst_length,
+ uint64_t cr3, struct vie *vie);
int vmm_decode_instruction(struct vie *vie);
diff --git a/sys/amd64/vmm/vmm_lapic.c b/sys/amd64/vmm/vmm_lapic.c
index 0d797e6..ace6010 100644
--- a/sys/amd64/vmm/vmm_lapic.c
+++ b/sys/amd64/vmm/vmm_lapic.c
@@ -177,25 +177,18 @@ lapic_wrmsr(struct vm *vm, int cpu, u_int msr, uint64_t val)
}
int
-lapic_mmio(struct vm *vm, int cpu, u_int offset, int read,
- uint64_t rip, uint64_t cr3)
+lapic_mmio(struct vm *vm, int cpu, u_int offset, int read, struct vie *vie)
{
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)
+ if (vie->op_size != VIE_OP_SIZE_32BIT)
return (UNHANDLED);
/*
@@ -207,35 +200,35 @@ lapic_mmio(struct vm *vm, int cpu, u_int offset, int read,
* This is a limitation of the vm_set_register() API
* and can be fixed if necessary.
*/
- if (vie.operand_register == VM_REG_GUEST_RSP)
+ if (vie->operand_register == VM_REG_GUEST_RSP)
return (UNHANDLED);
if (read) {
- if ((vie.opcode_flags & VIE_F_TO_REG) == 0)
+ if ((vie->opcode_flags & VIE_F_TO_REG) == 0)
return (UNHANDLED);
- if (vie.operand_register >= VM_REG_LAST)
+ 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,
+ error = vm_set_register(vm, cpu, vie->operand_register,
val);
if (error)
panic("lapic_mmio: error %d setting gpr %d",
- error, vie.operand_register);
+ 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,
+ 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);
+ error, vie->operand_register);
}
- } else if (vie.opcode_flags & VIE_F_FROM_IMM) {
- val = vie.immediate;
+ } else if (vie->opcode_flags & VIE_F_FROM_IMM) {
+ val = vie->immediate;
} else {
return (UNHANDLED);
}
diff --git a/sys/amd64/vmm/vmm_lapic.h b/sys/amd64/vmm/vmm_lapic.h
index 7bba4e3..e9ff8fd 100644
--- a/sys/amd64/vmm/vmm_lapic.h
+++ b/sys/amd64/vmm/vmm_lapic.h
@@ -30,13 +30,13 @@
#define _VMM_LAPIC_H_
struct vm;
+struct vie;
boolean_t lapic_msr(u_int num);
int lapic_rdmsr(struct vm *vm, int cpu, u_int msr, uint64_t *rval);
int lapic_wrmsr(struct vm *vm, int cpu, u_int msr, uint64_t wval);
-int lapic_mmio(struct vm *vm, int cpu, u_int offset, int read,
- uint64_t rip, uint64_t cr3);
+int lapic_mmio(struct vm *vm, int cpu, u_int offset, int rd, struct vie *);
void lapic_timer_tick(struct vm *vm, int cpu);
OpenPOWER on IntegriCloud