diff options
author | Neel Natu <neel@FreeBSD.org> | 2014-05-23 19:59:14 +0000 |
---|---|---|
committer | Neel Natu <neel@FreeBSD.org> | 2014-05-23 19:59:14 +0000 |
commit | a7424861fb6b4ee710fde4c6e0505dc3eb33ed1c (patch) | |
tree | 83967173eb01a2c4de511c4eb71440661a9ed8f1 | |
parent | fa2f411c4e8d8223ebab42f1ecf8f7ad8f155f75 (diff) | |
download | src-a7424861fb6b4ee710fde4c6e0505dc3eb33ed1c.tar.gz src-a7424861fb6b4ee710fde4c6e0505dc3eb33ed1c.zip |
Check for alignment check violation when processing in/out string instructions.
Notes
Notes:
svn path=/head/; revision=266595
-rw-r--r-- | sys/amd64/include/vmm_instruction_emul.h | 10 | ||||
-rw-r--r-- | sys/amd64/vmm/vmm_instruction_emul.c | 34 | ||||
-rw-r--r-- | sys/amd64/vmm/vmm_ioport.c | 15 | ||||
-rw-r--r-- | usr.sbin/bhyve/inout.c | 25 |
4 files changed, 49 insertions, 35 deletions
diff --git a/sys/amd64/include/vmm_instruction_emul.h b/sys/amd64/include/vmm_instruction_emul.h index d2cfdef06a2a..797cb399fbbd 100644 --- a/sys/amd64/include/vmm_instruction_emul.h +++ b/sys/amd64/include/vmm_instruction_emul.h @@ -116,6 +116,14 @@ int vmm_emulate_instruction(void *vm, int cpuid, uint64_t gpa, struct vie *vie, int vie_update_register(void *vm, int vcpuid, enum vm_reg_name reg, uint64_t val, int size); +/* + * Returns 1 if an alignment check exception should be injected and 0 otherwise. + */ +int vie_alignment_check(int cpl, int operand_size, uint64_t cr0, + uint64_t rflags, uint64_t gla); + +uint64_t vie_size2mask(int size); + #ifdef _KERNEL /* * APIs to fetch and decode the instruction from nested page fault handler. @@ -139,8 +147,6 @@ int vmm_gla2gpa(struct vm *vm, int vcpuid, uint64_t gla, uint64_t cr3, void vie_init(struct vie *vie); -uint64_t vie_size2mask(int size); - uint64_t vie_segbase(enum vm_reg_name segment, enum vie_cpu_mode cpu_mode, const struct seg_desc *desc); diff --git a/sys/amd64/vmm/vmm_instruction_emul.c b/sys/amd64/vmm/vmm_instruction_emul.c index 85343694a28d..3cf4c6b9c1f8 100644 --- a/sys/amd64/vmm/vmm_instruction_emul.c +++ b/sys/amd64/vmm/vmm_instruction_emul.c @@ -47,9 +47,14 @@ __FBSDID("$FreeBSD$"); #include <machine/vmm.h> +#include <assert.h> #include <vmmapi.h> +#define KASSERT(exp,msg) assert((exp)) #endif /* _KERNEL */ +#include <x86/psl.h> +#include <x86/specialreg.h> + /* struct vie_op.op_type */ enum { VIE_OP_TYPE_NONE = 0, @@ -561,6 +566,27 @@ vmm_emulate_instruction(void *vm, int vcpuid, uint64_t gpa, struct vie *vie, return (error); } +int +vie_alignment_check(int cpl, int size, uint64_t cr0, uint64_t rf, uint64_t gla) +{ + KASSERT(size == 1 || size == 2 || size == 4 || size == 8, + ("%s: invalid size %d", __func__, size)); + KASSERT(cpl >= 0 && cpl <= 3, ("%s: invalid cpl %d", __func__, cpl)); + + if (cpl != 3 || (cr0 & CR0_AM) == 0 || (rf & PSL_AC) == 0) + return (0); + + return ((gla & (size - 1)) ? 1 : 0); +} + +uint64_t +vie_size2mask(int size) +{ + KASSERT(size == 1 || size == 2 || size == 4 || size == 8, + ("vie_size2mask: invalid size %d", size)); + return (size2mask[size]); +} + #ifdef _KERNEL void vie_init(struct vie *vie) @@ -1220,14 +1246,6 @@ vmm_decode_instruction(struct vm *vm, int cpuid, uint64_t gla, } uint64_t -vie_size2mask(int size) -{ - KASSERT(size == 1 || size == 2 || size == 4 || size == 8, - ("vie_size2mask: invalid size %d", size)); - return (size2mask[size]); -} - -uint64_t vie_segbase(enum vm_reg_name seg, enum vie_cpu_mode cpu_mode, const struct seg_desc *desc) { diff --git a/sys/amd64/vmm/vmm_ioport.c b/sys/amd64/vmm/vmm_ioport.c index 20f249803164..e28d5101a567 100644 --- a/sys/amd64/vmm/vmm_ioport.c +++ b/sys/amd64/vmm/vmm_ioport.c @@ -144,7 +144,7 @@ emulate_inout_str(struct vm *vm, int vcpuid, struct vm_exit *vmexit, bool *retu) { struct vm_inout_str *vis; uint64_t gla, index, segbase; - int bytes, error, in; + int error, in; vis = &vmexit->u.inout_str; in = vis->inout.in; @@ -162,14 +162,10 @@ emulate_inout_str(struct vm *vm, int vcpuid, struct vm_exit *vmexit, bool *retu) /* * XXX - * inout string emulation only supported in 64-bit mode and only - * for byte instructions. + * inout string emulation only supported in 64-bit mode. * * The #GP(0) fault conditions described above don't apply in * 64-bit mode. - * - * The #AC(0) fault condition described above does not apply - * because byte accesses don't have alignment constraints. */ if (vis->cpu_mode != CPU_MODE_64BIT) { VCPU_CTR1(vm, vcpuid, "ins/outs not emulated in cpu mode %d", @@ -177,13 +173,6 @@ emulate_inout_str(struct vm *vm, int vcpuid, struct vm_exit *vmexit, bool *retu) return (EINVAL); } - bytes = vis->inout.bytes; - if (bytes != 1) { - VCPU_CTR1(vm, vcpuid, "ins/outs operand size %d not supported", - bytes); - return (EINVAL); - } - /* * XXX insb/insw/insd instructions not emulated at this time. */ diff --git a/usr.sbin/bhyve/inout.c b/usr.sbin/bhyve/inout.c index c2bb50a7dd07..0bbcde1bb786 100644 --- a/usr.sbin/bhyve/inout.c +++ b/usr.sbin/bhyve/inout.c @@ -33,6 +33,7 @@ __FBSDID("$FreeBSD$"); #include <sys/linker_set.h> #include <x86/psl.h> +#include <x86/segments.h> #include <machine/vmm.h> #include <vmmapi.h> @@ -110,13 +111,6 @@ emulate_inout(struct vmctx *ctx, int vcpu, struct vm_exit *vmexit, int strict) uint64_t index, count; struct vm_inout_str *vis; - static uint64_t size2mask[] = { - [1] = 0xff, - [2] = 0xffff, - [4] = 0xffffffff, - [8] = 0xffffffffffffffff, - }; - bytes = vmexit->u.inout.bytes; in = vmexit->u.inout.in; port = vmexit->u.inout.port; @@ -149,15 +143,22 @@ emulate_inout(struct vmctx *ctx, int vcpu, struct vm_exit *vmexit, int strict) /* Index register */ idxreg = in ? VM_REG_GUEST_RDI : VM_REG_GUEST_RSI; - index = vis->index & size2mask[addrsize]; + index = vis->index & vie_size2mask(addrsize); /* Count register */ - count = vis->count & size2mask[addrsize]; + count = vis->count & vie_size2mask(addrsize); gpa = vis->gpa; gpaend = rounddown(gpa + PAGE_SIZE, PAGE_SIZE); gva = paddr_guest2host(ctx, gpa, gpaend - gpa); + if (vie_alignment_check(vis->cpl, bytes, vis->cr0, + vis->rflags, vis->gla)) { + error = vm_inject_exception2(ctx, vcpu, IDT_AC, 0); + assert(error == 0); + return (INOUT_RESTART); + } + while (count != 0 && gpa < gpaend) { /* * XXX this may not work for unaligned accesses because @@ -209,12 +210,12 @@ emulate_inout(struct vmctx *ctx, int vcpu, struct vm_exit *vmexit, int strict) retval = INOUT_RESTART; } else { if (!in) { - val = vmexit->u.inout.eax & size2mask[bytes]; + val = vmexit->u.inout.eax & vie_size2mask(bytes); } retval = handler(ctx, vcpu, in, port, bytes, &val, arg); if (retval == 0 && in) { - vmexit->u.inout.eax &= ~size2mask[bytes]; - vmexit->u.inout.eax |= val & size2mask[bytes]; + vmexit->u.inout.eax &= ~vie_size2mask(bytes); + vmexit->u.inout.eax |= val & vie_size2mask(bytes); } } return (retval); |