Initial mmap()
authorBarret Rhoden <brho@cs.berkeley.edu>
Sun, 6 Sep 2009 08:26:45 +0000 (01:26 -0700)
committerBarret Rhoden <brho@cs.berkeley.edu>
Mon, 19 Oct 2009 00:20:01 +0000 (17:20 -0700)
Userspace must specify the address, it must be page aligned, and
everything must be free.  It's pretty much MAP_FIXED | MAP_ANONYMOUS
semantics, regardless of what userspace asks for, for now.

Needs locking and some love from mm.h, which is just a WIP for now.

kern/include/mm.h [new file with mode: 0644]
kern/include/pmap.h
kern/src/Makefrag
kern/src/mm.c [new file with mode: 0644]
kern/src/pmap.c
kern/src/process.c
kern/src/syscall.c
user/apps/roslib/mhello.c

diff --git a/kern/include/mm.h b/kern/include/mm.h
new file mode 100644 (file)
index 0000000..4a9e75d
--- /dev/null
@@ -0,0 +1,76 @@
+/*
+ * Copyright (c) 2009 The Regents of the University of California
+ * Barret Rhoden <brho@cs.berkeley.edu>
+ * See LICENSE for details.
+ *
+ * Memory management for processes: syscall related functions, virtual memory
+ * regions, etc.
+ */
+
+#ifndef ROS_KERN_MM_H
+#define ROS_KERN_MM_H
+
+#include <ros/common.h>
+#include <process.h>
+#include <atomic.h>
+#include <sys/queue.h>
+
+/* Memory region for a process, consisting of linear(virtual) addresses.  This
+ * is what the kernel allocates a process, and the physical mapping can be done
+ * lazily (or not).  This way, if a page is swapped out, and the PTE says it
+ * isn't present, we still have a way to account for how the whole region ought
+ * to be dealt with.
+ * Some things are per-region:
+ * - probably something with shared memory
+ * - mmaping files: we can have a logical connection to something other than
+ *   anonymous memory
+ * - on a fault, was this memory supposed to be there? (swap, lazy, etc), or is
+ *   the region free?
+ * Others are per-page:
+ * - was this page supposed to be protected somehow(guard)? could be per-region
+ * - where is this page in the swap?
+ * If we try to store this info in the PTE, we only have 31 bits, and it's more
+ * arch dependent.  Handling jumbos is a pain.  And it's replicated across all
+ * pages for a coarse granularity things.  And we can't add things easily.
+ *
+ * so a process has a (sorted) list of these for it's VA space, hanging off it's
+ * struct proc.  or off it's mm?
+ * - we don't share an mm between processes anymore (tasks/threads)
+ *   - though we share most everything with vpm.
+ *   - want to be able to do all the same things with vpm as with regular mem
+ *     (file back mmap, etc)
+ *   - contexts or whatever share lots of mem things, like accounting, limits,
+ *   overall process stuff, the rest of the page tables.
+ *     - so there should be some overall mm, and probably directly in the
+ *     struct proc (or just one other struct directly embedded, not a pointer
+ *     to one where a bunch of processes use it)
+ *             - if we embed, mm.h doesn't need to know about process.h
+ *   so an mm can have a bunch of "address spaces" - or at least different
+ *   contexts
+ *
+ * how does this change or where does this belong with virtual private memory?
+ * will also affect get_free_va_range
+ *     - also, do we want a separate brk per?  or just support mmap on private mem?
+ */
+struct vm_region {
+       TAILQ_ENTRY(vm_region) link; // actually, i'd like a sorted tree of these
+       uintptr_t base;
+       size_t len;
+       int perm;
+};
+TAILQ_HEAD(vm_region_list, vm_region); // Declares 'struct memregion_list'
+
+struct mm {
+       spinlock_t mm_lock;
+       // per-process memory management stuff
+       // cr3(s), accounting, possibly handler methods for certain types of faults
+       // lists of vm_regions for all contexts
+       // base cr3 for all contexts
+       // previous brk, last checked vm_region
+
+};
+// would rather this be a mm struct
+void *mmap(struct proc *p, uintptr_t addr, size_t len, int prot, int flags, int fd, size_t offset);
+
+
+#endif // !ROS_KERN_MM_H
index d980195..052f6b1 100644 (file)
@@ -1,4 +1,17 @@
-/* See COPYRIGHT for copyright information. */
+/* See COPYRIGHT for copyright information.
+ * Inlines, macros, and most function prototypes (c) the JOS project.
+ *
+ * Actual implementation:
+ * Copyright (c) 2009 The Regents of the University of California
+ * Barret Rhoden <brho@cs.berkeley.edu>
+ * Kevin Klues <klueska@cs.berkeley.edu> (multiboot functions)
+ * Ander Waterman <waterman@cs.berkeley.edu> (memcpy_from_user)
+ * Zach Anderson (zra@cs.berkeley.edu> (user_mem_strlcpy)
+ * See LICENSE for details.
+ *
+ * Physical memory mangement, low-level virtual address space initialization and
+ * management, and other things related to virtual->physical mappings.
+ */
 
 #ifndef ROS_KERN_PMAP_H
 #define ROS_KERN_PMAP_H
