root/vm.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. seginit
  2. walkpgdir
  3. mappages
  4. setupkvm
  5. kvmalloc
  6. switchkvm
  7. switchuvm
  8. inituvm
  9. loaduvm
  10. allocuvm
  11. deallocuvm
  12. freevm
  13. clearpteu
  14. copyuvm
  15. uva2ka
  16. copyout

   1 #include "param.h"
   2 #include "types.h"
   3 #include "defs.h"
   4 #include "x86.h"
   5 #include "memlayout.h"
   6 #include "mmu.h"
   7 #include "proc.h"
   8 #include "elf.h"
   9 
  10 extern char data[];  // defined by kernel.ld
  11 pde_t *kpgdir;  // for use in scheduler()
  12 struct segdesc gdt[NSEGS];
  13 
  14 // Set up CPU's kernel segment descriptors.
  15 // Run once on entry on each CPU.
  16 void
  17 seginit(void)
  18 {
  19   struct cpu *c;
  20 
  21   // Map "logical" addresses to virtual addresses using identity map.
  22   // Cannot share a CODE descriptor for both kernel and user
  23   // because it would have to have DPL_USR, but the CPU forbids
  24   // an interrupt from CPL=0 to DPL=3.
  25   c = &cpus[cpunum()];
  26   c->gdt[SEG_KCODE] = SEG(STA_X|STA_R, 0, 0xffffffff, 0);
  27   c->gdt[SEG_KDATA] = SEG(STA_W, 0, 0xffffffff, 0);
  28   c->gdt[SEG_UCODE] = SEG(STA_X|STA_R, 0, 0xffffffff, DPL_USER);
  29   c->gdt[SEG_UDATA] = SEG(STA_W, 0, 0xffffffff, DPL_USER);
  30 
  31   // Map cpu, and curproc
  32   c->gdt[SEG_KCPU] = SEG(STA_W, &c->cpu, 8, 0);
  33 
  34   lgdt(c->gdt, sizeof(c->gdt));
  35   loadgs(SEG_KCPU << 3);
  36   
  37   // Initialize cpu-local storage.
  38   cpu = c;
  39   proc = 0;
  40 }
  41 
  42 // Return the address of the PTE in page table pgdir
  43 // that corresponds to virtual address va.  If alloc!=0,
  44 // create any required page table pages.
  45 static pte_t *
  46 walkpgdir(pde_t *pgdir, const void *va, int alloc)
  47 {
  48   pde_t *pde;
  49   pte_t *pgtab;
  50 
  51   pde = &pgdir[PDX(va)];
  52   if(*pde & PTE_P){
  53     pgtab = (pte_t*)p2v(PTE_ADDR(*pde));
  54   } else {
  55     if(!alloc || (pgtab = (pte_t*)kalloc()) == 0)
  56       return 0;
  57     // Make sure all those PTE_P bits are zero.
  58     memset(pgtab, 0, PGSIZE);
  59     // The permissions here are overly generous, but they can
  60     // be further restricted by the permissions in the page table 
  61     // entries, if necessary.
  62     *pde = v2p(pgtab) | PTE_P | PTE_W | PTE_U;
  63   }
  64   return &pgtab[PTX(va)];
  65 }
  66 
  67 // Create PTEs for virtual addresses starting at va that refer to
  68 // physical addresses starting at pa. va and size might not
  69 // be page-aligned.
  70 static int
  71 mappages(pde_t *pgdir, void *va, uint size, uint pa, int perm)
  72 {
  73   char *a, *last;
  74   pte_t *pte;
  75   
  76   a = (char*)PGROUNDDOWN((uint)va);
  77   last = (char*)PGROUNDDOWN(((uint)va) + size - 1);
  78   for(;;){
  79     if((pte = walkpgdir(pgdir, a, 1)) == 0)
  80       return -1;
  81     if(*pte & PTE_P)
  82       panic("remap");
  83     *pte = pa | perm | PTE_P;
  84     if(a == last)
  85       break;
  86     a += PGSIZE;
  87     pa += PGSIZE;
  88   }
  89   return 0;
  90 }
  91 
  92 // There is one page table per process, plus one that's used when
  93 // a CPU is not running any process (kpgdir). The kernel uses the
  94 // current process's page table during system calls and interrupts;
  95 // page protection bits prevent user code from using the kernel's
  96 // mappings.
  97 // 
  98 // setupkvm() and exec() set up every page table like this:
  99 //
 100 //   0..KERNBASE: user memory (text+data+stack+heap), mapped to
 101 //                phys memory allocated by the kernel
 102 //   KERNBASE..KERNBASE+EXTMEM: mapped to 0..EXTMEM (for I/O space)
 103 //   KERNBASE+EXTMEM..data: mapped to EXTMEM..V2P(data)
 104 //                for the kernel's instructions and r/o data
 105 //   data..KERNBASE+PHYSTOP: mapped to V2P(data)..PHYSTOP, 
 106 //                                  rw data + free physical memory
 107 //   0xfe000000..0: mapped direct (devices such as ioapic)
 108 //
 109 // The kernel allocates physical memory for its heap and for user memory
 110 // between V2P(end) and the end of physical memory (PHYSTOP)
 111 // (directly addressable from end..P2V(PHYSTOP)).
 112 
 113 // This table defines the kernel's mappings, which are present in
 114 // every process's page table.
 115 static struct kmap {
 116   void *virt;
 117   uint phys_start;
 118   uint phys_end;
 119   int perm;
 120 } kmap[] = {
 121  { (void*)KERNBASE, 0,             EXTMEM,    PTE_W}, // I/O space
 122  { (void*)KERNLINK, V2P(KERNLINK), V2P(data), 0},     // kern text+rodata
 123  { (void*)data,     V2P(data),     PHYSTOP,   PTE_W}, // kern data+memory
 124  { (void*)DEVSPACE, DEVSPACE,      0,         PTE_W}, // more devices
 125 };
 126 
 127 // Set up kernel part of a page table.
 128 pde_t*
 129 setupkvm(void)
 130 {
 131   pde_t *pgdir;
 132   struct kmap *k;
 133 
 134   if((pgdir = (pde_t*)kalloc()) == 0)
 135     return 0;
 136   memset(pgdir, 0, PGSIZE);
 137   if (p2v(PHYSTOP) > (void*)DEVSPACE)
 138     panic("PHYSTOP too high");
 139   for(k = kmap; k < &kmap[NELEM(kmap)]; k++)
 140     if(mappages(pgdir, k->virt, k->phys_end - k->phys_start, 
 141                 (uint)k->phys_start, k->perm) < 0)
 142       return 0;
 143   return pgdir;
 144 }
 145 
 146 // Allocate one page table for the machine for the kernel address
 147 // space for scheduler processes.
 148 void
 149 kvmalloc(void)
 150 {
 151   kpgdir = setupkvm();
 152   switchkvm();
 153 }
 154 
 155 // Switch h/w page table register to the kernel-only page table,
 156 // for when no process is running.
 157 void
 158 switchkvm(void)
 159 {
 160   lcr3(v2p(kpgdir));   // switch to the kernel page table
 161 }
 162 
 163 // Switch TSS and h/w page table to correspond to process p.
 164 void
 165 switchuvm(struct proc *p)
 166 {
 167   pushcli();
 168   cpu->gdt[SEG_TSS] = SEG16(STS_T32A, &cpu->ts, sizeof(cpu->ts)-1, 0);
 169   cpu->gdt[SEG_TSS].s = 0;
 170   cpu->ts.ss0 = SEG_KDATA << 3;
 171   cpu->ts.esp0 = (uint)proc->kstack + KSTACKSIZE;
 172   ltr(SEG_TSS << 3);
 173   if(p->pgdir == 0)
 174     panic("switchuvm: no pgdir");
 175   lcr3(v2p(p->pgdir));  // switch to new address space
 176   popcli();
 177 }
 178 
 179 // Load the initcode into address 0 of pgdir.
 180 // sz must be less than a page.
 181 void
 182 inituvm(pde_t *pgdir, char *init, uint sz)
 183 {
 184   char *mem;
 185   
 186   if(sz >= PGSIZE)
 187     panic("inituvm: more than a page");
 188   mem = kalloc();
 189   memset(mem, 0, PGSIZE);
 190   mappages(pgdir, 0, PGSIZE, v2p(mem), PTE_W|PTE_U);
 191   memmove(mem, init, sz);
 192 }
 193 
 194 // Load a program segment into pgdir.  addr must be page-aligned
 195 // and the pages from addr to addr+sz must already be mapped.
 196 int
 197 loaduvm(pde_t *pgdir, char *addr, struct inode *ip, uint offset, uint sz)
 198 {
 199   uint i, pa, n;
 200   pte_t *pte;
 201 
 202   if((uint) addr % PGSIZE != 0)
 203     panic("loaduvm: addr must be page aligned");
 204   for(i = 0; i < sz; i += PGSIZE){
 205     if((pte = walkpgdir(pgdir, addr+i, 0)) == 0)
 206       panic("loaduvm: address should exist");
 207     pa = PTE_ADDR(*pte);
 208     if(sz - i < PGSIZE)
 209       n = sz - i;
 210     else
 211       n = PGSIZE;
 212     if(readi(ip, p2v(pa), offset+i, n) != n)
 213       return -1;
 214   }
 215   return 0;
 216 }
 217 
 218 // Allocate page tables and physical memory to grow process from oldsz to
 219 // newsz, which need not be page aligned.  Returns new size or 0 on error.
 220 int
 221 allocuvm(pde_t *pgdir, uint oldsz, uint newsz)
 222 {
 223   char *mem;
 224   uint a;
 225 
 226   if(newsz >= KERNBASE)
 227     return 0;
 228   if(newsz < oldsz)
 229     return oldsz;
 230 
 231   a = PGROUNDUP(oldsz);
 232   for(; a < newsz; a += PGSIZE){
 233     mem = kalloc();
 234     if(mem == 0){
 235       cprintf("allocuvm out of memory\n");
 236       deallocuvm(pgdir, newsz, oldsz);
 237       return 0;
 238     }
 239     memset(mem, 0, PGSIZE);
 240     mappages(pgdir, (char*)a, PGSIZE, v2p(mem), PTE_W|PTE_U);
 241   }
 242   return newsz;
 243 }
 244 
 245 // Deallocate user pages to bring the process size from oldsz to
 246 // newsz.  oldsz and newsz need not be page-aligned, nor does newsz
 247 // need to be less than oldsz.  oldsz can be larger than the actual
 248 // process size.  Returns the new process size.
 249 int
 250 deallocuvm(pde_t *pgdir, uint oldsz, uint newsz)
 251 {
 252   pte_t *pte;
 253   uint a, pa;
 254 
 255   if(newsz >= oldsz)
 256     return oldsz;
 257 
 258   a = PGROUNDUP(newsz);
 259   for(; a  < oldsz; a += PGSIZE){
 260     pte = walkpgdir(pgdir, (char*)a, 0);
 261     if(!pte)
 262       a += (NPTENTRIES - 1) * PGSIZE;
 263     else if((*pte & PTE_P) != 0){
 264       pa = PTE_ADDR(*pte);
 265       if(pa == 0)
 266         panic("kfree");
 267       char *v = p2v(pa);
 268       kfree(v);
 269       *pte = 0;
 270     }
 271   }
 272   return newsz;
 273 }
 274 
 275 // Free a page table and all the physical memory pages
 276 // in the user part.
 277 void
 278 freevm(pde_t *pgdir)
 279 {
 280   uint i;
 281 
 282   if(pgdir == 0)
 283     panic("freevm: no pgdir");
 284   deallocuvm(pgdir, KERNBASE, 0);
 285   for(i = 0; i < NPDENTRIES; i++){
 286     if(pgdir[i] & PTE_P){
 287       char * v = p2v(PTE_ADDR(pgdir[i]));
 288       kfree(v);
 289     }
 290   }
 291   kfree((char*)pgdir);
 292 }
 293 
 294 // Clear PTE_U on a page. Used to create an inaccessible
 295 // page beneath the user stack.
 296 void
 297 clearpteu(pde_t *pgdir, char *uva)
 298 {
 299   pte_t *pte;
 300 
 301   pte = walkpgdir(pgdir, uva, 0);
 302   if(pte == 0)
 303     panic("clearpteu");
 304   *pte &= ~PTE_U;
 305 }
 306 
 307 // Given a parent process's page table, create a copy
 308 // of it for a child.
 309 pde_t*
 310 copyuvm(pde_t *pgdir, uint sz)
 311 {
 312   pde_t *d;
 313   pte_t *pte;
 314   uint pa, i, flags;
 315   char *mem;
 316 
 317   if((d = setupkvm()) == 0)
 318     return 0;
 319   for(i = 0; i < sz; i += PGSIZE){
 320     if((pte = walkpgdir(pgdir, (void *) i, 0)) == 0)
 321       panic("copyuvm: pte should exist");
 322     if(!(*pte & PTE_P))
 323       panic("copyuvm: page not present");
 324     pa = PTE_ADDR(*pte);
 325     flags = PTE_FLAGS(*pte);
 326     if((mem = kalloc()) == 0)
 327       goto bad;
 328     memmove(mem, (char*)p2v(pa), PGSIZE);
 329     if(mappages(d, (void*)i, PGSIZE, v2p(mem), flags) < 0)
 330       goto bad;
 331   }
 332   return d;
 333 
 334 bad:
 335   freevm(d);
 336   return 0;
 337 }
 338 
 339 //PAGEBREAK!
 340 // Map user virtual address to kernel address.
 341 char*
 342 uva2ka(pde_t *pgdir, char *uva)
 343 {
 344   pte_t *pte;
 345 
 346   pte = walkpgdir(pgdir, uva, 0);
 347   if((*pte & PTE_P) == 0)
 348     return 0;
 349   if((*pte & PTE_U) == 0)
 350     return 0;
 351   return (char*)p2v(PTE_ADDR(*pte));
 352 }
 353 
 354 // Copy len bytes from p to user address va in page table pgdir.
 355 // Most useful when pgdir is not the current page table.
 356 // uva2ka ensures this only works for PTE_U pages.
 357 int
 358 copyout(pde_t *pgdir, uint va, void *p, uint len)
 359 {
 360   char *buf, *pa0;
 361   uint n, va0;
 362 
 363   buf = (char*)p;
 364   while(len > 0){
 365     va0 = (uint)PGROUNDDOWN(va);
 366     pa0 = uva2ka(pgdir, (char*)va0);
 367     if(pa0 == 0)
 368       return -1;
 369     n = PGSIZE - (va - va0);
 370     if(n > len)
 371       n = len;
 372     memmove(pa0 + (va - va0), buf, n);
 373     len -= n;
 374     buf += n;
 375     va = va0 + PGSIZE;
 376   }
 377   return 0;
 378 }
 379 
 380 //PAGEBREAK!
 381 // Blank page.
 382 //PAGEBREAK!
 383 // Blank page.
 384 //PAGEBREAK!
 385 // Blank page.
 386 

/* [<][>][^][v][top][bottom][index][help] */