Basic functions to dynamically adjust kernel vmaps
authorBarret Rhoden <brho@cs.berkeley.edu>
Tue, 14 Jun 2011 01:14:17 +0000 (18:14 -0700)
committerKevin Klues <klueska@cs.berkeley.edu>
Thu, 3 Nov 2011 00:36:04 +0000 (17:36 -0700)
For now, you can't dealloc or unmap regions.  There are two parts:
reservation/alloc/getting of a chunk of vm space (growing down from
KERN_DYN_TOP), and then mapping those vaddrs to paddrs.  This is not
like page_insert - there might not be real pages involved.  You'll have
to handle your own refcnting or otherwise handle whatever you are
mapping.

kern/include/mm.h
kern/include/pmap.h
kern/src/mm.c

index 313422a..509b0ee 100644 (file)
@@ -80,4 +80,13 @@ int __do_mprotect(struct proc *p, uintptr_t addr, size_t len, int prot);
 int __do_munmap(struct proc *p, uintptr_t addr, size_t len);
 int __handle_page_fault(struct proc* p, uintptr_t va, int prot);
 
+/* Kernel Dynamic Memory Mappings */
+/* These two are just about reserving VA space */
+uintptr_t get_vmap_segment(unsigned long num_pages);
+uintptr_t put_vmap_segment(uintptr_t vaddr, unsigned long num_pages);
+/* These two are about actually mapping stuff in some reserved space */
+int map_vmap_segment(uintptr_t vaddr, uintptr_t paddr, unsigned long num_pages,
+                     int perm);
+int unmap_vmap_segment(uintptr_t vaddr, unsigned long num_pages);
+
 #endif /* !ROS_KERN_MM_H */
index bddd3be..a593d14 100644 (file)
@@ -108,6 +108,11 @@ static inline page_t*COUNT(1) pa2page(physaddr_t pa)
        return &pages[LA2PPN(pa)];
 }
 
+static inline ppn_t pa2ppn(physaddr_t pa)
+{
+       return pa >> PGSHIFT;
+}
+
 static inline void*COUNT(PGSIZE) page2kva(page_t *pp)
 {
        return KADDR(page2pa(pp));
index edda11a..fede248 100644 (file)
@@ -636,3 +636,90 @@ int __handle_page_fault(struct proc *p, uintptr_t va, int prot)
        *pte = PTE(page2ppn(a_page), PTE_P | pte_prot);
        return 0;
 }
+
+/* Kernel Dynamic Memory Mappings */
+uintptr_t dyn_vmap_llim = KERN_DYN_TOP;
+spinlock_t dyn_vmap_lock = SPINLOCK_INITIALIZER;
+
+/* Reserve space in the kernel dynamic memory map area */
+uintptr_t get_vmap_segment(unsigned long num_pages)
+{
+       uintptr_t retval;
+       spin_lock(&dyn_vmap_lock);
+       retval = dyn_vmap_llim - num_pages * PGSIZE;
+       if ((retval > ULIM) && (retval < KERN_DYN_TOP)) {
+               dyn_vmap_llim = retval;
+       } else {
+               warn("[kernel] dynamic mapping failed!");
+               retval = 0;
+       }
+       spin_unlock(&dyn_vmap_lock);
+       return retval;
+}
+
+/* Give up your space.  Note this isn't supported yet */
+uintptr_t put_vmap_segment(uintptr_t vaddr, unsigned long num_pages)
+{
+       /* TODO: use vmem regions for adjustable vmap segments */
+       panic("Unsupported.\n");
+}
+
+/* Map a virtual address chunk to physical addresses.  Make sure you got a vmap
+ * segment before actually trying to do the mapping.
+ *
+ * Careful with more than one 'page', since it will assume your physical pages
+ * are also contiguous.  Most callers will only use one page.
+ *
+ * Finally, note that this does not care whether or not there are real pages
+ * being mapped, and will not attempt to incref your page (if there is such a
+ * thing).  Handle your own refcnting for pages. */
+int map_vmap_segment(uintptr_t vaddr, uintptr_t paddr, unsigned long num_pages,
+                     int perm)
+{
+       /* For now, we only handle the root pgdir, and not any of the other ones
+        * (like for processes).  To do so, we'll need to insert into every pgdir,
+        * and send tlb shootdowns to those that are active (which we don't track
+        * yet). */
+       extern int booting;
+       assert(booting);
+
+       /* TODO: (MM) you should lock on boot pgdir modifications.  A vm region lock
+        * isn't enough, since there might be a race on outer levels of page tables.
+        * For now, we'll just use the dyn_vmap_lock (which technically works). */
+       spin_lock(&dyn_vmap_lock);
+       pte_t *pte;
+#ifdef __i386__
+       perm |= PTE_G;
+#endif
+       for (int i = 0; i < num_pages; i++) {
+               pte = pgdir_walk(boot_pgdir, (void*)(vaddr + i * PGSIZE), 1);
+               if (!pte) {
+                       spin_unlock(&dyn_vmap_lock);
+                       return -ENOMEM;
+               }
+               *pte = PTE(pa2ppn(paddr + i * PGSIZE), perm);
+       }
+       spin_unlock(&dyn_vmap_lock);
+       return 0;
+}
+
+/* Unmaps / 0's the PTEs of a chunk of vaddr space */
+int unmap_vmap_segment(uintptr_t vaddr, unsigned long num_pages)
+{
+       /* Not a big deal - won't need this til we do something with kthreads */
+       panic("Incomplete, don't call this yet.");
+       spin_lock(&dyn_vmap_lock);
+       /* TODO: For all pgdirs */
+       pte_t *pte;
+       for (int i = 0; i < num_pages; i++) {
+               pte = pgdir_walk(boot_pgdir, (void*)(vaddr + i * PGSIZE), 1);
+               *pte = 0;
+       }
+       /* TODO: TLB shootdown.  Also note that the global flag is set on the PTE
+        * (for x86 for now), which requires a global shootdown.  bigger issue is
+        * the TLB shootdowns for multiple pgdirs.  We'll need to remove from every
+        * pgdir, and send tlb shootdowns to those that are active (which we don't
+        * track yet). */
+       spin_unlock(&dyn_vmap_lock);
+       return 0;
+}