X-Git-Url: http://v3vee.org/palacios/gitweb/gitweb.cgi?a=blobdiff_plain;f=palacios%2Fsrc%2Fpalacios%2Fvmm_emulator.c;h=1a0e8687a3d6a8bab06e0eadd199d27eb7845b45;hb=433cc616000bbede723288f391e01a7750f230f6;hp=5fb922640c3d324a1117a6b0a1ea698ef889e7af;hpb=82b8b87c344fcd1eab22e3f3be5ad54cbb3f8f68;p=palacios.git diff --git a/palacios/src/palacios/vmm_emulator.c b/palacios/src/palacios/vmm_emulator.c index 5fb9226..1a0e868 100644 --- a/palacios/src/palacios/vmm_emulator.c +++ b/palacios/src/palacios/vmm_emulator.c @@ -35,22 +35,34 @@ static int run_op(struct guest_info * info, v3_op_type_t op_type, addr_t src_add // 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), + 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 (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; + 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))) ? + + /*emulation_length = ( (dec_instr->str_op_length < (0x1000 - PAGE_OFFSET_4KB(write_gva))) ? dec_instr->str_op_length : - (0x1000 - PAGE_OFFSET_4KB(write_gva))); + (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; @@ -60,12 +72,12 @@ static int emulate_string_write_op(struct guest_info * info, struct x86_instr * // 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) { + 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 (guest_va_to_host_va(info, dec_instr->src_operand.operand, &src_addr) == -1) { + 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; } @@ -123,7 +135,7 @@ static int emulate_string_write_op(struct guest_info * info, struct x86_instr * return -1; } - if (write_fn(write_gpa, (void *)dst_addr, emulation_length, priv_data) != emulation_length) { + 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; } @@ -136,9 +148,269 @@ static int emulate_string_write_op(struct guest_info * info, struct x86_instr * } + +/* + This function is intended to handle pure read hooks, pure write hook, and full hooks, + with and without backing memory for writes + + 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 orig_src_addr, + addr_t write_gva, addr_t write_gpa, addr_t orig_dst_addr, + int (*read_fn)(struct guest_info * core, addr_t guest_addr, void * src, uint_t length, void * priv_data), + int (*write_fn)(struct guest_info * core, addr_t guest_addr, void * dst, uint_t length, void * priv_data), + void * 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_addr, dst_addr; + + + // Sanity check the decoded instruction + + if (info->shdw_pg_mode == SHADOW_PAGING) { + if (!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 (!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; + 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) { + src_addr = (addr_t) V3_Malloc(emulation_length); // hideous - should reuse memory + if (!src_addr) { + PrintError("Unable to allocate space for read operation in emulate_string_read_op\n"); + return -1; + } + if (read_fn(info, read_gpa, (void *)src_addr, emulation_length, priv_data) != emulation_length) { + PrintError("Did not fully read hooked data in emulate_string_op\n"); + return -1; + } + } else { + if (orig_src_addr) { + src_addr=orig_src_addr; + } else { + 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; + } + } + } + } + + // Now src_addr points to the fetched data in HVA + + // Allocate space for the write, in case we need to copy out later + if (write_fn) { + if (orig_dst_addr) { + dst_addr=orig_dst_addr; + } else { + dst_addr = (addr_t) V3_Malloc(emulation_length); // yuck + if (!dst_addr) { + PrintError("Unable to allocate space for write operation in emulate_string_op\n"); + if (read_fn) { + V3_Free((void*)src_addr); + } + return -1; + } + } + } else { + if (orig_dst_addr) { + dst_addr=orig_dst_addr; + } else { + if (info->mem_mode == PHYSICAL_MEM) { + if (v3_gpa_to_hva(info, dec_instr->dst_operand.operand, &dst_addr) == -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_addr) == -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; + + + switch (dec_instr->op_type) { + + case V3_OP_MOVS: { + + 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; + } + + // 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_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; + } + + } + break; + + default: { + PrintError("Unimplemented String operation\n"); + return -1; + } + break; + } + + // At this point, the data has been written over dst_addr, which + // is either our temporary buffer, or it's the requested target in orig_dst_addr + + if (write_fn) { + if (write_fn(info, write_gpa, (void *)dst_addr, emulation_length, priv_data) != emulation_length) { + PrintError("Did not fully write hooked data\n"); + return -1; + } + } + + if (emulation_length == dec_instr->str_op_length) { + info->rip += dec_instr->instr_length; + } + + + // Delete temporary buffers + if (read_fn) { + V3_Free((void*)src_addr); + } + if (write_fn && !orig_dst_addr) { + V3_Free((void*)dst_addr); + } + + + 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)(addr_t guest_addr, void * src, 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 src_addr = 0; addr_t em_dst_addr = 0; @@ -194,7 +466,7 @@ static int emulate_xchg_write_op(struct guest_info * info, struct x86_instr * de return -1; } - if (write_fn(write_gpa, (void *)dst_addr, dst_op_len, priv_data) != dst_op_len) { + 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; } @@ -208,8 +480,8 @@ static int emulate_xchg_write_op(struct guest_info * info, struct x86_instr * de 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), + 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; @@ -259,7 +531,7 @@ static int emulate_xchg_read_op(struct guest_info * info, struct x86_instr * dec (void *)em_dst_addr, (void *)em_src_addr); - if (read_fn(read_gpa, (void *)src_addr, src_op_len, priv_data) != src_op_len) { + 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; } @@ -269,7 +541,7 @@ static int emulate_xchg_read_op(struct guest_info * info, struct x86_instr * dec return -1; } - if (write_fn(read_gpa, (void *)src_addr, dst_op_len, priv_data) != dst_op_len) { + 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; } @@ -283,7 +555,7 @@ static int emulate_xchg_read_op(struct guest_info * info, struct x86_instr * dec 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), + 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]; @@ -296,9 +568,9 @@ int v3_emulate_write_op(struct guest_info * info, addr_t write_gva, addr_t write 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); + ret = v3_read_gpa_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); + ret = v3_read_gva_memory(info, get_addr_linear(info, info->rip, &(info->segments.cs)), 15, instr); } if (ret == -1) { @@ -335,12 +607,12 @@ int v3_emulate_write_op(struct guest_info * info, addr_t write_gva, addr_t write 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) { + 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 (guest_va_to_host_va(info, dec_instr.src_operand.operand, &src_addr) == -1) { + 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; } @@ -363,7 +635,7 @@ int v3_emulate_write_op(struct guest_info * info, addr_t write_gva, addr_t write return -1; } - if (write_fn(write_gpa, (void *)dst_addr, dst_op_len, priv_data) != dst_op_len) { + 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; } @@ -375,8 +647,8 @@ int v3_emulate_write_op(struct guest_info * info, addr_t write_gva, addr_t write 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), + 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]; @@ -389,9 +661,9 @@ int v3_emulate_read_op(struct guest_info * info, addr_t read_gva, addr_t read_gp 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); + ret = v3_read_gpa_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); + ret = v3_read_gva_memory(info, get_addr_linear(info, info->rip, &(info->segments.cs)), 15, instr); } if (ret == -1) { @@ -407,8 +679,11 @@ int v3_emulate_read_op(struct guest_info * info, addr_t read_gva, addr_t read_gp } if (dec_instr.is_str_op) { - PrintError("String operations not implemented on fully hooked regions\n"); - return -1; + return emulate_string_op(info,&dec_instr, + read_gva,read_gpa,0, + 0, 0, 0, + read_fn,write_fn, + priv_data); } 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); } @@ -426,12 +701,12 @@ int v3_emulate_read_op(struct guest_info * info, addr_t read_gva, addr_t read_gp 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) { + 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 (guest_va_to_host_va(info, dec_instr.dst_operand.operand, &dst_addr) == -1) { + 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; } @@ -448,7 +723,7 @@ int v3_emulate_read_op(struct guest_info * info, addr_t read_gva, addr_t read_gp 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) { + 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; }