Rename RCU CB context to 'cannot block' context
[akaros.git] / kern / src / pmap.c
index 16bdae3..9935287 100644 (file)
-/* See COPYRIGHT for copyright information. */
-
-/** @file 
- * This file is responsible for managing physical pages as they 
- * are mapped into the page tables of a particular virtual address
- * space.  The functions defined in this file operate on these
- * page tables to insert and remove physical pages from them at 
- * particular virtual addresses.
+/* Copyright (c) 2009,13 The Regents of the University of California
+ * Barret Rhoden <brho@cs.berkeley.edu>
+ * See LICENSE for details.
  *
- * @author Kevin Klues <klueska@cs.berkeley.edu>
- * @author Barret Rhoden <brho@cs.berkeley.edu>
- */
-
-#ifdef __SHARC__
-#pragma nosharc
-#endif
-
-#ifdef __DEPUTY__
-#pragma nodeputy
-#endif
+ * Arch independent physical memory and page table management.
+ *
+ * For page allocation, check out the family of page_alloc files. */
 
 #include <arch/arch.h>
 #include <arch/mmu.h>
 
-#include <ros/error.h>
+#include <error.h>
 
 #include <kmalloc.h>
 #include <atomic.h>
 #include <string.h>
 #include <assert.h>
 #include <pmap.h>
-#include <kclock.h>
 #include <process.h>
 #include <stdio.h>
+#include <mm.h>
+#include <multiboot.h>
+#include <arena.h>
+#include <init.h>
+
+physaddr_t max_pmem = 0;       /* Total amount of physical memory (bytes) */
+physaddr_t max_paddr = 0;      /* Maximum addressable physical address */
+size_t max_nr_pages = 0;       /* Number of addressable physical memory pages */
+struct page *pages = 0;
+struct multiboot_info *multiboot_kaddr = 0;
+uintptr_t boot_freemem = 0;
+uintptr_t boot_freelimit = 0;
+
+static size_t sizeof_mboot_mmentry(struct multiboot_mmap_entry *entry)
+{
+       /* Careful - len is a uint64 (need to cast down for 32 bit) */
+       return (size_t)(entry->len);
+}
 
-/**
- * @brief Global variable used to store erroneous virtual addresses as the
- *        result of a failed user_mem_check().
- *
- * zra: What if two checks fail at the same time? Maybe this should be per-cpu?
- *
- */
-static void *DANGEROUS RACY user_mem_check_addr;
+static void adjust_max_pmem(struct multiboot_mmap_entry *entry, void *data)
+{
+       if (entry->type != MULTIBOOT_MEMORY_AVAILABLE)
+               return;
+       /* Careful - addr + len is a uint64 (need to cast down for 32 bit) */
+       max_pmem = MAX(max_pmem, (size_t)(entry->addr + entry->len));
+}
 
