x86: changes pte_t to be a KPTE and an EPTE
authorBarret Rhoden <brho@cs.berkeley.edu>
Tue, 17 Mar 2015 13:38:57 +0000 (09:38 -0400)
committerBarret Rhoden <brho@cs.berkeley.edu>
Tue, 17 Mar 2015 14:56:00 +0000 (10:56 -0400)
Ideally, a PTE would always have a KPTE, and optionally have an EPTE.
Now, for all ops other than walk_okay, we have at least one of KPTE or
EPTE (and possibly both).  pgdir_walk() will return both.  memwalks will
have only one or the other.

The alternative is to come up with a way to walk both tables in
lockstep, jumping back and forth and building a full PTE for the
memcallback at each point.  We might do that later, if necessary.

kern/arch/x86/pmap64.c
kern/arch/x86/pmap_ops.h
kern/arch/x86/ros/mmu64.h

index 30c245c..265395a 100644 (file)
@@ -364,10 +364,14 @@ int unmap_segment(pgdir_t pgdir, uintptr_t va, size_t size)
  * memory, this returns 0 (subject to change based on pte_t). */
 pte_t pgdir_walk(pgdir_t pgdir, const void *va, int create)
 {
+       pte_t ret;
        int flags = PML1_SHIFT;
        if (create == 1)
                flags |= PG_WALK_CREATE;
-       return (pte_t)pml_walk(pgdir_get_kpt(pgdir), (uintptr_t)va, flags);
+       ret.kpte = pml_walk(pgdir_get_kpt(pgdir), (uintptr_t)va, flags);
+       /* TODO: (EPT) walk the EPT */
+       ret.epte = 0;
+       return ret;
 }
 
 static int pml_perm_walk(kpte_t *pml, const void *va, int pml_shift)