@@ -66,6 +79,7 @@ void setup_default_mtrrs(barrier_t* smp_barrier);
 void   tlb_invalidate(pde_t *COUNT(NPDENTRIES) pgdir, void *SNT va);
 void tlb_flush_global(void);
 
+/* TODO: either move these, or make them take a pgdir */
 void * (DALLOC(len) user_mem_check) (env_t *env, const void *DANGEROUS va,
                                      size_t len, int perm);
 
@@ -82,6 +96,8 @@ memcpy_from_user(env_t* env, void* COUNT(len) dest,
 /* Arch specific implementations for these */
 pte_t *pgdir_walk(pde_t *COUNT(NPDENTRIES) pgdir, const void *SNT va, int create);
 int get_va_perms(pde_t *COUNT(NPDENTRIES) pgdir, const void *SNT va);
+// TODO: should this be per process, per mm, per pgdir, etc?
+// - can't ask this question without knowing the "context"
 void *get_free_va_range(pde_t *pgdir, uintptr_t addr, size_t len);
 
 static inline page_t *SAFE ppn2page(size_t ppn)
@@ -123,23 +139,4 @@ static inline page_t* kva2page(void* addr)
        return pa2page(PADDR(addr));
 }
 
-/*
- * Memory management for processes: syscall related functions, virtual memory
- * regions, etc.
- */
-void *mmap(struct proc *p, uintptr_t addr, size_t len, int prot, int flags, int fd, size_t offset);
-
-/* Memory region for a process, consisting of linear(virtual) addresses.  This
- * is what the kernel allocates a process, and the physical mapping can be done
- * lazily (or not).  This way, if a page is swapped out, and the PTE says it
- * isn't present, we still have a way to account for how the whole region ought
- * to be dealt with. */
-struct memregion {
-       LIST_ENTRY(memregion) link; // actually, i'd like a sorted tree of these
-       uintptr_t base;
-       size_t len;
-       int perm;
-};
-TAILQ_HEAD(memregion_list, memregion); // Declares 'struct memregion_list'
-
 #endif /* !ROS_KERN_PMAP_H */
index ae700cf..ba6d4c4 100644 (file)
@@ -40,6 +40,7 @@ KERN_SRCFILES := $(KERN_ARCH_SRCFILES) \
                  $(KERN_SRC_DIR)/hashtable.c \
                  $(KERN_SRC_DIR)/hashtable_itr.c \
                  $(KERN_SRC_DIR)/schedule.c \
+                 $(KERN_SRC_DIR)/mm.c \
                  $(KERN_SRC_DIR)/testing.c
 
 # Only build files if they exist.
