X-Git-Url: http://v3vee.org/palacios/gitweb/gitweb.cgi?a=blobdiff_plain;f=palacios%2Fsrc%2Fpalacios%2Fvmx_assist.c;h=5766b15c4f06b3d805e45cd285bcaa935f6f065e;hb=a34275476494aeabed460b284a70cfc9b66a9896;hp=75f27746d5ed7fa0efa63b6cd053a90f86c1fd64;hpb=cfcceed5890430afedcc544bd7dbb69e29dfd65a;p=palacios.git diff --git a/palacios/src/palacios/vmx_assist.c b/palacios/src/palacios/vmx_assist.c index 75f2774..5766b15 100644 --- a/palacios/src/palacios/vmx_assist.c +++ b/palacios/src/palacios/vmx_assist.c @@ -21,9 +21,86 @@ #include #include #include +#include -static int vmx_save_world_ctx(struct guest_info * info, struct vmx_assist_context * ctx); -static int vmx_restore_world_ctx(struct guest_info * info, struct vmx_assist_context * ctx); +#ifndef V3_CONFIG_DEBUG_VMX +#undef PrintDebug +#define PrintDebug(fmt, args...) +#endif + + + + +#define VMXASSIST_MAGIC 0x17101966 + + +struct vmx_assist_header { + uint64_t rsvd; // 8 bytes of nothing + uint32_t magic; + uint32_t new_ctx_gpa; + uint32_t old_ctx_gpa; +} __attribute__((packed)); + + +union vmcs_arbytes { + struct arbyte_fields { + unsigned int seg_type : 4, + s : 1, + dpl : 2, + p : 1, + reserved0 : 4, + avl : 1, + reserved1 : 1, + default_ops_size: 1, + g : 1, + null_bit : 1, + reserved2 : 15; + } __attribute__((packed)) fields; + unsigned int bytes; +} __attribute__((packed)); + +struct vmx_assist_segment { + uint32_t sel; + uint32_t limit; + uint32_t base; + union vmcs_arbytes arbytes; +} __attribute__((packed)); + + +/* + * World switch state + */ +struct vmx_assist_context { + uint32_t eip; /* execution pointer */ + uint32_t esp; /* stack pointer */ + uint32_t eflags; /* flags register */ + uint32_t cr0; + uint32_t cr3; /* page table directory */ + uint32_t cr4; + + uint32_t idtr_limit; /* idt */ + uint32_t idtr_base; + + uint32_t gdtr_limit; /* gdt */ + uint32_t gdtr_base; + + struct vmx_assist_segment cs; + struct vmx_assist_segment ds; + struct vmx_assist_segment es; + struct vmx_assist_segment ss; + struct vmx_assist_segment fs; + struct vmx_assist_segment gs; + struct vmx_assist_segment tr; + struct vmx_assist_segment ldtr; + + + unsigned char rm_irqbase[2]; +} __attribute__((packed)); + + + +static void vmx_save_world_ctx(struct guest_info * info, struct vmx_assist_context * ctx); +static void vmx_restore_world_ctx(struct guest_info * info, struct vmx_assist_context * ctx); int v3_vmxassist_ctx_switch(struct guest_info * info) { struct vmx_assist_context * old_ctx = NULL; @@ -33,179 +110,268 @@ int v3_vmxassist_ctx_switch(struct guest_info * info) { - if (guest_pa_to_host_va(info, VMXASSIST_BASE, (addr_t *)&hdr) == -1) { - PrintError("Could not translate address for vmxassist header\n"); + if (v3_gpa_to_hva(info, VMXASSIST_START, (addr_t *)&hdr) == -1) { + PrintError(info->vm_info, info, "Could not translate address for vmxassist header\n"); return -1; } if (hdr->magic != VMXASSIST_MAGIC) { - PrintError("VMXASSIST_MAGIC field is invalid\n"); + PrintError(info->vm_info, info, "VMXASSIST_MAGIC field is invalid\n"); return -1; } - if (guest_pa_to_host_va(info, (addr_t)(hdr->old_ctx_gpa), (addr_t *)&(old_ctx)) == -1) { - PrintError("Could not translate address for VMXASSIST old context\n"); + if (v3_gpa_to_hva(info, (addr_t)(hdr->old_ctx_gpa), (addr_t *)&(old_ctx)) == -1) { + PrintError(info->vm_info, info, "Could not translate address for VMXASSIST old context\n"); return -1; } - if (guest_pa_to_host_va(info, (addr_t)(hdr->new_ctx_gpa), (addr_t *)&(new_ctx)) == -1) { - PrintError("Could not translate address for VMXASSIST new context\n"); + if (v3_gpa_to_hva(info, (addr_t)(hdr->new_ctx_gpa), (addr_t *)&(new_ctx)) == -1) { + PrintError(info->vm_info, info, "Could not translate address for VMXASSIST new context\n"); return -1; } - if (vmx_info->state == VMXASSIST_DISABLED) { + if (vmx_info->assist_state == VMXASSIST_OFF) { /* Save the old Context */ - if (vmx_save_world_ctx(info, old_ctx) != 0) { - PrintError("Could not save VMXASSIST world context\n"); - return -1; - } + vmx_save_world_ctx(info, old_ctx); /* restore new context, vmxassist should launch the bios the first time */ - if (vmx_restore_world_ctx(info, new_ctx) != 0) { - PrintError("VMXASSIST could not restore new context\n"); - return -1; - } + vmx_restore_world_ctx(info, new_ctx); - vmx_info->state = VMXASSIST_ENABLED; + vmx_info->assist_state = VMXASSIST_ON; - } else if (vmx_info->state == VMXASSIST_ENABLED) { + } else if (vmx_info->assist_state == VMXASSIST_ON) { /* restore old context */ - if (vmx_restore_world_ctx(info, old_ctx) != 0) { - PrintError("VMXASSIST could not restore old context\n"); - return -1; - } + vmx_restore_world_ctx(info, old_ctx); - vmx_info->state = VMXASSIST_DISABLED; + vmx_info->assist_state = VMXASSIST_OFF; } return 0; } - -int vmx_save_world_ctx(struct guest_info * info, struct vmx_assist_context * ctx) { - int error = 0; - PrintDebug("Writing from RIP: 0x%p\n", (void *)info->rip); +static void save_segment(struct v3_segment * seg, struct vmx_assist_segment * vmx_assist_seg) { + struct vmcs_segment tmp_seg; - error |= vmcs_read(VMCS_GUEST_RIP, &(ctx->eip)); - error |= vmcs_read(VMCS_GUEST_RSP, &(ctx->esp)); - error |= vmcs_read(VMCS_GUEST_RFLAGS, &(ctx->eflags)); + memset(&tmp_seg, 0, sizeof(struct vmcs_segment)); - error |= vmcs_read(VMCS_CR0_READ_SHDW, &(ctx->cr0)); - ctx->cr3 = info->shdw_pg_state.guest_cr3; - error |= vmcs_read(VMCS_CR4_READ_SHDW, &(ctx->cr4)); - - error |= vmcs_read(VMCS_GUEST_IDTR_LIMIT, &(ctx->idtr_limit)); - error |= vmcs_read(VMCS_GUEST_IDTR_BASE, &(ctx->idtr_base)); - - error |= vmcs_read(VMCS_GUEST_GDTR_LIMIT, &(ctx->gdtr_limit)); - error |= vmcs_read(VMCS_GUEST_GDTR_BASE, &(ctx->gdtr_base)); - - error |= vmcs_read(VMCS_GUEST_CS_SELECTOR, &(ctx->cs_sel)); - error |= vmcs_read(VMCS_GUEST_CS_LIMIT, &(ctx->cs_limit)); - error |= vmcs_read(VMCS_GUEST_CS_BASE, &(ctx->cs_base)); - error |= vmcs_read(VMCS_GUEST_CS_ACCESS, &(ctx->cs_arbytes.bytes)); - - error |= vmcs_read(VMCS_GUEST_DS_SELECTOR, &(ctx->ds_sel)); - error |= vmcs_read(VMCS_GUEST_DS_LIMIT, &(ctx->ds_limit)); - error |= vmcs_read(VMCS_GUEST_DS_BASE, &(ctx->ds_base)); - error |= vmcs_read(VMCS_GUEST_DS_ACCESS, &(ctx->ds_arbytes.bytes)); - - error |= vmcs_read(VMCS_GUEST_ES_SELECTOR, &(ctx->es_sel)); - error |= vmcs_read(VMCS_GUEST_ES_LIMIT, &(ctx->es_limit)); - error |= vmcs_read(VMCS_GUEST_ES_BASE, &(ctx->es_base)); - error |= vmcs_read(VMCS_GUEST_ES_ACCESS, &(ctx->es_arbytes.bytes)); - - error |= vmcs_read(VMCS_GUEST_SS_SELECTOR, &(ctx->ss_sel)); - error |= vmcs_read(VMCS_GUEST_SS_LIMIT, &(ctx->ss_limit)); - error |= vmcs_read(VMCS_GUEST_SS_BASE, &(ctx->ss_base)); - error |= vmcs_read(VMCS_GUEST_SS_ACCESS, &(ctx->ss_arbytes.bytes)); - - error |= vmcs_read(VMCS_GUEST_FS_SELECTOR, &(ctx->fs_sel)); - error |= vmcs_read(VMCS_GUEST_FS_LIMIT, &(ctx->fs_limit)); - error |= vmcs_read(VMCS_GUEST_FS_BASE, &(ctx->fs_base)); - error |= vmcs_read(VMCS_GUEST_FS_ACCESS, &(ctx->fs_arbytes.bytes)); - - error |= vmcs_read(VMCS_GUEST_GS_SELECTOR, &(ctx->gs_sel)); - error |= vmcs_read(VMCS_GUEST_GS_LIMIT, &(ctx->gs_limit)); - error |= vmcs_read(VMCS_GUEST_GS_BASE, &(ctx->gs_base)); - error |= vmcs_read(VMCS_GUEST_GS_ACCESS, &(ctx->gs_arbytes.bytes)); - - error |= vmcs_read(VMCS_GUEST_TR_SELECTOR, &(ctx->tr_sel)); - error |= vmcs_read(VMCS_GUEST_TR_LIMIT, &(ctx->tr_limit)); - error |= vmcs_read(VMCS_GUEST_TR_BASE, &(ctx->tr_base)); - error |= vmcs_read(VMCS_GUEST_TR_ACCESS, &(ctx->tr_arbytes.bytes)); - - error |= vmcs_read(VMCS_GUEST_LDTR_SELECTOR, &(ctx->ldtr_sel)); - error |= vmcs_read(VMCS_GUEST_LDTR_LIMIT, &(ctx->ldtr_limit)); - error |= vmcs_read(VMCS_GUEST_LDTR_BASE, &(ctx->ldtr_base)); - error |= vmcs_read(VMCS_GUEST_LDTR_ACCESS, &(ctx->ldtr_arbytes.bytes)); - - return error; + v3_seg_to_vmxseg(seg, &tmp_seg); + + vmx_assist_seg->sel = tmp_seg.selector; + vmx_assist_seg->limit = tmp_seg.limit; + vmx_assist_seg->base = tmp_seg.base; + vmx_assist_seg->arbytes.bytes = tmp_seg.access.val; } -int vmx_restore_world_ctx(struct guest_info * info, struct vmx_assist_context * ctx) { - int error = 0; - PrintDebug("ctx rip: %p\n", (void *)(addr_t)ctx->eip); +static void load_segment(struct vmx_assist_segment * vmx_assist_seg, struct v3_segment * seg) { + struct vmcs_segment tmp_seg; - error |= vmcs_write(VMCS_GUEST_RIP, ctx->eip); - error |= vmcs_write(VMCS_GUEST_RSP, ctx->esp); - error |= vmcs_write(VMCS_GUEST_RFLAGS, ctx->eflags); + memset(&tmp_seg, 0, sizeof(struct vmcs_segment)); + + tmp_seg.selector = vmx_assist_seg->sel; + tmp_seg.limit = vmx_assist_seg->limit; + tmp_seg.base = vmx_assist_seg->base; + tmp_seg.access.val = vmx_assist_seg->arbytes.bytes; + + v3_vmxseg_to_seg(&tmp_seg, seg); +} + +static void vmx_save_world_ctx(struct guest_info * info, struct vmx_assist_context * ctx) { + struct vmx_data * vmx_info = (struct vmx_data *)(info->vmm_data); + + PrintDebug(info->vm_info, info, "Writing from RIP: 0x%p\n", (void *)(addr_t)info->rip); + + ctx->eip = info->rip; + ctx->esp = info->vm_regs.rsp; + ctx->eflags = info->ctrl_regs.rflags; + + ctx->cr0 = info->shdw_pg_state.guest_cr0; + ctx->cr3 = info->shdw_pg_state.guest_cr3; + ctx->cr4 = vmx_info->guest_cr4; + + + save_segment(&(info->segments.cs), &(ctx->cs)); + save_segment(&(info->segments.ds), &(ctx->ds)); + save_segment(&(info->segments.es), &(ctx->es)); + save_segment(&(info->segments.ss), &(ctx->ss)); + save_segment(&(info->segments.fs), &(ctx->fs)); + save_segment(&(info->segments.gs), &(ctx->gs)); + save_segment(&(info->segments.tr), &(ctx->tr)); + save_segment(&(info->segments.ldtr), &(ctx->ldtr)); + + // Odd segments + ctx->idtr_limit = info->segments.idtr.limit; + ctx->idtr_base = info->segments.idtr.base; + + ctx->gdtr_limit = info->segments.gdtr.limit; + ctx->gdtr_base = info->segments.gdtr.base; +} - error |= vmcs_write(VMCS_CR0_READ_SHDW, ctx->cr0); +static void vmx_restore_world_ctx(struct guest_info * info, struct vmx_assist_context * ctx) { + struct vmx_data * vmx_info = (struct vmx_data *)(info->vmm_data); + + PrintDebug(info->vm_info, info, "ctx rip: %p\n", (void *)(addr_t)ctx->eip); + + info->rip = ctx->eip; + info->vm_regs.rsp = ctx->esp; + info->ctrl_regs.rflags = ctx->eflags; + + info->shdw_pg_state.guest_cr0 = ctx->cr0; info->shdw_pg_state.guest_cr3 = ctx->cr3; - error |= vmcs_write(VMCS_CR4_READ_SHDW, ctx->cr4); - - error |= vmcs_write(VMCS_GUEST_IDTR_LIMIT, ctx->idtr_limit); - error |= vmcs_write(VMCS_GUEST_IDTR_BASE, ctx->idtr_base); - - error |= vmcs_write(VMCS_GUEST_GDTR_LIMIT, ctx->gdtr_limit); - error |= vmcs_write(VMCS_GUEST_GDTR_BASE, ctx->gdtr_base); - - error |= vmcs_write(VMCS_GUEST_CS_SELECTOR, ctx->cs_sel); - error |= vmcs_write(VMCS_GUEST_CS_LIMIT, ctx->cs_limit); - error |= vmcs_write(VMCS_GUEST_CS_BASE, ctx->cs_base); - error |= vmcs_write(VMCS_GUEST_CS_ACCESS, ctx->cs_arbytes.bytes); - - error |= vmcs_write(VMCS_GUEST_DS_SELECTOR, ctx->ds_sel); - error |= vmcs_write(VMCS_GUEST_DS_LIMIT, ctx->ds_limit); - error |= vmcs_write(VMCS_GUEST_DS_BASE, ctx->ds_base); - error |= vmcs_write(VMCS_GUEST_DS_ACCESS, ctx->ds_arbytes.bytes); - - error |= vmcs_write(VMCS_GUEST_ES_SELECTOR, ctx->es_sel); - error |= vmcs_write(VMCS_GUEST_ES_LIMIT, ctx->es_limit); - error |= vmcs_write(VMCS_GUEST_ES_BASE, ctx->es_base); - error |= vmcs_write(VMCS_GUEST_ES_ACCESS, ctx->es_arbytes.bytes); - - error |= vmcs_write(VMCS_GUEST_SS_SELECTOR, ctx->ss_sel); - error |= vmcs_write(VMCS_GUEST_SS_LIMIT, ctx->ss_limit); - error |= vmcs_write(VMCS_GUEST_SS_BASE, ctx->ss_base); - error |= vmcs_write(VMCS_GUEST_SS_ACCESS, ctx->ss_arbytes.bytes); - - error |= vmcs_write(VMCS_GUEST_FS_SELECTOR, ctx->fs_sel); - error |= vmcs_write(VMCS_GUEST_FS_LIMIT, ctx->fs_limit); - error |= vmcs_write(VMCS_GUEST_FS_BASE, ctx->fs_base); - error |= vmcs_write(VMCS_GUEST_FS_ACCESS, ctx->fs_arbytes.bytes); - - error |= vmcs_write(VMCS_GUEST_GS_SELECTOR, ctx->gs_sel); - error |= vmcs_write(VMCS_GUEST_GS_LIMIT, ctx->gs_limit); - error |= vmcs_write(VMCS_GUEST_GS_BASE, ctx->gs_base); - error |= vmcs_write(VMCS_GUEST_GS_ACCESS, ctx->gs_arbytes.bytes); - - error |= vmcs_write(VMCS_GUEST_TR_SELECTOR, ctx->tr_sel); - error |= vmcs_write(VMCS_GUEST_TR_LIMIT, ctx->tr_limit); - error |= vmcs_write(VMCS_GUEST_TR_BASE, ctx->tr_base); - error |= vmcs_write(VMCS_GUEST_TR_ACCESS, ctx->tr_arbytes.bytes); - - error |= vmcs_write(VMCS_GUEST_LDTR_SELECTOR, ctx->ldtr_sel); - error |= vmcs_write(VMCS_GUEST_LDTR_LIMIT, ctx->ldtr_limit); - error |= vmcs_write(VMCS_GUEST_LDTR_BASE, ctx->ldtr_base); - error |= vmcs_write(VMCS_GUEST_LDTR_ACCESS, ctx->ldtr_arbytes.bytes); - - return error; + vmx_info->guest_cr4 = ctx->cr4; + + load_segment(&(ctx->cs), &(info->segments.cs)); + load_segment(&(ctx->ds), &(info->segments.ds)); + load_segment(&(ctx->es), &(info->segments.es)); + load_segment(&(ctx->ss), &(info->segments.ss)); + load_segment(&(ctx->fs), &(info->segments.fs)); + load_segment(&(ctx->gs), &(info->segments.gs)); + load_segment(&(ctx->tr), &(info->segments.tr)); + load_segment(&(ctx->ldtr), &(info->segments.ldtr)); + + // odd segments + info->segments.idtr.limit = ctx->idtr_limit; + info->segments.idtr.base = ctx->idtr_base; + + info->segments.gdtr.limit = ctx->gdtr_limit; + info->segments.gdtr.base = ctx->gdtr_base; + } +int v3_vmxassist_init(struct guest_info * core, struct vmx_data * vmx_state) { + + core->rip = 0xd0000; + core->vm_regs.rsp = 0x80000; + ((struct rflags *)&(core->ctrl_regs.rflags))->rsvd1 = 1; + +#define GUEST_CR0_MASK 0x80010031 +#define GUEST_CR4_MASK 0x00002010 + core->ctrl_regs.cr0 |= GUEST_CR0_MASK; + core->ctrl_regs.cr4 |= GUEST_CR4_MASK; + + ((struct cr0_32 *)&(core->shdw_pg_state.guest_cr0))->pe = 1; + ((struct cr0_32 *)&(core->shdw_pg_state.guest_cr0))->wp = 1; + ((struct cr0_32 *)&(core->shdw_pg_state.guest_cr0))->ne = 1; + + + // Setup segment registers + { + struct v3_segment * seg_reg = (struct v3_segment *)&(core->segments); + + int i; + + for (i = 0; i < 10; i++) { + seg_reg[i].selector = 3 << 3; + seg_reg[i].limit = 0xffff; + seg_reg[i].base = 0x0; + } + + core->segments.cs.selector = 2 << 3; + + /* Set only the segment registers */ + for (i = 0; i < 6; i++) { + seg_reg[i].limit = 0xfffff; + seg_reg[i].granularity = 1; + seg_reg[i].type = 3; + seg_reg[i].system = 1; + seg_reg[i].dpl = 0; + seg_reg[i].present = 1; + seg_reg[i].db = 1; + } + + core->segments.cs.type = 0xb; + + core->segments.ldtr.selector = 0x20; + core->segments.ldtr.type = 2; + core->segments.ldtr.system = 0; + core->segments.ldtr.present = 1; + core->segments.ldtr.granularity = 0; + + + /************* Map in GDT and vmxassist *************/ + + uint64_t gdt[] __attribute__ ((aligned(32))) = { + 0x0000000000000000ULL, /* 0x00: reserved */ + 0x0000830000000000ULL, /* 0x08: 32-bit TSS */ + //0x0000890000000000ULL, /* 0x08: 32-bit TSS */ + 0x00CF9b000000FFFFULL, /* 0x10: CS 32-bit */ + 0x00CF93000000FFFFULL, /* 0x18: DS 32-bit */ + 0x000082000000FFFFULL, /* 0x20: LDTR 32-bit */ + }; + + + if (v3_write_gpa_memory(core, VMXASSIST_GDT, sizeof(uint64_t)*5, (void*)gdt)!=sizeof(uint64_t)*5) { + PrintError(core->vm_info, core, "Could not write VMXASSIST GDT\n"); + return -1; + } + + core->segments.gdtr.base = VMXASSIST_GDT; + + + uint64_t vmxassist_tss = VMXASSIST_TSS; + gdt[0x08 / sizeof(gdt[0])] |= + ((vmxassist_tss & 0xFF000000) << (56 - 24)) | + ((vmxassist_tss & 0x00FF0000) << (32 - 16)) | + ((vmxassist_tss & 0x0000FFFF) << (16)) | + (8392 - 1); + + core->segments.tr.selector = 0x08; + core->segments.tr.base = vmxassist_tss; + + //core->segments.tr.type = 0x9; + core->segments.tr.type = 0x3; + core->segments.tr.system = 0; + core->segments.tr.present = 1; + core->segments.tr.granularity = 0; + } + + if (core->shdw_pg_mode == NESTED_PAGING) { + // setup 1to1 page table internally. + int i = 0; + pde32_4MB_t * pde = NULL; + + V3_Print(core->vm_info, core, "Setting up internal VMXASSIST page tables\n"); + + if (v3_gpa_to_hva(core, VMXASSIST_1to1_PT, (addr_t *)(&pde)) == -1) { + PrintError(core->vm_info, core, "Could not find VMXASSIST 1to1 PT destination\n"); + return -1; + } + + memset(pde, 0, PAGE_SIZE); + + for (i = 0; i < 1024; i++) { + pde[i].present = 1; + pde[i].writable = 1; + pde[i].user_page = 1; + pde[i].large_page = 1; + pde[i].page_base_addr = PAGE_BASE_ADDR_4MB(i * PAGE_SIZE_4MB); + + // PrintError(core->vm_info, core, "PDE %d: %x\n", i, *(uint32_t *)&(pde[i])); + } + + core->ctrl_regs.cr3 = VMXASSIST_1to1_PT; + + } + + // setup VMXASSIST + { + + extern uint8_t v3_vmxassist_start[]; + extern uint8_t v3_vmxassist_end[]; + + if (v3_write_gpa_memory(core, VMXASSIST_START, v3_vmxassist_end-v3_vmxassist_start,v3_vmxassist_start)!=v3_vmxassist_end-v3_vmxassist_start) { + PrintError(core->vm_info, core, "Could not write VMXASSIST\n"); + return -1; + } + + + vmx_state->assist_state = VMXASSIST_OFF; + } + + + return 0; +}