Memory protection and page fault handling
[akaros.git] / kern / pmap.c
index b3ad4f2..62c6a91 100644 (file)
@@ -24,6 +24,8 @@ static char* boot_freemem;    // Pointer to next byte of free mem
 struct Page* pages;            // Virtual address of physical page array
 static struct Page_list page_free_list;        // Free list of physical pages
 
+extern struct Env *envs;
+
 // Global descriptor table.
 //
 // The kernel and user segments are identical (except for the DPL).
@@ -363,7 +365,15 @@ i386_vm_init(void)
        //    - envs itself -- kernel RW, user NONE
        //    - the image of envs mapped at UENVS  -- kernel R, user R
        
-       // LAB 3: Your code here.
+       // round up to the nearest page
+       size_t env_array_size = ROUNDUP(NENV*sizeof(struct Env), PGSIZE);
+       envs = (struct Env*)boot_alloc(env_array_size, PGSIZE);
+       memset(envs, 0, env_array_size);
+       if (env_array_size > PTSIZE) {
+               warn("env_array_size bigger than PTSIZE, userland will not see all environments");
+               env_array_size = PTSIZE;
+       }
+       boot_map_segment(pgdir, UENVS, env_array_size, PADDR(envs), PTE_U);
 
        // Check that the initial page directory has been set up correctly.
        check_boot_pgdir(pse);
@@ -451,12 +461,10 @@ check_boot_pgdir(bool pse)
        for (i = 0; i < n; i += PGSIZE)
                assert(check_va2pa(pgdir, UPAGES + i) == PADDR(pages) + i);
 
-       /* // TODO - turn this on
        // check envs array (new test for lab 3)
        n = ROUNDUP(NENV*sizeof(struct Env), PGSIZE);
        for (i = 0; i < n; i += PGSIZE)
                assert(check_va2pa(pgdir, UENVS + i) == PADDR(envs) + i);
-       */
 
        // check phys mem
        //for (i = 0; KERNBASE + i != 0; i += PGSIZE)
@@ -479,7 +487,7 @@ check_boot_pgdir(bool pse)
                case PDX(UVPT):
                case PDX(KSTACKTOP-1):
                case PDX(UPAGES):
-               //case PDX(UENVS): // TODO - turn this on
+               case PDX(UENVS):
                        assert(pgdir[i]);
                        break;
                default:
@@ -845,8 +853,38 @@ static uintptr_t user_mem_check_addr;
 int
 user_mem_check(struct Env *env, const void *va, size_t len, int perm)
 {
-       // LAB 3: Your code here. 
+       // 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 *start, *end;
+       size_t num_pages, i;
+       pte_t *pte;
 
+       perm |= PTE_P;
+       start = ROUNDDOWN((void*)va, PGSIZE);
+       end = ROUNDUP((void*)va + len, PGSIZE);
+       if (start >= end) {
+               warn("Blimey!  Wrap around in VM range calculation!");  
+               return -E_FAULT;
+       }
+       num_pages = PPN(end - start);
+       for (i = 0; i < num_pages; i++, start += PGSIZE) {
+               pte = pgdir_walk(env->env_pgdir, start, 0);
+               // ensures the bits we want on are turned on.  if not, E_FAULT
+               if ( !pte || ((*pte & perm) != perm) ) {
+                       if (i = 0)
+                               user_mem_check_addr = (uintptr_t)va;
+                       else
+                               user_mem_check_addr = (uintptr_t)start;
+                       return -E_FAULT;
+               }
+       }
+       // 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 -E_FAULT;
+       }
        return 0;
 }