-volatile uint32_t vpt_lock = 0;
-volatile uint32_t vpd_lock = 0;
+static void kpages_arena_init(void)
+{
+       void *kpages_pg;
+
+       kpages_pg = arena_alloc(base_arena, PGSIZE, MEM_WAIT);
+       kpages_arena = arena_builder(kpages_pg, "kpages", PGSIZE, arena_alloc,
+                                    arena_free, base_arena, 8 * PGSIZE);
+}
 
 /**
- * @brief Initialize the array of physical pages and memory free list.
+ * @brief Initializes physical memory.  Determines the pmem layout, sets up the
+ * base and kpages arenas, and turns on virtual memory/page tables.
+ *
+ * Regarding max_pmem vs max_paddr and max_nr_pages: max_pmem is the largest
+ * physical address that is in a FREE region.  It includes RESERVED regions that
+ * are below this point.  max_paddr is the largest physical address, <=
+ * max_pmem, that the KERNBASE mapping can map.  It too may include reserved
+ * ranges.  The 'pages' array will track all physical pages up to max_paddr.
+ * There are max_nr_pages of them.  On 64 bit systems, max_pmem == max_paddr. */
+void pmem_init(struct multiboot_info *mbi)
+{
+       mboot_detect_memory(mbi);
+       mboot_print_mmap(mbi);
+       /* adjust the max memory based on the mmaps, since the old detection doesn't
+        * help much on 64 bit systems */
+       mboot_foreach_mmap(mbi, adjust_max_pmem, 0);
+       /* KERN_VMAP_TOP - KERNBASE is the max amount of virtual addresses we can
+        * use for the physical memory mapping (aka - the KERNBASE mapping).
+        * Should't be an issue on 64b, but is usually for 32 bit. */
+       max_paddr = MIN(max_pmem, KERN_VMAP_TOP - KERNBASE);
+       /* Note not all of this memory is free. */
+       max_nr_pages = max_paddr / PGSIZE;
+       printk("Max physical RAM (appx, bytes): %lu\n", max_pmem);
+       printk("Max addressable physical RAM (appx): %lu\n", max_paddr);
+       printk("Highest page number (including reserved): %lu\n", max_nr_pages);
+       /* We should init the page structs, but zeroing happens to work, except for
+        * the sems.  Those are init'd by the page cache before they are used. */
+       pages = (struct page*)boot_zalloc(max_nr_pages * sizeof(struct page),
+                                         PGSIZE);
+       base_arena_init(mbi);
+       /* kpages will use some of the basic slab caches.  kmem_cache_init needs to
+        * not do memory allocations (which it doesn't, and it can base_alloc()). */
+       kmem_cache_init();
+       kpages_arena_init();
+       printk("Base arena total mem: %lu\n", arena_amt_total(base_arena));
+       vm_init();
+
+       static_assert(PROCINFO_NUM_PAGES*PGSIZE <= PTSIZE);
+       static_assert(PROCDATA_NUM_PAGES*PGSIZE <= PTSIZE);
+}
+
+static void set_largest_freezone(struct multiboot_mmap_entry *entry, void *data)
+{
+       struct multiboot_mmap_entry **boot_zone =
+              (struct multiboot_mmap_entry**)data;
+
+       if (entry->type != MULTIBOOT_MEMORY_AVAILABLE)
+               return;
+       if (!*boot_zone || (sizeof_mboot_mmentry(entry) >
+                          sizeof_mboot_mmentry(*boot_zone)))
+               *boot_zone = entry;
+}
+
+/* Initialize boot freemem and its limit.
  *
- * The 'pages' array has one 'page_t' entry per physical page.
- * Pages are reference counted, and free pages are kept on a linked list.
- */
-void page_init(void)
+ * "end" is a symbol marking the end of the kernel.  This covers anything linked
+ * in with the kernel (KFS, etc).  However, 'end' is a kernel load address,
+ * which differs from kernbase addrs in 64 bit.  We need to use the kernbase
+ * mapping for anything dynamic (because it could go beyond 1 GB).
+ *
+ * Ideally, we'll use the largest mmap zone, as reported by multiboot.  If we
+ * don't have one (riscv), we'll just use the memory after the kernel.
+ *
+ * If we do have a zone, there is a chance we've already used some of it (for
+ * the kernel, etc).  We'll use the lowest address in the zone that is
+ * greater than "end" (and adjust the limit accordingly).  */
+static void boot_alloc_init(void)
 {
-       /*
-     * First, make 'pages' point to an array of size 'npages' of
-        * type 'page_t'.
-        * The kernel uses this structure to keep track of physical pages;
-        * 'npages' equals the number of physical pages in memory.
-        * round up to the nearest page
-        */
-       pages = (page_t*)boot_alloc(npages*sizeof(page_t), PGSIZE);
-       memset(pages, 0, npages*sizeof(page_t));
+       extern char end[];
+       uintptr_t boot_zone_start, boot_zone_end;
+       uintptr_t end_kva = (uintptr_t)KBASEADDR(end);
+       struct multiboot_mmap_entry *boot_zone = 0;
+
+       /* Find our largest mmap_entry; that will set bootzone */
+       mboot_foreach_mmap(multiboot_kaddr, set_largest_freezone, &boot_zone);
+       if (boot_zone) {
+               boot_zone_start = (uintptr_t)KADDR(boot_zone->addr);
+               /* one issue for 32b is that the boot_zone_end could be beyond max_paddr
+                * and even wrap-around.  Do the min check as a uint64_t.  The result
+                * should be a safe, unwrapped 32/64b when cast to physaddr_t. */
+               boot_zone_end = (uintptr_t)KADDR(MIN(boot_zone->addr + boot_zone->len,
+                                                (uint64_t)max_paddr));
+               /* using KERNBASE (kva, btw) which covers the kernel and anything before
+                * it (like the stuff below EXTPHYSMEM on x86) */
+               if (regions_collide_unsafe(KERNBASE, end_kva,
+                                          boot_zone_start, boot_zone_end))
+                       boot_freemem = end_kva;
+               else
+                       boot_freemem = boot_zone_start;
+               boot_freelimit = boot_zone_end;
+       } else {
+               boot_freemem = end_kva;
+               boot_freelimit = max_paddr + KERNBASE;
+       }
+       printd("boot_zone: %p, paddr base: 0x%llx, paddr len: 0x%llx\n", boot_zone,
+              boot_zone ? boot_zone->addr : 0,
+              boot_zone ? boot_zone->len : 0);
+       printd("boot_freemem: %p, boot_freelimit %p\n", boot_freemem,
+              boot_freelimit);
+}
 
