From: Jack Lange Date: Wed, 6 Apr 2011 18:15:06 +0000 (-0500) Subject: cleaned up the emulation code to be more general X-Git-Url: http://v3vee.org/palacios/gitweb/gitweb.cgi?a=commitdiff_plain;h=0a4bd37f65c15fe50680e51b68f3f95ec8d347ea;p=palacios.git cleaned up the emulation code to be more general --- diff --git a/palacios/include/palacios/vmm_decoder.h b/palacios/include/palacios/vmm_decoder.h index 2229cd8..3631e14 100644 --- a/palacios/include/palacios/vmm_decoder.h +++ b/palacios/include/palacios/vmm_decoder.h @@ -34,7 +34,7 @@ typedef enum { V3_INVALID_OP, V3_OP_SETB, V3_OP_SETBE, V3_OP_SETL, V3_OP_SETLE, V3_OP_SETNB, V3_OP_SETNBE, V3_OP_SETNL, V3_OP_SETNLE, V3_OP_SETNO, V3_OP_SETNP, V3_OP_SETNS, V3_OP_SETNZ, V3_OP_SETO, V3_OP_SETP, V3_OP_SETS, - V3_OP_SETZ, V3_OP_MOVS, V3_OP_STOS, V3_OP_MOVZX, V3_OP_MOVSX} v3_op_type_t; + V3_OP_SETZ, V3_OP_MOVS, V3_OP_STOS, V3_OP_MOVZX, V3_OP_MOVSX } v3_op_type_t; typedef enum {INVALID_OPERAND, REG_OPERAND, MEM_OPERAND, IMM_OPERAND} v3_operand_type_t; @@ -43,6 +43,8 @@ struct x86_operand { addr_t operand; uint_t size; v3_operand_type_t type; + uint8_t read : 1; + uint8_t write : 1; }; struct x86_prefixes { @@ -83,7 +85,6 @@ struct x86_instr { struct x86_operand third_operand; addr_t str_op_length; addr_t is_str_op; - // void * decoder_data; }; diff --git a/palacios/include/palacios/vmm_emulator.h b/palacios/include/palacios/vmm_emulator.h index 9d1b6be..2f7913a 100644 --- a/palacios/include/palacios/vmm_emulator.h +++ b/palacios/include/palacios/vmm_emulator.h @@ -26,15 +26,11 @@ #include +struct x86_instr; -int v3_emulate_write_op(struct guest_info * info, addr_t write_gva, addr_t write_gpa, addr_t dst_addr, - int (*write_fn)(struct guest_info * core, addr_t guest_addr, void * src, uint_t length, void * priv_data), - void * priv_data); +int v3_emulate(struct guest_info * core, struct x86_instr * instr, + int mem_op_size, addr_t mem_hva_src, addr_t mem_hva_dst); -int v3_emulate_read_op(struct guest_info * info, addr_t read_gva, addr_t read_gpa, addr_t src_addr, - int (*read_fn)(struct guest_info * core, addr_t guest_addr, void * dst, uint_t length, void * priv_data), - int (*write_fn)(struct guest_info * core, addr_t guest_addr, void * src, uint_t length, void * priv_data), - void * priv_data); #endif // !__V3VEE__ diff --git a/palacios/include/palacios/vmm_mem_hook.h b/palacios/include/palacios/vmm_mem_hook.h index cd87518..702ba44 100644 --- a/palacios/include/palacios/vmm_mem_hook.h +++ b/palacios/include/palacios/vmm_mem_hook.h @@ -24,12 +24,24 @@ #ifdef __V3VEE__ +struct hashtable; + struct v3_mem_hooks { - void * hook_hvas; // this is an array of pages, equal to the number of cores + /* Scratch memory pages for full hooks (1 per core) */ + void * hook_hvas_1; + + /* A second set of scratch memory pages */ + /* The ONLY reason this exists is because of 'rep cmps'... */ + void * hook_hvas_2; + struct list_head hook_list; + + /* We track memory hooks via a hash table */ + /* keyed to the memory region pointer */ + struct hashtable * reg_table; }; @@ -50,11 +62,7 @@ int v3_hook_write_mem(struct v3_vm_info * vm, uint16_t core_id, int v3_unhook_mem(struct v3_vm_info * vm, uint16_t core_id, addr_t guest_addr_start); -int v3_find_mem_hook(struct v3_vm_info *vm, uint16_t core_id, addr_t guest_addr, - int (**read)(struct guest_info * core, addr_t guest_addr, void * dst, uint_t length, void * priv_data), - void **read_priv_data, - int (**write)(struct guest_info * core, addr_t guest_addr, void * src, uint_t length, void * priv_data), - void **write_priv_data); + diff --git a/palacios/src/palacios/vmm_emulator.c b/palacios/src/palacios/vmm_emulator.c index f371356..e5cf1f2 100644 --- a/palacios/src/palacios/vmm_emulator.c +++ b/palacios/src/palacios/vmm_emulator.c @@ -20,10 +20,9 @@ #include #include -#include #include -#include #include +#include #ifndef CONFIG_DEBUG_EMULATOR #undef PrintDebug @@ -31,847 +30,9 @@ #endif -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)(struct guest_info * core, 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; - - if (info->shdw_pg_mode == SHADOW_PAGING) { - 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; - } - } else { - // Nested paging (Need check??) - } - - /*emulation_length = ( (dec_instr->str_op_length < (0x1000 - PAGE_OFFSET_4KB(write_gva))) ? - dec_instr->str_op_length : - (0x1000 - PAGE_OFFSET_4KB(write_gva)));*/ - - if ((dec_instr->str_op_length * (dec_instr->dst_operand.size)) < (0x1000 - PAGE_OFFSET_4KB(write_gva))) { - emulation_length = dec_instr->str_op_length * dec_instr->dst_operand.size; - } else { - emulation_length = (0x1000 - PAGE_OFFSET_4KB(write_gva)); - PrintError("Warning: emulate_string_write_op emulating %u length operation, but request is for %u length\n", - emulation_length, (uint32_t)(dec_instr->str_op_length*(dec_instr->dst_operand.size))); - } - - /* ** 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 (v3_gpa_to_hva(info, dec_instr->src_operand.operand, &src_addr) == -1) { - PrintError("Could not translate write Source (Physical) to host VA\n"); - return -1; - } - } else { - if (v3_gva_to_hva(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; - } - - 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(info, 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; -} - - - -/* - This function is intended to handle pure read hooks, pure write hooks, and full hooks, - with and without backing memory for reads and writes - - A MAXIMUM OF ONE PAGE IS TRANSFERED BUT REGISTERS ARE UPDATED SO THAT - THE INSTRUCTION CAN BE RESTARTED - - read_fn == NULL - orig_src_addr == NULL => data at read_gpa is read - orig_src_addr != NULL => data at orig_src_addr is read - read_fn != NULL data is collected using read_fn - - write_fn == NULL - orig_dst_addr == NULL => data is written to write_gpa - orig_dst_addr != NULL => data is written to orig_dst_addr - write_fn != NULL - orig_dst_addr == NULL => data is sent to write_fn - orig_dst_addr != NULL => data is written to orig_dst_addr, then via write_fn - - -*/ -static int emulate_string_op(struct guest_info * info, struct x86_instr * dec_instr, - addr_t read_gva, addr_t read_gpa, addr_t read_hva, - addr_t write_gva, addr_t write_gpa, addr_t write_hva, - int (*read_fn)(struct guest_info * core, addr_t guest_addr, void * src, uint_t length, void * priv_data), - void * read_priv_data, - int (*write_fn)(struct guest_info * core, addr_t guest_addr, void * dst, uint_t length, void * priv_data), - void * write_priv_data) -{ - uint_t src_emulation_length = 0; - uint_t dst_emulation_length = 0; - uint_t emulation_length = 0; - uint_t emulation_iter_cnt = 0; - addr_t tmp_rcx = 0; - addr_t src_hva, dst_hva; - - - PrintDebug("emulate_string_op: read_gva=0x%p, read_gpa=0x%p, read_hva=0x%p, write_gva=0x%p, write_gpa=0x%p, write_hva=0x%p, read_fn=0x%p, read_priv_data=0x%p, write_fn=0x%p, write_priv_data=0x%p, len=0x%p\n", - (void*)read_gva,(void*)read_gpa,(void*)read_hva, (void*)write_gva,(void*)write_gpa,(void*)write_hva, - (void*)read_fn, (void*)read_priv_data, (void*)write_fn, (void*)write_priv_data, (void*)(dec_instr->str_op_length)); - - // v3_print_instr(dec_instr); - - // Sanity check the decoded instruction - - if (info->shdw_pg_mode == SHADOW_PAGING) { - // If we're reading, we better have a sane gva - if ((read_hva || read_fn) && (dec_instr->src_operand.operand != read_gva)) { - PrintError("Inconsistency between Pagefault and Instruction Decode XED_ADDR=%p, PF_ADDR=%p (Read)\n", - (void *)dec_instr->src_operand.operand, (void *)read_gva); - return -1; - } - // if we're writing, we better have a sane gva - if ((write_hva || write_fn) && (dec_instr->dst_operand.operand != write_gva)) { - PrintError("Inconsistency between Pagefault and Instruction Decode XED_ADDR=%p, PF_ADDR=%p (Write)\n", - (void *)dec_instr->dst_operand.operand, (void *)write_gva); - return -1; - } - } else { - // Nested paging (Need check??) - } - - - if (dec_instr->src_operand.size != dec_instr->dst_operand.size) { - PrintError("Source and Destination Operands are of different sizes\n"); - return -1; - } - - - // We will only read up to the next page boundary - - if ((dec_instr->str_op_length * (dec_instr->src_operand.size)) < (0x1000 - PAGE_OFFSET_4KB(read_gva))) { - src_emulation_length = dec_instr->str_op_length * dec_instr->src_operand.size; - } else { - src_emulation_length = (0x1000 - PAGE_OFFSET_4KB(read_gva)); - PrintError("Warning: emulate_string_op emulating src of %u length operation, but request is for %u length\n", - src_emulation_length, (uint32_t) (dec_instr->str_op_length*(dec_instr->src_operand.size))); - } - - // We will only write up to the next page boundary - - if ((dec_instr->str_op_length * (dec_instr->dst_operand.size)) < (0x1000 - PAGE_OFFSET_4KB(write_gva))) { - dst_emulation_length = dec_instr->str_op_length * dec_instr->dst_operand.size; - } else { - dst_emulation_length = (0x1000 - PAGE_OFFSET_4KB(write_gva)); - PrintError("Warning: emulate_string_op emulating dst of %u length operation, but request is for %u length\n", - dst_emulation_length, (uint32_t) (dec_instr->str_op_length*(dec_instr->dst_operand.size))); - } - - // We will only copy the minimum of what is available to be read or written - - if (src_emulation_lengthdst_emulation_length) { - emulation_length=dst_emulation_length; - // Note that this error is what is to be expected if you're coping to a different offset on a page - PrintError("Warning: emulate_string_op has src length %u but dst length %u\n", src_emulation_length, dst_emulation_length); - } else { - // equal - emulation_length=src_emulation_length; - } - - - // Fetch the data - - if (read_fn) { - // This is a full hook - full hooks never have backing memory - // This should use the scratch page allocated for the hook, but - // we do not know where that is at this point - src_hva = (addr_t) V3_Malloc(emulation_length); // hideous - should reuse memory - if (!src_hva) { - PrintError("Unable to allocate space for read operation in emulate_string_read_op\n"); - return -1; - } - if (read_fn(info, read_gpa, (void *)src_hva, emulation_length, read_priv_data) != emulation_length) { - PrintError("Did not fully read hooked data in emulate_string_op\n"); - return -1; - } - } else { - // This is ordinary memory - if (read_hva) { - // The caller told us where to read from - src_hva=read_hva; - } else { - // We need to figure out where to read from - if (info->mem_mode == PHYSICAL_MEM) { - if (v3_gpa_to_hva(info, dec_instr->src_operand.operand, &src_hva) == -1) { - PrintError("Could not translate write Source (Physical) to host VA\n"); - return -1; - } - } else { - if (v3_gva_to_hva(info, dec_instr->src_operand.operand, &src_hva) == -1) { - PrintError("Could not translate write Source (Virtual) to host VA\n"); - return -1; - } - } - } - } - - // Now src_hva points to the fetched data or to the in-VM data - - // Allocate space for the write, in case we need to copy out later - if (write_fn) { - // This is a full hook or a write hook - if (write_hva) { - // This is a write hook with backing memory - // The caller already told us where that memory is - dst_hva = write_hva; - } else { - // This is a full hook without backing memory - // Again, should use the scratch memory - dst_hva = (addr_t) V3_Malloc(emulation_length); // yuck - if (!dst_hva) { - PrintError("Unable to allocate space for write operation in emulate_string_op\n"); - if (read_fn) { - V3_Free((void*)src_hva); - } - return -1; - } - } - } else { - // This is ordinary memory - if (write_hva) { - // The caller told us where to write - dst_hva=write_hva; - } else { - // We need to figure out where to write - if (info->mem_mode == PHYSICAL_MEM) { - if (v3_gpa_to_hva(info, dec_instr->dst_operand.operand, &dst_hva) == -1) { - PrintError("Could not translate write Dest (Physical) to host VA\n"); - return -1; - } - } else { - if (v3_gva_to_hva(info, dec_instr->dst_operand.operand, &dst_hva) == -1) { - PrintError("Could not translate write Dest (Virtual) to host VA\n"); - return -1; - } - } - } - } - - // Now dst_addr points to where we will copy the data - - // How many items to copy - emulation_iter_cnt = emulation_length / dec_instr->dst_operand.size; - tmp_rcx = emulation_iter_cnt; - - - // Do the actual emulation - // The instruction implementation operates from data at src_hva to data at dest_hva - // Furthemore, it must operate for emulation_length steps - // And update tmp_rcx - // And the real rcx if we do have a rep prefix - switch (dec_instr->op_type) { - - case V3_OP_MOVS: { - - if (dec_instr->dst_operand.size == 1) { - movs8((addr_t *)&dst_hva, &src_hva, &tmp_rcx, (addr_t *)&(info->ctrl_regs.rflags)); - } else if (dec_instr->dst_operand.size == 2) { - movs16((addr_t *)&dst_hva, &src_hva, &tmp_rcx, (addr_t *)&(info->ctrl_regs.rflags)); - } else if (dec_instr->dst_operand.size == 4) { - movs32((addr_t *)&dst_hva, &src_hva, &tmp_rcx, (addr_t *)&(info->ctrl_regs.rflags)); -#ifdef __V3_64BIT__ - } else if (dec_instr->dst_operand.size == 8) { - movs64((addr_t *)&dst_hva, &src_hva, &tmp_rcx, (addr_t *)&(info->ctrl_regs.rflags)); -#endif - } else { - PrintError("Invalid operand length\n"); - return -1; - } - - // Can't these count down too? - PAD - - 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; - } - - } - break; - - case V3_OP_STOS: { - - if (dec_instr->dst_operand.size == 1) { - stos8((addr_t *)&dst_hva, (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_hva, (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_hva, (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_hva, (addr_t *)&(info->vm_regs.rax), &tmp_rcx, (addr_t *)&(info->ctrl_regs.rflags)); -#endif - } else { - PrintError("Invalid operand length\n"); - return -1; - } - - 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; - } - - } - break; - - default: { - PrintError("Unimplemented String operation\n"); - return -1; - } - break; - } - - // At this point, the data has been written over dst_hva, which - // is either our temporary buffer, or it's the requested target in write_hva - - if (write_fn) { - if (write_fn(info, write_gpa, (void *)dst_hva, emulation_length, write_priv_data) != emulation_length) { - PrintError("Did not fully write hooked data\n"); - return -1; - } - } - - // We only goto the next instruction if we have finished operating on all the data. - // If we haven't we'll restart the same instruction, but with rdi/rsi/rcx updated - // This is also how we handle going over a page boundary - if (emulation_length == dec_instr->str_op_length) { - info->rip += dec_instr->instr_length; - } - - - // Delete temporary buffers - if (read_fn) { - V3_Free((void*)src_hva); - } - if (write_fn && !write_hva) { - V3_Free((void*)dst_hva); - } - - - return emulation_length; -} - - - - - -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)(struct guest_info * core, 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(info, 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; -} - - - -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)(struct guest_info * core, addr_t guest_addr, void * dst, uint_t length, void * priv_data), - int (*write_fn)(struct guest_info * core, 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(info, 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(info, 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_write_op(struct guest_info * info, addr_t write_gva, addr_t write_gpa, addr_t dst_addr, - int (*write_fn)(struct guest_info * core, 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 %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 = v3_read_gpa_memory(info, get_addr_linear(info, info->rip, &(info->segments.cs)), 15, instr); - } else { - ret = v3_read_gva_memory(info, get_addr_linear(info, info->rip, &(info->segments.cs)), 15, instr); - } - - if (ret == -1) { - return -1; - } - - if (v3_decode(info, (addr_t)instr, &dec_instr) == -1) { - PrintError("Decoding Error\n"); - // Kick off single step emulator - return -1; - } - - /* - * 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 (v3_gpa_to_hva(info, dec_instr.src_operand.operand, &src_addr) == -1) { - PrintError("Could not translate write Source (Physical) to host VA\n"); - return -1; - } - } else { - if (v3_gva_to_hva(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(info, 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; -} - - -int v3_emulate_read_op(struct guest_info * info, addr_t read_gva, addr_t read_gpa, addr_t src_addr, - int (*read_fn)(struct guest_info * core, addr_t guest_addr, void * dst, uint_t length, void * priv_data), - int (*write_fn)(struct guest_info * core, 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 = v3_read_gpa_memory(info, get_addr_linear(info, info->rip, &(info->segments.cs)), 15, instr); - } else { - ret = v3_read_gva_memory(info, get_addr_linear(info, info->rip, &(info->segments.cs)), 15, instr); - } - - if (ret == -1) { - PrintError("Could not read instruction for Emulated Read at %p\n", (void *)(addr_t)(info->rip)); - return -1; - } - - - if (v3_decode(info, (addr_t)instr, &dec_instr) == -1) { - PrintError("Decoding Error\n"); - // Kick off single step emulator - return -1; - } - - if (dec_instr.is_str_op) { - // We got here due to a read fault due to a full memory hook on the - // region being READ. Thus our current write_fn is also for that region - // We need the region that will be WRITTEN, which we need to look up - // That region could be write or full hooked, in which case we need - // the associated write function for that region. If it's not - // hooked, then we need the relevant hva - // - // This all assumes that emulate_string_op() will handle at most - // a single page. Therefore we can consider only the starting pages - // for the read and write sides. We will restart the instruction on - // the next page, if needed. - addr_t write_gpa=0; - addr_t write_gva=0; - addr_t write_hva=0; - int (*dest_write_fn)(struct guest_info * core, addr_t guest_addr, void * src, uint_t length, void * priv_data)=0; - void *dest_write_priv_data; - struct v3_mem_region *dest_reg; - - if (dec_instr.dst_operand.type != MEM_OPERAND) { - write_gpa=0; - write_gva=0; - write_hva=0; // should calc target here and continue - dest_write_fn=0; - dest_write_priv_data=0; - PrintError("Emulation of string ops with non-memory destinations currently unsupported\n"); - v3_print_instr(&dec_instr); - return -1; - } else { - if (info->mem_mode == PHYSICAL_MEM) { - write_gpa = dec_instr.dst_operand.operand; - write_gva = write_gpa; - } else { - write_gva = dec_instr.dst_operand.operand; - if (v3_gva_to_gpa(info, dec_instr.dst_operand.operand, &write_gpa) == -1) { - // We are going to inject "Not Present" here to try to force - // the guest to build a PTE we can use. - // This needs to be fixed to inject the appropraite page fault - // given access permissions - struct pf_error_code c; - c.present=0; - c.write=0; - c.user=0; - c.rsvd_access=0; - c.ifetch=0; - c.rsvd=0; - v3_inject_guest_pf(info,write_gva,c); - return 0; - } - } - - // First we need to find the region to determine if we will need to write - // back to it and to check access - if (!(dest_reg=v3_get_mem_region(info->vm_info,info->cpu_id,write_gpa))) { - PrintError("Could not look up region for destination of string op\n"); - v3_print_instr(&dec_instr); - return -1; - } - - - if (dest_reg->flags.alloced) { - // We will need to write back to memory in addition to any hook function - if (v3_gpa_to_hva(info, write_gpa, &write_hva) == -1) { - PrintError("Unable to convert gpa to hva in emulation of string op write\n"); - v3_print_instr(&dec_instr); - return -1; - } - } else { - write_hva=0; // no actual writeback - hook function only - } - - // Now that we have the write_gpa, we need to find out whether it's a hooked region - // or just plain memory - - if (v3_find_mem_hook(info->vm_info, info->cpu_id, write_gpa, - 0, 0, // don't want the read function/data even if they exist - &dest_write_fn,&dest_write_priv_data) == -1) { - PrintError("Finding write destination memory hook failed\n"); - v3_print_instr(&dec_instr); - return -1; - } - - // We must have either or both of a write_hva and a dest_write_fn - if (!dest_write_fn && !write_hva) { - PrintError("Destination of string write has neither physical memory nor write hook!\n"); - v3_print_instr(&dec_instr); - return -1; - } - } - - - return emulate_string_op(info,&dec_instr, - read_gva,read_gpa, 0, // 0=> read hook has no backing memory - write_gva, write_gpa, write_hva, - read_fn, priv_data, // This is from the original call - dest_write_fn, dest_write_priv_data); // This is from our lookup - - } 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 (v3_gpa_to_hva(info, dec_instr.dst_operand.operand, &dst_addr) == -1) { - PrintError("Could not translate Read Destination (Physical) to host VA\n"); - return -1; - } - } else { - if (v3_gva_to_hva(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(info, 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; -} - - - - - - -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) { +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"); @@ -1140,3 +301,126 @@ static int run_op(struct guest_info * info, v3_op_type_t op_type, addr_t src_add return 0; } + + + +/* Returns the number of bytes written, or -1 if there is an error */ +static int run_str_op(struct guest_info * core, struct x86_instr * instr, + addr_t src_addr, addr_t dst_addr, + int op_size, int rep_cnt) { + + addr_t tmp_rcx = rep_cnt; + int emulation_length = op_size * rep_cnt; + struct rflags * flags_reg = (struct rflags *)&(core->ctrl_regs.rflags); + + if (instr->op_type == V3_OP_MOVS) { + if (op_size== 1) { + movs8((addr_t *)&dst_addr, &src_addr, &tmp_rcx, (addr_t *)&(core->ctrl_regs.rflags)); + } else if (op_size == 2) { + movs16((addr_t *)&dst_addr, &src_addr, &tmp_rcx, (addr_t *)&(core->ctrl_regs.rflags)); + } else if (op_size == 4) { + movs32((addr_t *)&dst_addr, &src_addr, &tmp_rcx, (addr_t *)&(core->ctrl_regs.rflags)); +#ifdef __V3_64BIT__ + } else if (op_size == 8) { + movs64((addr_t *)&dst_addr, &src_addr, &tmp_rcx, (addr_t *)&(core->ctrl_regs.rflags)); +#endif + } else { + PrintError("Invalid operand length\n"); + return -1; + } + + if (flags_reg->df == 0) { + core->vm_regs.rdi += emulation_length; + core->vm_regs.rsi += emulation_length; + } else { + core->vm_regs.rdi -= emulation_length; + core->vm_regs.rsi -= emulation_length; + } + + // RCX is only modified if the rep prefix is present + if (instr->prefixes.rep == 1) { + core->vm_regs.rcx -= rep_cnt; + } + + } else if (instr->op_type == V3_OP_STOS) { + if (op_size == 1) { + stos8((addr_t *)&dst_addr, (addr_t *)&(core->vm_regs.rax), &tmp_rcx, (addr_t *)&(core->ctrl_regs.rflags)); + } else if (op_size == 2) { + stos16((addr_t *)&dst_addr, (addr_t *)&(core->vm_regs.rax), &tmp_rcx, (addr_t *)&(core->ctrl_regs.rflags)); + } else if (op_size == 4) { + stos32((addr_t *)&dst_addr, (addr_t *)&(core->vm_regs.rax), &tmp_rcx, (addr_t *)&(core->ctrl_regs.rflags)); +#ifdef __V3_64BIT__ + } else if (op_size == 8) { + stos64((addr_t *)&dst_addr, (addr_t *)&(core->vm_regs.rax), &tmp_rcx, (addr_t *)&(core->ctrl_regs.rflags)); +#endif + } else { + PrintError("Invalid operand length\n"); + return -1; + } + + if (flags_reg->df == 0) { + core->vm_regs.rdi += emulation_length; + } else { + core->vm_regs.rdi -= emulation_length; + } + + // RCX is only modified if the rep prefix is present + if (instr->prefixes.rep == 1) { + core->vm_regs.rcx -= rep_cnt; + } + } else { + PrintError("Unimplemented String operation\n"); + return -1; + } + + return emulation_length; +} + + + +int v3_emulate(struct guest_info * core, struct x86_instr * instr, + int mem_op_size, addr_t mem_hva_src, addr_t mem_hva_dst) { + + addr_t src_hva = 0; + addr_t dst_hva = 0; + + PrintError("USING THE NEW EMULATOR\n"); + + if (instr->src_operand.type == MEM_OPERAND) { + src_hva = mem_hva_src; + } else if (instr->src_operand.type == REG_OPERAND) { + src_hva = instr->src_operand.operand; + } else { + src_hva = (addr_t)&(instr->src_operand.operand); + } + + if (instr->dst_operand.type == MEM_OPERAND) { + dst_hva = mem_hva_dst; + } else if (instr->dst_operand.type == REG_OPERAND) { + dst_hva = instr->dst_operand.operand; + } else { + dst_hva = (addr_t)&(instr->dst_operand.operand); + } + + + if (instr->is_str_op == 0) { + int src_op_len = instr->src_operand.size; + int dst_op_len = instr->dst_operand.size; + + run_op(core, instr->op_type, src_hva, dst_hva, src_op_len, dst_op_len); + + return dst_op_len; + } else { + // String Operation + int rep_cnt = 0; + + /* Both src and dst operand sizes should be identical */ + rep_cnt = mem_op_size / instr->src_operand.size; + + return run_str_op(core, instr, src_hva, dst_hva, instr->src_operand.size, rep_cnt); + } + + + + return -1; +} diff --git a/palacios/src/palacios/vmm_mem_hook.c b/palacios/src/palacios/vmm_mem_hook.c index 0b6c6b0..6993c9e 100644 --- a/palacios/src/palacios/vmm_mem_hook.c +++ b/palacios/src/palacios/vmm_mem_hook.c @@ -22,6 +22,8 @@ #include #include #include +#include +#include struct mem_hook { @@ -31,9 +33,9 @@ struct mem_hook { int (*write)(struct guest_info * core, addr_t guest_addr, void * src, uint_t length, void * priv_data); void * priv_data; - addr_t hook_hva; struct v3_mem_region * region; + struct list_head hook_node; }; @@ -41,14 +43,24 @@ struct mem_hook { static int free_hook(struct v3_vm_info * vm, struct mem_hook * hook); +static uint_t mem_hash_fn(addr_t key) { + return v3_hash_long(key, sizeof(void *) * 8); +} + +static int mem_eq_fn(addr_t key1, addr_t key2) { + return (key1 == key2); +} int v3_init_mem_hooks(struct v3_vm_info * vm) { struct v3_mem_hooks * hooks = &(vm->mem_hooks); - hooks->hook_hvas = V3_VAddr(V3_AllocPages(vm->num_cores)); + hooks->hook_hvas_1 = V3_VAddr(V3_AllocPages(vm->num_cores)); + hooks->hook_hvas_2 = V3_VAddr(V3_AllocPages(vm->num_cores)); INIT_LIST_HEAD(&(hooks->hook_list)); + hooks->reg_table = v3_create_htable(0, mem_hash_fn, mem_eq_fn); + return 0; } @@ -70,56 +82,218 @@ int v3_deinit_mem_hooks(struct v3_vm_info * vm) { } - V3_FreePages(V3_PAddr(hooks->hook_hvas), vm->num_cores); + v3_free_htable(hooks->reg_table, 0, 0); + + V3_FreePages(V3_PAddr(hooks->hook_hvas_1), vm->num_cores); + V3_FreePages(V3_PAddr(hooks->hook_hvas_2), vm->num_cores); return 0; } +static inline int get_op_length(struct x86_instr * instr, struct x86_operand * operand, addr_t tgt_addr) { + + if (instr->is_str_op) { + if ((instr->str_op_length * operand->size) < (0x1000 - PAGE_OFFSET_4KB(tgt_addr))) { + return (instr->str_op_length * operand->size); + } else { + return (0x1000 - PAGE_OFFSET_4KB(tgt_addr)); + } + } else { + return instr->src_operand.size; + } + +} + + -static int handle_mem_hook(struct guest_info * info, addr_t guest_va, addr_t guest_pa, +static int handle_mem_hook(struct guest_info * core, addr_t guest_va, addr_t guest_pa, struct v3_mem_region * reg, pf_error_t access_info) { - struct mem_hook * hook = reg->priv_data; - struct v3_mem_hooks * hooks = &(info->vm_info->mem_hooks); - addr_t op_addr = 0; + struct v3_mem_hooks * hooks = &(core->vm_info->mem_hooks); + struct x86_instr instr; + void * instr_ptr = NULL; + int bytes_emulated = 0; + int mem_op_size = 0; + int ret = 0; + + struct mem_hook * src_hook = NULL; + addr_t src_mem_op_hva = 0; + addr_t src_mem_op_gpa = 0; + int src_req_size = -1; + + struct mem_hook * dst_hook = NULL; + addr_t dst_mem_op_hva = 0; + addr_t dst_mem_op_gpa = 0; + int dst_req_size = -1; + + /* Find and decode hooked instruction */ + if (core->mem_mode == PHYSICAL_MEM) { + ret = v3_gpa_to_hva(core, get_addr_linear(core, core->rip, &(core->segments.cs)), (addr_t *)&instr_ptr); + } else { + ret = v3_gva_to_hva(core, get_addr_linear(core, core->rip, &(core->segments.cs)), (addr_t *)&instr_ptr); + } + + if (ret == -1) { + PrintError("Could not translate Instruction Address (%p)\n", (void *)core->rip); + return -1; + } + + if (v3_decode(core, (addr_t)instr_ptr, &instr) == -1) { + PrintError("Decoding Error\n"); + return -1; + } + - if (reg->flags.alloced == 0) { - if (hook->hook_hva & 0xfff) { - op_addr = (addr_t)(hooks->hook_hvas + (PAGE_SIZE * info->cpu_id)); + // Test source operand, if it's memory we need to do some translations, and handle a possible hook + if (instr.src_operand.type == MEM_OPERAND) { + struct v3_mem_region * src_reg = NULL; + + if (core->mem_mode == PHYSICAL_MEM) { + src_mem_op_gpa = instr.src_operand.operand; + } else { + if (v3_gva_to_gpa(core, instr.src_operand.operand, &src_mem_op_gpa) == -1) { + pf_error_t error = access_info; + + error.present = 0; + v3_inject_guest_pf(core, instr.src_operand.operand, error); + + return 0; + } + } + + if ((guest_pa >= reg->guest_start) && + (guest_pa <= reg->guest_end)) { + // Src address corresponds to faulted region + src_reg = reg; } else { - op_addr = hook->hook_hva; + // Note that this should only trigger for string operations + src_reg = v3_get_mem_region(core->vm_info, core->cpu_id, src_mem_op_gpa); } - } else { - if (v3_gpa_to_hva(info, guest_pa, &op_addr) == -1) { - PrintError("Could not translate hook address (%p)\n", (void *)guest_pa); + + if (src_reg == NULL) { + PrintError("Error finding Source region (addr=%p)\n", (void *)src_mem_op_gpa); return -1; } + + src_hook = (struct mem_hook *)v3_htable_search(hooks->reg_table, (addr_t)src_reg); + + // We don't check whether the region is a hook here because it doesn't yet matter. + // These hva calculations will be true regardless + if (src_reg->flags.alloced == 0) { + src_mem_op_hva = (addr_t)(hooks->hook_hvas_1 + (PAGE_SIZE * core->cpu_id)); + } else { + // We already have the region so we can do the conversion ourselves + src_mem_op_hva = (addr_t)V3_VAddr((void *)((src_mem_op_gpa - src_reg->guest_start) + src_reg->host_addr)); + } + + src_req_size = get_op_length(&instr, &(instr.src_operand), src_mem_op_hva); } - - if (access_info.write == 1) { - // Write Operation - if (v3_emulate_write_op(info, guest_va, guest_pa, op_addr, - hook->write, hook->priv_data) == -1) { - PrintError("Write Full Hook emulation failed\n"); + // Now do the same translation / hook handling for the second operand + if (instr.dst_operand.type == MEM_OPERAND) { + struct v3_mem_region * dst_reg = NULL; + + + if (core->mem_mode == PHYSICAL_MEM) { + dst_mem_op_gpa = instr.dst_operand.operand; + } else { + if (v3_gva_to_gpa(core, instr.dst_operand.operand, &dst_mem_op_gpa) == -1) { + pf_error_t error = access_info; + + error.present = 0; + v3_inject_guest_pf(core, instr.dst_operand.operand, error); + + return 0; + } + } + + if ((guest_pa >= reg->guest_start) && + (guest_pa <= reg->guest_end)) { + // Dst address corresponds to faulted region + dst_reg = reg; + } else { + // Note that this should only trigger for string operations + dst_reg = v3_get_mem_region(core->vm_info, core->cpu_id, dst_mem_op_gpa); + } + + if (dst_reg == NULL) { + PrintError("Error finding Source region (addr=%p)\n", (void *)dst_mem_op_gpa); return -1; } - } else { - // Read Operation or a read->write (e.g., string ops) - if (reg->flags.read == 1) { - PrintError("Tried to emulate read for a guest Readable page\n"); + dst_hook = (struct mem_hook *)v3_htable_search(hooks->reg_table, (addr_t)dst_reg); + + // We don't check whether the region is a hook here because it doesn't yet matter. + // These hva calculations will be true regardless + if (dst_reg->flags.alloced == 0) { + dst_mem_op_hva = (addr_t)(hooks->hook_hvas_2 + (PAGE_SIZE * core->cpu_id)); + } else { + // We already have the region so we can do the conversion ourselves + dst_mem_op_hva = (addr_t)V3_VAddr((void *)((dst_mem_op_gpa - dst_reg->guest_start) + dst_reg->host_addr)); + } + + dst_req_size = get_op_length(&instr, &(instr.dst_operand), dst_mem_op_hva); + } + + + mem_op_size = ((uint_t)src_req_size < (uint_t)dst_req_size) ? src_req_size : dst_req_size; + + + /* Now handle the hooks if necessary */ + if ( (src_hook != NULL) && (src_hook->read != NULL) && + (instr.src_operand.read == 1) ) { + + // Read in data from hook + + if (src_hook->read(core, src_mem_op_gpa, (void *)src_mem_op_hva, mem_op_size, src_hook->priv_data) == -1) { + PrintError("Read hook error at src_mem_op_gpa=%p\n", (void *)src_mem_op_gpa); + return -1; + } + } + + if ( (dst_hook != NULL) && (dst_hook->read != NULL) && + (instr.dst_operand.read == 1) ) { + + // Read in data from hook + + if (dst_hook->read(core, dst_mem_op_gpa, (void *)dst_mem_op_hva, mem_op_size, dst_hook->priv_data) == -1) { + PrintError("Read hook error at dst_mem_op_gpa=%p\n", (void *)dst_mem_op_gpa); + return -1; + } + } + + bytes_emulated = v3_emulate(core, &instr, mem_op_size, src_mem_op_hva, dst_mem_op_hva); + + if (bytes_emulated == -1) { + PrintError("Error emulating instruction\n"); + return -1; + } + + + if ( (src_hook != NULL) && (src_hook->write != NULL) && + (instr.src_operand.write == 1) ) { + + if (src_hook->write(core, src_mem_op_gpa, (void *)src_mem_op_hva, bytes_emulated, src_hook->priv_data) == -1) { + PrintError("Write hook error at src_mem_op_gpa=%p\n", (void *)src_mem_op_gpa); return -1; } - if (v3_emulate_read_op(info, guest_va, guest_pa, op_addr, - hook->read, hook->write, - hook->priv_data) == -1) { - PrintError("Read Full Hook emulation failed\n"); + } + + + if ( (dst_hook != NULL) && (dst_hook->write != NULL) && + (instr.dst_operand.write == 1) ) { + + if (dst_hook->write(core, dst_mem_op_gpa, (void *)dst_mem_op_hva, bytes_emulated, dst_hook->priv_data) == -1) { + PrintError("Write hook error at dst_mem_op_gpa=%p\n", (void *)dst_mem_op_gpa); return -1; } + } + + if (instr.is_str_op == 0) { + core->rip += instr.instr_length; } @@ -142,10 +316,9 @@ int v3_hook_write_mem(struct v3_vm_info * vm, uint16_t core_id, hook->write = write; hook->read = NULL; hook->priv_data = priv_data; - hook->hook_hva = (addr_t)V3_VAddr((void *)host_addr); entry = v3_create_mem_region(vm, core_id, guest_addr_start, guest_addr_end); - + hook->region = entry; entry->host_addr = host_addr; @@ -162,6 +335,7 @@ int v3_hook_write_mem(struct v3_vm_info * vm, uint16_t core_id, return -1; } + v3_htable_insert(hooks->reg_table, (addr_t)entry, (addr_t)hook); list_add(&(hook->hook_node), &(hooks->hook_list)); return 0; @@ -184,7 +358,6 @@ int v3_hook_full_mem(struct v3_vm_info * vm, uint16_t core_id, hook->write = write; hook->read = read; hook->priv_data = priv_data; - hook->hook_hva = (addr_t)0xfff; entry = v3_create_mem_region(vm, core_id, guest_addr_start, guest_addr_end); hook->region = entry; @@ -199,6 +372,7 @@ int v3_hook_full_mem(struct v3_vm_info * vm, uint16_t core_id, } list_add(&(hook->hook_node), &(hooks->hook_list)); + v3_htable_insert(hooks->reg_table, (addr_t)entry, (addr_t)hook); return 0; @@ -226,43 +400,4 @@ int v3_unhook_mem(struct v3_vm_info * vm, uint16_t core_id, addr_t guest_addr_st return 0; } -// Return the read and/or write hook functions of the region associated -// with the address, if any. -// -int v3_find_mem_hook(struct v3_vm_info *vm, uint16_t core_id, addr_t guest_addr, - int (**read)(struct guest_info * core, addr_t guest_addr, void * dst, uint_t length, void * priv_data), - void **read_priv_data, - int (**write)(struct guest_info * core, addr_t guest_addr, void * src, uint_t length, void * priv_data), - void **write_priv_data) -{ - struct v3_mem_region * reg = v3_get_mem_region(vm, core_id, guest_addr); - - if (!reg) { - return -1; - } - - // Should probably sanity-check the region smarter than the following - - struct mem_hook * hook = reg->priv_data; - - if (!hook) { - // This must be a simple memory region without hooks - if (read) { *read=0;} - if (read_priv_data) { *read_priv_data=0;} - if (write) { *write=0;} - if (write_priv_data) { *write_priv_data=0;} - return 0; - } - - // There is some form of hook here - copy it out for the caller - - if (read) { *read=hook->read;} - if (read_priv_data) { *read_priv_data=hook->priv_data;} - if (write) { *write=hook->write;} - if (write_priv_data) { *write_priv_data=hook->priv_data;} - - return 0; - -} - diff --git a/palacios/src/palacios/vmm_v3dec.c b/palacios/src/palacios/vmm_v3dec.c index 1b2436f..74c8c56 100644 --- a/palacios/src/palacios/vmm_v3dec.c +++ b/palacios/src/palacios/vmm_v3dec.c @@ -165,6 +165,7 @@ static int parse_operands(struct guest_info * core, uint8_t * instr_ptr, instr->src_operand.type = IMM_OPERAND; instr->src_operand.size = operand_width; + if (operand_width == 1) { instr->src_operand.operand = *(uint8_t *)instr_ptr; } else if (operand_width == 2) { @@ -176,6 +177,9 @@ static int parse_operands(struct guest_info * core, uint8_t * instr_ptr, return -1; } + instr->src_operand.read = 1; + instr->dst_operand.write = 1; + instr_ptr += operand_width; instr->num_operands = 2; @@ -210,6 +214,10 @@ static int parse_operands(struct guest_info * core, uint8_t * instr_ptr, instr->src_operand.type = REG_OPERAND; instr->src_operand.size = operand_width; + instr->src_operand.read = 1; + instr->dst_operand.write = 1; + + decode_gpr(core, reg_code, &(instr->src_operand)); instr->num_operands = 2; @@ -244,6 +252,9 @@ static int parse_operands(struct guest_info * core, uint8_t * instr_ptr, instr->dst_operand.type = REG_OPERAND; decode_gpr(core, reg_code, &(instr->dst_operand)); + instr->src_operand.read = 1; + instr->dst_operand.write = 1; + instr->num_operands = 2; break; @@ -256,7 +267,8 @@ static int parse_operands(struct guest_info * core, uint8_t * instr_ptr, case XOR_IMM2SX_8: { uint8_t reg_code = 0; - ret = decode_rm_operand(core, instr_ptr, form, instr, &(instr->src_operand), ®_code); + ret = decode_rm_operand(core, instr_ptr, form, instr, &(instr->dst_operand), ®_code); + if (ret == -1) { PrintError("Error decoding operand\n"); @@ -269,6 +281,9 @@ static int parse_operands(struct guest_info * core, uint8_t * instr_ptr, instr->src_operand.size = operand_width; instr->src_operand.operand = *(sint8_t *)instr_ptr; // sign extend. + instr->src_operand.read = 1; + instr->dst_operand.write = 1; + instr_ptr += 1; instr->num_operands = 2; @@ -292,10 +307,15 @@ static int parse_operands(struct guest_info * core, uint8_t * instr_ptr, instr->src_operand.size = operand_width; instr->src_operand.operand = get_addr_linear(core, MASK(core->vm_regs.rsi, addr_width), &(core->segments.ds)); + instr->dst_operand.type = MEM_OPERAND; instr->dst_operand.size = operand_width; instr->dst_operand.operand = get_addr_linear(core, MASK(core->vm_regs.rdi, addr_width), &(core->segments.es)); + + instr->src_operand.read = 1; + instr->dst_operand.write = 1; + instr->num_operands = 2; break; @@ -317,6 +337,9 @@ static int parse_operands(struct guest_info * core, uint8_t * instr_ptr, instr->dst_operand.size = operand_width; decode_cr(core, reg_code, &(instr->dst_operand)); + instr->src_operand.read = 1; + instr->dst_operand.write = 1; + instr->num_operands = 2; break; } @@ -326,6 +349,7 @@ static int parse_operands(struct guest_info * core, uint8_t * instr_ptr, ret = decode_rm_operand(core, instr_ptr, form, instr, &(instr->dst_operand), ®_code); + if (ret == -1) { PrintError("Error decoding operand for (%s)\n", op_form_to_str(form)); return -1; @@ -337,6 +361,9 @@ static int parse_operands(struct guest_info * core, uint8_t * instr_ptr, instr->src_operand.size = operand_width; decode_cr(core, reg_code, &(instr->src_operand)); + instr->src_operand.read = 1; + instr->dst_operand.write = 1; + instr->num_operands = 2; break; } @@ -358,6 +385,9 @@ static int parse_operands(struct guest_info * core, uint8_t * instr_ptr, instr->dst_operand.size = operand_width; instr->dst_operand.operand = get_addr_linear(core, MASK(core->vm_regs.rdi, addr_width), &(core->segments.es)); + instr->src_operand.read = 1; + instr->dst_operand.write = 1; + instr->num_operands = 2; break; @@ -365,8 +395,6 @@ static int parse_operands(struct guest_info * core, uint8_t * instr_ptr, case INVLPG: { uint8_t reg_code = 0; - // We use the dst operand here to maintain bug-for-bug compatibility with XED - ret = decode_rm_operand(core, instr_ptr, form, instr, &(instr->dst_operand), ®_code); if (ret == -1) { diff --git a/palacios/src/palacios/vmm_xed.c b/palacios/src/palacios/vmm_xed.c index b727d89..80d7858 100644 --- a/palacios/src/palacios/vmm_xed.c +++ b/palacios/src/palacios/vmm_xed.c @@ -203,11 +203,15 @@ static int decode_string_op(struct guest_info * info, return -1; } + if (get_memory_operand(info, xed_instr, 1, &(instr->src_operand)) == -1) { PrintError("Could not get Source memory operand\n"); return -1; } + instr->dst_operand.write = 1; + instr->src_operand.read = 1; + if (instr->prefixes.rep == 1) { addr_t reg_addr = 0; uint_t reg_length = 0; @@ -232,6 +236,9 @@ static int decode_string_op(struct guest_info * info, &(instr->src_operand.size)); instr->src_operand.type = REG_OPERAND; + instr->src_operand.read = 1; + instr->dst_operand.write = 1; + if (instr->prefixes.rep == 1) { addr_t reg_addr = 0; uint_t reg_length = 0; @@ -395,6 +402,18 @@ int v3_decode(struct guest_info * info, addr_t instr_ptr, struct x86_instr * ins v3_op = &(instr->dst_operand); + if ((op->_rw == XED_OPERAND_ACTION_RW) || + (op->_rw == XED_OPERAND_ACTION_R)|| + (op->_rw == XED_OPERAND_ACTION_RCW)) { + v3_op->read = 1; + } + + if ((op->_rw == XED_OPERAND_ACTION_RW) || + (op->_rw == XED_OPERAND_ACTION_W) || + (op->_rw == XED_OPERAND_ACTION_CRW)) { + v3_op->write = 1; + } + if (xed_operand_is_register(op_enum)) { xed_reg_enum_t xed_reg = xed_decoded_inst_get_reg(&xed_instr, op_enum); int v3_reg_type = xed_reg_to_v3_reg(info, @@ -461,6 +480,18 @@ int v3_decode(struct guest_info * info, addr_t instr_ptr, struct x86_instr * ins */ v3_op = &(instr->src_operand); + if ((op->_rw == XED_OPERAND_ACTION_RW) || + (op->_rw == XED_OPERAND_ACTION_R)|| + (op->_rw == XED_OPERAND_ACTION_RCW)) { + v3_op->read = 1; + } + + if ((op->_rw == XED_OPERAND_ACTION_RW) || + (op->_rw == XED_OPERAND_ACTION_W) || + (op->_rw == XED_OPERAND_ACTION_CRW)) { + v3_op->write = 1; + } + if (xed_operand_is_register(op_enum)) { xed_reg_enum_t xed_reg = xed_decoded_inst_get_reg(&xed_instr, op_enum); int v3_reg_type = xed_reg_to_v3_reg(info, @@ -527,6 +558,18 @@ int v3_decode(struct guest_info * info, addr_t instr_ptr, struct x86_instr * ins xed_operand_type_enum_t op_type = xed_operand_type(op); xed_operand_enum_t op_enum = xed_operand_name(op); + if ((op->_rw == XED_OPERAND_ACTION_RW) || + (op->_rw == XED_OPERAND_ACTION_R)|| + (op->_rw == XED_OPERAND_ACTION_RCW)) { + instr->third_operand.read = 1; + } + + if ((op->_rw == XED_OPERAND_ACTION_RW) || + (op->_rw == XED_OPERAND_ACTION_W) || + (op->_rw == XED_OPERAND_ACTION_CRW)) { + instr->third_operand.write = 1; + } + if (xed_operand_is_register(op_enum)) { xed_reg_enum_t xed_reg = xed_decoded_inst_get_reg(&xed_instr, op_enum); int v3_reg_type = xed_reg_to_v3_reg(info,