Adds instructions for using the profiler
[akaros.git] / kern / arch / x86 / pmap64.c
index 901a9af..f8ccc76 100644 (file)
@@ -43,11 +43,22 @@ pseudodesc_t gdt_pd;
 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,
                  void *arg);
-int unmap_segment(pgdir_t pgdir, uintptr_t va, size_t size);
+/* Helpers for PML for-each walks */
+static inline bool pte_is_final(pte_t pte, int pml_shift)
+{
+       return (pml_shift == PML1_SHIFT) || pte_is_jumbo(pte);
+}
+
+static inline bool pte_is_intermediate(pte_t pte, int pml_shift)
+{
+       return !pte_is_final(pte, pml_shift);
+}
 
 /* Helper: gets the kpte_t pointer which is the base of the PML4 from pgdir */
 static kpte_t *pgdir_get_kpt(pgdir_t pgdir)
@@ -80,12 +91,14 @@ static kpte_t *kpte2pml(kpte_t kpte)
 static kpte_t *__pml_walk(kpte_t *pml, uintptr_t va, int flags, int pml_shift)
 {
        kpte_t *kpte;
+       epte_t *epte;
        void *new_pml_kva;
 
        kpte = &pml[PMLx(va, 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);
@@ -98,6 +111,14 @@ 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;
        }
        return __pml_walk(kpte2pml(*kpte), va, flags, pml_shift - BITS_PER_PML);
 }
@@ -250,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;
@@ -296,7 +317,7 @@ 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
@@ -333,9 +354,9 @@ 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)) {
+               if (pte_is_final(kpte, shift)) {
                        *kpte = 0;
                        return 0;
                }
@@ -369,10 +390,7 @@ pte_t pgdir_walk(pgdir_t pgdir, const void *va, int create)
        int flags = PML1_SHIFT;
        if (create == 1)
                flags |= PG_WALK_CREATE;
-       ret.kpte = pml_walk(pgdir_get_kpt(pgdir), (uintptr_t)va, flags);
-       /* TODO: (EPT) walk the EPT */
-       ret.epte = 0;
-       return ret;
+       return pml_walk(pgdir_get_kpt(pgdir), (uintptr_t)va, flags);
 }
 
 static int pml_perm_walk(kpte_t *pml, const void *va, int pml_shift)
@@ -381,7 +399,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))
@@ -434,7 +452,7 @@ void vm_init(void)
 
        boot_cr3 = get_boot_pml4();
        boot_pgdir.kpte = boot_kpt;
-       boot_pgdir.epte = 0;
+       boot_pgdir.eptp = 0;
        gdt = KADDR(get_gdt64());
 
        /* We need to limit our mappings on machines that don't support 1GB pages */
@@ -495,48 +513,26 @@ int env_user_mem_walk(struct proc *p, void *start, size_t len,
                          void *data)
        {
                struct tramp_package *tp = (struct tramp_package*)data;
-               pte_t half_pte = {.kpte = kpte, .epte = 0};
                assert(tp->cb);
                /* memwalk CBs don't know how to handle intermediates or jumbos */
                if (shift != PML1_SHIFT)
                        return 0;
-               return tp->cb(tp->p, half_pte, (void*)kva, tp->cb_arg);
+               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);
        tlbflush();
@@ -598,8 +594,6 @@ int arch_pgdir_setup(pgdir_t boot_copy, pgdir_t *new_pd)
        kpt[PML4(UVPT)] = PTE(LA2PPN(PADDR(kpt)), PTE_USER_RO);
 
        new_pd->kpte = kpt;
-       /* Processes do not have EPTs by default, only once they are VMMs */
-       new_pd->epte = ept;     /* TODO: remove this soon */
        new_pd->eptp = construct_eptp(PADDR(ept));
        return 0;
 }
@@ -612,7 +606,7 @@ physaddr_t arch_pgdir_get_cr3(pgdir_t pd)
 void arch_pgdir_clear(pgdir_t *pd)
 {
        pd->kpte = 0;
-       pd->epte = 0;
+       pd->eptp = 0;
 }
 
 /* Returns the page shift of the largest jumbo supported */
@@ -657,3 +651,47 @@ void debug_print_pgdir(kpte_t *pgdir)
                pml_for_each(pgdir, KERNBASE, VPT - KERNBASE, print_pte, 0);
        pml_for_each(pgdir, VPT_TOP, MAX_VADDR - VPT_TOP, print_pte, 0);
 }
+
+/* Debug helper - makes sure the KPT == EPT for [0, UVPT) */
+int debug_check_kpt_ept(void)
+{
+       int db_cb(kpte_t *kpte, uintptr_t kva, int shift, bool visited_subs,
+                 void *data)
+       {
+               epte_t *epte = kpte_to_epte(kpte);
+               char *reason;
+               int pa_offset = 0;
+
+               if (kpte_is_present(kpte) != epte_is_present(epte)) {
+                       reason = "present bit";
+                       goto fail;
+               }
+               if (kpte_is_mapped(kpte) != epte_is_mapped(epte)) {
+                       reason = "mapped or not";
+                       goto fail;
+               }
+               if (kpte_is_jumbo(kpte) != epte_is_jumbo(epte)) {
+                       reason = "jumbo";
+                       goto fail;
+               }
+               /* Intermediate PTEs have the EPTE pointing to PADDR + PGSIZE */
+               if (pte_is_present(kpte) && pte_is_intermediate(kpte, shift))
+                       pa_offset = PGSIZE;
+               if (kpte_get_paddr(kpte) + pa_offset != epte_get_paddr(epte)) {
+                       reason = "paddr";
+                       goto fail;
+               }
+               if ((kpte_get_settings(kpte) & PTE_PERM) !=
+                   (epte_get_settings(epte) & PTE_PERM)) {
+                       reason = "permissions";
+                       goto fail;
+               }
+               return 0;
+
+fail:
+               panic("kpte %p (%p) epte %p (%p) kva %p shift %d: %s",
+                      kpte, *kpte, epte, *epte, kva, shift, reason);
+               return -1;
+       }
+       return pml_for_each(current->env_pgdir.kpte, 0, UVPT - 0, db_cb, 0);
+}