VMX: change msr emulation to pass in the vm trapframe
[akaros.git] / kern / arch / x86 / pmap64.c
index 29387d8..e8d36b5 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;
@@ -41,10 +42,6 @@ pseudodesc_t gdt_pd;
 #define PG_WALK_CREATE                         0x0100
 
 kpte_t *pml_walk(kpte_t *pml, uintptr_t va, int flags);
-void map_segment(pgdir_t pgdir, uintptr_t va, size_t size, physaddr_t pa,
-                 int perm, int pml_shift);
-int unmap_segment(pgdir_t pgdir, uintptr_t va, size_t size);
-
 typedef int (*kpte_cb_t)(kpte_t *kpte, uintptr_t kva, int pml_shift,
                         bool visited_subs, void *arg);
 int pml_for_each(kpte_t *pml, uintptr_t start, size_t len, kpte_cb_t callback,
@@ -98,10 +95,10 @@ static kpte_t *__pml_walk(kpte_t *pml, uintptr_t va, int flags, int pml_shift)
        epte = kpte_to_epte(kpte);
        if (walk_is_complete(kpte, pml_shift, flags))
                return kpte;
-       if (!(*kpte & PTE_P)) {
+       if (!kpte_is_present(kpte)) {
                if (!(flags & PG_WALK_CREATE))
                        return NULL;
-               new_pml_kva = get_cont_pages(1, KMALLOC_WAIT);
+               new_pml_kva = kpages_alloc(2 * PGSIZE, MEM_WAIT);
                memset(new_pml_kva, 0, PGSIZE * 2);
                /* Might want better error handling (we're probably out of memory) */
                if (!new_pml_kva)
@@ -111,14 +108,18 @@ static kpte_t *__pml_walk(kpte_t *pml, uintptr_t va, int flags, int pml_shift)
                 * translation is !User).  We put the perms on the last entry, not the
                 * intermediates. */
                *kpte = PADDR(new_pml_kva) | PTE_P | PTE_U | PTE_W;
-               /* The physaddr of the new_pml is one page higher than the KPT page.  A
-                * few other things:
-                * - for the same reason that we have U and X set on all intermediate
-                * PTEs, we now set R, X, and W for the EPTE.
-                * - All EPTEs have U perms
-                * - We can't use epte_write since we're workin on intermediate PTEs,
-                * and they don't have the memory type set. */
-               *epte = (PADDR(new_pml_kva) + PGSIZE) | EPTE_R | EPTE_X | EPTE_W;
+               /* For a dose of paranoia, we'll avoid mapping intermediate eptes when
+                * we know we're using an address that should never be ept-accesible. */
+               if (va < ULIM) {
+                       /* The physaddr of the new_pml is one page higher than the KPT page.
+                        * A few other things:
+                        * - for the same reason that we have U and X set on all
+                        *   intermediate PTEs, we now set R, X, and W for the EPTE.
+                        * - All EPTEs have U perms
+                        * - We can't use epte_write since we're workin on intermediate
+                        *   PTEs, and they don't have the memory type set. */
+                       *epte = (PADDR(new_pml_kva) + PGSIZE) | EPTE_R | EPTE_X | EPTE_W;
+               }
        }
        return __pml_walk(kpte2pml(*kpte), va, flags, pml_shift - BITS_PER_PML);
 }
@@ -184,8 +185,7 @@ static void map_my_pages(kpte_t *pgdir, uintptr_t va, size_t size,
             pa += pgsize) {
                kpte = get_next_pte(kpte, pgdir, va, PG_WALK_CREATE | pml_shift);
                assert(kpte);
-               *kpte = PTE_ADDR(pa) | perm |
-                       (pml_shift != PML1_SHIFT ? PTE_PS : 0);
+               pte_write(kpte, pa, perm | (pml_shift != PML1_SHIFT ? PTE_PS : 0));
                printd("Wrote *kpte %p, for va %p to pa %p tried to cover %p\n",
                       *kpte, va, pa, amt_mapped);
        }
