summaryrefslogtreecommitdiffstats
path: root/sys/amd64
diff options
context:
space:
mode:
authorneel <neel@FreeBSD.org>2014-05-22 03:14:54 +0000
committerneel <neel@FreeBSD.org>2014-05-22 03:14:54 +0000
commit645d479a58a5eafe6f6c79b7f30b38ef8b865e60 (patch)
treeb691b9b7b90a6c77cccd436ff24422fe17a9017e /sys/amd64
parent9e29b5f65069140228be54ba30dab6ace4548361 (diff)
downloadFreeBSD-src-645d479a58a5eafe6f6c79b7f30b38ef8b865e60.zip
FreeBSD-src-645d479a58a5eafe6f6c79b7f30b38ef8b865e60.tar.gz
Inject page fault into the guest if the page table walker detects an invalid
translation for the guest linear address.
Diffstat (limited to 'sys/amd64')
-rw-r--r--sys/amd64/include/vmm.h1
-rw-r--r--sys/amd64/include/vmm_instruction_emul.h10
-rw-r--r--sys/amd64/vmm/vmm.c21
-rw-r--r--sys/amd64/vmm/vmm_instruction_emul.c75
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);
OpenPOWER on IntegriCloud