X-Git-Url: http://v3vee.org/palacios/gitweb/gitweb.cgi?a=blobdiff_plain;f=palacios%2Fsrc%2Fpalacios%2Fvmm_emulator.c;h=5fb922640c3d324a1117a6b0a1ea698ef889e7af;hb=82b8b87c344fcd1eab22e3f3be5ad54cbb3f8f68;hp=73a3565a562e3c41ecc706a60eeba6495b91d862;hpb=851cedf7660970f7af19bffb63b1ba1d70f77e3e;p=palacios.git diff --git a/palacios/src/palacios/vmm_emulator.c b/palacios/src/palacios/vmm_emulator.c index 73a3565..5fb9226 100644 --- a/palacios/src/palacios/vmm_emulator.c +++ b/palacios/src/palacios/vmm_emulator.c @@ -1,362 +1,739 @@ +/* + * This file is part of the Palacios Virtual Machine Monitor developed + * by the V3VEE Project with funding from the United States National + * Science Foundation and the Department of Energy. + * + * The V3VEE Project is a joint project between Northwestern University + * and the University of New Mexico. You can find out more at + * http://www.v3vee.org + * + * Copyright (c) 2008, Jack Lange + * Copyright (c) 2008, The V3VEE Project + * All rights reserved. + * + * Author: Jack Lange + * + * This is free software. You are permitted to use, + * redistribute, and modify it as specified in the file "V3VEE_LICENSE". + */ + #include #include #include #include -#include -#include -#include - -static const char VMMCALL[3] = {0x0f, 0x01, 0xd9}; +#include +#include -#ifndef DEBUG_EMULATOR +#ifndef CONFIG_DEBUG_EMULATOR #undef PrintDebug #define PrintDebug(fmt, args...) #endif -int init_emulator(struct guest_info * info) { - struct emulation_state * emulator = &(info->emulator); - - emulator->num_emulated_pages = 0; - INIT_LIST_HEAD(&(emulator->emulated_pages)); +static int run_op(struct guest_info * info, v3_op_type_t op_type, addr_t src_addr, addr_t dst_addr, int src_op_size, int dst_op_size); +// We emulate up to the next 4KB page boundry +static int emulate_string_write_op(struct guest_info * info, struct x86_instr * dec_instr, + addr_t write_gva, addr_t write_gpa, addr_t dst_addr, + int (*write_fn)(addr_t guest_addr, void * src, uint_t length, void * priv_data), + void * priv_data) { + uint_t emulation_length = 0; + uint_t emulation_iter_cnt = 0; + addr_t tmp_rcx = 0; + addr_t src_addr = 0; - emulator->num_saved_pages = 0; - INIT_LIST_HEAD(&(emulator->saved_pages)); + if (dec_instr->dst_operand.operand != write_gva) { + PrintError("Inconsistency between Pagefault and Instruction Decode XED_ADDR=%p, PF_ADDR=%p\n", + (void *)dec_instr->dst_operand.operand, (void *)write_gva); + return -1; + } - emulator->num_write_regions = 0; - INIT_LIST_HEAD(&(emulator->write_regions)); - - emulator->running = 0; - emulator->instr_length = 0; - - emulator->tf_enabled = 0; - - return 0; -} - -static addr_t get_new_page() { - void * page = V3_AllocPages(1); - memset(page, 0, PAGE_SIZE); - - return (addr_t)page; -} - -/* -static int setup_code_page(struct guest_info * info, char * instr, struct basic_instr_info * instr_info ) { - addr_t code_page_offset = PT32_PAGE_OFFSET(info->rip); - addr_t code_page = get_new_page(); - struct emulated_page * new_code_page = V3_Malloc(sizeof(struct emulated_page)); - struct saved_page * saved_code_page = V3_Malloc(sizeof(struct saved_page)); - - - saved_code_page->va = PT32_PAGE_ADDR(info->rip); - - new_code_page->page_addr = code_page; - new_code_page->va = PT32_PAGE_ADDR(info->rip); - - new_code_page->pte.present = 1; - new_code_page->pte.writable = 0; - new_code_page->pte.user_page = 1; - new_code_page->pte.page_base_addr = PT32_BASE_ADDR(code_page); - - memcpy((void *)(code_page + code_page_offset), instr, instr_info->instr_length); - memcpy((void *)(code_page + code_page_offset + instr_info->instr_length), VMMCALL, 3); - -#ifdef DEBUG_EMULATOR - PrintDebug("New Instr Stream:\n"); - PrintTraceMemDump((void *)(code_page + code_page_offset), 32); - PrintDebug("rip =%x\n", info->rip); + emulation_length = ( (dec_instr->str_op_length < (0x1000 - PAGE_OFFSET_4KB(write_gva))) ? + dec_instr->str_op_length : + (0x1000 - PAGE_OFFSET_4KB(write_gva))); + + /* ** Fix emulation length so that it doesn't overrun over the src page either ** */ + emulation_iter_cnt = emulation_length / dec_instr->dst_operand.size; + tmp_rcx = emulation_iter_cnt; + + if (dec_instr->op_type == V3_OP_MOVS) { + + // figure out addresses here.... + if (info->mem_mode == PHYSICAL_MEM) { + if (guest_pa_to_host_va(info, dec_instr->src_operand.operand, &src_addr) == -1) { + PrintError("Could not translate write Source (Physical) to host VA\n"); + return -1; + } + } else { + if (guest_va_to_host_va(info, dec_instr->src_operand.operand, &src_addr) == -1) { + PrintError("Could not translate write Source (Virtual) to host VA\n"); + return -1; + } + } + + if (dec_instr->dst_operand.size == 1) { + movs8((addr_t *)&dst_addr, &src_addr, &tmp_rcx, (addr_t *)&(info->ctrl_regs.rflags)); + } else if (dec_instr->dst_operand.size == 2) { + movs16((addr_t *)&dst_addr, &src_addr, &tmp_rcx, (addr_t *)&(info->ctrl_regs.rflags)); + } else if (dec_instr->dst_operand.size == 4) { + movs32((addr_t *)&dst_addr, &src_addr, &tmp_rcx, (addr_t *)&(info->ctrl_regs.rflags)); +#ifdef __V3_64BIT__ + } else if (dec_instr->dst_operand.size == 8) { + movs64((addr_t *)&dst_addr, &src_addr, &tmp_rcx, (addr_t *)&(info->ctrl_regs.rflags)); #endif + } else { + PrintError("Invalid operand length\n"); + return -1; + } + + info->vm_regs.rdi += emulation_length; + info->vm_regs.rsi += emulation_length; + + // RCX is only modified if the rep prefix is present + if (dec_instr->prefixes.rep == 1) { + info->vm_regs.rcx -= emulation_iter_cnt; + } + + } else if (dec_instr->op_type == V3_OP_STOS) { + + if (dec_instr->dst_operand.size == 1) { + stos8((addr_t *)&dst_addr, (addr_t *)&(info->vm_regs.rax), &tmp_rcx, (addr_t *)&(info->ctrl_regs.rflags)); + } else if (dec_instr->dst_operand.size == 2) { + stos16((addr_t *)&dst_addr, (addr_t *)&(info->vm_regs.rax), &tmp_rcx, (addr_t *)&(info->ctrl_regs.rflags)); + } else if (dec_instr->dst_operand.size == 4) { + stos32((addr_t *)&dst_addr, (addr_t *)&(info->vm_regs.rax), &tmp_rcx, (addr_t *)&(info->ctrl_regs.rflags)); +#ifdef __V3_64BIT__ + } else if (dec_instr->dst_operand.size == 8) { + stos64((addr_t *)&dst_addr, (addr_t *)&(info->vm_regs.rax), &tmp_rcx, (addr_t *)&(info->ctrl_regs.rflags)); +#endif + } else { + PrintError("Invalid operand length\n"); + return -1; + } - - - v3_replace_shdw_page32(info, new_code_page->va, &(new_code_page->pte), &(saved_code_page->pte)); - - - list_add(&(new_code_page->page_list), &(info->emulator.emulated_pages)); - info->emulator.num_emulated_pages++; - - list_add(&(saved_code_page->page_list), &(info->emulator.saved_pages)); - info->emulator.num_saved_pages++; - - return 0; -} -*/ - - -static int set_stepping(struct guest_info * info) { - vmcb_ctrl_t * ctrl_area = GET_VMCB_CTRL_AREA((vmcb_t*)(info->vmm_data)); - ctrl_area->exceptions.db = 1; - - info->emulator.tf_enabled = ((struct rflags *)&(info->ctrl_regs.rflags))->tf; - - ((struct rflags *)&(info->ctrl_regs.rflags))->tf = 1; - - return 0; + info->vm_regs.rdi += emulation_length; + + // RCX is only modified if the rep prefix is present + if (dec_instr->prefixes.rep == 1) { + info->vm_regs.rcx -= emulation_iter_cnt; + } + + } else { + PrintError("Unimplemented String operation\n"); + return -1; + } + + if (write_fn(write_gpa, (void *)dst_addr, emulation_length, priv_data) != emulation_length) { + PrintError("Did not fully read hooked data\n"); + return -1; + } + + if (emulation_length == dec_instr->str_op_length) { + info->rip += dec_instr->instr_length; + } + + return emulation_length; } -static int unset_stepping(struct guest_info * info) { - vmcb_ctrl_t * ctrl_area = GET_VMCB_CTRL_AREA((vmcb_t*)(info->vmm_data)); - ctrl_area->exceptions.db = 0; - - ((struct rflags *)&(info->ctrl_regs.rflags))->tf = info->emulator.tf_enabled; - - if (info->emulator.tf_enabled) { - // Inject breakpoint exception into guest - } - - return 0; - +static int emulate_xchg_write_op(struct guest_info * info, struct x86_instr * dec_instr, + addr_t write_gva, addr_t write_gpa, addr_t dst_addr, + int (*write_fn)(addr_t guest_addr, void * src, uint_t length, void * priv_data), + void * priv_data) { + addr_t src_addr = 0; + addr_t em_dst_addr = 0; + int src_op_len = 0; + int dst_op_len = 0; + PrintDebug("Emulating XCHG write\n"); + + if (dec_instr->src_operand.type == MEM_OPERAND) { + if (info->shdw_pg_mode == SHADOW_PAGING) { + if (dec_instr->src_operand.operand != write_gva) { + PrintError("XCHG: Inconsistency between Pagefault and Instruction Decode XED_ADDR=%p, PF_ADDR=%p\n", + (void *)dec_instr->src_operand.operand, (void *)write_gva); + return -1; + } + } + + src_addr = dst_addr; + } else if (dec_instr->src_operand.type == REG_OPERAND) { + src_addr = dec_instr->src_operand.operand; + } else { + src_addr = (addr_t)&(dec_instr->src_operand.operand); + } + + + + if (dec_instr->dst_operand.type == MEM_OPERAND) { + if (info->shdw_pg_mode == SHADOW_PAGING) { + if (dec_instr->dst_operand.operand != write_gva) { + PrintError("XCHG: Inconsistency between Pagefault and Instruction Decode XED_ADDR=%p, PF_ADDR=%p\n", + (void *)dec_instr->dst_operand.operand, (void *)write_gva); + return -1; + } + } else { + //check that the operand (GVA) maps to the the faulting GPA + } + + em_dst_addr = dst_addr; + } else if (dec_instr->src_operand.type == REG_OPERAND) { + em_dst_addr = dec_instr->src_operand.operand; + } else { + em_dst_addr = (addr_t)&(dec_instr->src_operand.operand); + } + + dst_op_len = dec_instr->dst_operand.size; + src_op_len = dec_instr->src_operand.size; + + PrintDebug("Dst_Addr = %p, SRC operand = %p\n", + (void *)dst_addr, (void *)src_addr); + + + if (run_op(info, dec_instr->op_type, src_addr, em_dst_addr, src_op_len, dst_op_len) == -1) { + PrintError("Instruction Emulation Failed\n"); + return -1; + } + + if (write_fn(write_gpa, (void *)dst_addr, dst_op_len, priv_data) != dst_op_len) { + PrintError("Did not fully write hooked data\n"); + return -1; + } + + info->rip += dec_instr->instr_length; + + return dst_op_len; } + -// get the current instr -// check if rep + remove -// put into new page, vmexit after -// replace new page with current eip page -// -int v3_emulate_memory_read(struct guest_info * info, addr_t read_gva, - int (*read)(addr_t read_addr, void * dst, uint_t length, void * priv_data), - addr_t read_gpa, void * private_data) { - struct basic_instr_info instr_info; - char instr[15]; - int ret; - struct emulated_page * data_page = V3_Malloc(sizeof(struct emulated_page)); - addr_t data_addr_offset = PT32_PAGE_OFFSET(read_gva); - pte32_t saved_pte; - - PrintDebug("Emulating Read\n"); - - if (info->mem_mode == PHYSICAL_MEM) { - ret = read_guest_pa_memory(info, get_addr_linear(info, info->rip, &(info->segments.cs)), 15, instr); - } else { - ret = read_guest_va_memory(info, get_addr_linear(info, info->rip, &(info->segments.cs)), 15, instr); - } - -#ifdef DEBUG_EMULATOR - PrintDebug("Instr (15 bytes) at %x:\n", instr); - PrintTraceMemDump(instr, 15); -#endif - - - if (v3_basic_mem_decode(info, (addr_t)instr, &instr_info) == -1) { - PrintError("Could not do a basic memory instruction decode\n"); - V3_Free(data_page); - return -1; - } - - /* - if (instr_info.has_rep == 1) { - PrintError("We currently don't handle rep* instructions\n"); - V3_Free(data_page); - return -1; - } - */ - - data_page->page_addr = get_new_page(); - data_page->va = PT32_PAGE_ADDR(read_gva); - data_page->pte.present = 1; - data_page->pte.writable = 0; - data_page->pte.user_page = 1; - data_page->pte.page_base_addr = PT32_BASE_ADDR(data_page->page_addr); - - - // Read the data directly onto the emulated page - if (read(read_gpa, (void *)(data_page->page_addr + data_addr_offset), instr_info.op_size, private_data) != instr_info.op_size) { - PrintError("Read error in emulator\n"); - V3_FreePage((void *)(data_page->page_addr)); - V3_Free(data_page); - return -1; - } - - v3_replace_shdw_page32(info, data_page->va, &(data_page->pte), &saved_pte); - - - list_add(&(data_page->page_list), &(info->emulator.emulated_pages)); - info->emulator.num_emulated_pages++; - - if (saved_pte.present == 1) { - struct saved_page * saved_data_page = V3_Malloc(sizeof(struct saved_page)); - saved_data_page->pte = saved_pte; - saved_data_page->va = PT32_PAGE_ADDR(read_gva); - - list_add(&(saved_data_page->page_list), &(info->emulator.saved_pages)); - info->emulator.num_saved_pages++; - } - - - // setup_code_page(info, instr, &instr_info); - set_stepping(info); - - info->emulator.running = 1; - info->run_state = VM_EMULATING; - info->emulator.instr_length = instr_info.instr_length; - - return 0; +static int emulate_xchg_read_op(struct guest_info * info, struct x86_instr * dec_instr, + addr_t read_gva, addr_t read_gpa, addr_t src_addr, + int (*read_fn)(addr_t guest_addr, void * dst, uint_t length, void * priv_data), + int (*write_fn)(addr_t guest_addr, void * src, uint_t length, void * priv_data), + void * priv_data) { + addr_t em_src_addr = 0; + addr_t em_dst_addr = 0; + int src_op_len = 0; + int dst_op_len = 0; + + PrintDebug("Emulating XCHG Read\n"); + + if (dec_instr->src_operand.type == MEM_OPERAND) { + if (dec_instr->src_operand.operand != read_gva) { + PrintError("XCHG: Inconsistency between Pagefault and Instruction Decode XED_ADDR=%p, PF_ADDR=%p\n", + (void *)dec_instr->src_operand.operand, (void *)read_gva); + return -1; + } + + em_src_addr = src_addr; + } else if (dec_instr->src_operand.type == REG_OPERAND) { + em_src_addr = dec_instr->src_operand.operand; + } else { + em_src_addr = (addr_t)&(dec_instr->src_operand.operand); + } + + + + if (dec_instr->dst_operand.type == MEM_OPERAND) { + if (info->shdw_pg_mode == SHADOW_PAGING) { + if (dec_instr->dst_operand.operand != read_gva) { + PrintError("XCHG: Inconsistency between Pagefault and Instruction Decode XED_ADDR=%p, PF_ADDR=%p\n", + (void *)dec_instr->dst_operand.operand, (void *)read_gva); + return -1; + } + } else { + //check that the operand (GVA) maps to the the faulting GPA + } + + em_dst_addr = src_addr; + } else if (dec_instr->src_operand.type == REG_OPERAND) { + em_dst_addr = dec_instr->src_operand.operand; + } else { + em_dst_addr = (addr_t)&(dec_instr->src_operand.operand); + } + + dst_op_len = dec_instr->dst_operand.size; + src_op_len = dec_instr->src_operand.size; + + PrintDebug("Dst_Addr = %p, SRC operand = %p\n", + (void *)em_dst_addr, (void *)em_src_addr); + + + if (read_fn(read_gpa, (void *)src_addr, src_op_len, priv_data) != src_op_len) { + PrintError("Did not fully read hooked data\n"); + return -1; + } + + if (run_op(info, dec_instr->op_type, em_src_addr, em_dst_addr, src_op_len, dst_op_len) == -1) { + PrintError("Instruction Emulation Failed\n"); + return -1; + } + + if (write_fn(read_gpa, (void *)src_addr, dst_op_len, priv_data) != dst_op_len) { + PrintError("Did not fully write hooked data\n"); + return -1; + } + + info->rip += dec_instr->instr_length; + + return dst_op_len; } -int v3_emulate_memory_write(struct guest_info * info, addr_t write_gva, - int (*write)(addr_t write_addr, void * src, uint_t length, void * priv_data), - addr_t write_gpa, void * private_data) { - struct basic_instr_info instr_info; - char instr[15]; - int ret; - struct write_region * write_op = V3_Malloc(sizeof(struct write_region )); - struct emulated_page * data_page = V3_Malloc(sizeof(struct emulated_page)); - addr_t data_addr_offset = PT32_PAGE_OFFSET(write_gva); - pte32_t saved_pte; - int i; +int v3_emulate_write_op(struct guest_info * info, addr_t write_gva, addr_t write_gpa, addr_t dst_addr, + int (*write_fn)(addr_t guest_addr, void * src, uint_t length, void * priv_data), + void * priv_data) { + struct x86_instr dec_instr; + uchar_t instr[15]; + int ret = 0; + addr_t src_addr = 0; + int src_op_len = 0; + int dst_op_len = 0; - PrintDebug("Emulating Write for instruction at 0x%x\n",info->rip); + PrintDebug("Emulating Write for instruction at %p\n", (void *)(addr_t)(info->rip)); + PrintDebug("GVA=%p Dst_Addr=%p\n", (void *)write_gva, (void *)dst_addr); - if (info->mem_mode == PHYSICAL_MEM) { - ret = read_guest_pa_memory(info, get_addr_linear(info, info->rip, &(info->segments.cs)), 15, instr); - } else { - ret = read_guest_va_memory(info, get_addr_linear(info, info->rip, &(info->segments.cs)), 15, instr); - } + if (info->mem_mode == PHYSICAL_MEM) { + ret = read_guest_pa_memory(info, get_addr_linear(info, info->rip, &(info->segments.cs)), 15, instr); + } else { + ret = read_guest_va_memory(info, get_addr_linear(info, info->rip, &(info->segments.cs)), 15, instr); + } + if (ret == -1) { + return -1; + } - PrintDebug("Instruction is"); - for (i=0;i<15;i++) { PrintDebug(" 0x%x",instr[i]); } - PrintDebug("\n"); + if (v3_decode(info, (addr_t)instr, &dec_instr) == -1) { + PrintError("Decoding Error\n"); + // Kick off single step emulator + return -1; + } - if (v3_basic_mem_decode(info, (addr_t)instr, &instr_info) == -1) { - PrintError("Could not do a basic memory instruction decode\n"); - V3_Free(write_op); - V3_Free(data_page); - return -1; - } - - if (instr_info.has_rep==1) { - PrintDebug("Emulated instruction has rep\n"); - } - - /* - if (instr_info.has_rep == 1) { - PrintError("We currently don't handle rep* instructions\n"); - V3_Free(write_op); - V3_Free(data_page); - return -1; - } - */ - - data_page->page_addr = get_new_page(); - data_page->va = PT32_PAGE_ADDR(write_gva); - data_page->pte.present = 1; - data_page->pte.writable = 1; - data_page->pte.user_page = 1; - data_page->pte.page_base_addr = PT32_BASE_ADDR(data_page->page_addr); - - - - write_op->write = write; - write_op->write_addr = write_gpa; - write_op->length = instr_info.op_size; - write_op->private_data = private_data; - - write_op->write_data = (void *)(data_page->page_addr + data_addr_offset); - - list_add(&(write_op->write_list), &(info->emulator.write_regions)); - info->emulator.num_write_regions--; - - v3_replace_shdw_page32(info, data_page->va, &(data_page->pte), &saved_pte); - - - list_add(&(data_page->page_list), &(info->emulator.emulated_pages)); - info->emulator.num_emulated_pages++; - - if (saved_pte.present == 1) { - struct saved_page * saved_data_page = V3_Malloc(sizeof(struct saved_page)); - saved_data_page->pte = saved_pte; - saved_data_page->va = PT32_PAGE_ADDR(write_gva); - - list_add(&(saved_data_page->page_list), &(info->emulator.saved_pages)); - info->emulator.num_saved_pages++; - } - - - if (info->emulator.running == 0) { - // setup_code_page(info, instr, &instr_info); - set_stepping(info); - info->emulator.running = 1; - info->run_state = VM_EMULATING; - info->emulator.instr_length = instr_info.instr_length; - } - - return 0; + /* + * Instructions needing to be special cased.... * + */ + if (dec_instr.is_str_op) { + return emulate_string_write_op(info, &dec_instr, write_gva, write_gpa, dst_addr, write_fn, priv_data); + } else if (dec_instr.op_type == V3_OP_XCHG) { + return emulate_xchg_write_op(info, &dec_instr, write_gva, write_gpa, dst_addr, write_fn, priv_data); + } + + + if (info->shdw_pg_mode == SHADOW_PAGING) { + if ((dec_instr.dst_operand.type != MEM_OPERAND) || + (dec_instr.dst_operand.operand != write_gva)) { + PrintError("Inconsistency between Pagefault and Instruction Decode XED_ADDR=%p, PF_ADDR=%p\n", + (void *)dec_instr.dst_operand.operand, (void *)write_gva); + return -1; + } + } else { + //check that the operand (GVA) maps to the the faulting GPA + } + + + if (dec_instr.src_operand.type == MEM_OPERAND) { + if (info->mem_mode == PHYSICAL_MEM) { + if (guest_pa_to_host_va(info, dec_instr.src_operand.operand, &src_addr) == -1) { + PrintError("Could not translate write Source (Physical) to host VA\n"); + return -1; + } + } else { + if (guest_va_to_host_va(info, dec_instr.src_operand.operand, &src_addr) == -1) { + PrintError("Could not translate write Source (Virtual) to host VA\n"); + return -1; + } + } + } else if (dec_instr.src_operand.type == REG_OPERAND) { + src_addr = dec_instr.src_operand.operand; + } else { + src_addr = (addr_t)&(dec_instr.src_operand.operand); + } + + dst_op_len = dec_instr.dst_operand.size; + src_op_len = dec_instr.src_operand.size; + + PrintDebug("Dst_Addr = %p, SRC operand = %p\n", + (void *)dst_addr, (void *)src_addr); + + + if (run_op(info, dec_instr.op_type, src_addr, dst_addr, src_op_len, dst_op_len) == -1) { + PrintError("Instruction Emulation Failed\n"); + return -1; + } + + if (write_fn(write_gpa, (void *)dst_addr, dst_op_len, priv_data) != dst_op_len) { + PrintError("Did not fully write hooked data\n"); + return -1; + } + + info->rip += dec_instr.instr_length; + + return dst_op_len; } -// end emulation -int v3_emulation_exit_handler(struct guest_info * info) { - struct saved_page * svpg, * p_svpg; - struct emulated_page * empg, * p_empg; - struct write_region * wr_reg, * p_wr_reg; - pte32_t dummy_pte; - - // Complete the writes - // delete writes - // swap out emulated pages with blank dummies - // swap in saved pages - // increment rip - - PrintDebug("V3 Emulation Exit Handler\n"); - - list_for_each_entry_safe(wr_reg, p_wr_reg, &(info->emulator.write_regions), write_list) { - wr_reg->write(wr_reg->write_addr, wr_reg->write_data, wr_reg->length, wr_reg->private_data); - PrintDebug("Writing \n"); +int v3_emulate_read_op(struct guest_info * info, addr_t read_gva, addr_t read_gpa, addr_t src_addr, + int (*read_fn)(addr_t guest_addr, void * dst, uint_t length, void * priv_data), + int (*write_fn)(addr_t guest_addr, void * src, uint_t length, void * priv_data), + void * priv_data) { + struct x86_instr dec_instr; + uchar_t instr[15]; + int ret = 0; + addr_t dst_addr = 0; + int src_op_len = 0; + int dst_op_len = 0; + + PrintDebug("Emulating Read for instruction at %p\n", (void *)(addr_t)(info->rip)); + PrintDebug("GVA=%p\n", (void *)read_gva); + + if (info->mem_mode == PHYSICAL_MEM) { + ret = read_guest_pa_memory(info, get_addr_linear(info, info->rip, &(info->segments.cs)), 15, instr); + } else { + ret = read_guest_va_memory(info, get_addr_linear(info, info->rip, &(info->segments.cs)), 15, instr); + } - list_del(&(wr_reg->write_list)); - V3_Free(wr_reg); - - } - info->emulator.num_write_regions = 0; + if (ret == -1) { + PrintError("Could not read instruction for Emulated Read at %p\n", (void *)(addr_t)(info->rip)); + return -1; + } - *(uint_t *)&dummy_pte = 0; + if (v3_decode(info, (addr_t)instr, &dec_instr) == -1) { + PrintError("Decoding Error\n"); + // Kick off single step emulator + return -1; + } - list_for_each_entry_safe(empg, p_empg, &(info->emulator.emulated_pages), page_list) { - pte32_t empte32_t; - - PrintDebug("wiping page %x\n", empg->va); - - v3_replace_shdw_page32(info, empg->va, &dummy_pte, &empte32_t); - V3_FreePage((void *)(empg->page_addr)); - - list_del(&(empg->page_list)); - V3_Free(empg); - } - info->emulator.num_emulated_pages = 0; + if (dec_instr.is_str_op) { + PrintError("String operations not implemented on fully hooked regions\n"); + return -1; + } else if (dec_instr.op_type == V3_OP_XCHG) { + return emulate_xchg_read_op(info, &dec_instr, read_gva, read_gpa, src_addr, read_fn, write_fn, priv_data); + } + + if (info->shdw_pg_mode == SHADOW_PAGING) { + if ((dec_instr.src_operand.type != MEM_OPERAND) || + (dec_instr.src_operand.operand != read_gva)) { + PrintError("Inconsistency between Pagefault and Instruction Decode XED_ADDR=%p, PF_ADDR=%p operand_type=%d\n", + (void *)dec_instr.src_operand.operand, (void *)read_gva, dec_instr.src_operand.type); + return -1; + } + } else { + //check that the operand (GVA) maps to the the faulting GPA + } + + if (dec_instr.dst_operand.type == MEM_OPERAND) { + if (info->mem_mode == PHYSICAL_MEM) { + if (guest_pa_to_host_va(info, dec_instr.dst_operand.operand, &dst_addr) == -1) { + PrintError("Could not translate Read Destination (Physical) to host VA\n"); + return -1; + } + } else { + if (guest_va_to_host_va(info, dec_instr.dst_operand.operand, &dst_addr) == -1) { + PrintError("Could not translate Read Destination (Virtual) to host VA\n"); + return -1; + } + } + } else if (dec_instr.dst_operand.type == REG_OPERAND) { + dst_addr = dec_instr.dst_operand.operand; + } else { + dst_addr = (addr_t)&(dec_instr.dst_operand.operand); + } + + src_op_len = dec_instr.src_operand.size; + dst_op_len = dec_instr.dst_operand.size; + + PrintDebug("Dst_Addr = %p, SRC Addr = %p\n", + (void *)dst_addr, (void *)src_addr); + + if (read_fn(read_gpa, (void *)src_addr, src_op_len, priv_data) != src_op_len) { + PrintError("Did not fully read hooked data\n"); + return -1; + } + + if (run_op(info, dec_instr.op_type, src_addr, dst_addr, src_op_len, dst_op_len) == -1) { + PrintError("Instruction Emulation Failed\n"); + return -1; + } + + info->rip += dec_instr.instr_length; + + return src_op_len; +} - list_for_each_entry_safe(svpg, p_svpg, &(info->emulator.saved_pages), page_list) { - PrintDebug("Setting Saved page %x back\n", svpg->va); - v3_replace_shdw_page32(info, empg->va, &(svpg->pte), &dummy_pte); - - list_del(&(svpg->page_list)); - V3_Free(svpg); - } - info->emulator.num_saved_pages = 0; - info->run_state = VM_RUNNING; - info->emulator.running = 0; - //info->rip += info->emulator.instr_length; - PrintDebug("Returning to rip: 0x%x\n", info->rip); - - info->emulator.instr_length = 0; - - - unset_stepping(info); +static int run_op(struct guest_info * info, v3_op_type_t op_type, addr_t src_addr, addr_t dst_addr, int src_op_size, int dst_op_size) { + + if (src_op_size == 1) { + PrintDebug("Executing 8 bit instruction\n"); + + switch (op_type) { + case V3_OP_ADC: + adc8((addr_t *)dst_addr, (addr_t *)src_addr, (addr_t *)&(info->ctrl_regs.rflags)); + break; + case V3_OP_ADD: + add8((addr_t *)dst_addr, (addr_t *)src_addr, (addr_t *)&(info->ctrl_regs.rflags)); + break; + case V3_OP_AND: + and8((addr_t *)dst_addr, (addr_t *)src_addr, (addr_t *)&(info->ctrl_regs.rflags)); + break; + case V3_OP_OR: + or8((addr_t *)dst_addr, (addr_t *)src_addr, (addr_t *)&(info->ctrl_regs.rflags)); + break; + case V3_OP_XOR: + xor8((addr_t *)dst_addr, (addr_t *)src_addr, (addr_t *)&(info->ctrl_regs.rflags)); + break; + case V3_OP_SUB: + sub8((addr_t *)dst_addr, (addr_t *)src_addr, (addr_t *)&(info->ctrl_regs.rflags)); + break; + + case V3_OP_MOV: + mov8((addr_t *)dst_addr, (addr_t *)src_addr); + break; + + case V3_OP_MOVZX: + movzx8((addr_t *)dst_addr, (addr_t *)src_addr, dst_op_size); + break; + case V3_OP_MOVSX: + movsx8((addr_t *)dst_addr, (addr_t *)src_addr, dst_op_size); + break; + + case V3_OP_NOT: + not8((addr_t *)dst_addr); + break; + case V3_OP_XCHG: + xchg8((addr_t *)dst_addr, (addr_t *)src_addr); + break; + + + case V3_OP_INC: + inc8((addr_t *)dst_addr, (addr_t *)&(info->ctrl_regs.rflags)); + break; + case V3_OP_DEC: + dec8((addr_t *)dst_addr, (addr_t *)&(info->ctrl_regs.rflags)); + break; + case V3_OP_NEG: + neg8((addr_t *)dst_addr, (addr_t *)&(info->ctrl_regs.rflags)); + break; + case V3_OP_SETB: + setb8((addr_t *)dst_addr, (addr_t *)&(info->ctrl_regs.rflags)); + break; + case V3_OP_SETBE: + setbe8((addr_t *)dst_addr, (addr_t *)&(info->ctrl_regs.rflags)); + break; + case V3_OP_SETL: + setl8((addr_t *)dst_addr, (addr_t *)&(info->ctrl_regs.rflags)); + break; + case V3_OP_SETLE: + setle8((addr_t *)dst_addr, (addr_t *)&(info->ctrl_regs.rflags)); + break; + case V3_OP_SETNB: + setnb8((addr_t *)dst_addr, (addr_t *)&(info->ctrl_regs.rflags)); + break; + case V3_OP_SETNBE: + setnbe8((addr_t *)dst_addr, (addr_t *)&(info->ctrl_regs.rflags)); + break; + case V3_OP_SETNL: + setnl8((addr_t *)dst_addr, (addr_t *)&(info->ctrl_regs.rflags)); + break; + case V3_OP_SETNLE: + setnle8((addr_t *)dst_addr, (addr_t *)&(info->ctrl_regs.rflags)); + break; + case V3_OP_SETNO: + setno8((addr_t *)dst_addr, (addr_t *)&(info->ctrl_regs.rflags)); + break; + case V3_OP_SETNP: + setnp8((addr_t *)dst_addr, (addr_t *)&(info->ctrl_regs.rflags)); + break; + case V3_OP_SETNS: + setns8((addr_t *)dst_addr, (addr_t *)&(info->ctrl_regs.rflags)); + break; + case V3_OP_SETNZ: + setnz8((addr_t *)dst_addr, (addr_t *)&(info->ctrl_regs.rflags)); + break; + case V3_OP_SETO: + seto8((addr_t *)dst_addr, (addr_t *)&(info->ctrl_regs.rflags)); + break; + case V3_OP_SETP: + setp8((addr_t *)dst_addr, (addr_t *)&(info->ctrl_regs.rflags)); + break; + case V3_OP_SETS: + sets8((addr_t *)dst_addr, (addr_t *)&(info->ctrl_regs.rflags)); + break; + case V3_OP_SETZ: + setz8((addr_t *)dst_addr, (addr_t *)&(info->ctrl_regs.rflags)); + break; + + default: + PrintError("Unknown 8 bit instruction\n"); + return -1; + } + + } else if (src_op_size == 2) { + PrintDebug("Executing 16 bit instruction\n"); + + switch (op_type) { + case V3_OP_ADC: + adc16((addr_t *)dst_addr, (addr_t *)src_addr, (addr_t *)&(info->ctrl_regs.rflags)); + break; + case V3_OP_ADD: + add16((addr_t *)dst_addr, (addr_t *)src_addr, (addr_t *)&(info->ctrl_regs.rflags)); + break; + case V3_OP_AND: + and16((addr_t *)dst_addr, (addr_t *)src_addr, (addr_t *)&(info->ctrl_regs.rflags)); + break; + case V3_OP_OR: + or16((addr_t *)dst_addr, (addr_t *)src_addr, (addr_t *)&(info->ctrl_regs.rflags)); + break; + case V3_OP_XOR: + xor16((addr_t *)dst_addr, (addr_t *)src_addr, (addr_t *)&(info->ctrl_regs.rflags)); + break; + case V3_OP_SUB: + sub16((addr_t *)dst_addr, (addr_t *)src_addr, (addr_t *)&(info->ctrl_regs.rflags)); + break; + + + case V3_OP_INC: + inc16((addr_t *)dst_addr, (addr_t *)&(info->ctrl_regs.rflags)); + break; + case V3_OP_DEC: + dec16((addr_t *)dst_addr, (addr_t *)&(info->ctrl_regs.rflags)); + break; + case V3_OP_NEG: + neg16((addr_t *)dst_addr, (addr_t *)&(info->ctrl_regs.rflags)); + break; + + case V3_OP_MOV: + mov16((addr_t *)dst_addr, (addr_t *)src_addr); + break; + case V3_OP_MOVZX: + movzx16((addr_t *)dst_addr, (addr_t *)src_addr, dst_op_size); + break; + case V3_OP_MOVSX: + movsx16((addr_t *)dst_addr, (addr_t *)src_addr, dst_op_size); + break; + case V3_OP_NOT: + not16((addr_t *)dst_addr); + break; + case V3_OP_XCHG: + xchg16((addr_t *)dst_addr, (addr_t *)src_addr); + break; + + default: + PrintError("Unknown 16 bit instruction\n"); + return -1; + } + + } else if (src_op_size == 4) { + PrintDebug("Executing 32 bit instruction\n"); + + switch (op_type) { + case V3_OP_ADC: + adc32((addr_t *)dst_addr, (addr_t *)src_addr, (addr_t *)&(info->ctrl_regs.rflags)); + break; + case V3_OP_ADD: + add32((addr_t *)dst_addr, (addr_t *)src_addr, (addr_t *)&(info->ctrl_regs.rflags)); + break; + case V3_OP_AND: + and32((addr_t *)dst_addr, (addr_t *)src_addr, (addr_t *)&(info->ctrl_regs.rflags)); + break; + case V3_OP_OR: + or32((addr_t *)dst_addr, (addr_t *)src_addr, (addr_t *)&(info->ctrl_regs.rflags)); + break; + case V3_OP_XOR: + xor32((addr_t *)dst_addr, (addr_t *)src_addr, (addr_t *)&(info->ctrl_regs.rflags)); + break; + case V3_OP_SUB: + sub32((addr_t *)dst_addr, (addr_t *)src_addr, (addr_t *)&(info->ctrl_regs.rflags)); + break; + + case V3_OP_INC: + inc32((addr_t *)dst_addr, (addr_t *)&(info->ctrl_regs.rflags)); + break; + case V3_OP_DEC: + dec32((addr_t *)dst_addr, (addr_t *)&(info->ctrl_regs.rflags)); + break; + case V3_OP_NEG: + neg32((addr_t *)dst_addr, (addr_t *)&(info->ctrl_regs.rflags)); + break; + + case V3_OP_MOV: + mov32((addr_t *)dst_addr, (addr_t *)src_addr); + break; + + case V3_OP_NOT: + not32((addr_t *)dst_addr); + break; + case V3_OP_XCHG: + xchg32((addr_t *)dst_addr, (addr_t *)src_addr); + break; + + default: + PrintError("Unknown 32 bit instruction\n"); + return -1; + } + +#ifdef __V3_64BIT__ + } else if (src_op_size == 8) { + PrintDebug("Executing 64 bit instruction\n"); + + switch (op_type) { + case V3_OP_ADC: + adc64((addr_t *)dst_addr, (addr_t *)src_addr, (addr_t *)&(info->ctrl_regs.rflags)); + break; + case V3_OP_ADD: + add64((addr_t *)dst_addr, (addr_t *)src_addr, (addr_t *)&(info->ctrl_regs.rflags)); + break; + case V3_OP_AND: + and64((addr_t *)dst_addr, (addr_t *)src_addr, (addr_t *)&(info->ctrl_regs.rflags)); + break; + case V3_OP_OR: + or64((addr_t *)dst_addr, (addr_t *)src_addr, (addr_t *)&(info->ctrl_regs.rflags)); + break; + case V3_OP_XOR: + xor64((addr_t *)dst_addr, (addr_t *)src_addr, (addr_t *)&(info->ctrl_regs.rflags)); + break; + case V3_OP_SUB: + sub64((addr_t *)dst_addr, (addr_t *)src_addr, (addr_t *)&(info->ctrl_regs.rflags)); + break; + + case V3_OP_INC: + inc64((addr_t *)dst_addr, (addr_t *)&(info->ctrl_regs.rflags)); + break; + case V3_OP_DEC: + dec64((addr_t *)dst_addr, (addr_t *)&(info->ctrl_regs.rflags)); + break; + case V3_OP_NEG: + neg64((addr_t *)dst_addr, (addr_t *)&(info->ctrl_regs.rflags)); + break; + + case V3_OP_MOV: + mov64((addr_t *)dst_addr, (addr_t *)src_addr); + break; + + case V3_OP_NOT: + not64((addr_t *)dst_addr); + break; + case V3_OP_XCHG: + xchg64((addr_t *)dst_addr, (addr_t *)src_addr); + break; + + default: + PrintError("Unknown 64 bit instruction\n"); + return -1; + } +#endif - PrintDebug("returning from emulation\n"); + } else { + PrintError("Invalid Operation Size\n"); + return -1; + } - return 0; + return 0; }