From: Jack Lange Date: Fri, 4 Apr 2008 06:01:57 +0000 (+0000) Subject: added full io support X-Git-Tag: working-cdboot-physical-but-not-qemu~42 X-Git-Url: http://v3vee.org/palacios/gitweb/gitweb.cgi?p=palacios.git;a=commitdiff_plain;h=8c38488bc7d510a85ad23222ddd228924d14be8a added full io support --- diff --git a/palacios/build/vm_kernel b/palacios/build/vm_kernel index e6c7836..5b5ab6d 100755 Binary files a/palacios/build/vm_kernel and b/palacios/build/vm_kernel differ diff --git a/palacios/include/geekos/svm.h b/palacios/include/geekos/svm.h index ddd3414..8b606b4 100644 --- a/palacios/include/geekos/svm.h +++ b/palacios/include/geekos/svm.h @@ -68,6 +68,8 @@ int init_svm_guest(struct guest_info *info); int start_svm_guest(struct guest_info * info); +inline addr_t get_rip_linear(struct guest_info * info, addr_t rip, addr_t cs_base); + #endif diff --git a/palacios/include/geekos/svm_io.h b/palacios/include/geekos/svm_io.h index 83ee21d..361a1d9 100644 --- a/palacios/include/geekos/svm_io.h +++ b/palacios/include/geekos/svm_io.h @@ -12,9 +12,9 @@ struct svm_io_info { uint_t sz8 : 1 PACKED; // 8 bit op size uint_t sz16 : 1 PACKED; // 16 bit op size uint_t sz32 : 1 PACKED; // 32 bit op size - uint_t A16 : 1 PACKED; // 16 bit addr - uint_t A32 : 1 PACKED; // 32 bit addr - uint_t A64 : 1 PACKED; // 64 bit addr + uint_t addr16 : 1 PACKED; // 16 bit addr + uint_t addr32 : 1 PACKED; // 32 bit addr + uint_t addr64 : 1 PACKED; // 64 bit addr uint_t rsvd2 : 6 PACKED; // Should be Zero ushort_t port PACKED; // port number }; diff --git a/palacios/include/geekos/vmm_ctrl_regs.h b/palacios/include/geekos/vmm_ctrl_regs.h index f0d63fe..0bebdbc 100644 --- a/palacios/include/geekos/vmm_ctrl_regs.h +++ b/palacios/include/geekos/vmm_ctrl_regs.h @@ -115,4 +115,33 @@ struct cr4_64 { uint_t rsvd2 : 32; }; + + +struct rflags { + uint_t cf : 1; // carry flag + uint_t rsvd1 : 1; // Must be 1 + uint_t pf : 1; // parity flag + uint_t rsvd2 : 1; // Read as 0 + uint_t af : 1; // Auxillary flag + uint_t rsvd3 : 1; // Read as 0 + uint_t zf : 1; // zero flag + uint_t sf : 1; // sign flag + uint_t tf : 1; // trap flag + uint_t intr : 1; // interrupt flag + uint_t df : 1; // direction flag + uint_t of : 1; // overflow flag + uint_t iopl : 2; // IO privilege level + uint_t nt : 1; // nested task + uint_t rsvd4 : 1; // read as 0 + uint_t rf : 1; // resume flag + uint_t vm : 1; // Virtual-8086 mode + uint_t ac : 1; // alignment check + uint_t vif : 1; // virtual interrupt flag + uint_t vip : 1; // virtual interrupt pending + uint_t id : 1; // ID flag + uint_t rsvd5 : 10; // Read as 0 + uint_t rsvd6 : 32; // Read as 0 +}; + + #endif diff --git a/palacios/include/geekos/vmm_emulate.h b/palacios/include/geekos/vmm_emulate.h index 5157214..4b843cc 100644 --- a/palacios/include/geekos/vmm_emulate.h +++ b/palacios/include/geekos/vmm_emulate.h @@ -150,6 +150,21 @@ static inline int is_prefix_byte(char byte) { +static inline addr_t get_rip_linear(struct guest_info * info, addr_t rip, addr_t cs_base) { + switch (info->cpu_mode) { + case REAL: + return rip + (cs_base << 4); + break; + case PROTECTED: + case PROTECTED_PG: + return rip + cs_base; + break; + default: + return 0; + } +} + + typedef enum {INVALID_ADDR_TYPE, REG, DISP0, DISP8, DISP16, DISP32} modrm_mode_t; typedef enum {INVALID_REG_SIZE, REG64, REG32, REG16, REG8} reg_size_t; typedef enum {INVALID_OPERAND, REG_OPERAND, MEM_OPERAND} operand_type_t; diff --git a/palacios/include/geekos/vmm_io.h b/palacios/include/geekos/vmm_io.h index 8909d55..556be88 100644 --- a/palacios/include/geekos/vmm_io.h +++ b/palacios/include/geekos/vmm_io.h @@ -13,10 +13,10 @@ typedef struct vmm_io_hook { ushort_t port; // Reads data into the IO port (IN, INS) - int (*read)(ushort_t port, void * dst, uint_t length, uint_t io_width); + int (*read)(ushort_t port, void * dst, uint_t length); // Writes data from the IO port (OUT, OUTS) - int (*write)(ushort_t port, void * src, uint_t length, uint_t io_width); + int (*write)(ushort_t port, void * src, uint_t length); struct vmm_io_hook * next; struct vmm_io_hook * prev; @@ -42,8 +42,8 @@ vmm_io_hook_t * get_io_hook(vmm_io_map_t * io_map, uint_t port); /* External API */ void hook_io_port(vmm_io_map_t * io_map, uint_t port, - int (*read)(ushort_t port, void * dst, uint_t length, uint_t io_width), - int (*write)(ushort_t port, void * src, uint_t length, uint_t io_width)); + int (*read)(ushort_t port, void * dst, uint_t length), + int (*write)(ushort_t port, void * src, uint_t length)); void init_vmm_io_map(vmm_io_map_t * io_map); diff --git a/palacios/src/geekos/main.c b/palacios/src/geekos/main.c index 9877cd7..ea43291 100644 --- a/palacios/src/geekos/main.c +++ b/palacios/src/geekos/main.c @@ -3,7 +3,7 @@ * Copyright (c) 2001,2003,2004 David H. Hovemeyer * Copyright (c) 2003, Jeffrey K. Hollingsworth * Copyright (c) 2004, Iulian Neamtiu - * $Revision: 1.29 $ + * $Revision: 1.30 $ * * This is free software. You are permitted to use, * redistribute, and modify it as specified in the file "COPYING". @@ -88,7 +88,7 @@ inline uchar_t MyIn_Byte(ushort_t port) -int IO_Read(ushort_t port, void * dst, uint_t length, uint_t io_width) { +int IO_Read(ushort_t port, void * dst, uint_t length) { uchar_t * iter = dst; uint_t i; @@ -102,7 +102,7 @@ int IO_Read(ushort_t port, void * dst, uint_t length, uint_t io_width) { -int IO_Write(ushort_t port, void * src, uint_t length, uint_t io_width) { +int IO_Write(ushort_t port, void * src, uint_t length) { uchar_t * iter = src; uint_t i; @@ -117,9 +117,24 @@ int IO_Write(ushort_t port, void * src, uint_t length, uint_t io_width) { -int IO_Write_to_Serial(ushort_t port, void * src, uint_t length, uint_t io_width) { - SerialPrint("Output from Guest on port %d (0x%x) Length=%d\n", port, port, length); - SerialMemDump(src, length); +int IO_Write_to_Serial(ushort_t port, void * src, uint_t length) { + PrintBoth("Output from Guest on port %d (0x%x) Length=%d\n", port, port, length); + switch (length) { + + case 1: + PrintBoth(">0x%.2x\n", *(char*)src); + break; + case 2: + PrintBoth(">0x%.4x\n", *(ushort_t*)src); + break; + case 4: + PrintBoth(">0x%.8x\n", *(uint_t*)src); + break; + default: + break; + } + + // SerialMemDump(src, length); return length; } diff --git a/palacios/src/geekos/svm_ctrl_regs.c b/palacios/src/geekos/svm_ctrl_regs.c index 99d1f94..ffdd3de 100644 --- a/palacios/src/geekos/svm_ctrl_regs.c +++ b/palacios/src/geekos/svm_ctrl_regs.c @@ -25,7 +25,7 @@ int handle_cr0_write(struct guest_info * info) { int ret; // The real rip address is actually a combination of the rip + CS base - ret = read_guest_pa_memory(info, (addr_t)guest_state->rip + (guest_state->cs.base << 4), 15, instr); + ret = read_guest_pa_memory(info, get_rip_linear(info, guest_state->rip, guest_state->cs.base), 15, instr); if (ret != 15) { // I think we should inject a GPF into the guest PrintDebug("Could not read instruction (ret=%d)\n", ret); @@ -117,7 +117,7 @@ int handle_cr0_write(struct guest_info * info) { PrintDebug("Protected Mode write to CR0\n"); // The real rip address is actually a combination of the rip + CS base - ret = read_guest_pa_memory(info, (addr_t)guest_state->rip + guest_state->cs.base, 15, instr); + ret = read_guest_pa_memory(info, get_rip_linear(info, guest_state->rip, guest_state->cs.base), 15, instr); if (ret != 0) { // I think we should inject a GPF into the guest PrintDebug("Could not read instruction (ret=%d)\n", ret); @@ -128,6 +128,8 @@ int handle_cr0_write(struct guest_info * info) { index++; } + + /* CHECK IF MOV_TO_CR CAN TAKE MEMORY OPERANDS... */ if ((instr[index] == cr_access_byte) && (instr[index + 1] == mov_to_cr_byte)) { @@ -198,7 +200,7 @@ int handle_cr0_read(struct guest_info * info) { int ret; // The real rip address is actually a combination of the rip + CS base - ret = read_guest_pa_memory(info, (addr_t)guest_state->rip + (guest_state->cs.base << 4), 15, instr); + ret = read_guest_pa_memory(info, get_rip_linear(info, guest_state->rip, guest_state->cs.base), 15, instr); if (ret != 15) { // I think we should inject a GPF into the guest PrintDebug("Could not read instruction (ret=%d)\n", ret); @@ -255,7 +257,7 @@ int handle_cr0_read(struct guest_info * info) { int ret; // The real rip address is actually a combination of the rip + CS base - ret = read_guest_pa_memory(info, (addr_t)guest_state->rip + guest_state->cs.base, 15, instr); + ret = read_guest_pa_memory(info, get_rip_linear(info, guest_state->rip, guest_state->cs.base), 15, instr); if (ret != 15) { // I think we should inject a GPF into the guest PrintDebug("Could not read instruction (ret=%d)\n", ret); diff --git a/palacios/src/geekos/svm_io.c b/palacios/src/geekos/svm_io.c index f44c2b6..96c746d 100644 --- a/palacios/src/geekos/svm_io.c +++ b/palacios/src/geekos/svm_io.c @@ -1,6 +1,8 @@ #include #include - +#include +#include +#include // This should package up an IO request and call vmm_handle_io @@ -28,7 +30,7 @@ int handle_svm_io_in(struct guest_info * info) { } - if (hook->read(io_info->port, &(info->vm_regs.rax), read_size, read_size) != read_size) { + if (hook->read(io_info->port, &(info->vm_regs.rax), read_size) != read_size) { // not sure how we handle errors..... return -1; } @@ -39,12 +41,93 @@ int handle_svm_io_in(struct guest_info * info) { } + + + +/* We might not handle wrap around of the RDI register correctly... + * In that if we do wrap around the effect will manifest in the higher bits of the register + */ int handle_svm_io_ins(struct guest_info * info) { + vmcb_ctrl_t * ctrl_area = GET_VMCB_CTRL_AREA((vmcb_t *)(info->vmm_data)); + vmcb_saved_state_t * guest_state = GET_VMCB_SAVE_STATE_AREA((vmcb_t*)(info->vmm_data)); + + struct svm_io_info * io_info = (struct svm_io_info *)&(ctrl_area->exit_info1); + + vmm_io_hook_t * hook = get_io_hook(&(info->io_map), io_info->port); + uint_t read_size = 0; + addr_t base_addr = guest_state->es.base; + addr_t dst_addr = 0; + uint_t rep_num = 1; + ullong_t mask = 0; + + // This is kind of hacky... + // direction can equal either 1 or -1 + // We will multiply the final added offset by this value to go the correct direction + int direction = 1; + struct rflags * flags = (struct rflags *)&(guest_state->rflags); + if (flags->df) { + direction = -1; + } + + + if (hook == NULL) { + // error, we should not have exited on this port + return -1; + } + + PrintDebug("INS on port %d (0x%x)\n", io_info->port, io_info->port); + + if (io_info->sz8) { + read_size = 1; + } else if (io_info->sz16) { + read_size = 2; + } else if (io_info->sz32) { + read_size = 4; + } + + + if (io_info->addr16) { + mask = 0xffff; + } else if (io_info->addr32) { + mask = 0xffffffff; + } else if (io_info->addr64) { + mask = 0xffffffffffffffffLL; + } else { + // should never happen + return -1; + } + + if (io_info->rep) { + rep_num = info->vm_regs.rcx & mask; + } + - // PrintDebug("INS on port %d (0x%x)\n", io_info->port, io_info->port); - return -1; + while (rep_num > 0) { + addr_t host_addr; + dst_addr = base_addr + (info->vm_regs.rdi & mask); + + if (guest_va_to_host_va(info, dst_addr, &host_addr) == -1) { + // either page fault or gpf... + } + if (hook->read(io_info->port, (char*)host_addr, read_size) != read_size) { + // not sure how we handle errors..... + return -1; + } + + info->vm_regs.rdi += read_size * direction; + + if (io_info->rep) + info->vm_regs.rcx--; + + rep_num--; + } + + + info->rip = ctrl_area->exit_info2; + + return 0; } int handle_svm_io_out(struct guest_info * info) { @@ -71,7 +154,7 @@ int handle_svm_io_out(struct guest_info * info) { } - if (hook->write(io_info->port, &(info->vm_regs.rax), write_size, write_size) != write_size) { + if (hook->write(io_info->port, &(info->vm_regs.rax), write_size) != write_size) { // not sure how we handle errors..... return -1; } @@ -82,6 +165,92 @@ int handle_svm_io_out(struct guest_info * info) { } +/* We might not handle wrap around of the RSI register correctly... + * In that if we do wrap around the effect will manifest in the higher bits of the register + */ + int handle_svm_io_outs(struct guest_info * info) { - return -1; + vmcb_ctrl_t * ctrl_area = GET_VMCB_CTRL_AREA((vmcb_t *)(info->vmm_data)); + vmcb_saved_state_t * guest_state = GET_VMCB_SAVE_STATE_AREA((vmcb_t*)(info->vmm_data)); + + struct svm_io_info * io_info = (struct svm_io_info *)&(ctrl_area->exit_info1); + + vmm_io_hook_t * hook = get_io_hook(&(info->io_map), io_info->port); + uint_t write_size = 0; + addr_t base_addr = guest_state->ds.base; + addr_t dst_addr = 0; + uint_t rep_num = 1; + ullong_t mask = 0; + + // This is kind of hacky... + // direction can equal either 1 or -1 + // We will multiply the final added offset by this value to go the correct direction + int direction = 1; + struct rflags * flags = (struct rflags *)&(guest_state->rflags); + if (flags->df) { + direction = -1; + } + + + if (hook == NULL) { + // error, we should not have exited on this port + return -1; + } + + PrintDebug("OUTS on port %d (0x%x)\n", io_info->port, io_info->port); + + if (io_info->sz8) { + write_size = 1; + } else if (io_info->sz16) { + write_size = 2; + } else if (io_info->sz32) { + write_size = 4; + } + + + if (io_info->addr16) { + mask = 0xffff; + } else if (io_info->addr32) { + mask = 0xffffffff; + } else if (io_info->addr64) { + mask = 0xffffffffffffffffLL; + } else { + // should never happen + return -1; + } + + if (io_info->rep) { + rep_num = info->vm_regs.rcx & mask; + } + + + while (rep_num > 0) { + addr_t host_addr; + dst_addr = base_addr + (info->vm_regs.rsi & mask); + + if (guest_va_to_host_va(info, dst_addr, &host_addr) == -1) { + // either page fault or gpf... + } + + if (hook->write(io_info->port, (char*)host_addr, write_size) != write_size) { + // not sure how we handle errors..... + return -1; + } + + info->vm_regs.rsi += write_size * direction; + + if (io_info->rep) + info->vm_regs.rcx--; + + rep_num--; + } + + + info->rip = ctrl_area->exit_info2; + + + return 0; + + + } diff --git a/palacios/src/geekos/svm_lowlevel.asm b/palacios/src/geekos/svm_lowlevel.asm index 3b100d3..5ce9650 100644 --- a/palacios/src/geekos/svm_lowlevel.asm +++ b/palacios/src/geekos/svm_lowlevel.asm @@ -173,7 +173,8 @@ safe_svm_launch: ;; mov eax, [esp + 4] ;; mov guest GPR pointer to eax - Restore_SVM_Registers [esp + 4] ;; Restore Guest GPR state + ;; this is plus 8 because we push eax in the macro + Restore_SVM_Registers [esp + 8] ;; Restore Guest GPR state pop eax ;; pop VMCB pointer into eax vmload @@ -181,7 +182,8 @@ safe_svm_launch: vmsave ;; pop eax ;; pop Guest GPR pointer into eax - Save_SVM_Registers [esp] ;; save guest GPRs + ;; this is plus 4 because we push eax in the macro NEED TO CHANGE + Save_SVM_Registers [esp+4] ;; save guest GPRs add esp, 4 ;; skip past the gpr ptr diff --git a/palacios/src/geekos/vmm_io.c b/palacios/src/geekos/vmm_io.c index a45e2ca..69e4abe 100644 --- a/palacios/src/geekos/vmm_io.c +++ b/palacios/src/geekos/vmm_io.c @@ -52,8 +52,8 @@ void add_io_hook(vmm_io_map_t * io_map, vmm_io_hook_t * io_hook) { } void hook_io_port(vmm_io_map_t * io_map, uint_t port, - int (*read)(ushort_t port, void * dst, uint_t length, uint_t io_width), - int (*write)(ushort_t port, void * src, uint_t length, uint_t io_width)) { + int (*read)(ushort_t port, void * dst, uint_t length), + int (*write)(ushort_t port, void * src, uint_t length)) { vmm_io_hook_t * io_hook = os_hooks->malloc(sizeof(vmm_io_hook_t)); io_hook->port = port;