Merge remote branch 'origin/sparc-dev'
[akaros.git] / kern / src / pmap.c
index 4b4c304..cc12b7c 100644 (file)
 #pragma nosharc
 #endif
 
+#ifdef __DEPUTY__
+#pragma nodeputy
+#endif
+
 #include <arch/arch.h>
 #include <arch/mmu.h>
 
 /**
  * @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 user_mem_check_addr;
+static void *DANGEROUS RACY user_mem_check_addr;
 
 volatile uint32_t vpt_lock = 0;
 volatile uint32_t vpd_lock = 0;
@@ -238,7 +245,9 @@ void tlb_invalidate(pde_t *pgdir, void *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.
+ * 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:
@@ -260,13 +269,18 @@ void tlb_invalidate(pde_t *pgdir, void *va)
  */
 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;
-       pte_t *pte;
+       int page_perms = 0;
 
        perm |= PTE_P;
        start = ROUNDDOWN((void*DANGEROUS)va, PGSIZE);
@@ -275,11 +289,11 @@ void* user_mem_check(env_t *env, const void *DANGEROUS va, size_t len, int perm)
                warn("Blimey!  Wrap around in VM range calculation!");  
                return NULL;
        }
-       num_pages = PPN(end - start);
+       num_pages = LA2PPN(end - start);
        for (i = 0; i < num_pages; i++, start += PGSIZE) {
-               pte = pgdir_walk(env->env_pgdir, start, 0);
+               page_perms = get_va_perms(env->env_pgdir, start);
                // ensures the bits we want on are turned on.  if not, error out
-               if ( !pte || ((*pte & perm) != perm) ) {
+               if ((page_perms & perm) != perm) {
                        if (i == 0)
                                user_mem_check_addr = (void*DANGEROUS)va;
                        else
@@ -312,20 +326,23 @@ void* user_mem_check(env_t *env, const void *DANGEROUS va, size_t len, int perm)
  * @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,
+user_mem_strlcpy(env_t *env, char *_dst, const char *DANGEROUS va,
                  size_t _len, int perm)
 {
        const char *DANGEROUS src = va;
        size_t len = _len;
-       char *NT COUNT(_len-1) dst_in = dst;
+       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++;
                }
@@ -357,10 +374,15 @@ user_mem_strlcpy(env_t *env, char *dst, const char *DANGEROUS va,
 void *
 user_mem_assert(env_t *env, const void *DANGEROUS va, size_t len, int perm)
 {
-    void *COUNT(len) res = user_mem_check(env,va,len,perm | PTE_USER_RO);
+       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);
+                       "va %08x\n", env->pid, user_mem_check_addr);
                proc_destroy(env);      // may not return
         return NULL;
        }
@@ -397,21 +419,21 @@ error_t memcpy_from_user(env_t* env, void* COUNT(len) dest,
        if(start >= (void*SNT)ULIM || end >= (void*SNT)ULIM)
                return -EFAULT;
 
-       num_pages = PPN(end - start);
+       num_pages = LA2PPN(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* src_start = i > 0 ? kpage : kpage+(va-start);
+               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-(start+len);
+                       copy_len -= end-(va+len);
 
                memcpy(dst_start,src_start,copy_len);
                bytes_copied += copy_len;
@@ -422,3 +444,57 @@ error_t memcpy_from_user(env_t* env, void* COUNT(len) dest,
        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 = LA2PPN(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;
+}