@@ -271,8 +271,8 @@ void map_segment(pgdir_t pgdir, uintptr_t va, size_t size, physaddr_t pa,
         * largest possible jumbo page, up to whatever we were asked for. */
        if (pml_shift != PGSHIFT) {
                max_shift_possible = max_possible_shift(va, pa);
-               /* arch-specific limitation (can't have jumbos beyond PML3) */
-               max_shift_possible = MIN(max_shift_possible, PML3_SHIFT);
+               max_shift_possible = MIN(max_shift_possible,
+                                        arch_max_jumbo_page_shift());
                /* Assumes we were given a proper PML shift 12, 21, 30, etc */
                while (pml_shift > max_shift_possible)
                        pml_shift -= BITS_PER_PML;
@@ -317,14 +317,16 @@ static int __pml_for_each(kpte_t *pml,  uintptr_t start, size_t len,
        for (kpte_i = kpte_s; kpte_i <= kpte_e; kpte_i++, kva += pgsize) {
                visited_all_subs = FALSE;
                /* Complete only on the last level (PML1_SHIFT) or on a jumbo */
-               if ((*kpte_i & PTE_P) &&
+               if (kpte_is_present(kpte_i) &&
                    (!walk_is_complete(kpte_i, pml_shift, PML1_SHIFT))) {
                        /* only pass truncated end points (e.g. start may not be page
                         * aligned) when we're on the first (or last) item.  For the middle
                         * entries, we want the subpmls to process the full range they are
                         * responsible for: [kva, kva + pgsize). */
                        uintptr_t sub_start = MAX(kva, start);
-                       size_t sub_len = MIN(start + len, kva + pgsize) - sub_start;
+                       size_t sub_len = MIN(start + len - sub_start,
+                                            kva + pgsize - sub_start);
+
                        ret = __pml_for_each(kpte2pml(*kpte_i), sub_start, sub_len,
                                             callback, arg, pml_shift - BITS_PER_PML);
                        if (ret)
@@ -346,20 +348,25 @@ int pml_for_each(kpte_t *pml, uintptr_t start, size_t len, kpte_cb_t callback,
        return __pml_for_each(pml, start, len, callback, arg, PML4_SHIFT);
 }
 
-/* Unmaps [va, va + size) from pgdir, freeing any intermediate page tables.
- * This does not free the actual memory pointed to by the page tables, nor does
- * it flush the TLB. */
+/* Unmaps [va, va + size) from pgdir, freeing any intermediate page tables for
+ * non-kernel mappings.  This does not free the actual memory pointed to by the
+ * page tables, nor does it flush the TLB. */
 int unmap_segment(pgdir_t pgdir, uintptr_t va, size_t size)
 {
        int pt_free_cb(kpte_t *kpte, uintptr_t kva, int shift, bool visited_subs,
                       void *data)
        {
-               if (!(*kpte & PTE_P))
+               if (!kpte_is_present(kpte))
                        return 0;
-               if ((shift == PML1_SHIFT) || (*kpte & PTE_PS)) {
-                       *kpte = 0;
+               if (pte_is_final(kpte, shift)) {
+                       pte_clear(kpte);
                        return 0;
                }
+               /* Never remove intermediate pages for any kernel mappings.  This is
+                * also important for x86 so that we don't accidentally free any of the
+                * boot PMLs, which aren't two-page alloc'd from kpages_arena. */
+               if (kva >= ULIM)
+                       return 0;
                /* If we haven't visited all of our subs, we might still have some
                 * mappings hanging off this page table. */
                if (!visited_subs) {
@@ -370,12 +377,10 @@ int unmap_segment(pgdir_t pgdir, uintptr_t va, size_t size)
                                        return 0;
                        }
                }
-               free_cont_pages(KADDR(PTE_ADDR(*kpte)), 1);
-               *kpte = 0;
+               kpages_free(KADDR(PTE_ADDR(*kpte)), 2 * PGSIZE);
+               pte_clear(kpte);
                return 0;
        }
-       /* Don't accidentally unmap the boot mappings */
-       assert((va < KERNBASE) && (va + size < KERNBASE));
        return pml_for_each(pgdir_get_kpt(pgdir), va, size, pt_free_cb, 0);
 }
 
@@ -399,7 +404,7 @@ static int pml_perm_walk(kpte_t *pml, const void *va, int pml_shift)
        int perms_here;
 
        kpte = &pml[PMLx(va, pml_shift)];
-       if (!(*kpte & PTE_P))
+       if (!kpte_is_present(kpte))
                return 0;
        perms_here = *kpte & PTE_PERM;
        if (walk_is_complete(kpte, pml_shift, PML1_SHIFT))
@@ -426,20 +431,20 @@ static void check_syms_va(void)
 {
        /* Make sure our symbols are up to date (see arch/ros/mmu64.h) */
        check_sym_va(KERN_LOAD_ADDR, 0xffffffffc0000000);
-       check_sym_va(LAPIC_BASE,     0xffffffffbff00000);
-       check_sym_va(IOAPIC_BASE,    0xffffffffbfe00000);
+       check_sym_va(IOAPIC_BASE,    0xffffffffbff00000);
        check_sym_va(VPT_TOP,        0xffffff0000000000);
        check_sym_va(VPT,            0xfffffe8000000000);
        check_sym_va(KERN_VMAP_TOP,  0xfffffe8000000000);
        check_sym_va(KERNBASE,       0xffff800000000000);
        check_sym_va(ULIM,           0x0000800000000000);
        check_sym_va(UVPT,           0x00007f8000000000);
-       check_sym_va(UINFO,          0x00007f7fffe00000);
-       check_sym_va(UWLIM,          0x00007f7fffe00000);
-       check_sym_va(UDATA,          0x00007f7fffc00000);
-       check_sym_va(UGDATA,         0x00007f7fffbff000);
-       check_sym_va(UMAPTOP,        0x00007f7fffbff000);
-       check_sym_va(USTACKTOP,      0x00007f7fffbff000);
+       check_sym_va(UGINFO,         0x00007f7fffe00000);
+       check_sym_va(UINFO,          0x00007f7fffc00000);
+       check_sym_va(UWLIM,          0x00007f7fffc00000);
+       check_sym_va(UDATA,          0x00007f7fffa00000);
+       check_sym_va(UGDATA,         0x00007f7fff9ff000);
+       check_sym_va(UMAPTOP,        0x00007f7fff9ff000);
+       check_sym_va(USTACKTOP,      0x00007f7fff9ff000);
        check_sym_va(BRK_END,        0x0000400000000000);
 }
 
@@ -467,10 +472,8 @@ void vm_init(void)
        }
        /* For the LAPIC and IOAPIC, we use PAT (but not *the* PAT flag) to make
         * these type UC */
-       map_segment(boot_pgdir, LAPIC_BASE, APIC_SIZE, LAPIC_PBASE,
-                   PTE_PCD | PTE_PWT | PTE_KERN_RW | PTE_G, max_jumbo_shift);
        map_segment(boot_pgdir, IOAPIC_BASE, APIC_SIZE, IOAPIC_PBASE,
-                   PTE_PCD | PTE_PWT | PTE_KERN_RW | PTE_G, max_jumbo_shift);
+                   PTE_NOCACHE | PTE_KERN_RW | PTE_G, max_jumbo_shift);
        /* VPT mapping: recursive PTE inserted at the VPT spot */
        boot_kpt[PML4(VPT)] = PADDR(boot_kpt) | PTE_W | PTE_P;
        /* same for UVPT, accessible by userspace (RO). */
@@ -492,7 +495,7 @@ void x86_cleanup_bootmem(void)
         * anything - it just unmaps but leave 2 KPTs (4 pages) sitting around. */
        //unmap_segment(boot_pgdir, 0, PML3_PTE_REACH); // want to do this
        boot_pgdir.kpte[0] = 0;
-       tlbflush();
+       tlb_flush_global();
 }
 
 /* Walks len bytes from start, executing 'callback' on every PTE, passing it a
@@ -520,42 +523,21 @@ int env_user_mem_walk(struct proc *p, void *start, size_t len,
                return tp->cb(tp->p, kpte, (void*)kva, tp->cb_arg);
        }
 
-       int ret;
        struct tramp_package local_tp;
        local_tp.p = p;
        local_tp.cb = callback;
        local_tp.cb_arg = arg;
-       /* Walking both in parallel and making a joint PTE is a pain.  instead, we
-        * walk one at a time, each with only a half_pte.  Most all of the pmap_ops
-        * need to deal with a getting half PTE.  Ideally, we'd combine the two
-        * walks and then know that we always have a kpte. */
-       ret = pml_for_each(pgdir_get_kpt(p->env_pgdir), (uintptr_t)start, len,
+       return pml_for_each(pgdir_get_kpt(p->env_pgdir), (uintptr_t)start, len,
                           trampoline_cb, &local_tp);
-       /* TODO: walk the EPT, combine with ret */
-       return ret;
 }
 
 /* Frees (decrefs) all pages of the process's page table, including the page
  * directory.  Does not free the memory that is actually mapped. */
 void env_pagetable_free(struct proc *p)
 {
-       /* callback: given an intermediate kpte (not a final one), removes the page
-        * table the PTE points to */
-       int pt_free_cb(kpte_t *kpte, uintptr_t kva, int shift, bool visited_subs,
-                      void *data)
-       {
-               if (!(*kpte & PTE_P))
-                       return 0;
-               if ((shift == PML1_SHIFT) || (*kpte & PTE_PS))
-                       return 0;
-               free_cont_pages(KADDR(PTE_ADDR(*kpte)), 1);
-               return 0;
-       }
-               
-       assert(p->env_cr3 != rcr3());
-       pml_for_each(pgdir_get_kpt(p->env_pgdir), 0, UVPT, pt_free_cb, 0);
+       unmap_segment(p->env_pgdir, 0, UVPT - 0);
        /* the page directory is not a PTE, so it never was freed */
-       free_cont_pages(pgdir_get_kpt(p->env_pgdir), 1);
+       kpages_free(pgdir_get_kpt(p->env_pgdir), 2 * PGSIZE);
        tlbflush();
 }
 
@@ -570,6 +552,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,
@@ -605,14 +627,31 @@ void page_check(void)
  * data segment). */
 int arch_pgdir_setup(pgdir_t boot_copy, pgdir_t *new_pd)
 {
-       kpte_t *kpt = get_cont_pages(1, KMALLOC_WAIT);
+       kpte_t *kpt;
+       epte_t *ept;
+
+       kpt = kpages_alloc(2 * PGSIZE, MEM_WAIT);
        memcpy(kpt, boot_copy.kpte, PGSIZE);
-       epte_t *ept = kpte_to_epte(kpt);
+       ept = kpte_to_epte(kpt);
        memset(ept, 0, PGSIZE);
 
+       /* This bit of paranoia slows process creation a little, but makes sure that
+        * there is nothing below ULIM in boot_pgdir.  Any PML4 entries copied from
+        * boot_pgdir (e.g. the kernel's memory) will be *shared* among all
+        * processes, including *everything* under the PML4 entries reach (e.g.
+        * PML4_PTE_REACH = 512 GB) and any activity would need to be synchronized.
+        *
+        * We could do this once at boot time, but that would miss out on potential
+        * changes to the boot_pgdir at runtime.
+        *
+        * We could also just memset that region to 0.  For now, I want to catch
+        * whatever mappings exist, since they are probably bugs. */
+       for (int i = 0; i < PML4(ULIM - 1); i++)
+               assert(kpt[i] == 0);
+
        /* VPT and UVPT map the proc's page table, with different permissions. */
-       kpt[PML4(VPT)]  = PTE(LA2PPN(PADDR(kpt)), PTE_KERN_RW);
-       kpt[PML4(UVPT)] = PTE(LA2PPN(PADDR(kpt)), PTE_USER_RO);
+       kpt[PML4(VPT)]  = build_kpte(PADDR(kpt), PTE_KERN_RW);
+       kpt[PML4(UVPT)] = build_kpte(PADDR(kpt), PTE_USER_RO);
 
        new_pd->kpte = kpt;
        new_pd->eptp = construct_eptp(PADDR(ept));
@@ -638,6 +677,45 @@ int arch_max_jumbo_page_shift(void)
        return edx & (1 << 26) ? PML3_SHIFT : PML2_SHIFT;
 }
 
+/* Adds empty intermediate PTs to the top-most PML in pgdir for the given range.
+ * On a 4-PML system, this will add entries to PML4, consisting of a bunch of
+ * empty PML3s, such that [va, va+len) has intermediate tables in pgdir.
+ *
+ * A few related notes:
+ *
+ * The boot_pgdir is where we do the original kernel mappings.  All of the PML4
+ * entries are filled in, pointing to intermediate PML3s.  All other pgdirs copy
+ * the kernel mapping, which means they have the same content.  That content
+ * never changes at runtime.  What changes is the contents of the PML3s and
+ * below, which are pointed to by all pgdirs.
+ *
+ * The proc pgdirs do not have KPT or EPT mappings above ULIM, so if the
+ * intermediate PTs have EPT entries, it's just a waste of memory, but not a
+ * mapping the user could exploit.
+ *
+ * On occasion, there might be code that maps things into boot_pgdir below ULIM,
+ * though right now this is just an out-of-branch "mmap a page at 0" debugging
+ * hack. */
+void arch_add_intermediate_pts(pgdir_t pgdir, uintptr_t va, size_t len)
+{
+       kpte_t *pml4 = pgdir_get_kpt(pgdir);
+       kpte_t *kpte;
+       epte_t *epte;
+       void *new_pml_kva;
+
+       for (size_t i = 0; i < len; i += PML4_PTE_REACH, va += PML4_PTE_REACH) {
+               kpte = &pml4[PML4(va)];
+               epte = kpte_to_epte(kpte);
+               if (kpte_is_present(kpte))
+                       continue;
+               new_pml_kva = kpages_zalloc(2 * PGSIZE, MEM_WAIT);
+               /* We insert the same as for __pml_walk. */
+               *kpte = PADDR(new_pml_kva) | PTE_P | PTE_U | PTE_W;
+               if (va < ULIM)
+                       *epte = (PADDR(new_pml_kva) + PGSIZE) | EPTE_R | EPTE_X | EPTE_W;
+       }
+}
+
 /* Debugging */
 static int print_pte(kpte_t *kpte, uintptr_t kva, int shift, bool visited_subs,
                      void *data)
@@ -702,7 +780,8 @@ int debug_check_kpt_ept(void)
                        reason = "paddr";
                        goto fail;
                }
-               if (kpte_get_perm(kpte) != epte_get_perm(epte)) {
+               if ((kpte_get_settings(kpte) & PTE_PERM) !=
+                   (epte_get_settings(epte) & PTE_PERM)) {
                        reason = "permissions";
                        goto fail;
                }