-       /*
-     * Then initilaize everything so pages can start to be alloced and freed
-        * from the memory free list
-        */
-       page_alloc_init();
+/* Low-level allocator, used before page_alloc is on.  Returns size bytes,
+ * aligned to align (should be a power of 2).  Retval is a kernbase addr.  Will
+ * panic on failure. */
+void *boot_alloc(size_t amt, size_t align)
+{
+       uintptr_t retval;
+
+       if (!boot_freemem)
+               boot_alloc_init();
+       boot_freemem = ROUNDUP(boot_freemem, align);
+       retval = boot_freemem;
+       if (boot_freemem + amt > boot_freelimit){
+               printk("boot_alloc: boot_freemem is 0x%x\n", boot_freemem);
+               printk("boot_alloc: amt is %d\n", amt);
+               printk("boot_freelimit is 0x%x\n", boot_freelimit);
+               printk("boot_freemem + amt is > boot_freelimit\n");
+               panic("Out of memory in boot alloc, you fool!\n");
+       }
+       boot_freemem += amt;
+       printd("boot alloc from %p to %p\n", retval, boot_freemem);
+       /* multiboot info probably won't ever conflict with our boot alloc */
+       if (mboot_region_collides(multiboot_kaddr, retval, boot_freemem))
+               panic("boot allocation could clobber multiboot info!  Get help!");
+       return (void*)retval;
+}
+
+void *boot_zalloc(size_t amt, size_t align)
+{
+       /* boot_alloc panics on failure */
+       void *v = boot_alloc(amt, align);
+       memset(v, 0, amt);
+       return v;
 }
 
-/** 
+/**
  * @brief Map the physical page 'pp' into the virtual address 'va' in page
  *        directory 'pgdir'
  *
  * Map the physical page 'pp' at virtual address 'va'.
  * The permissions (the low 12 bits) of the page table
  * entry should be set to 'perm|PTE_P'.
- * 
+ *
  * Details:
  *   - If there is already a page mapped at 'va', it is page_remove()d.
- *   - If necessary, on demand, allocates a page table and inserts it into 
+ *   - If necessary, on demand, allocates a page table and inserts it into
  *     'pgdir'.
- *   - page_incref() should be called if the insertion succeeds. 
+ *   - This saves your refcnt in the pgdir (refcnts going away soon).
  *   - The TLB must be invalidated if a page was formerly present at 'va'.
  *     (this is handled in page_remove)
  *
  * No support for jumbos here.  We will need to be careful when trying to
  * insert regular pages into something that was already jumbo.  We will
- * also need to be careful with our overloading of the PTE_PS and 
+ * also need to be careful with our overloading of the PTE_PS and
  * PTE_PAT flags...
  *
  * @param[in] pgdir the page directory to insert the page into
@@ -96,7 +216,7 @@ void page_init(void)
  *                  physical page that should be inserted.
  * @param[in] va    the virtual address where the page should be
  *                  inserted.
- * @param[in] perm  the permition bits with which to set up the 
+ * @param[in] perm  the permition bits with which to set up the
  *                  virtual mapping.
  *
  * @return ESUCCESS  on success
@@ -104,68 +224,19 @@ void page_init(void)
  *                   into which the page should be inserted
  *
  */
