Skip site navigation (1)Skip section navigation (2)
Date:      Wed, 26 Jun 2019 21:19:43 +0000 (UTC)
From:      "Rodney W. Grimes" <rgrimes@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org
Subject:   svn commit: r349441 - head/sys/amd64/vmm
Message-ID:  <201906262119.x5QLJhkq099460@repo.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: rgrimes
Date: Wed Jun 26 21:19:43 2019
New Revision: 349441
URL: https://svnweb.freebsd.org/changeset/base/349441

Log:
  Emulate the "TEST r/m{16,32,64}, imm{16,32,32}" instructions (opcode F7H).
  
  This adds emulation for:
  	test r/m16, imm16
  	test r/m32, imm32
  	test r/m64, imm32 sign-extended to 64
  
  OpenBSD guests compiled with clang 8.0.0 use TEST directly against a
  Local APIC register instead of separate read via MOV followed by a
  TEST against the register.
  
  PR:		238794
  Submitted by:	jhb
  Reported by:	Jason Tubnor jason@tubnor.net
  Tested by:	Jason Tubnor jason@tubnor.net
  Reviewed by:	markj, Patrick Mooney patrick.mooney@joyent.com
  MFC after:	3 days
  Differential Revision:	https://reviews.freebsd.org/D20755

Modified:
  head/sys/amd64/vmm/vmm_instruction_emul.c

Modified: head/sys/amd64/vmm/vmm_instruction_emul.c
==============================================================================
--- head/sys/amd64/vmm/vmm_instruction_emul.c	Wed Jun 26 20:25:57 2019	(r349440)
+++ head/sys/amd64/vmm/vmm_instruction_emul.c	Wed Jun 26 21:19:43 2019	(r349441)
@@ -78,6 +78,7 @@ enum {
 	VIE_OP_TYPE_BITTEST,
 	VIE_OP_TYPE_TWOB_GRP15,
 	VIE_OP_TYPE_ADD,
+	VIE_OP_TYPE_TEST,
 	VIE_OP_TYPE_LAST
 };
 
@@ -221,6 +222,12 @@ static const struct vie_op one_byte_opcodes[256] = {
 		.op_byte = 0x8F,
 		.op_type = VIE_OP_TYPE_POP,
 	},
+	[0xF7] = {
+		/* XXX Group 3 extended opcode - not just TEST */
+		.op_byte = 0xF7,
+		.op_type = VIE_OP_TYPE_TEST,
+		.op_flags = VIE_OP_F_IMM,
+	},
 	[0xFF] = {
 		/* XXX Group 5 extended opcode - not just PUSH */
 		.op_byte = 0xFF,
@@ -450,6 +457,41 @@ getaddflags(int opsize, uint64_t x, uint64_t y)
 		return (getaddflags64(x, y));
 }
 
+/*
+ * Return the status flags that would result from doing (x & y).
+ */
+#define	GETANDFLAGS(sz)							\
+static u_long								\
+getandflags##sz(uint##sz##_t x, uint##sz##_t y)				\
+{									\
+	u_long rflags;							\
+									\
+	__asm __volatile("and %2,%1; pushfq; popq %0" :			\
+	    "=r" (rflags), "+r" (x) : "m" (y));				\
+	return (rflags);						\
+} struct __hack
+
+GETANDFLAGS(8);
+GETANDFLAGS(16);
+GETANDFLAGS(32);
+GETANDFLAGS(64);
+
+static u_long
+getandflags(int opsize, uint64_t x, uint64_t y)
+{
+	KASSERT(opsize == 1 || opsize == 2 || opsize == 4 || opsize == 8,
+	    ("getandflags: invalid operand size %d", opsize));
+
+	if (opsize == 1)
+		return (getandflags8(x, y));
+	else if (opsize == 2)
+		return (getandflags16(x, y));
+	else if (opsize == 4)
+		return (getandflags32(x, y));
+	else
+		return (getandflags64(x, y));
+}
+
 static int
 emulate_mov(void *vm, int vcpuid, uint64_t gpa, struct vie *vie,
 	    mem_region_read_t memread, mem_region_write_t memwrite, void *arg)
@@ -1219,6 +1261,55 @@ emulate_cmp(void *vm, int vcpuid, uint64_t gpa, struct
 }
 
 static int
+emulate_test(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 op1, rflags, rflags2;
+
+	size = vie->opsize;
+	error = EINVAL;
+
+	switch (vie->op.op_byte) {
+	case 0xF7:
+		/*
+		 * F7 /0		test r/m16, imm16
+		 * F7 /0		test r/m32, imm32
+		 * REX.W + F7 /0	test r/m64, imm32 sign-extended to 64
+		 *
+		 * Test mem (ModRM:r/m) with immediate and set status
+		 * flags according to the results.  The comparison is
+		 * performed by anding the immediate from the first
+		 * operand and then setting the status flags.
+		 */
+		if ((vie->reg & 7) != 0)
+			return (EINVAL);
+
+		error = memread(vm, vcpuid, gpa, &op1, size, arg);
+		if (error)
+			return (error);
+
+		rflags2 = getandflags(size, op1, vie->immediate);
+		break;
+	default:
+		return (EINVAL);
+	}
+	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.
+	 */
+	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);
+}
+
+static int
 emulate_add(void *vm, int vcpuid, uint64_t gpa, struct vie *vie,
 	    mem_region_read_t memread, mem_region_write_t memwrite, void *arg)
 {
@@ -1642,6 +1733,10 @@ vmm_emulate_instruction(void *vm, int vcpuid, uint64_t
 	case VIE_OP_TYPE_ADD:
 		error = emulate_add(vm, vcpuid, gpa, vie, memread,
 		    memwrite, memarg);
+		break;
+	case VIE_OP_TYPE_TEST:
+		error = emulate_test(vm, vcpuid, gpa, vie,
+		    memread, memwrite, memarg);
 		break;
 	default:
 		error = EINVAL;



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201906262119.x5QLJhkq099460>