diff --git a/kern/src/mm.c b/kern/src/mm.c
new file mode 100644 (file)
index 0000000..c2192e7
--- /dev/null
@@ -0,0 +1,78 @@
+/*
+ * Copyright (c) 2009 The Regents of the University of California
+ * Barret Rhoden <brho@cs.berkeley.edu>
+ * See LICENSE for details.
+ *
+ */
+
+#include <ros/common.h>
+#include <ros/mman.h>
+#include <pmap.h>
+#include <mm.h>
+#include <process.h>
+#include <stdio.h>
+
+/* mmap2() semantics on the offset (num pages, not bytes) */
+void *mmap(struct proc *p, uintptr_t addr, size_t len, int prot, int flags,
+           int fd, size_t offset)
+{
+       if (fd || offset) {
+               printk("[kernel] mmap() does not support files yet.\n");
+               return (void*SAFE)TC(-1);
+       }
+       if (flags != MAP_ANONYMOUS)
+               printk("[kernel] mmap() only supports MAP_ANONYMOUS for now, other"
+                      "flags ignored.\n");
+       /* TODO: make this work, instead of a ghetto hack
+        * Find a valid range, make sure it doesn't run into the kernel
+        * make sure there's enough memory (not exceeding quotas)
+        * allocate and map the pages, update appropriate structures (vm_region)
+        * return appropriate pointer
+        * Right now, all we can do is give them the range they ask for.
+        */
+       //void *tmp = get_free_va_range(p->env_pgdir, addr, len);
+       //printk("tmp = 0x%08x\n", tmp);
+       if (!addr) {
+               printk("[kernel] mmap() requires an address, since it's ghetto\n");
+               return (void*SAFE)TC(-1);
+       }
+       // brief sanity check.  must be page aligned and not reaching too high
+       if (PGOFF(addr)) {
+               printk("[kernel] mmap() page align your addr.\n");
+               return (void*SAFE)TC(-1);
+       }
+       int num_pages = ROUNDUP(len, PGSIZE) / PGSIZE;
+       pte_t *a_pte;
+       // TODO: grab the appropriate mm_lock
+       // make sure all pages are available, and in a reasonable range
+       // TODO: can probably do this better with vm_regions.
+       for (int i = 0; i < num_pages; i++) {
+               a_pte = pgdir_walk(p->env_pgdir, (void*SNT)addr, 0);
+               if (a_pte && *a_pte & PTE_P)
+                       goto mmap_abort;
+               if (addr + i*PGSIZE >= USTACKTOP - PGSIZE)
+                       goto mmap_abort;
+       }
+       page_t *a_page;
+       for (int i = 0; i < num_pages; i++) {
+               if (page_alloc(&a_page))
+                       goto mmap_abort;
+               // TODO: give them the permissions they actually want
+               if (page_insert(p->env_pgdir, a_page, (void*SNT)addr + i*PGSIZE,
+                               PTE_USER_RW)) {
+                       page_free(a_page);
+                       goto mmap_abort;
+               }
+       }
+       // TODO: release the appropriate mm_lock
+       return (void*SAFE)TC(addr);
+
+       // TODO: if there's a failure, we should go back through the addr+len range
+       // and dealloc everything.  or at least define what we want to do if we run
+       // out of memory.
+       mmap_abort:
+               // TODO: release the appropriate mm_lock
+               printk("[kernel] mmap() aborted!\n");
+               // mmap's semantics.  we need a better error propagation system
+               return (void*SAFE)TC(-1); // this is also ridiculous
+}
index 4960e5f..afcc0a9 100644 (file)
@@ -443,16 +443,3 @@ error_t memcpy_from_user(env_t* env, void* COUNT(len) dest,
 
        return ESUCCESS;
 }
-
-/* mmap2() semantics on the offset */
-void *mmap(struct proc *p, uintptr_t addr, size_t len, int prot, int flags, int fd,
-           size_t offset)
-{
-       if (fd || offset) {
-               printk("[kernel] mmap() does not support files yet.\n");
-               return (void*SAFE)TC(-1);
-       }
-       void *tmp = get_free_va_range(p->env_pgdir, addr, len);
-       printk("tmp = 0x%08x\n", tmp);
-       return 0;
-}
index b7071f8..9533a98 100644 (file)
@@ -357,6 +357,8 @@ void proc_destroy(struct proc *p)
        /* If we were running the process, we should have received an IPI by now.
         * If not, odds are interrupts are disabled, which shouldn't happen while
         * servicing syscalls. */
+       // TODO: this trips if we die in a trap that kills a process, like a page
+       // fault
        assert(current != p);
        return;
 }
index 821b42e..d83dd64 100644 (file)
@@ -17,6 +17,7 @@
 #include <process.h>
 #include <schedule.h>
 #include <pmap.h>
+#include <mm.h>
 #include <trap.h>
 #include <syscall.h>
 #include <kmalloc.h>
index 417c805..8b2dc7e 100644 (file)
@@ -1,4 +1,5 @@
 #include <lib.h>
+#include <ros/mman.h>
 #include <stdio.h>
 
 // ghetto udelay, put in a lib somewhere and export the tsc freq
@@ -19,8 +20,17 @@ void udelay(uint64_t usec, uint64_t tsc_freq)
 
 int main(int argc, char** argv)
 {
+       void* addr;
        cprintf("Multi-Goodbye, world, from PID: %d!\n", sys_getpid());
-       sys_mmap((void*SNT)1, 2, 3, 4, 0, 0);
+       addr = sys_mmap((void*)USTACKTOP - 20*PGSIZE, 8*PGSIZE, 3, MAP_FIXED, 0, 0);
+       cprintf("got addr = 0x%08x\n", addr);
+       *(int*)addr = 0xdeadbeef;
+       *(int*)(addr + 3*PGSIZE) = 0xcafebabe;
+       // these should work
+       cprintf("reading addr: 0x%08x\n", *(int*)addr);
+       cprintf("reading addr+3pg: 0x%08x\n", *(int*)(addr + 3*PGSIZE));
+       // this should fault
+       //*(int*)(addr - 3*PGSIZE) = 0xdeadbeef;
        while(1);
        udelay(5000000, 1995014570); // KVM's freq.  Whatever.