aboutsummaryrefslogtreecommitdiff
path: root/sys/amd64
diff options
context:
space:
mode:
authorNeel Natu <neel@FreeBSD.org>2014-08-30 19:59:42 +0000
committerNeel Natu <neel@FreeBSD.org>2014-08-30 19:59:42 +0000
commit4c98655ece7a1943dac4838dc24eaf6b8d87d05b (patch)
treeb369a50dd9e95c3940b74da17884c90e96dd42a8 /sys/amd64
parent1bffa9511fe94125820858b0ead796db54ac019d (diff)
downloadsrc-4c98655ece7a1943dac4838dc24eaf6b8d87d05b.tar.gz
src-4c98655ece7a1943dac4838dc24eaf6b8d87d05b.zip
The "SUB" instruction used in getcc() actually does 'x -= y' so use the
proper constraint for 'x'. The "+r" constraint indicates that 'x' is an input and output register operand. While here generate code for different variants of getcc() using a macro GETCC(sz) where 'sz' indicates the operand size. Update the status bits in %rflags when emulating AND and OR opcodes. Reviewed by: grehan
Notes
Notes: svn path=/head/; revision=270857
Diffstat (limited to 'sys/amd64')
-rw-r--r--sys/amd64/vmm/vmm_instruction_emul.c108
1 files changed, 66 insertions, 42 deletions
diff --git a/sys/amd64/vmm/vmm_instruction_emul.c b/sys/amd64/vmm/vmm_instruction_emul.c
index 09453a2a6255..0d48895b348a 100644
--- a/sys/amd64/vmm/vmm_instruction_emul.c
+++ b/sys/amd64/vmm/vmm_instruction_emul.c
@@ -316,46 +316,36 @@ vie_update_register(void *vm, int vcpuid, enum vm_reg_name reg,
return (error);
}
+#define RFLAGS_STATUS_BITS (PSL_C | PSL_PF | PSL_AF | PSL_Z | PSL_N | PSL_V)
+
/*
* Return the status flags that would result from doing (x - y).
*/
-static u_long
-getcc16(uint16_t x, uint16_t y)
-{
- u_long rflags;
-
- __asm __volatile("sub %1,%2; pushfq; popq %0" :
- "=r" (rflags) : "m" (y), "r" (x));
- return (rflags);
-}
-
-static u_long
-getcc32(uint32_t x, uint32_t y)
-{
- u_long rflags;
-
- __asm __volatile("sub %1,%2; pushfq; popq %0" :
- "=r" (rflags) : "m" (y), "r" (x));
- return (rflags);
-}
-
-static u_long
-getcc64(uint64_t x, uint64_t y)
-{
- u_long rflags;
-
- __asm __volatile("sub %1,%2; pushfq; popq %0" :
- "=r" (rflags) : "m" (y), "r" (x));
- return (rflags);
-}
+#define GETCC(sz) \
+static u_long \
+getcc##sz(uint##sz##_t x, uint##sz##_t y) \
+{ \
+ u_long rflags; \
+ \
+ __asm __volatile("sub %2,%1; pushfq; popq %0" : \
+ "=r" (rflags), "+r" (x) : "m" (y)); \
+ return (rflags); \
+} struct __hack
+
+GETCC(8);
+GETCC(16);
+GETCC(32);
+GETCC(64);
static u_long
getcc(int opsize, uint64_t x, uint64_t y)
{
- KASSERT(opsize == 2 || opsize == 4 || opsize == 8,
+ KASSERT(opsize == 1 || opsize == 2 || opsize == 4 || opsize == 8,
("getcc: invalid operand size %d", opsize));
- if (opsize == 2)
+ if (opsize == 1)
+ return (getcc8(x, y));
+ else if (opsize == 2)
return (getcc16(x, y));
else if (opsize == 4)
return (getcc32(x, y));
@@ -569,7 +559,7 @@ emulate_and(void *vm, int vcpuid, uint64_t gpa, struct vie *vie,
{
int error, size;
enum vm_reg_name reg;
- uint64_t val1, val2;
+ uint64_t result, rflags, rflags2, val1, val2;
size = vie->opsize;
error = EINVAL;
@@ -597,8 +587,8 @@ emulate_and(void *vm, int vcpuid, uint64_t gpa, struct vie *vie,
break;
/* perform the operation and write the result */
- val1 &= val2;
- error = vie_update_register(vm, vcpuid, reg, val1, size);
+ result = val1 & val2;
+ error = vie_update_register(vm, vcpuid, reg, result, size);
break;
case 0x81:
/*
@@ -625,11 +615,11 @@ emulate_and(void *vm, int vcpuid, uint64_t gpa, struct vie *vie,
switch (vie->reg & 7) {
case 0x4:
/* modrm:reg == b100, AND */
- val1 &= vie->immediate;
+ result = val1 & vie->immediate;
break;
case 0x1:
/* modrm:reg == b001, OR */
- val1 |= vie->immediate;
+ result = val1 | vie->immediate;
break;
default:
error = EINVAL;
@@ -638,11 +628,29 @@ emulate_and(void *vm, int vcpuid, uint64_t gpa, struct vie *vie,
if (error)
break;
- error = memwrite(vm, vcpuid, gpa, val1, size, arg);
+ error = memwrite(vm, vcpuid, gpa, result, size, arg);
break;
default:
break;
}
+ if (error)
+ return (error);
+
+ error = vie_read_register(vm, vcpuid, VM_REG_GUEST_RFLAGS, &rflags);
+ if (error)
+ return (error);
+
+ /*
+ * OF and CF are cleared; the SF, ZF and PF flags are set according
+ * to the result; AF is undefined.
+ *
+ * The updated status flags are obtained by subtracting 0 from 'result'.
+ */
+ rflags2 = getcc(size, result, 0);
+ rflags &= ~RFLAGS_STATUS_BITS;
+ rflags |= rflags2 & (PSL_PF | PSL_Z | PSL_N);
+
+ error = vie_update_register(vm, vcpuid, VM_REG_GUEST_RFLAGS, rflags, 8);
return (error);
}
@@ -651,7 +659,7 @@ emulate_or(void *vm, int vcpuid, uint64_t gpa, struct vie *vie,
mem_region_read_t memread, mem_region_write_t memwrite, void *arg)
{
int error, size;
- uint64_t val1;
+ uint64_t val1, result, rflags, rflags2;
size = vie->opsize;
error = EINVAL;
@@ -681,17 +689,33 @@ emulate_or(void *vm, int vcpuid, uint64_t gpa, struct vie *vie,
* perform the operation with the pre-fetched immediate
* operand and write the result
*/
- val1 |= vie->immediate;
- error = memwrite(vm, vcpuid, gpa, val1, size, arg);
+ result = val1 | vie->immediate;
+ error = memwrite(vm, vcpuid, gpa, result, size, arg);
break;
default:
break;
}
+ if (error)
+ return (error);
+
+ error = vie_read_register(vm, vcpuid, VM_REG_GUEST_RFLAGS, &rflags);
+ if (error)
+ return (error);
+
+ /*
+ * OF and CF are cleared; the SF, ZF and PF flags are set according
+ * to the result; AF is undefined.
+ *
+ * The updated status flags are obtained by subtracting 0 from 'result'.
+ */
+ rflags2 = getcc(size, result, 0);
+ rflags &= ~RFLAGS_STATUS_BITS;
+ rflags |= rflags2 & (PSL_PF | PSL_Z | PSL_N);
+
+ error = vie_update_register(vm, vcpuid, VM_REG_GUEST_RFLAGS, rflags, 8);
return (error);
}
-#define RFLAGS_STATUS_BITS (PSL_C | PSL_PF | PSL_AF | PSL_Z | PSL_N | PSL_V)
-
static int
emulate_cmp(void *vm, int vcpuid, uint64_t gpa, struct vie *vie,
mem_region_read_t memread, mem_region_write_t memwrite, void *arg)