aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNeel Natu <neel@FreeBSD.org>2014-05-23 19:59:14 +0000
committerNeel Natu <neel@FreeBSD.org>2014-05-23 19:59:14 +0000
commita7424861fb6b4ee710fde4c6e0505dc3eb33ed1c (patch)
tree83967173eb01a2c4de511c4eb71440661a9ed8f1
parentfa2f411c4e8d8223ebab42f1ecf8f7ad8f155f75 (diff)
downloadsrc-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.h10
-rw-r--r--sys/amd64/vmm/vmm_instruction_emul.c34
-rw-r--r--sys/amd64/vmm/vmm_ioport.c15
-rw-r--r--usr.sbin/bhyve/inout.c25
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);