MPC TLB shootdowns
[akaros.git] / kern / src / mm.c
index cf3dc65..988f417 100644 (file)
@@ -29,6 +29,10 @@ void *mmap(struct proc *p, uintptr_t addr, size_t len, int prot, int flags,
                printk("[kernel] mmap() with MAP_ANONYMOUS requires fd == -1.\n");
                return (void*)-1;
        }
+       if((flags & MAP_FIXED) && PGOFF(addr)) {
+               printk("[kernel] mmap() page align your addr.\n");
+               return (void*SAFE)TC(-1);
+       }
 
        struct file* file = NULL;
        if(fd != -1)
@@ -49,24 +53,22 @@ void *mmap(struct proc *p, uintptr_t addr, size_t len, int prot, int flags,
 void *do_mmap(struct proc *p, uintptr_t addr, size_t len, int prot, int flags,
               struct file* file, size_t offset)
 {
-       /* 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.
-        * (or try to find one on sparc) */
-
-       if((flags & MAP_FIXED) && PGOFF(addr)) {
-               printk("[kernel] mmap() page align your addr.\n");
-               return (void*SAFE)TC(-1);
-       }
-
        // TODO: grab the appropriate mm_lock
-       spin_lock_irqsave(&p->proc_lock);
+       spin_lock(&p->proc_lock);
+       void* ret = __do_mmap(p,addr,len,prot,flags,file,offset);
+       spin_unlock(&p->proc_lock);
+       return ret;
+}
 
+void *__do_mmap(struct proc *p, uintptr_t addr, size_t len, int prot, int flags,
+                struct file* file, size_t offset)
+{
        int num_pages = ROUNDUP(len, PGSIZE) / PGSIZE;
 
+#ifndef __CONFIG_DEMAND_PAGING__
+       flags |= MAP_POPULATE;
+#endif
+       
        if(!(flags & MAP_FIXED))
        {
                addr = (uintptr_t)get_free_va_range(p->env_pgdir,addr,len);
@@ -109,8 +111,6 @@ void *do_mmap(struct proc *p, uintptr_t addr, size_t len, int prot, int flags,
        }
 
        // make the lazy mapping finally
-       int perm = (prot & PROT_WRITE) ? PTE_USER_RW :
-                  (prot & (PROT_READ|PROT_EXEC))  ? PTE_USER_RO : 0;
        for(int i = 0; i < num_pages; i++)
        {
                // free an old page that was present here
@@ -126,26 +126,25 @@ void *do_mmap(struct proc *p, uintptr_t addr, size_t len, int prot, int flags,
                // zero-fill end of last page
                if(i == num_pages-1 && len % PGSIZE)
                        pfis[i]->read_len = len % PGSIZE;
-               pfis[i]->perm = perm;
+               pfis[i]->prot = prot;
                *ptes[i] = PFAULT_INFO2PTE(pfis[i]);
-
-               // uncomment the line below to simulate aggressive loading
-               //assert(handle_page_fault(p,(char*)addr+i*PGSIZE,PROT_READ) == 0);
        }
 
        kfree(ptes);
        kfree(pfis);
 
-       // TODO: release the appropriate mm_lock
-       spin_unlock_irqsave(&p->proc_lock);
+       // fault in pages now if MAP_POPULATE.  die on failure.
+       if(flags & MAP_POPULATE)
+               for(int i = 0; i < num_pages; i++)
+                       if(__handle_page_fault(p,addr+i*PGSIZE,PROT_READ))
+                               proc_destroy(p);
+
        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
-               spin_unlock_irqsave(&p->proc_lock);
                // not a kernel problem, like if they ask to mmap a mapped location.
                printk("[kernel] mmap() aborted!\n");
                // mmap's semantics.  we need a better error propagation system
@@ -169,11 +168,19 @@ int mprotect(struct proc* p, void* addr, size_t len, int prot)
                return -1;
        }
 
-       spin_lock_irqsave(&p->proc_lock);
+       spin_lock(&p->proc_lock);
+       int ret = __mprotect(p,addr,len,prot);
+       spin_unlock(&p->proc_lock);
+
+       return ret;
+}
 
+int __mprotect(struct proc* p, void* addr, size_t len, int prot)
+{
        int newperm = (prot & PROT_WRITE) ? PTE_USER_RW :
                      (prot & (PROT_READ|PROT_EXEC)) ? PTE_USER_RO : 0;
 
+       char* end = ROUNDUP((char*)addr+len,PGSIZE);
        for(char* a = (char*)addr; a < end; a += PGSIZE)
        {
                pte_t* pte = pgdir_walk(p->env_pgdir,a,0);
@@ -208,14 +215,14 @@ int mprotect(struct proc* p, void* addr, size_t len, int prot)
                                *pte = 0;
                        }
                        else
-                               PTE2PFAULT_INFO(*pte)->perm = newperm;
+                               PTE2PFAULT_INFO(*pte)->prot = prot;
                }
        }
 
-       spin_unlock_irqsave(&p->proc_lock);
-
-       //TODO: TLB shootdown - needs to be process wide
-       tlbflush();
+       /* TODO: (TLB) make this take a sensible range.  For now, it doesn't matter
+        * since we ignore it in the process code.  Also, make sure you are holding
+        * the proc_lock when calling this. */
+       __proc_tlbshootdown(p, 0, 0xffffffff);
        return 0;
 }
 
