VMX: change msr emulation to pass in the vm trapframe
[akaros.git] / kern / arch / x86 / pmap64.c
index 1c2ac2d..e8d36b5 100644 (file)
@@ -42,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,
@@ -102,7 +98,7 @@ static kpte_t *__pml_walk(kpte_t *pml, uintptr_t va, int flags, int pml_shift)
        if (!kpte_is_present(kpte)) {
                if (!(flags & PG_WALK_CREATE))
                        return NULL;
-               new_pml_kva = get_cont_pages(1, MEM_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)
@@ -112,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);
 }
@@ -185,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);
        }
@@ -325,7 +324,9 @@ static int __pml_for_each(kpte_t *pml,  uintptr_t start, size_t len,
                         * 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)
@@ -347,9 +348,9 @@ 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,
@@ -358,9 +359,14 @@ int unmap_segment(pgdir_t pgdir, uintptr_t va, size_t size)
                if (!kpte_is_present(kpte))
                        return 0;
                if (pte_is_final(kpte, shift)) {
-                       *kpte = 0;
+                       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) {
@@ -371,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);
 }
 
@@ -491,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
@@ -533,7 +537,7 @@ void env_pagetable_free(struct proc *p)
 {
        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();
 }
 
@@ -623,11 +627,28 @@ uintptr_t gva2gpa(struct proc *p, uintptr_t cr3, uintptr_t gva)
  * data segment). */
 int arch_pgdir_setup(pgdir_t boot_copy, pgdir_t *new_pd)
 {
-       kpte_t *kpt = get_cont_pages(1, MEM_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)]  = build_kpte(PADDR(kpt), PTE_KERN_RW);
        kpt[PML4(UVPT)] = build_kpte(PADDR(kpt), PTE_USER_RO);
@@ -656,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)