-int page_insert(pde_t *pgdir, page_t *pp, void *va, int perm) 
+int page_insert(pgdir_t pgdir, struct page *page, void *va, int perm)
 {
-       pte_t* pte = pgdir_walk(pgdir, va, 1);
-       if (!pte)
+       pte_t pte = pgdir_walk(pgdir, va, 1);
+       if (!pte_walk_okay(pte))
                return -ENOMEM;
-       // need to up the ref count in case pp is already mapped at va
-       // and we don't want to page_remove (which could free pp) and then 
-       // continue as if pp wasn't freed.  moral = up the ref asap
-       page_incref(pp);
-       if (*pte & PTE_P) {
-               page_remove(pgdir, va);
-       }
-       *pte = PTE(page2ppn(pp), PTE_P | perm);
+       /* Leftover from older times, but we no longer suppor this: */
+       assert(!pte_is_mapped(pte));
+       pte_write(pte, page2pa(page), perm);
        return 0;
 }
 
 /**
- * @brief Map the physical page 'pp' at the first virtual address that is free 
- * in the range 'vab' to 'vae' in page directory 'pgdir'.
- *
- * The permissions (the low 12 bits) of the page table entry get set to 
- * 'perm|PTE_P'.
- *
- * Details:
- *   - If there is no free entry in the range 'vab' to 'vae' this 
- *     function returns NULL.
- *   - If necessary, on demand, this function will allocate a page table 
- *     and inserts it into 'pgdir'.
- *   - page_incref() will be called if the insertion succeeds.
- * 
- * @param[in] pgdir the page directory to insert the page into
- * @param[in] pp    a pointr to the page struct representing the
- *                  physical page that should be inserted.
- * @param[in] vab   the first virtual address in the range in which the 
- *                  page can be inserted.
- * @param[in] vae   the last virtual address in the range in which the 
- *                  page can be inserted.
- * @param[in] perm  the permition bits with which to set up the 
- *                  virtual mapping.
- *
- * @return VA   the virtual address where pp has been mapped in the 
- *              range (vab, vae)
- * @return NULL no free va in the range (vab, vae) could be found
- */
-void* page_insert_in_range(pde_t *pgdir, page_t *pp, 
-                           void *vab, void *vae, int perm) 
-{
-       pte_t* pte = NULL;
-       void*SNT new_va;
-       
-       for(new_va = vab; new_va <= vae; new_va+= PGSIZE) {
-               pte = pgdir_walk(pgdir, new_va, 1);
-               if(pte != NULL && !(*pte & PTE_P)) break;
-               else pte = NULL;
-       }
-       if (!pte) return NULL;
-       *pte = page2pa(pp) | PTE_P | perm;
-       return TC(new_va); // trusted because mapping a page is like allocation
-}
-
-/**
- * @brief Return the page mapped at virtual address 'va' in 
+ * @brief Return the page mapped at virtual address 'va' in
  * page directory 'pgdir'.
  *
  * If pte_store is not NULL, then we store in it the address
@@ -179,23 +250,23 @@ void* page_insert_in_range(pde_t *pgdir, page_t *pp,
  * @param[out] pte_store the address of the page table entry for the returned page
  *
  * @return PAGE the page mapped at virtual address 'va'
- * @return NULL No mapping exists at virtual address 'va'   
+ * @return NULL No mapping exists at virtual address 'va', or it's paged out
  */
