| /* | 
 |  * Copyright 2015 Google Inc. | 
 |  * | 
 |  * This file is part of Akaros. | 
 |  * | 
 |  * Akaros is free software: you can redistribute it and/or modify | 
 |  * it under the terms of the GNU General Public License as published by | 
 |  * the Free Software Foundation, version 2 of the License. | 
 |  * | 
 |  * Akaros is distributed in the hope that it will be useful, | 
 |  * but WITHOUT ANY WARRANTY; without even the implied warranty of | 
 |  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | 
 |  * Lesser GNU General Public License for more details. | 
 |  * | 
 |  * See COPYING.LESSER for details on the GNU Lesser General Public License. | 
 |  * See COPYING for details on the GNU General Public License. | 
 |  * | 
 |  * | 
 |  * A few notes: | 
 |  * - We only emulate memory operations, so we don't need to worry about decoding | 
 |  *   the target addresses or whatnot.  We could, just for sanity reasons, though | 
 |  *   the registers (via modrm/sib) are in the guest virtual address space.  We | 
 |  *   operate in the guest physical space.  Having the GPA from the fault makes | 
 |  *   this all easier. | 
 |  * - Just like with fetch_insn(), since we use the GPA, we assume that the | 
 |  *   target of our memory access is also contiguous physically.  The guest could | 
 |  *   have two virtual pages, one mapped to something that triggers an EPT fault | 
 |  *   and the other doesn't.  The upper part of that access will go to the | 
 |  *   adjacent physical page (e.g. a virtio region), and not to the actual | 
 |  *   destination that the guest had mapped.  Buyer beware.  I'm less concerned | 
 |  *   about this than I am with instructions. | 
 |  * - To emulate instructions that set rflags, like add and cmp, I just execute | 
 |  *   the instruction with inline asm and pop rflags.  Let the hardware do it. | 
 |  * - The inline asm often uses %2,%1 and not %1,%2 for the args to e.g. cmp. | 
 |  *   The good book is in Intel syntax.  Code AT&T.  It's easier to read the args | 
 |  *   as if they are in the book, and just switch the ASM args like that. | 
 |  * - add and cmp (80 /0) have a rex, and the Good Book says to add a | 
 |  *   sign-extended imm8 to r/m8.  Extended to what?  I skipped that, and treated | 
 |  *   it like a regular imm8.  The rex should apply for register selection still. | 
 |  */ | 
 |  | 
 | #include <parlib/stdio.h> | 
 | #include <sys/types.h> | 
 | #include <sys/stat.h> | 
 | #include <fcntl.h> | 
 | #include <parlib/arch/arch.h> | 
 | #include <parlib/ros_debug.h> | 
 | #include <unistd.h> | 
 | #include <errno.h> | 
 | #include <stdlib.h> | 
 | #include <string.h> | 
 | #include <sys/uio.h> | 
 | #include <stdint.h> | 
 | #include <err.h> | 
 | #include <sys/mman.h> | 
 | #include <vmm/vmm.h> | 
 | #include <vmm/virtio.h> | 
 | #include <vmm/virtio_mmio.h> | 
 | #include <vmm/virtio_ids.h> | 
 | #include <vmm/virtio_config.h> | 
 | #include <ros/arch/mmu.h> | 
 | #include <ros/arch/trapframe.h> | 
 |  | 
 | int debug_decode = 0; | 
 | #define DPRINTF(fmt, ...) \ | 
 | 	do { \ | 
 | 		if (debug_decode) { \ | 
 | 			fprintf(stderr, "decode: " fmt, ## __VA_ARGS__); \ | 
 | 		} \ | 
 | 	} \ | 
 | 	while (0) | 
 |  | 
 | enum x86_register { | 
 | 	X86_RAX = 0, | 
 | 	X86_RCX = 1, | 
 | 	X86_RDX = 2, | 
 | 	X86_RBX = 3, | 
 | 	X86_RSP = 4, | 
 | 	X86_RBP = 5, | 
 | 	X86_RSI = 6, | 
 | 	X86_RDI = 7, | 
 | 	X86_R8  = 8, | 
 | 	X86_R9  = 9, | 
 | 	X86_R10 = 10, | 
 | 	X86_R11 = 11, | 
 | 	X86_R12 = 12, | 
 | 	X86_R13 = 13, | 
 | 	X86_R14 = 14, | 
 | 	X86_R15 = 15, | 
 | }; | 
 |  | 
 | static const char * const reg_names[] = { | 
 | 	"rax", "rcx", "rdx", "rbx", "rsp", "rbp", "rsi", "rdi", | 
 | 	"r8",  "r9",  "r10", "r11", "r12", "r13", "r14", "r15" | 
 | }; | 
 |  | 
 | static const char *regname(uint8_t reg) | 
 | { | 
 | 	return reg_names[reg]; | 
 | } | 
 |  | 
 | /* Helper: points to the reg in the VMTF */ | 
 | static uint64_t *reg_to_vmtf_reg(struct vm_trapframe *vm_tf, int reg) | 
 | { | 
 | 	switch (reg) { | 
 | 	case 0: | 
 | 		return &vm_tf->tf_rax; | 
 | 	case 1: | 
 | 		return &vm_tf->tf_rcx; | 
 | 	case 2: | 
 | 		return &vm_tf->tf_rdx; | 
 | 	case 3: | 
 | 		return &vm_tf->tf_rbx; | 
 | 	case 4: | 
 | 		return &vm_tf->tf_rsp; | 
 | 	case 5: | 
 | 		return &vm_tf->tf_rbp; | 
 | 	case 6: | 
 | 		return &vm_tf->tf_rsi; | 
 | 	case 7: | 
 | 		return &vm_tf->tf_rdi; | 
 | 	case 8: | 
 | 		return &vm_tf->tf_r8; | 
 | 	case 9: | 
 | 		return &vm_tf->tf_r9; | 
 | 	case 10: | 
 | 		return &vm_tf->tf_r10; | 
 | 	case 11: | 
 | 		return &vm_tf->tf_r11; | 
 | 	case 12: | 
 | 		return &vm_tf->tf_r12; | 
 | 	case 13: | 
 | 		return &vm_tf->tf_r13; | 
 | 	case 14: | 
 | 		return &vm_tf->tf_r14; | 
 | 	case 15: | 
 | 		return &vm_tf->tf_r15; | 
 | 	} | 
 | 	panic("Unknown reg %d\n", reg); | 
 | } | 
 |  | 
 | struct x86_decode { | 
 | 	uint8_t prefix_sz; | 
 | 	uint8_t opcode_sz; | 
 | 	uint8_t modrm_sib_sz; | 
 | 	uint8_t imm_sz; | 
 | 	uint8_t operand_bytes; | 
 | 	uint8_t address_bytes; | 
 | 	bool has_modrm; | 
 | 	bool is_store; | 
 | 	bool rex_r; | 
 | 	bool rex_x; | 
 | 	bool rex_b; | 
 | }; | 
 |  | 
 | /* We decode instructions in response to memory faults, so most every | 
 |  * instruction will have modrm. */ | 
 | #define X86_DECODE_64_DEFAULT { \ | 
 | 	.prefix_sz = 0, \ | 
 | 	.opcode_sz = 1, \ | 
 | 	.modrm_sib_sz = 0, \ | 
 | 	.imm_sz = 0, \ | 
 | 	.operand_bytes = 4, \ | 
 | 	.address_bytes = 8, \ | 
 | 	.has_modrm = true, \ | 
 | 	.is_store = false, \ | 
 | 	.rex_r = false, \ | 
 | 	.rex_x = false, \ | 
 | 	.rex_b = false, \ | 
 | } | 
 |  | 
 | static void print_decoded_insn(uint8_t *insn, struct x86_decode *d); | 
 | static void run_decode_hokey_tests(void); | 
 |  | 
 | static int decode_prefix(uint8_t *insn, struct x86_decode *d) | 
 | { | 
 | 	uint8_t *p = insn; | 
 |  | 
 | 	for (;; p++) { | 
 | 		/* Operand-size override prefix */ | 
 | 		if (p[0] == 0x66) { | 
 | 			/* Ignore 0x66 if REX.W changed us to 8 bytes (64). | 
 | 			 * Though we should only see 0x66 before REX.W. | 
 | 			 * | 
 | 			 * If this was handling 32 bit code but with cs.d clear | 
 | 			 * (default 16), 66 should set us to 4 bytes. */ | 
 | 			if (d->operand_bytes == 4) | 
 | 				d->operand_bytes = 2; | 
 | 			continue; | 
 | 		} | 
 | 		/* Address-size override prefix */ | 
 | 		if (p[0] == 0x67) { | 
 | 			d->address_bytes = 4; | 
 | 			continue; | 
 | 		} | 
 | 		/* REX.* */ | 
 | 		if ((p[0] & 0xf0) == 0x40) { | 
 | 			if (p[0] & 0x08) | 
 | 				d->operand_bytes = 8; | 
 | 			if (p[0] & 0x04) | 
 | 				d->rex_r = true; | 
 | 			if (p[0] & 0x02) | 
 | 				d->rex_x = true; | 
 | 			if (p[0] & 0x01) | 
 | 				d->rex_b = true; | 
 | 			continue; | 
 | 		} | 
 | 		break; | 
 | 	} | 
 | 	d->prefix_sz = p - insn; | 
 | 	return 0; | 
 | } | 
 |  | 
 | static uint8_t *get_modrm(uint8_t *insn, struct x86_decode *d) | 
 | { | 
 | 	if (!d->has_modrm) | 
 | 		return NULL; | 
 | 	return insn + d->prefix_sz + d->opcode_sz; | 
 | } | 
 |  | 
 | static uint8_t modrm_sib_bytes_16(int mod, int rm, struct x86_decode *d) | 
 | { | 
 | 	uint8_t ret = 1;	/* counting the mod/rm byte itself */ | 
 |  | 
 | 	switch (mod) { | 
 | 	case 0: | 
 | 		if (rm == 6) | 
 | 			ret += 2; /* disp16 */ | 
 | 		break; | 
 | 	case 1: | 
 | 		ret += 1; /* disp8 */ | 
 | 		if (rm == 4) | 
 | 			ret += 1; /* SIB */ | 
 | 		break; | 
 | 	case 2: | 
 | 		ret += 2; /* disp16 */ | 
 | 		break; | 
 | 	case 3: | 
 | 		break; | 
 | 	} | 
 | 	return ret; | 
 | } | 
 |  | 
 | static uint8_t modrm_sib_bytes_32(int mod, int rm, struct x86_decode *d) | 
 | { | 
 | 	uint8_t ret = 1;	/* counting the mod/rm byte itself */ | 
 |  | 
 | 	switch (mod) { | 
 | 	case 0: | 
 | 		if (rm == 4) | 
 | 			ret += 1; /* SIB */ | 
 | 		else if (rm == 5) | 
 | 			ret += 4; /* disp32 */ | 
 | 		break; | 
 | 	case 1: | 
 | 		ret += 1; /* disp8 */ | 
 | 		if (rm == 4) | 
 | 			ret += 1; /* SIB */ | 
 | 		break; | 
 | 	case 2: | 
 | 		ret += 4; /* disp32 */ | 
 | 		if (rm == 4) /* SIB */ | 
 | 			ret += 1; | 
 | 		break; | 
 | 	case 3: | 
 | 		break; | 
 | 	} | 
 | 	return ret; | 
 | } | 
 |  | 
 | /* Returns the number of bytes in the instruction due to things encoded by | 
 |  * mod/rm, such as displacements (disp32) or the SIB byte, including the mod/rm | 
 |  * byte itself. */ | 
 | static uint8_t modrm_sib_bytes(uint8_t *insn, struct x86_decode *d) | 
 | { | 
 | 	uint8_t *modrm = get_modrm(insn, d); | 
 | 	int mod, rm; | 
 |  | 
 | 	if (!modrm) | 
 | 		return 0; | 
 | 	mod = *modrm >> 6; | 
 | 	rm = *modrm & 0x7; | 
 |  | 
 | 	switch (d->address_bytes) { | 
 | 	case 2: | 
 | 		/* We should never see this, but was easy enough to code */ | 
 | 		fprintf(stderr, "decode: %s had %u address bytes!\n", __func__, | 
 | 			d->address_bytes); | 
 | 		return modrm_sib_bytes_16(mod, rm, d); | 
 | 	case 4: | 
 | 	case 8: | 
 | 		return modrm_sib_bytes_32(mod, rm, d); | 
 | 	default: | 
 | 		panic("decode: %s had %u address bytes!\n", __func__, | 
 | 			d->address_bytes); | 
 | 		return 0; | 
 | 	} | 
 | } | 
 |  | 
 | static uint8_t modrm_get_reg_16(uint8_t reg, struct x86_decode *d) | 
 | { | 
 | 	return reg; | 
 | } | 
 |  | 
 | static uint8_t modrm_get_reg_32(uint8_t reg, struct x86_decode *d) | 
 | { | 
 | 	return reg + (d->rex_r ? 8 : 0); | 
 | } | 
 |  | 
 | static uint64_t get_imm(uint8_t *insn, struct x86_decode *d) | 
 | { | 
 | 	uint8_t *imm = insn + d->prefix_sz + d->opcode_sz + d->modrm_sib_sz; | 
 | 	uint64_t ret = 0; | 
 |  | 
 | 	switch (d->imm_sz) { | 
 | 	case 8: | 
 | 		ret |= (uint64_t)imm[7] << (8 * 7); | 
 | 		ret |= (uint64_t)imm[6] << (8 * 6); | 
 | 		ret |= (uint64_t)imm[5] << (8 * 5); | 
 | 		ret |= (uint64_t)imm[4] << (8 * 4); | 
 | 		/* fall-through */ | 
 | 	case 4: | 
 | 		ret |= (uint64_t)imm[3] << (8 * 3); | 
 | 		ret |= (uint64_t)imm[2] << (8 * 2); | 
 | 		/* fall-through */ | 
 | 	case 2: | 
 | 		ret |= (uint64_t)imm[1] << (8 * 1); | 
 | 		/* fall-through */ | 
 | 	case 1: | 
 | 		ret |= (uint64_t)imm[0] << (8 * 0); | 
 | 		break; | 
 | 	default: | 
 | 		panic("Bad IMM size %u", d->imm_sz); | 
 | 	} | 
 | 	return ret; | 
 | } | 
 |  | 
 | /* This is the three-bit (or more with REX) register used by opcodes that have | 
 |  * /r.  The first argument for opcodes is in the modrm part, e.g. [eax]+disp8. | 
 |  * We don't need to parse that, since we know the faulting GPA. */ | 
 | static uint8_t modrm_get_reg(uint8_t *insn, struct x86_decode *d) | 
 | { | 
 | 	uint8_t *modrm = get_modrm(insn, d); | 
 | 	uint8_t reg; | 
 |  | 
 | 	if (!modrm) { | 
 | 		fprintf(stderr, "%s called with no modrm!\n, insn: %p\n", | 
 | 			__func__, insn); | 
 | 		hexdump(stderr, insn, 15); | 
 | 		panic("Continuing could corrupt registers"); | 
 | 	} | 
 | 	reg = (*modrm >> 3) & 7; | 
 |  | 
 | 	switch (d->address_bytes) { | 
 | 	case 2: | 
 | 		fprintf(stderr, "decode: %s had %u address bytes!\n", __func__, | 
 | 			d->address_bytes); | 
 | 		return modrm_get_reg_16(reg, d); | 
 | 	case 4: | 
 | 	case 8: | 
 | 		return modrm_get_reg_32(reg, d); | 
 | 	default: | 
 | 		panic("decode: %s had %u address bytes!\n", __func__, | 
 | 			d->address_bytes); | 
 | 	} | 
 | } | 
 |  | 
 | /* Decodes the actual opcode, storing things we care about in d. | 
 |  * -1 on error (for opcodes we haven't coded up), 0 success. | 
 |  * | 
 |  * Sets operand_bytes, various sizes, is_store, etc. */ | 
 | static int decode_opcode(uint8_t *insn, struct x86_decode *d) | 
 | { | 
 | 	uint8_t *opcode = insn + d->prefix_sz; | 
 |  | 
 | 	/* If we don't set anything, we're using the defaults:  1 byte opcode, | 
 | 	 * has_modrm, operand_bytes determined by the prefix/64-bit mode, etc */ | 
 | 	switch (opcode[0]) { | 
 | 	case 0x80: | 
 | 		switch (modrm_get_reg(insn, d)) { | 
 | 		case 0: // add | 
 | 		case 7: // cmp | 
 | 			break; | 
 | 		default: | 
 | 			goto unknown; | 
 | 		} | 
 | 		d->imm_sz = 1; | 
 | 		d->operand_bytes = 1; | 
 | 		break; | 
 | 	case 0x81: | 
 | 		switch (modrm_get_reg(insn, d)) { | 
 | 		case 0: // add | 
 | 		case 7: // cmp | 
 | 			break; | 
 | 		default: | 
 | 			goto unknown; | 
 | 		} | 
 | 		d->imm_sz = d->address_bytes == 2 ? 2 : 4; | 
 | 		break; | 
 | 	case 0x3a:	// cmp /r | 
 | 		d->operand_bytes = 1; | 
 | 		break; | 
 | 	case 0x88:	// mov | 
 | 	case 0x8a:	// mov | 
 | 		d->operand_bytes = 1; | 
 | 		/* Instructions that could be loads or stores differ in bit 2. | 
 | 		 * e.g.  88 (store, bit 2 unset) vs 8a (load, bit 2 set). */ | 
 | 		d->is_store = !(opcode[0] & 2); | 
 | 		break; | 
 | 	case 0x89:	// mov | 
 | 	case 0x8b:	// mov | 
 | 		d->is_store = !(opcode[0] & 2); | 
 | 		break; | 
 | 	case 0x0f: | 
 | 		d->opcode_sz = 2; | 
 | 		switch (opcode[1]) { | 
 | 		case 0xb7:	// movzw | 
 | 			d->operand_bytes = 2; | 
 | 			break; | 
 | 		case 0xb6:	// movzb | 
 | 			d->operand_bytes = 1; | 
 | 			break; | 
 | 		default: | 
 | 			goto unknown; | 
 | 		} | 
 | 		break; | 
 | 	default: | 
 | 	unknown: | 
 | 		fprintf(stderr, "unknown decode %02x %02x %02x@ %p\n", | 
 | 			opcode[0], opcode[1], opcode[2], opcode); | 
 | 		return -1; | 
 | 	} | 
 |  | 
 | 	d->modrm_sib_sz = modrm_sib_bytes(insn, d); | 
 | 	return 0; | 
 | } | 
 |  | 
 | static void set_rflags_status_bits(uint64_t *rfl, uint64_t new) | 
 | { | 
 | 	*rfl &= ~FL_STATUS; | 
 | 	new &= FL_STATUS; | 
 | 	*rfl |= new; | 
 | } | 
 |  | 
 | static int add_8081(struct guest_thread *gth, uint8_t *insn, | 
 | 		    struct x86_decode *d, emu_mem_access access, | 
 | 		    uint64_t gpa) | 
 | { | 
 | 	struct vm_trapframe *vm_tf = gth_to_vmtf(gth); | 
 | 	int ret; | 
 | 	uint64_t scratch = 0; | 
 | 	int8_t imm8, scr8; | 
 | 	int16_t imm16, scr16; | 
 | 	int32_t imm32, scr32; | 
 | 	int64_t imm64, scr64; | 
 | 	unsigned long rflags; | 
 |  | 
 | 	ret = access(gth, gpa, &scratch, d->operand_bytes, false); | 
 | 	if (ret < 0) | 
 | 		return ret; | 
 | 	switch (d->operand_bytes) { | 
 | 	case 1: | 
 | 		imm8 = get_imm(insn, d); | 
 | 		/* scratch is input and output, but you can't cast it in an | 
 | 		 * output operand */ | 
 | 		scr8 = scratch; | 
 | 		asm volatile ("addb %2,%1; pushfq; popq %0" | 
 | 			      : "=r"(rflags), | 
 | 			        "+r"(scr8) | 
 | 			      : "r"(imm8) | 
 | 			      : "cc"); | 
 | 		scratch = scr8; | 
 | 		break; | 
 | 	case 2: | 
 | 		imm16 = get_imm(insn, d); | 
 | 		scr16 = scratch; | 
 | 		asm volatile ("addw %2,%1; pushfq; popq %0" | 
 | 			      : "=r"(rflags), | 
 | 			        "+r"(scr16) | 
 | 			      : "r"(imm16) | 
 | 			      : "cc"); | 
 | 		scratch = scr16; | 
 | 		break; | 
 | 	case 4: | 
 | 		imm32 = get_imm(insn, d); | 
 | 		scr32 = scratch; | 
 | 		asm volatile ("addl %2,%1; pushfq; popq %0" | 
 | 			      : "=r"(rflags), | 
 | 			        "+r"(scr32) | 
 | 			      : "r"(imm32) | 
 | 			      : "cc"); | 
 | 		scratch = scr32; | 
 | 		break; | 
 | 	case 8: | 
 | 		imm32 = get_imm(insn, d); | 
 | 		scr64 = scratch; | 
 | 		asm volatile ("addq %2,%1; pushfq; popq %0" | 
 | 			      : "=r"(rflags), | 
 | 			        "+r"(scr64) | 
 | 			      : "r"((int64_t)imm32) | 
 | 			      : "cc"); | 
 | 		scratch = scr64; | 
 | 		break; | 
 | 	} | 
 | 	set_rflags_status_bits(&vm_tf->tf_rflags, rflags); | 
 | 	return access(gth, gpa, &scratch, d->operand_bytes, true); | 
 | } | 
 |  | 
 | static int cmp_8081(struct guest_thread *gth, uint8_t *insn, | 
 | 		    struct x86_decode *d, emu_mem_access access, | 
 | 		    uint64_t gpa) | 
 | { | 
 | 	struct vm_trapframe *vm_tf = gth_to_vmtf(gth); | 
 | 	int ret; | 
 | 	uint64_t scratch = 0; | 
 | 	int8_t imm8; | 
 | 	int16_t imm16; | 
 | 	int32_t imm32; | 
 | 	int64_t imm64; | 
 | 	unsigned long rflags; | 
 |  | 
 | 	ret = access(gth, gpa, &scratch, d->operand_bytes, false); | 
 | 	if (ret < 0) | 
 | 		return ret; | 
 | 	switch (d->operand_bytes) { | 
 | 	case 1: | 
 | 		imm8 = get_imm(insn, d); | 
 | 		asm volatile ("cmpb %2,%1; pushfq; popq %0" | 
 | 			      : "=r"(rflags) | 
 | 			      : "r"((int8_t)scratch), | 
 | 			        "r"(imm8) | 
 | 			      : "cc"); | 
 | 		break; | 
 | 	case 2: | 
 | 		imm16 = get_imm(insn, d); | 
 | 		asm volatile ("cmpw %2,%1; pushfq; popq %0" | 
 | 			      : "=r"(rflags) | 
 | 			      : "r"((int16_t)scratch), | 
 | 			        "r"(imm16) | 
 | 			      : "cc"); | 
 | 		break; | 
 | 	case 4: | 
 | 		imm32 = get_imm(insn, d); | 
 | 		asm volatile ("cmpl %2,%1; pushfq; popq %0" | 
 | 			      : "=r"(rflags) | 
 | 			      : "r"((int32_t)scratch), | 
 | 			        "r"(imm32) | 
 | 			      : "cc"); | 
 | 		break; | 
 | 	case 8: | 
 | 		imm32 = get_imm(insn, d); | 
 | 		asm volatile ("cmpq %2,%1; pushfq; popq %0" | 
 | 			      : "=r"(rflags) | 
 | 			      : "r"((int64_t)scratch), | 
 | 			        "r"((int64_t)imm32) | 
 | 			      : "cc"); | 
 | 		break; | 
 | 	} | 
 | 	set_rflags_status_bits(&vm_tf->tf_rflags, rflags); | 
 | 	return 0; | 
 | } | 
 |  | 
 | static int cmp_3a(struct guest_thread *gth, uint8_t *insn, | 
 | 		  struct x86_decode *d, emu_mem_access access, | 
 | 		  uint64_t gpa) | 
 | { | 
 | 	struct vm_trapframe *vm_tf = gth_to_vmtf(gth); | 
 | 	int ret; | 
 | 	uint64_t scratch = 0; | 
 | 	int8_t reg8; | 
 | 	unsigned long rflags; | 
 | 	int mod_reg = modrm_get_reg(insn, d); | 
 |  | 
 | 	assert(d->operand_bytes == 1); | 
 | 	ret = access(gth, gpa, &scratch, 1, false); | 
 | 	if (ret < 0) | 
 | 		return ret; | 
 | 	reg8 = (int8_t)*reg_to_vmtf_reg(vm_tf, mod_reg); | 
 | 	asm volatile ("cmpb %2,%1; pushfq; popq %0" | 
 | 		      : "=r"(rflags) | 
 | 		      : "r"(reg8), | 
 | 		        "r"((int8_t)scratch) | 
 | 		      : "cc"); | 
 | 	set_rflags_status_bits(&vm_tf->tf_rflags, rflags); | 
 | 	return 0; | 
 | } | 
 |  | 
 | static int execute_op(struct guest_thread *gth, uint8_t *insn, | 
 | 		      struct x86_decode *d, emu_mem_access access, | 
 | 		      uint64_t gpa) | 
 | { | 
 | 	struct vm_trapframe *vm_tf = gth_to_vmtf(gth); | 
 | 	uint8_t *opcode = insn + d->prefix_sz; | 
 | 	int ret, mod_reg; | 
 |  | 
 | 	switch (opcode[0]) { | 
 | 	case 0x80: | 
 | 	case 0x81: | 
 | 		switch (modrm_get_reg(insn, d)) { | 
 | 		case 0: // add | 
 | 			return add_8081(gth, insn, d, access, gpa); | 
 | 		case 7: // cmp | 
 | 			return cmp_8081(gth, insn, d, access, gpa); | 
 | 		} | 
 | 		goto unknown; | 
 | 	case 0x3a:	// cmp | 
 | 		return cmp_3a(gth, insn, d, access, gpa); | 
 | 	case 0x89:	// mov | 
 | 	case 0x8b:	// mov | 
 | 	case 0x8a:	// mov | 
 | 	case 0x88:	// mov | 
 | 		mod_reg = modrm_get_reg(insn, d); | 
 | 		ret = access(gth, gpa, reg_to_vmtf_reg(vm_tf, mod_reg), | 
 | 			     d->operand_bytes, d->is_store); | 
 | 		/* We set a register's value.  For 32-bit operands, we need to | 
 | 		 * zero the upper 32 bits. */ | 
 | 		if (!ret && !d->is_store && d->operand_bytes == 4) | 
 | 			*reg_to_vmtf_reg(vm_tf, mod_reg) &= 0xffffffff; | 
 | 		return ret; | 
 | 	case 0x0f: | 
 | 		switch (opcode[1]) { | 
 | 		case 0xb7:	// movzw | 
 | 		case 0xb6:	// movzb | 
 | 			mod_reg = modrm_get_reg(insn, d); | 
 | 			*reg_to_vmtf_reg(vm_tf, mod_reg) = 0; | 
 | 			return access(gth, gpa, reg_to_vmtf_reg(vm_tf, mod_reg), | 
 | 				      d->operand_bytes, d->is_store); | 
 | 		} | 
 | 		goto unknown; | 
 | 	default: | 
 | 	unknown: | 
 | 		fprintf(stderr, "unknown execute %02x %02x %02x@ %p\n", | 
 | 			opcode[0], opcode[1], opcode[2], opcode); | 
 | 		return -1; | 
 | 	} | 
 | } | 
 |  | 
 | static int decode_inst_size(uint8_t *insn, struct x86_decode *d) | 
 | { | 
 | 	return d->prefix_sz + d->opcode_sz + d->modrm_sib_sz + | 
 | 	       + d->imm_sz; | 
 | } | 
 |  | 
 | /* Emulates a memory operation that faulted/vmexited.  Despite the file name, | 
 |  * this is x86-specific, so we only have at most one address involved.  We have | 
 |  * at least one address involved, since it is a memory operation. | 
 |  * | 
 |  * The main thing our caller provides is a function pointer for accessing | 
 |  * memory.  The address is gpa, the register (which doesn't have to be a real | 
 |  * register in a VMTF) involved for the source/destination (based on 'store'). | 
 |  */ | 
 | int emulate_mem_insn(struct guest_thread *gth, uint8_t *insn, | 
 | 		     emu_mem_access access, int *advance) | 
 | { | 
 | 	struct x86_decode d[1] = {X86_DECODE_64_DEFAULT}; | 
 | 	uintptr_t gpa; | 
 |  | 
 | 	// Duh, which way did he go George? Which way did he go? | 
 | 	// First hit on Google gets you there! | 
 | 	// This is the guest physical address of the access. | 
 | 	// This is nice, because if we ever go with more complete | 
 | 	// instruction decode, knowing this gpa reduces our work: | 
 | 	// we don't have to find the source address in registers, | 
 | 	// only the register holding or receiving the value. | 
 | 	gpa = gth_to_vmtf(gth)->tf_guest_pa; | 
 | 	if (decode_prefix(insn, d) < 0) | 
 | 		return -1; | 
 | 	if (decode_opcode(insn, d) < 0) | 
 | 		return -1; | 
 | 	if (execute_op(gth, insn, d, access, gpa) < 0) | 
 | 		return -1; | 
 | 	*advance = decode_inst_size(insn, d); | 
 |  | 
 | 	if (debug_decode) { | 
 | 		/* This is racy - multiple decoded threads on different cores | 
 | 		 * will clutter the output. */ | 
 | 		fprintf(stderr, "gpa %p", gpa); | 
 | 		print_decoded_insn(insn, d); | 
 | 	} | 
 | 	return 0; | 
 | } | 
 |  | 
 | /* Debugging */ | 
 |  | 
 | static void print_decoded_insn(uint8_t *insn, struct x86_decode *d) | 
 | { | 
 | 	uint8_t inst_sz = decode_inst_size(insn, d); | 
 |  | 
 | 	fprintf(stderr, | 
 | 		"oprnd_bs %d, addr_bs %d, reg %2d, imm 0x%08llx, inst_size %2d:", | 
 | 		d->operand_bytes, d->address_bytes, | 
 | 		get_modrm(insn, d) ? modrm_get_reg(insn, d) : -1, | 
 | 		d->imm_sz ? get_imm(insn, d) : 0xdeadbeef, inst_sz); | 
 | 	for (int i = 0; i < inst_sz; i++) | 
 | 		fprintf(stderr, " %02x", insn[i]); | 
 | 	fprintf(stderr, "\n"); | 
 | } | 
 |  | 
 | static int tst_mem_access(struct guest_thread *gth, uintptr_t gpa, | 
 | 			  unsigned long *regp, size_t size, bool store) | 
 | { | 
 | 	if (store) { | 
 | 		switch (size) { | 
 | 		case 1: | 
 | 			*(uint8_t*)gpa = *(uint8_t*)regp; | 
 | 			break; | 
 | 		case 2: | 
 | 			*(uint16_t*)gpa = *(uint16_t*)regp; | 
 | 			break; | 
 | 		case 4: | 
 | 			*(uint32_t*)gpa = *(uint32_t*)regp; | 
 | 			break; | 
 | 		case 8: | 
 | 			*(uint64_t*)gpa = *(uint64_t*)regp; | 
 | 			break; | 
 | 		} | 
 | 	} else { | 
 | 		switch (size) { | 
 | 		case 1: | 
 | 			*(uint8_t*)regp = *(uint8_t*)gpa; | 
 | 			break; | 
 | 		case 2: | 
 | 			*(uint16_t*)regp = *(uint16_t*)gpa; | 
 | 			break; | 
 | 		case 4: | 
 | 			*(uint32_t*)regp = *(uint32_t*)gpa; | 
 | 			break; | 
 | 		case 8: | 
 | 			*(uint64_t*)regp = *(uint64_t*)gpa; | 
 | 			break; | 
 | 		} | 
 | 	} | 
 | 	return 0; | 
 | } | 
 |  | 
 | /* Far from exhaustive, and you need to call it manually.  It actually caught a | 
 |  * bug though. */ | 
 | static void run_decode_hokey_tests(void) | 
 | { | 
 | 	struct guest_thread gth[1] = {0}; | 
 | 	struct vm_trapframe *vm_tf = gth_to_vmtf(gth); | 
 | 	uint8_t insn[VMM_MAX_INSN_SZ]; | 
 | 	uint8_t ram[16]; | 
 | 	int inst_size, ret; | 
 |  | 
 | 	vm_tf->tf_guest_pa = (uint64_t)ram; | 
 |  | 
 | 	// movw [rax],ecx (rax ignored, we use the GPA) | 
 | 	memcpy(insn, "\x89\x08", 2); | 
 | 	memset(ram, 0, sizeof(ram)); | 
 | 	vm_tf->tf_rcx = 0x1234; | 
 | 	ret = emulate_mem_insn(gth, insn, tst_mem_access, &inst_size); | 
 | 	assert(!ret); | 
 | 	assert(inst_size = 2); | 
 | 	assert(*(uint16_t*)ram == 0x1234); | 
 |  | 
 | 	// add8 /0 [rbx], -1 | 
 | 	memcpy(insn, "\x80\x03\xff", 3); | 
 | 	memset(ram, 0, sizeof(ram)); | 
 | 	ram[0] = 1; | 
 | 	ret = emulate_mem_insn(gth, insn, tst_mem_access, &inst_size); | 
 | 	assert(!ret); | 
 | 	assert(inst_size == 3); | 
 | 	assert(ram[0] == 0); | 
 | 	assert(vm_tf->tf_rflags & FL_ZF); | 
 | 	assert(!(vm_tf->tf_rflags & FL_SF)); | 
 |  | 
 | 	// REX.W add /0, r/m64/imm32: 84: SIB+disp32 /0. (-1 sign extend) | 
 | 	memcpy(insn, "\x48\x81\x84\x00\x00\x00\x00\x00\xff\xff\xff\xff", 12); | 
 | 	memset(ram, 0, sizeof(ram)); | 
 | 	ram[0] = 2; | 
 | 	ret = emulate_mem_insn(gth, insn, tst_mem_access, &inst_size); | 
 | 	assert(!ret); | 
 | 	assert(inst_size == 12); | 
 | 	assert(*(uint64_t*)ram == 1); | 
 | 	assert(!(vm_tf->tf_rflags & FL_ZF)); | 
 | 	assert(!(vm_tf->tf_rflags & FL_SF)); | 
 |  | 
 | 	// REX.R movzw, 14: SIB, reg rdx -> r10 | 
 | 	memcpy(insn, "\x44\x0f\xb7\x14\x00", 5); | 
 | 	memset(ram, 0, sizeof(ram)); | 
 | 	ram[0] = 0x12; | 
 | 	vm_tf->tf_r10 = 0xffffffffffffffff; | 
 | 	ret = emulate_mem_insn(gth, insn, tst_mem_access, &inst_size); | 
 | 	assert(!ret); | 
 | 	assert(inst_size == 5); | 
 | 	assert(vm_tf->tf_r10 == 0x12); | 
 |  | 
 | 	fprintf(stderr, "Hokey decode tests passed\n"); | 
 | 	return; | 
 |  | 
 | 	/* Helpful debuggers for the debugger */ | 
 | 	fprintf(stderr, "ram %x %x %x %x %x %x %x %x\n", | 
 | 		ram[0], ram[1], ram[2], ram[3], ram[4], ram[5], ram[6], ram[7]); | 
 | } |