@@ -486,19 +490,28 @@ 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, (pte_t)kpte, (void*)kva, tp->cb_arg);
+               return tp->cb(tp->p, half_pte, (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;
-       return pml_for_each((kpte_t*)p->env_pgdir, (uintptr_t)start, len,
-                           trampoline_cb, &local_tp);
+       /* TODO: walk the EPT too.
+        *
+        * 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((kpte_t*)p->env_pgdir, (uintptr_t)start, len,
+                          trampoline_cb, &local_tp);
+       return ret;
 }
 
 /* Frees (decrefs) all pages of the process's page table, including the page
index c5eae53..7a2293e 100644 (file)
@@ -2,14 +2,25 @@
  * Barret Rhoden <brho@cs.berkeley.edu>
  * See LICENSE for details.
  *
- * Arch-specific operations for page tables and PTEs */
+ * Arch-specific operations for page tables and PTEs.
+ *
+ * Unfortunately, many of these ops are called from within a memwalk callback,
+ * which expects a full pte.  But doing walks for a KPT and an EPT at the same
+ * time is a pain, and for now we'll do the walks serially.  Because of that, a
+ * given pte_t may have a KPTE and/or an EPTE.  Ideally, it'd be *and*. */
 
 #ifndef ROS_ARCH_PMAPS_OPS_H
 #define ROS_ARCH_PMAPS_OPS_H
 
+/* TODO: (EPT)  build a CONFIG mode where we assert the EPT agrees with the KPT
+ * for all of the read ops */
+
 static inline bool pte_walk_okay(pte_t pte)
 {
-       return pte ? TRUE : FALSE;
+       /* walk_okay should only be called after a walk, when we have both a KPTE
+        * and an EPTE */
+       dassert(pte.kpte ? TRUE : !pte.epte);
+       return pte.kpte ? TRUE : FALSE;
 }
 
 /* PTE states:
@@ -24,64 +35,92 @@ static inline bool pte_walk_okay(pte_t pte)
  *     - unmapped: completely unused. (0 value) */
 static inline bool pte_is_present(pte_t pte)
 {
-       return *(kpte_t*)pte & PTE_P ? TRUE : FALSE;
+#if 0  /* could do some debuggin like this.  painful. */
+       bool ret_kpte, ret_epte;
+       assert(pte.kpte || pte.epte);
+       ret_kpte = pte.kpte ? (*pte.kpte & PTE_P ? TRUE : FALSE) : 0;
+       /* TODO: EPT check */
+       ret_epte = pte.epte ? (*pte.epte & PTE_P ? TRUE : FALSE) : 0;
+       if (pte.kpte && pte.epte)
+               assert(ret_kpte == ret_epte);
+       return pte.kpte ? ret_kpte : ret_epte;
+#endif
+       return pte.kpte ? (*pte.kpte & PTE_P ? TRUE : FALSE)
+                       : 0; /* TODO: EPT check */
 }
 
 static inline bool pte_is_unmapped(pte_t pte)
 {
-       return PAGE_UNMAPPED(*(kpte_t*)pte);
+       return pte.kpte ? PAGE_UNMAPPED(*pte.kpte)
+                       : 0; /* TODO: EPT check */
 }
 
 static inline bool pte_is_mapped(pte_t pte)
 {
-       return !PAGE_UNMAPPED(*(kpte_t*)pte);
+       return pte.kpte ? !PAGE_UNMAPPED(*pte.kpte)
+                       : 0; /* TODO: EPT check */
 }
 
 static inline bool pte_is_paged_out(pte_t pte)
 {
-       return PAGE_PAGED_OUT(*(kpte_t*)pte);
+       return pte.kpte ? PAGE_PAGED_OUT(*pte.kpte)
+                       : 0; /* TODO: EPT check */
 }
 
 static inline bool pte_is_dirty(pte_t pte)
 {
-       return *(kpte_t*)pte & PTE_D ? TRUE : FALSE;
+       return pte.kpte ? (*pte.kpte & PTE_D ? TRUE : FALSE)
+                       : 0; /* TODO: EPT check */
 }
 
 static inline bool pte_is_accessed(pte_t pte)
 {
-       return *(kpte_t*)pte & PTE_A ? TRUE : FALSE;
+       return pte.kpte ? (*pte.kpte & PTE_A ? TRUE : FALSE)
+                       : 0; /* TODO: EPT check */
 }
 
 /* Used in debugging code - want something better involving the walk */
 static inline bool pte_is_jumbo(pte_t pte)
 {
-       return *(kpte_t*)pte & PTE_PS ? TRUE : FALSE;
+       return pte.kpte ? (*pte.kpte & PTE_PS ? TRUE : FALSE)
+                       : 0; /* TODO: EPT check */
 }
 
 static inline physaddr_t pte_get_paddr(pte_t pte)
 {
-       return PTE_ADDR(*(kpte_t*)pte);
+       return pte.kpte ? PTE_ADDR(*pte.kpte)
+                       : 0; /* TODO: EPT check */
 }
 
 /* Returns the PTE in an unsigned long, for debugging mostly. */
 static inline unsigned long pte_print(pte_t pte)
 {
-       return *(kpte_t*)pte;
+       return pte.kpte ? *pte.kpte
+                       : 0; /* TODO: EPT check */
 }
 
 static inline void pte_write(pte_t pte, physaddr_t pa, int perm)
 {
-       *(kpte_t*)pte = PTE(pa2ppn(pa), perm);
+       if (pte.kpte)
+               *pte.kpte = PTE(pa2ppn(pa), perm);
+       if (pte.epte)
+               /* TODO: EPT write (if EPT) */;
 }
 
 static inline void pte_clear_present(pte_t pte)
 {
-       *(kpte_t*)pte &= ~PTE_P;
+       if (pte.kpte)
+               *pte.kpte &= ~PTE_P;
+       if (pte.epte)
+               /* TODO: EPT write (if EPT) */;
 }
 
 static inline void pte_clear(pte_t pte)
 {
-       *(kpte_t*)pte = 0;
+       if (pte.kpte)
+               *pte.kpte = 0;
+       if (pte.epte)
+               /* TODO: EPT write (if EPT) */;
 }
 
 /* These are used by memcpy_*_user, but are very dangerous (and possibly used
@@ -92,24 +131,30 @@ static inline void pte_clear(pte_t pte)
  * to an intermediate PTE, we'd miss that. */
 static inline bool pte_has_perm_ur(pte_t pte)
 {
-       return *(kpte_t*)pte & PTE_USER_RO ? TRUE : FALSE;
+       return pte.kpte ? (*pte.kpte & PTE_USER_RO ? TRUE : FALSE)
+                       : 0; /* TODO: EPT check */
 }
 
 static inline bool pte_has_perm_urw(pte_t pte)
 {
-       return *(kpte_t*)pte & PTE_USER_RW ? TRUE : FALSE;
+       return pte.kpte ? (*pte.kpte & PTE_USER_RW ? TRUE : FALSE)
+                       : 0; /* TODO: EPT check */
 }
 
 /* return the arch-independent format for prots - whatever you'd expect to
  * receive for pte_write.  Careful with the ret, since a valid type is 0. */
 static inline int pte_get_perm(pte_t pte)
 {
-       return *(kpte_t*)pte & PTE_PERM;
+       return pte.kpte ? *pte.kpte & PTE_PERM
+                       : 0; /* TODO: EPT check */
 }
 
 static inline void pte_replace_perm(pte_t pte, int perm)
 {
-       *(kpte_t*)pte = (*(kpte_t*)pte & ~PTE_PERM) | perm;
+       if (pte.kpte)
+               *pte.kpte = (*pte.kpte & ~PTE_PERM) | perm;
+       if (pte.epte)
+               /* TODO: EPT write (if EPT) */;
 }
 
 #endif /* ROS_ARCH_PMAPS_OPS_H */
index 947eb9f..434df25 100644 (file)
@@ -8,7 +8,13 @@
 #ifndef __ASSEMBLER__
 #include <ros/common.h>
 typedef unsigned long kpte_t;
-typedef unsigned long pte_t;
+typedef unsigned long epte_t;
+
+typedef struct x86_pte {
+       kpte_t  *kpte;
+       epte_t  *epte;
+} pte_t;
+
 typedef unsigned long pgdir_t;
 #endif