-page_t *page_lookup(pde_t *pgdir, void *va, pte_t **pte_store)
+page_t *page_lookup(pgdir_t pgdir, void *va, pte_t *pte_store)
 {
-       pte_t* pte = pgdir_walk(pgdir, va, 0);
-       if (!pte || !(*pte & PTE_P))
+       pte_t pte = pgdir_walk(pgdir, va, 0);
+       if (!pte_walk_okay(pte) || !pte_is_mapped(pte))
                return 0;
        if (pte_store)
                *pte_store = pte;
-       return pa2page(PTE_ADDR(*pte));
+       return pa2page(pte_get_paddr(pte));
 }
 
 /**
  * @brief Unmaps the physical page at virtual address 'va' in page directory
  * 'pgdir'.
  *
- * If there is no physical page at that address, this function silently 
+ * If there is no physical page at that address, this function silently
  * does nothing.
  *
  * Details:
@@ -205,296 +276,90 @@ page_t *page_lookup(pde_t *pgdir, void *va, pte_t **pte_store)
  *     (if such a PTE exists)
  *   - The TLB is invalidated if an entry is removes from the pg dir/pg table.
  *
- * This may be wonky wrt Jumbo pages and decref.  
+ * This may be wonky wrt Jumbo pages and decref.
  *
  * @param pgdir the page directory from with the page sholuld be removed
- * @param va    the virtual address at which the page we are trying to 
+ * @param va    the virtual address at which the page we are trying to
  *              remove is mapped
- */
-void page_remove(pde_t *pgdir, void *va)
+ * TODO: consider deprecating this, or at least changing how it works with TLBs.
+ * Might want to have the caller need to manage the TLB.  Also note it is used
+ * in env_user_mem_free, minus the walk. */
+void page_remove(pgdir_t pgdir, void *va)
 {
-       pte_t* pte;
+       pte_t pte;
        page_t *page;
-       page = page_lookup(pgdir, va, &pte);
-       if (!page)
+
+       pte = pgdir_walk(pgdir,va,0);
+       if (!pte_walk_okay(pte) || pte_is_unmapped(pte))
                return;
-       *pte = 0;
-       tlb_invalidate(pgdir, va);
-       page_decref(page);
+
+       if (pte_is_mapped(pte)) {
+               /* TODO: (TLB) need to do a shootdown, inval sucks.  And might want to
+                * manage the TLB / free pages differently. (like by the caller).
+                * Careful about the proc/memory lock here. */
+               page = pa2page(pte_get_paddr(pte));
+               pte_clear(pte);
+               tlb_invalidate(pgdir, va);
+               page_decref(page);
+       } else if (pte_is_paged_out(pte)) {
+               /* TODO: (SWAP) need to free this from the swap */
+               panic("Swapping not supported!");
+               pte_clear(pte);
+       }
 }
 
 /**
  * @brief Invalidate a TLB entry, but only if the page tables being
  * edited are the ones currently in use by the processor.
  *
- * TODO: Need to sort this for cross core lovin'
+ * TODO: (TLB) Need to sort this for cross core lovin'
  *
- * @param pgdir the page directory assocaited with the tlb entry 
+ * @param pgdir the page directory assocaited with the tlb entry
  *              we are trying to invalidate
  * @param va    the virtual address associated with the tlb entry
  *              we are trying to invalidate
  */
-void tlb_invalidate(pde_t *pgdir, void *va)
+void tlb_invalidate(pgdir_t pgdir, void *va)
 {
        // Flush the entry only if we're modifying the current address space.
        // For now, there is only one address space, so always invalidate.
        invlpg(va);
 }
 
-/**
- * @brief Check that an environment is allowed to access the range of memory
- * [va, va+len) with permissions 'perm | PTE_P'.
- *
- * Normally 'perm' will contain PTE_U at least, but this is not required.  The
- * function get_va_perms only checks for PTE_U, PTE_W, and PTE_P.  It won't
- * check for things like PTE_PS, PTE_A, etc.
- * 'va' and 'len' need not be page-aligned;
- *
- * A user program can access a virtual address if:
- *     -# the address is below ULIM
- *     -# the page table gives it permission.  
- *
- * If there is an error, 'user_mem_check_addr' is set to the first
- * erroneous virtual address.
- *
- * @param env  the environment associated with the user program trying to access
- *             the virtual address range
- * @param va   the first virtual address in the range
- * @param len  the length of the virtual address range
- * @param perm the permissions the user is trying to access the virtual address 
- *             range with
- *
- * @return VA a pointer of type COUNT(len) to the address range
- * @return NULL trying to access this range of virtual addresses is not allowed
- */
-void* user_mem_check(env_t *env, const void *DANGEROUS va, size_t len, int perm)
-{
-       if (len == 0) {
-               warn("Called user_mem_check with a len of 0. Don't do that. Returning NULL");
-               return NULL;
-       }
-       
-       // TODO - will need to sort this out wrt page faulting / PTE_P
-       // also could be issues with sleeping and waking up to find pages
-       // are unmapped, though i think the lab ignores this since the 
-       // kernel is uninterruptible
-       void *DANGEROUS start, *DANGEROUS end;
-       size_t num_pages, i;
-       int page_perms = 0;
-
-       perm |= PTE_P;
-       start = ROUNDDOWN((void*DANGEROUS)va, PGSIZE);
-       end = ROUNDUP((void*DANGEROUS)va + len, PGSIZE);
-       if (start >= end) {
-               warn("Blimey!  Wrap around in VM range calculation!");  
-               return NULL;
-       }
-       num_pages = PPN(end - start);
-       for (i = 0; i < num_pages; i++, start += PGSIZE) {
-               page_perms = get_va_perms(env->env_pgdir, start);
-               // ensures the bits we want on are turned on.  if not, error out
-               if ((page_perms & perm) != perm) {
-                       if (i == 0)
-                               user_mem_check_addr = (void*DANGEROUS)va;
-                       else
-                               user_mem_check_addr = start;
-                       return NULL;
-               }
-       }
-       // this should never be needed, since the perms should catch it
-       if ((uintptr_t)end > ULIM) {
-               warn ("I suck - Bug in user permission mappings!");
-               return NULL;
-       }
-       return (void *COUNT(len))TC(va);
-}
-
-/**
- * @brief Use the kernel to copy a string from a buffer stored in userspace
- *        to a buffer stored elsewhere in the address space (potentially in 
- *        memory only accessible by the kernel)
- *
- * @param env  the environment associated with the user program from which
- *             the string is being copied
- * @param dst  the destination of the buffer into which the string 
- *             is being copied
- * @param va   the start address of the buffer where the string resides
- * @param len  the length of the buffer 
- * @param perm the permissions with which the user is trying to access 
- *             elements of the original buffer 
- *
- * @return LEN the length of the new buffer copied into 'dst'
- */
-size_t
-user_mem_strlcpy(env_t *env, char *_dst, const char *DANGEROUS va,
-                 size_t _len, int perm)
+static void __tlb_global(uint32_t srcid, long a0, long a1, long a2)
 {
-       const char *DANGEROUS src = va;
-       size_t len = _len;
-       char *NT COUNT(_len-1) dst_in = _dst;
-       char *NT BND(_dst,_dst + _len - 1) dst = _dst;
-
-       if (len > 0) {
-               while (1) {
-                       char *c;
-                       // what if len was 1?
-                       if (--len <= 0) break;
-                       c = user_mem_check(env, src, 1, perm);
-                       if (!c) break;
-                       if (*c == '\0') break;
-                       // TODO: ivy bitches about this
-                       *dst++ = *c;
-                       src++;
-               }
-               *dst = '\0';
-       }
-
-       return dst - dst_in;
+       tlb_flush_global();
 }
 
