os161-1.99
 All Data Structures
dumbvm.c
00001 /*
00002  * Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2008, 2009
00003  *      The President and Fellows of Harvard College.
00004  *
00005  * Redistribution and use in source and binary forms, with or without
00006  * modification, are permitted provided that the following conditions
00007  * are met:
00008  * 1. Redistributions of source code must retain the above copyright
00009  *    notice, this list of conditions and the following disclaimer.
00010  * 2. Redistributions in binary form must reproduce the above copyright
00011  *    notice, this list of conditions and the following disclaimer in the
00012  *    documentation and/or other materials provided with the distribution.
00013  * 3. Neither the name of the University nor the names of its contributors
00014  *    may be used to endorse or promote products derived from this software
00015  *    without specific prior written permission.
00016  *
00017  * THIS SOFTWARE IS PROVIDED BY THE UNIVERSITY AND CONTRIBUTORS ``AS IS'' AND
00018  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
00019  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
00020  * ARE DISCLAIMED.  IN NO EVENT SHALL THE UNIVERSITY OR CONTRIBUTORS BE LIABLE
00021  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
00022  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
00023  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
00024  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
00025  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
00026  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
00027  * SUCH DAMAGE.
00028  */
00029 
00030 #include <types.h>
00031 #include <kern/errno.h>
00032 #include <lib.h>
00033 #include <spl.h>
00034 #include <spinlock.h>
00035 #include <proc.h>
00036 #include <current.h>
00037 #include <mips/tlb.h>
00038 #include <addrspace.h>
00039 #include <vm.h>
00040 
00041 /*
00042  * Dumb MIPS-only "VM system" that is intended to only be just barely
00043  * enough to struggle off the ground. You should replace all of this
00044  * code while doing the VM assignment. In fact, starting in that
00045  * assignment, this file is not included in your kernel!
00046  */
00047 
00048 /* under dumbvm, always have 48k of user stack */
00049 #define DUMBVM_STACKPAGES    12
00050 
00051 /*
00052  * Wrap rma_stealmem in a spinlock.
00053  */
00054 static struct spinlock stealmem_lock = SPINLOCK_INITIALIZER;
00055 
00056 void
00057 vm_bootstrap(void)
00058 {
00059         /* Do nothing. */
00060 }
00061 
00062 static
00063 paddr_t
00064 getppages(unsigned long npages)
00065 {
00066         paddr_t addr;
00067 
00068         spinlock_acquire(&stealmem_lock);
00069 
00070         addr = ram_stealmem(npages);
00071         
00072         spinlock_release(&stealmem_lock);
00073         return addr;
00074 }
00075 
00076 /* Allocate/free some kernel-space virtual pages */
00077 vaddr_t 
00078 alloc_kpages(int npages)
00079 {
00080         paddr_t pa;
00081         pa = getppages(npages);
00082         if (pa==0) {
00083                 return 0;
00084         }
00085         return PADDR_TO_KVADDR(pa);
00086 }
00087 
00088 void 
00089 free_kpages(vaddr_t addr)
00090 {
00091         /* nothing - leak the memory. */
00092 
00093         (void)addr;
00094 }
00095 
00096 void
00097 vm_tlbshootdown_all(void)
00098 {
00099         panic("dumbvm tried to do tlb shootdown?!\n");
00100 }
00101 
00102 void
00103 vm_tlbshootdown(const struct tlbshootdown *ts)
00104 {
00105         (void)ts;
00106         panic("dumbvm tried to do tlb shootdown?!\n");
00107 }
00108 
00109 int
00110 vm_fault(int faulttype, vaddr_t faultaddress)
00111 {
00112         vaddr_t vbase1, vtop1, vbase2, vtop2, stackbase, stacktop;
00113         paddr_t paddr;
00114         int i;
00115         uint32_t ehi, elo;
00116         struct addrspace *as;
00117         int spl;
00118 
00119         faultaddress &= PAGE_FRAME;
00120 
00121         DEBUG(DB_VM, "dumbvm: fault: 0x%x\n", faultaddress);
00122 
00123         switch (faulttype) {
00124             case VM_FAULT_READONLY:
00125                 /* We always create pages read-write, so we can't get this */
00126                 panic("dumbvm: got VM_FAULT_READONLY\n");
00127             case VM_FAULT_READ:
00128             case VM_FAULT_WRITE:
00129                 break;
00130             default:
00131                 return EINVAL;
00132         }
00133 
00134         if (curproc == NULL) {
00135                 /*
00136                  * No process. This is probably a kernel fault early
00137                  * in boot. Return EFAULT so as to panic instead of
00138                  * getting into an infinite faulting loop.
00139                  */
00140                 return EFAULT;
00141         }
00142 
00143         as = curproc_getas();
00144         if (as == NULL) {
00145                 /*
00146                  * No address space set up. This is probably also a
00147                  * kernel fault early in boot.
00148                  */
00149                 return EFAULT;
00150         }
00151 
00152         /* Assert that the address space has been set up properly. */
00153         KASSERT(as->as_vbase1 != 0);
00154         KASSERT(as->as_pbase1 != 0);
00155         KASSERT(as->as_npages1 != 0);
00156         KASSERT(as->as_vbase2 != 0);
00157         KASSERT(as->as_pbase2 != 0);
00158         KASSERT(as->as_npages2 != 0);
00159         KASSERT(as->as_stackpbase != 0);
00160         KASSERT((as->as_vbase1 & PAGE_FRAME) == as->as_vbase1);
00161         KASSERT((as->as_pbase1 & PAGE_FRAME) == as->as_pbase1);
00162         KASSERT((as->as_vbase2 & PAGE_FRAME) == as->as_vbase2);
00163         KASSERT((as->as_pbase2 & PAGE_FRAME) == as->as_pbase2);
00164         KASSERT((as->as_stackpbase & PAGE_FRAME) == as->as_stackpbase);
00165 
00166         vbase1 = as->as_vbase1;
00167         vtop1 = vbase1 + as->as_npages1 * PAGE_SIZE;
00168         vbase2 = as->as_vbase2;
00169         vtop2 = vbase2 + as->as_npages2 * PAGE_SIZE;
00170         stackbase = USERSTACK - DUMBVM_STACKPAGES * PAGE_SIZE;
00171         stacktop = USERSTACK;
00172 
00173         if (faultaddress >= vbase1 && faultaddress < vtop1) {
00174                 paddr = (faultaddress - vbase1) + as->as_pbase1;
00175         }
00176         else if (faultaddress >= vbase2 && faultaddress < vtop2) {
00177                 paddr = (faultaddress - vbase2) + as->as_pbase2;
00178         }
00179         else if (faultaddress >= stackbase && faultaddress < stacktop) {
00180                 paddr = (faultaddress - stackbase) + as->as_stackpbase;
00181         }
00182         else {
00183                 return EFAULT;
00184         }
00185 
00186         /* make sure it's page-aligned */
00187         KASSERT((paddr & PAGE_FRAME) == paddr);
00188 
00189         /* Disable interrupts on this CPU while frobbing the TLB. */
00190         spl = splhigh();
00191 
00192         for (i=0; i<NUM_TLB; i++) {
00193                 tlb_read(&ehi, &elo, i);
00194                 if (elo & TLBLO_VALID) {
00195                         continue;
00196                 }
00197                 ehi = faultaddress;
00198                 elo = paddr | TLBLO_DIRTY | TLBLO_VALID;
00199                 DEBUG(DB_VM, "dumbvm: 0x%x -> 0x%x\n", faultaddress, paddr);
00200                 tlb_write(ehi, elo, i);
00201                 splx(spl);
00202                 return 0;
00203         }
00204 
00205         kprintf("dumbvm: Ran out of TLB entries - cannot handle page fault\n");
00206         splx(spl);
00207         return EFAULT;
00208 }
00209 
00210 struct addrspace *
00211 as_create(void)
00212 {
00213         struct addrspace *as = kmalloc(sizeof(struct addrspace));
00214         if (as==NULL) {
00215                 return NULL;
00216         }
00217 
00218         as->as_vbase1 = 0;
00219         as->as_pbase1 = 0;
00220         as->as_npages1 = 0;
00221         as->as_vbase2 = 0;
00222         as->as_pbase2 = 0;
00223         as->as_npages2 = 0;
00224         as->as_stackpbase = 0;
00225 
00226         return as;
00227 }
00228 
00229 void
00230 as_destroy(struct addrspace *as)
00231 {
00232         kfree(as);
00233 }
00234 
00235 void
00236 as_activate(void)
00237 {
00238         int i, spl;
00239         struct addrspace *as;
00240 
00241         as = curproc_getas();
00242 #ifdef UW
00243         /* Kernel threads don't have an address spaces to activate */
00244 #endif
00245         if (as == NULL) {
00246                 return;
00247         }
00248 
00249         /* Disable interrupts on this CPU while frobbing the TLB. */
00250         spl = splhigh();
00251 
00252         for (i=0; i<NUM_TLB; i++) {
00253                 tlb_write(TLBHI_INVALID(i), TLBLO_INVALID(), i);
00254         }
00255 
00256         splx(spl);
00257 }
00258 
00259 void
00260 as_deactivate(void)
00261 {
00262         /* nothing */
00263 }
00264 
00265 int
00266 as_define_region(struct addrspace *as, vaddr_t vaddr, size_t sz,
00267                  int readable, int writeable, int executable)
00268 {
00269         size_t npages; 
00270 
00271         /* Align the region. First, the base... */
00272         sz += vaddr & ~(vaddr_t)PAGE_FRAME;
00273         vaddr &= PAGE_FRAME;
00274 
00275         /* ...and now the length. */
00276         sz = (sz + PAGE_SIZE - 1) & PAGE_FRAME;
00277 
00278         npages = sz / PAGE_SIZE;
00279 
00280         /* We don't use these - all pages are read-write */
00281         (void)readable;
00282         (void)writeable;
00283         (void)executable;
00284 
00285         if (as->as_vbase1 == 0) {
00286                 as->as_vbase1 = vaddr;
00287                 as->as_npages1 = npages;
00288                 return 0;
00289         }
00290 
00291         if (as->as_vbase2 == 0) {
00292                 as->as_vbase2 = vaddr;
00293                 as->as_npages2 = npages;
00294                 return 0;
00295         }
00296 
00297         /*
00298          * Support for more than two regions is not available.
00299          */
00300         kprintf("dumbvm: Warning: too many regions\n");
00301         return EUNIMP;
00302 }
00303 
00304 static
00305 void
00306 as_zero_region(paddr_t paddr, unsigned npages)
00307 {
00308         bzero((void *)PADDR_TO_KVADDR(paddr), npages * PAGE_SIZE);
00309 }
00310 
00311 int
00312 as_prepare_load(struct addrspace *as)
00313 {
00314         KASSERT(as->as_pbase1 == 0);
00315         KASSERT(as->as_pbase2 == 0);
00316         KASSERT(as->as_stackpbase == 0);
00317 
00318         as->as_pbase1 = getppages(as->as_npages1);
00319         if (as->as_pbase1 == 0) {
00320                 return ENOMEM;
00321         }
00322 
00323         as->as_pbase2 = getppages(as->as_npages2);
00324         if (as->as_pbase2 == 0) {
00325                 return ENOMEM;
00326         }
00327 
00328         as->as_stackpbase = getppages(DUMBVM_STACKPAGES);
00329         if (as->as_stackpbase == 0) {
00330                 return ENOMEM;
00331         }
00332         
00333         as_zero_region(as->as_pbase1, as->as_npages1);
00334         as_zero_region(as->as_pbase2, as->as_npages2);
00335         as_zero_region(as->as_stackpbase, DUMBVM_STACKPAGES);
00336 
00337         return 0;
00338 }
00339 
00340 int
00341 as_complete_load(struct addrspace *as)
00342 {
00343         (void)as;
00344         return 0;
00345 }
00346 
00347 int
00348 as_define_stack(struct addrspace *as, vaddr_t *stackptr)
00349 {
00350         KASSERT(as->as_stackpbase != 0);
00351 
00352         *stackptr = USERSTACK;
00353         return 0;
00354 }
00355 
00356 int
00357 as_copy(struct addrspace *old, struct addrspace **ret)
00358 {
00359         struct addrspace *new;
00360 
00361         new = as_create();
00362         if (new==NULL) {
00363                 return ENOMEM;
00364         }
00365 
00366         new->as_vbase1 = old->as_vbase1;
00367         new->as_npages1 = old->as_npages1;
00368         new->as_vbase2 = old->as_vbase2;
00369         new->as_npages2 = old->as_npages2;
00370 
00371         /* (Mis)use as_prepare_load to allocate some physical memory. */
00372         if (as_prepare_load(new)) {
00373                 as_destroy(new);
00374                 return ENOMEM;
00375         }
00376 
00377         KASSERT(new->as_pbase1 != 0);
00378         KASSERT(new->as_pbase2 != 0);
00379         KASSERT(new->as_stackpbase != 0);
00380 
00381         memmove((void *)PADDR_TO_KVADDR(new->as_pbase1),
00382                 (const void *)PADDR_TO_KVADDR(old->as_pbase1),
00383                 old->as_npages1*PAGE_SIZE);
00384 
00385         memmove((void *)PADDR_TO_KVADDR(new->as_pbase2),
00386                 (const void *)PADDR_TO_KVADDR(old->as_pbase2),
00387                 old->as_npages2*PAGE_SIZE);
00388 
00389         memmove((void *)PADDR_TO_KVADDR(new->as_stackpbase),
00390                 (const void *)PADDR_TO_KVADDR(old->as_stackpbase),
00391                 DUMBVM_STACKPAGES*PAGE_SIZE);
00392         
00393         *ret = new;
00394         return 0;
00395 }
 All Data Structures