@@ -224,17 +231,28 @@ int munmap(struct proc* p, void* addr, size_t len)
        return mprotect(p, addr, len, PROT_UNMAP);
 }
 
+int __munmap(struct proc* p, void* addr, size_t len)
+{
+       return __mprotect(p, addr, len, PROT_UNMAP);
+}
+
 int handle_page_fault(struct proc* p, uintptr_t va, int prot)
 {
-       int ret = -1;
        va = ROUNDDOWN(va,PGSIZE);
 
        if(prot != PROT_READ && prot != PROT_WRITE && prot != PROT_EXEC)
                panic("bad prot!");
 
-       //spin_lock_irqsave(&p->proc_lock);
-
-       /// find offending PTE
+       spin_lock(&p->proc_lock);
+       int ret = __handle_page_fault(p,va,prot);
+       spin_unlock(&p->proc_lock);
+       return ret;
+}
+       
+int __handle_page_fault(struct proc* p, uintptr_t va, int prot)
+{
+       int ret = -1;
+       // find offending PTE
        pte_t* ppte = pgdir_walk(p->env_pgdir,(void*)va,0);
        // if PTE is NULL, this is a fault that should kill the process
        if(!ppte)
@@ -245,6 +263,7 @@ int handle_page_fault(struct proc* p, uintptr_t va, int prot)
        // if PTE is present, why did we fault?
        if(PAGE_PRESENT(pte))
        {
+               int perm = pte & PTE_PERM;
                // a race is possible: the page might have been faulted in by
                // another core already, in which case we should just return.
                // otherwise, it's a fault that should kill the user
@@ -252,11 +271,11 @@ int handle_page_fault(struct proc* p, uintptr_t va, int prot)
                {
                        case PROT_READ:
                        case PROT_EXEC:
-                               if(pte == PTE_USER_RO || pte == PTE_USER_RW)
+                               if(perm == PTE_USER_RO || perm == PTE_USER_RW)
                                        ret = 0;
                                goto out;
                        case PROT_WRITE:
-                               if(pte == PTE_USER_RW)
+                               if(perm == PTE_USER_RW)
                                        ret = 0;
                                goto out;
                }
@@ -289,20 +308,24 @@ int handle_page_fault(struct proc* p, uintptr_t va, int prot)
                // if we read too much, zero that part out
                if(info->read_len < read_len)
                        memset(page2kva(a_page)+info->read_len,0,read_len-info->read_len);
+
+               // if this is an executable page, we might have to flush
+               // the instruction cache if our HW requires it
+               if(info->prot & PROT_EXEC)
+                       icache_flush_page((void*)va,page2kva(a_page));
        }
 
+       int perm = (info->prot & PROT_WRITE) ? PTE_USER_RW :
+                  (info->prot & (PROT_READ|PROT_EXEC))  ? PTE_USER_RO : 0;
+
        // update the page table
-       if(page_insert(p->env_pgdir, a_page, (void*)va, info->perm))
-       {
-               page_free(a_page);
-               goto out;
-       }
+       page_incref(a_page);
+       *ppte = PTE(page2ppn(a_page),PTE_P | perm);
 
        pfault_info_free(info);
        ret = 0;
 
 out:
-       //spin_unlock_irqsave(&p->proc_lock);
        tlbflush();
        return ret;
 }