VMM: Add a gva2gpa() helper
authorBarret Rhoden <brho@cs.berkeley.edu>
Mon, 1 Feb 2016 16:44:39 +0000 (11:44 -0500)
committerBarret Rhoden <brho@cs.berkeley.edu>
Tue, 2 Feb 2016 22:43:52 +0000 (17:43 -0500)
Instead of just assuming we can do the math for Linux's KERNBASE mapping,
this will check the page tables.

'cr3' is guest physical / user (ring 3) memory.  This should be safe from
unmapped user memory or other issues, thanks to copy_from_user().

Signed-off-by: Barret Rhoden <brho@cs.berkeley.edu>
kern/arch/riscv/pmap.c
kern/arch/x86/pmap64.c
kern/include/umem.h

index 72e5406..7191b4e 100644 (file)
@@ -98,6 +98,12 @@ page_check(void)
 {
 }
 
+uintptr_t gva2gpa(struct proc *p, uintptr_t cr3, uintptr_t gva)
+{
+       panic("Unimplemented");
+       return 0;
+}
+
 int arch_pgdir_setup(pgdir_t boot_copy, pgdir_t *new_pd)
 {
        pte_t *kpt = kpage_alloc_addr();
index 7d91345..f609b86 100644 (file)
@@ -30,6 +30,7 @@
 #include <stdio.h>
 #include <kmalloc.h>
 #include <page_alloc.h>
+#include <umem.h>
 
 extern char boot_pml4[], gdt64[], gdt64desc[];
 pgdir_t boot_pgdir;
@@ -549,6 +550,46 @@ void page_check(void)
 {
 }
 
+/* Similar to the kernels page table walk, but walks the guest page tables for a
+ * guest_va.  Takes a proc and user virtual (guest physical) address for the
+ * PML, returning the actual PTE (copied out of userspace). */
+static kpte_t __guest_pml_walk(struct proc *p, kpte_t *u_pml, uintptr_t gva,
+                               int flags, int pml_shift)
+{
+       kpte_t pte;
+
+       if (memcpy_from_user(p, &pte, &u_pml[PMLx(gva, pml_shift)],
+                            sizeof(kpte_t))) {
+               printk("Buggy pml %p, tried %p\n", u_pml, &u_pml[PMLx(gva, pml_shift)]);
+               return 0;
+       }
+       if (walk_is_complete(&pte, pml_shift, flags))
+               return pte;
+       if (!kpte_is_present(&pte))
+               return 0;
+       return __guest_pml_walk(p, (kpte_t*)PTE_ADDR(pte), gva, flags,
+                               pml_shift - BITS_PER_PML);
+}
+
+uintptr_t gva2gpa(struct proc *p, uintptr_t cr3, uintptr_t gva)
+{
+       kpte_t pte;
+       int shift = PML1_SHIFT;
+
+       pte = __guest_pml_walk(p, (kpte_t*)cr3, gva, shift, PML4_SHIFT);
+       if (!pte)
+               return 0;
+       /* TODO: Jumbos mess with us.  We need to know the shift the walk did.  This
+        * is a little nasty, but will work til we make Akaros more jumbo-aware. */
+       while (pte & PTE_PS) {
+               shift += BITS_PER_PML;
+               pte = __guest_pml_walk(p, (kpte_t*)cr3, gva, shift, PML4_SHIFT);
+               if (!pte)
+                       return 0;
+       }
+       return (pte & ~((1 << shift) - 1)) | (gva & ((1 << shift) - 1));
+}
+
 /* Sets up the page directory, based on boot_copy.
  *
  * For x86, to support VMs, all processes will have an EPT and a KPT.  Ideally,
index 2401002..88b2807 100644 (file)
@@ -71,6 +71,8 @@ void free_path(struct proc *p, char *t_path);
 void *kmalloc_errno(int len);
 bool uva_is_kva(struct proc *p, void *uva, void *kva);
 uintptr_t uva2kva(struct proc *p, void *uva, size_t len, int prot);
+/* In arch/pmap{64}.c */
+uintptr_t gva2gpa(struct proc *p, uintptr_t cr3, uintptr_t gva);
 
 /* Helper for is_user_r{w,}addr.
  *