From 645d479a58a5eafe6f6c79b7f30b38ef8b865e60 Mon Sep 17 00:00:00 2001 From: neel Date: Thu, 22 May 2014 03:14:54 +0000 Subject: Inject page fault into the guest if the page table walker detects an invalid translation for the guest linear address. --- sys/amd64/include/vmm.h | 1 + sys/amd64/include/vmm_instruction_emul.h | 10 +++++ sys/amd64/vmm/vmm.c | 21 ++++++++- sys/amd64/vmm/vmm_instruction_emul.c | 75 ++++++++++++++++++++++---------- 4 files changed, 82 insertions(+), 25 deletions(-) diff --git a/sys/amd64/include/vmm.h b/sys/amd64/include/vmm.h index 94bdcf4..439f5aa 100644 --- a/sys/amd64/include/vmm.h +++ b/sys/amd64/include/vmm.h @@ -236,6 +236,7 @@ int vm_exception_pending(struct vm *vm, int vcpuid, struct vm_exception *vme); void vm_inject_gp(struct vm *vm, int vcpuid); /* general protection fault */ void vm_inject_ud(struct vm *vm, int vcpuid); /* undefined instruction fault */ +void vm_inject_pf(struct vm *vm, int vcpuid, int error_code); /* page fault */ #endif /* KERNEL */ diff --git a/sys/amd64/include/vmm_instruction_emul.h b/sys/amd64/include/vmm_instruction_emul.h index 0af9b4a..02cb922 100644 --- a/sys/amd64/include/vmm_instruction_emul.h +++ b/sys/amd64/include/vmm_instruction_emul.h @@ -122,6 +122,16 @@ int vmm_fetch_instruction(struct vm *vm, int cpuid, enum vie_paging_mode paging_mode, int cpl, struct vie *vie); +/* + * Translate the guest linear address 'gla' to a guest physical address. + * + * Returns 0 on success and '*gpa' contains the result of the translation. + * Returns 1 if a page fault exception was injected into the guest. + * Returns -1 otherwise. + */ +int vmm_gla2gpa(struct vm *vm, int vcpuid, uint64_t gla, uint64_t cr3, + uint64_t *gpa, enum vie_paging_mode paging_mode, int cpl, int prot); + void vie_init(struct vie *vie); /* diff --git a/sys/amd64/vmm/vmm.c b/sys/amd64/vmm/vmm.c index 7ab0b1e..b009905 100644 --- a/sys/amd64/vmm/vmm.c +++ b/sys/amd64/vmm/vmm.c @@ -1155,9 +1155,14 @@ vm_handle_inst_emul(struct vm *vm, int vcpuid, bool *retu) vie_init(vie); /* Fetch, decode and emulate the faulting instruction */ - if (vmm_fetch_instruction(vm, vcpuid, rip, inst_length, cr3, - paging_mode, cpl, vie) != 0) + error = vmm_fetch_instruction(vm, vcpuid, rip, inst_length, cr3, + paging_mode, cpl, vie); + if (error == 1) + return (0); /* Resume guest to handle page fault */ + else if (error == -1) return (EFAULT); + else if (error != 0) + panic("%s: vmm_fetch_instruction error %d", __func__, error); if (vmm_decode_instruction(vm, vcpuid, gla, cpu_mode, vie) != 0) return (EFAULT); @@ -1431,6 +1436,18 @@ vm_inject_fault(struct vm *vm, int vcpuid, struct vm_exception *exception) } void +vm_inject_pf(struct vm *vm, int vcpuid, int error_code) +{ + struct vm_exception pf = { + .vector = IDT_PF, + .error_code_valid = 1, + .error_code = error_code + }; + + vm_inject_fault(vm, vcpuid, &pf); +} + +void vm_inject_gp(struct vm *vm, int vcpuid) { struct vm_exception gpf = { diff --git a/sys/amd64/vmm/vmm_instruction_emul.c b/sys/amd64/vmm/vmm_instruction_emul.c index 873f41f..d4c9264 100644 --- a/sys/amd64/vmm/vmm_instruction_emul.c +++ b/sys/amd64/vmm/vmm_instruction_emul.c @@ -572,6 +572,23 @@ vie_init(struct vie *vie) vie->index_register = VM_REG_LAST; } +static int +pf_error_code(int usermode, int prot, uint64_t pte) +{ + int error_code = 0; + + if (pte & PG_V) + error_code |= PGEX_P; + if (prot & VM_PROT_WRITE) + error_code |= PGEX_W; + if (usermode) + error_code |= PGEX_U; + if (prot & VM_PROT_EXECUTE) + error_code |= PGEX_I; + + return (error_code); +} + static void ptp_release(void **cookie) { @@ -591,11 +608,11 @@ ptp_hold(struct vm *vm, vm_paddr_t ptpphys, size_t len, void **cookie) return (ptr); } -static int -gla2gpa(struct vm *vm, uint64_t gla, uint64_t ptpphys, uint64_t *gpa, - enum vie_paging_mode paging_mode, int cpl, int prot) +int +vmm_gla2gpa(struct vm *vm, int vcpuid, uint64_t gla, uint64_t ptpphys, + uint64_t *gpa, enum vie_paging_mode paging_mode, int cpl, int prot) { - int nlevels, ptpshift, ptpindex, retval, usermode, writable; + int nlevels, pfcode, ptpshift, ptpindex, retval, usermode, writable; u_int retries; uint64_t *ptpbase, pte, pgsize; uint32_t *ptpbase32, pte32; @@ -604,7 +621,7 @@ gla2gpa(struct vm *vm, uint64_t gla, uint64_t ptpphys, uint64_t *gpa, usermode = (cpl == 3 ? 1 : 0); writable = prot & VM_PROT_WRITE; cookie = NULL; - retval = -1; + retval = 0; retries = 0; restart: ptp_release(&cookie); @@ -633,11 +650,13 @@ restart: pte32 = ptpbase32[ptpindex]; - if ((pte32 & PG_V) == 0) - goto error; - - if (usermode && (pte32 & PG_U) == 0) - goto error; + if ((pte32 & PG_V) == 0 || + (usermode && (pte32 & PG_U) == 0) || + (writable && (pte32 & PG_RW) == 0)) { + pfcode = pf_error_code(usermode, prot, pte32); + vm_inject_pf(vm, vcpuid, pfcode); + goto pagefault; + } if (writable && (pte32 & PG_RW) == 0) goto error; @@ -689,8 +708,11 @@ restart: pte = ptpbase[ptpindex]; - if ((pte & PG_V) == 0) - goto error; + if ((pte & PG_V) == 0) { + pfcode = pf_error_code(usermode, prot, pte); + vm_inject_pf(vm, vcpuid, pfcode); + goto pagefault; + } ptpphys = pte; @@ -711,11 +733,13 @@ restart: pte = ptpbase[ptpindex]; - if ((pte & PG_V) == 0) - goto error; - - if (usermode && (pte & PG_U) == 0) - goto error; + if ((pte & PG_V) == 0 || + (usermode && (pte & PG_U) == 0) || + (writable && (pte & PG_RW) == 0)) { + pfcode = pf_error_code(usermode, prot, pte); + vm_inject_pf(vm, vcpuid, pfcode); + goto pagefault; + } if (writable && (pte & PG_RW) == 0) goto error; @@ -748,10 +772,14 @@ restart: pte >>= ptpshift; pte <<= (ptpshift + 12); pte >>= 12; *gpa = pte | (gla & (pgsize - 1)); done: - retval = 0; -error: ptp_release(&cookie); return (retval); +error: + retval = -1; + goto done; +pagefault: + retval = 1; + goto done; } int @@ -759,7 +787,7 @@ vmm_fetch_instruction(struct vm *vm, int cpuid, uint64_t rip, int inst_length, uint64_t cr3, enum vie_paging_mode paging_mode, int cpl, struct vie *vie) { - int n, err, prot; + int n, error, prot; uint64_t gpa, off; void *hpa, *cookie; @@ -773,9 +801,10 @@ vmm_fetch_instruction(struct vm *vm, int cpuid, uint64_t rip, int inst_length, /* Copy the instruction into 'vie' */ while (vie->num_valid < inst_length) { - err = gla2gpa(vm, rip, cr3, &gpa, paging_mode, cpl, prot); - if (err) - break; + error = vmm_gla2gpa(vm, cpuid, rip, cr3, &gpa, paging_mode, + cpl, prot); + if (error) + return (error); off = gpa & PAGE_MASK; n = min(inst_length - vie->num_valid, PAGE_SIZE - off); -- cgit v1.1