-/**
- * @brief Checks that environment 'env' is allowed to access the range
- * of memory [va, va+len) with permissions 'perm | PTE_U'. Destroy 
- * environment 'env' if the assertion fails.
- *
- * This function is identical to user_mem_assert() except that it has a side
- * affect of destroying the environment 'env' if the memory check fails.
- *
- * @param env  the environment associated with the user program trying to access
- *             the virtual address range
- * @param va   the first virtual address in the range
- * @param len  the length of the virtual address range
- * @param perm the permissions the user is trying to access the virtual address 
- *             range with
- *
- * @return VA a pointer of type COUNT(len) to the address range
- * @return NULL trying to access this range of virtual addresses is not allowed
- *              environment 'env' is destroyed
- */
-void *
-user_mem_assert(env_t *env, const void *DANGEROUS va, size_t len, int perm)
+/* Does a global TLB flush on all cores. */
+void tlb_shootdown_global(void)
 {
-       if (len == 0) {
-               warn("Called user_mem_assert with a len of 0. Don't do that. Returning NULL");
-               return NULL;
-       }
-       
-    void *COUNT(len) res = user_mem_check(env,va,len,perm | PTE_USER_RO);
-       if (!res) {
-               cprintf("[%08x] user_mem_check assertion failure for "
-                       "va %08x\n", env->env_id, user_mem_check_addr);
-               proc_destroy(env);      // may not return
-        return NULL;
+       tlb_flush_global();
+       if (booting)
+               return;
+       /* TODO: consider a helper for broadcast messages, though note that we're
+        * doing our flush immediately, which our caller expects from us before it
+        * returns. */
+       for (int i = 0; i < num_cores; i++) {
+               if (i == core_id())
+                       continue;
+               send_kernel_message(i, __tlb_global, 0, 0, 0, KMSG_IMMEDIATE);
        }
-    return res;
 }
 
-/**
- * @brief Copies data from a user buffer to a kernel buffer.
- * 
- * @param env  the environment associated with the user program
- *             from which the buffer is being copied
- * @param dest the destination address of the kernel buffer
- * @param va   the address of the userspace buffer from which we are copying
- * @param len  the length of the userspace buffer
- *
- * @return ESUCCESS on success
- * @return -EFAULT  the page assocaited with 'va' is not present, the user 
- *                  lacks the proper permissions, or there was an invalid 'va'
- */
-error_t memcpy_from_user(env_t* env, void* COUNT(len) dest,
-                 const void *DANGEROUS va, size_t len)
+/* Helper, returns true if any part of (start1, end1) is within (start2, end2).
+ * Equality of endpoints (like end1 == start2) is okay.
+ * Assumes no wrap-around. */
+bool regions_collide_unsafe(uintptr_t start1, uintptr_t end1,
+                            uintptr_t start2, uintptr_t end2)
 {
-       const void *DANGEROUS start, *DANGEROUS end;
-       size_t num_pages, i;
-       pte_t *pte;
-       uintptr_t perm = PTE_P | PTE_USER_RO;
-       size_t bytes_copied = 0;
-
-       static_assert(ULIM % PGSIZE == 0 && ULIM != 0); // prevent wrap-around
-
-       start = ROUNDDOWN(va, PGSIZE);
-       end = ROUNDUP(va + len, PGSIZE);
-
-       if(start >= (void*SNT)ULIM || end >= (void*SNT)ULIM)
-               return -EFAULT;
-
-       num_pages = PPN(end - start);
-       for(i = 0; i < num_pages; i++)
-       {
-               pte = pgdir_walk(env->env_pgdir, start+i*PGSIZE, 0);
-               if(!pte || (*pte & perm) != perm)
-                       return -EFAULT;
-
-               void*COUNT(PGSIZE) kpage = KADDR(PTE_ADDR(*pte));
-               const void* src_start = i > 0 ? kpage : kpage+(va-start);
-               void* dst_start = dest+bytes_copied;
-               size_t copy_len = PGSIZE;
-               if(i == 0)
-                       copy_len -= va-start;
-               if(i == num_pages-1)
-                       copy_len -= end-(va+len);
-
-               memcpy(dst_start,src_start,copy_len);
-               bytes_copied += copy_len;
+       if (start1 <= start2) {
+               if (end1 <= start2)
+                       return FALSE;
+               return TRUE;
+       } else {
+               if (end2 <= start1)
+                       return FALSE;
+               return TRUE;
        }
-
-       assert(bytes_copied == len);
-
-       return ESUCCESS;
-}
-
-/**
- * @brief Copies data to a user buffer from a kernel buffer.
- * 
- * @param env  the environment associated with the user program
- *             to which the buffer is being copied
- * @param dest the destination address of the user buffer
- * @param va   the address of the kernel buffer from which we are copying
- * @param len  the length of the user buffer
- *
- * @return ESUCCESS on success
- * @return -EFAULT  the page assocaited with 'va' is not present, the user 
- *                  lacks the proper permissions, or there was an invalid 'va'
- */
-error_t memcpy_to_user(env_t* env, void*DANGEROUS va,
-                 const void *COUNT(len) src, size_t len)
-{
-       const void *DANGEROUS start, *DANGEROUS end;
-       size_t num_pages, i;
-       pte_t *pte;
-       uintptr_t perm = PTE_P | PTE_USER_RW;
-       size_t bytes_copied = 0;
-
-       static_assert(ULIM % PGSIZE == 0 && ULIM != 0); // prevent wrap-around
-
-       start = ROUNDDOWN(va, PGSIZE);
-       end = ROUNDUP(va + len, PGSIZE);
-
-       if(start >= (void*SNT)ULIM || end >= (void*SNT)ULIM)
-               return -EFAULT;
-
-       num_pages = PPN(end - start);
-       for(i = 0; i < num_pages; i++)
-       {
-               pte = pgdir_walk(env->env_pgdir, start+i*PGSIZE, 0);
-               if(!pte || (*pte & perm) != perm)
-                       return -EFAULT;
-
-               void*COUNT(PGSIZE) kpage = KADDR(PTE_ADDR(*pte));
-               void* dst_start = i > 0 ? kpage : kpage+(va-start);
-               const void* src_start = src+bytes_copied;
-               size_t copy_len = PGSIZE;
-               if(i == 0)
-                       copy_len -= va-start;
-               if(i == num_pages-1)
-                       copy_len -= end-(va+len);
-
-               memcpy(dst_start,src_start,copy_len);
-               bytes_copied += copy_len;
-       }
-
-       assert(bytes_copied == len);
-
-       return ESUCCESS;
 }