root/kernel/vm.c

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

DEFINITIONS

This source file includes following definitions.
  1. kvmmake
  2. kvminit
  3. kvminithart
  4. walk
  5. walkaddr
  6. kvmmap
  7. mappages
  8. uvmunmap
  9. uvmcreate
  10. uvmfirst
  11. uvmalloc
  12. uvmdealloc
  13. freewalk
  14. uvmfree
  15. uvmcopy
  16. uvmclear
  17. copyout
  18. copyin
  19. copyinstr

   1 #include "param.h"
   2 #include "types.h"
   3 #include "memlayout.h"
   4 #include "elf.h"
   5 #include "riscv.h"
   6 #include "defs.h"
   7 #include "fs.h"
   8 
   9 /*
  10  * the kernel's page table.
  11  */
  12 pagetable_t kernel_pagetable;
  13 
  14 extern char etext[];  // kernel.ld sets this to end of kernel code.
  15 
  16 extern char trampoline[]; // trampoline.S
  17 
  18 // Make a direct-map page table for the kernel.
  19 pagetable_t
  20 kvmmake(void)
  21 {
  22   pagetable_t kpgtbl;
  23 
  24   kpgtbl = (pagetable_t) kalloc();
  25   memset(kpgtbl, 0, PGSIZE);
  26 
  27   // uart registers
  28   kvmmap(kpgtbl, UART0, UART0, PGSIZE, PTE_R | PTE_W);
  29 
  30   // virtio mmio disk interface
  31   kvmmap(kpgtbl, VIRTIO0, VIRTIO0, PGSIZE, PTE_R | PTE_W);
  32 
  33   // PLIC
  34   kvmmap(kpgtbl, PLIC, PLIC, 0x4000000, PTE_R | PTE_W);
  35 
  36   // map kernel text executable and read-only.
  37   kvmmap(kpgtbl, KERNBASE, KERNBASE, (uint64)etext-KERNBASE, PTE_R | PTE_X);
  38 
  39   // map kernel data and the physical RAM we'll make use of.
  40   kvmmap(kpgtbl, (uint64)etext, (uint64)etext, PHYSTOP-(uint64)etext, PTE_R | PTE_W);
  41 
  42   // map the trampoline for trap entry/exit to
  43   // the highest virtual address in the kernel.
  44   kvmmap(kpgtbl, TRAMPOLINE, (uint64)trampoline, PGSIZE, PTE_R | PTE_X);
  45 
  46   // allocate and map a kernel stack for each process.
  47   proc_mapstacks(kpgtbl);
  48   
  49   return kpgtbl;
  50 }
  51 
  52 // Initialize the one kernel_pagetable
  53 void
  54 kvminit(void)
  55 {
  56   kernel_pagetable = kvmmake();
  57 }
  58 
  59 // Switch h/w page table register to the kernel's page table,
  60 // and enable paging.
  61 void
  62 kvminithart()
  63 {
  64   // wait for any previous writes to the page table memory to finish.
  65   sfence_vma();
  66 
  67   w_satp(MAKE_SATP(kernel_pagetable));
  68 
  69   // flush stale entries from the TLB.
  70   sfence_vma();
  71 }
  72 
  73 // Return the address of the PTE in page table pagetable
  74 // that corresponds to virtual address va.  If alloc!=0,
  75 // create any required page-table pages.
  76 //
  77 // The risc-v Sv39 scheme has three levels of page-table
  78 // pages. A page-table page contains 512 64-bit PTEs.
  79 // A 64-bit virtual address is split into five fields:
  80 //   39..63 -- must be zero.
  81 //   30..38 -- 9 bits of level-2 index.
  82 //   21..29 -- 9 bits of level-1 index.
  83 //   12..20 -- 9 bits of level-0 index.
  84 //    0..11 -- 12 bits of byte offset within the page.
  85 pte_t *
  86 walk(pagetable_t pagetable, uint64 va, int alloc)
  87 {
  88   if(va >= MAXVA)
  89     panic("walk");
  90 
  91   for(int level = 2; level > 0; level--) {
  92     pte_t *pte = &pagetable[PX(level, va)];
  93     if(*pte & PTE_V) {
  94       pagetable = (pagetable_t)PTE2PA(*pte);
  95     } else {
  96       if(!alloc || (pagetable = (pde_t*)kalloc()) == 0)
  97         return 0;
  98       memset(pagetable, 0, PGSIZE);
  99       *pte = PA2PTE(pagetable) | PTE_V;
 100     }
 101   }
 102   return &pagetable[PX(0, va)];
 103 }
 104 
 105 // Look up a virtual address, return the physical address,
 106 // or 0 if not mapped.
 107 // Can only be used to look up user pages.
 108 uint64
 109 walkaddr(pagetable_t pagetable, uint64 va)
 110 {
 111   pte_t *pte;
 112   uint64 pa;
 113 
 114   if(va >= MAXVA)
 115     return 0;
 116 
 117   pte = walk(pagetable, va, 0);
 118   if(pte == 0)
 119     return 0;
 120   if((*pte & PTE_V) == 0)
 121     return 0;
 122   if((*pte & PTE_U) == 0)
 123     return 0;
 124   pa = PTE2PA(*pte);
 125   return pa;
 126 }
 127 
 128 // add a mapping to the kernel page table.
 129 // only used when booting.
 130 // does not flush TLB or enable paging.
 131 void
 132 kvmmap(pagetable_t kpgtbl, uint64 va, uint64 pa, uint64 sz, int perm)
 133 {
 134   if(mappages(kpgtbl, va, sz, pa, perm) != 0)
 135     panic("kvmmap");
 136 }
 137 
 138 // Create PTEs for virtual addresses starting at va that refer to
 139 // physical addresses starting at pa.
 140 // va and size MUST be page-aligned.
 141 // Returns 0 on success, -1 if walk() couldn't
 142 // allocate a needed page-table page.
 143 int
 144 mappages(pagetable_t pagetable, uint64 va, uint64 size, uint64 pa, int perm)
 145 {
 146   uint64 a, last;
 147   pte_t *pte;
 148 
 149   if((va % PGSIZE) != 0)
 150     panic("mappages: va not aligned");
 151 
 152   if((size % PGSIZE) != 0)
 153     panic("mappages: size not aligned");
 154 
 155   if(size == 0)
 156     panic("mappages: size");
 157   
 158   a = va;
 159   last = va + size - PGSIZE;
 160   for(;;){
 161     if((pte = walk(pagetable, a, 1)) == 0)
 162       return -1;
 163     if(*pte & PTE_V)
 164       panic("mappages: remap");
 165     *pte = PA2PTE(pa) | perm | PTE_V;
 166     if(a == last)
 167       break;
 168     a += PGSIZE;
 169     pa += PGSIZE;
 170   }
 171   return 0;
 172 }
 173 
 174 // Remove npages of mappings starting from va. va must be
 175 // page-aligned. The mappings must exist.
 176 // Optionally free the physical memory.
 177 void
 178 uvmunmap(pagetable_t pagetable, uint64 va, uint64 npages, int do_free)
 179 {
 180   uint64 a;
 181   pte_t *pte;
 182 
 183   if((va % PGSIZE) != 0)
 184     panic("uvmunmap: not aligned");
 185 
 186   for(a = va; a < va + npages*PGSIZE; a += PGSIZE){
 187     if((pte = walk(pagetable, a, 0)) == 0)
 188       panic("uvmunmap: walk");
 189     if((*pte & PTE_V) == 0)
 190       panic("uvmunmap: not mapped");
 191     if(PTE_FLAGS(*pte) == PTE_V)
 192       panic("uvmunmap: not a leaf");
 193     if(do_free){
 194       uint64 pa = PTE2PA(*pte);
 195       kfree((void*)pa);
 196     }
 197     *pte = 0;
 198   }
 199 }
 200 
 201 // create an empty user page table.
 202 // returns 0 if out of memory.
 203 pagetable_t
 204 uvmcreate()
 205 {
 206   pagetable_t pagetable;
 207   pagetable = (pagetable_t) kalloc();
 208   if(pagetable == 0)
 209     return 0;
 210   memset(pagetable, 0, PGSIZE);
 211   return pagetable;
 212 }
 213 
 214 // Load the user initcode into address 0 of pagetable,
 215 // for the very first process.
 216 // sz must be less than a page.
 217 void
 218 uvmfirst(pagetable_t pagetable, uchar *src, uint sz)
 219 {
 220   char *mem;
 221 
 222   if(sz >= PGSIZE)
 223     panic("uvmfirst: more than a page");
 224   mem = kalloc();
 225   memset(mem, 0, PGSIZE);
 226   mappages(pagetable, 0, PGSIZE, (uint64)mem, PTE_W|PTE_R|PTE_X|PTE_U);
 227   memmove(mem, src, sz);
 228 }
 229 
 230 // Allocate PTEs and physical memory to grow process from oldsz to
 231 // newsz, which need not be page aligned.  Returns new size or 0 on error.
 232 uint64
 233 uvmalloc(pagetable_t pagetable, uint64 oldsz, uint64 newsz, int xperm)
 234 {
 235   char *mem;
 236   uint64 a;
 237 
 238   if(newsz < oldsz)
 239     return oldsz;
 240 
 241   oldsz = PGROUNDUP(oldsz);
 242   for(a = oldsz; a < newsz; a += PGSIZE){
 243     mem = kalloc();
 244     if(mem == 0){
 245       uvmdealloc(pagetable, a, oldsz);
 246       return 0;
 247     }
 248     memset(mem, 0, PGSIZE);
 249     if(mappages(pagetable, a, PGSIZE, (uint64)mem, PTE_R|PTE_U|xperm) != 0){
 250       kfree(mem);
 251       uvmdealloc(pagetable, a, oldsz);
 252       return 0;
 253     }
 254   }
 255   return newsz;
 256 }
 257 
 258 // Deallocate user pages to bring the process size from oldsz to
 259 // newsz.  oldsz and newsz need not be page-aligned, nor does newsz
 260 // need to be less than oldsz.  oldsz can be larger than the actual
 261 // process size.  Returns the new process size.
 262 uint64
 263 uvmdealloc(pagetable_t pagetable, uint64 oldsz, uint64 newsz)
 264 {
 265   if(newsz >= oldsz)
 266     return oldsz;
 267 
 268   if(PGROUNDUP(newsz) < PGROUNDUP(oldsz)){
 269     int npages = (PGROUNDUP(oldsz) - PGROUNDUP(newsz)) / PGSIZE;
 270     uvmunmap(pagetable, PGROUNDUP(newsz), npages, 1);
 271   }
 272 
 273   return newsz;
 274 }
 275 
 276 // Recursively free page-table pages.
 277 // All leaf mappings must already have been removed.
 278 void
 279 freewalk(pagetable_t pagetable)
 280 {
 281   // there are 2^9 = 512 PTEs in a page table.
 282   for(int i = 0; i < 512; i++){
 283     pte_t pte = pagetable[i];
 284     if((pte & PTE_V) && (pte & (PTE_R|PTE_W|PTE_X)) == 0){
 285       // this PTE points to a lower-level page table.
 286       uint64 child = PTE2PA(pte);
 287       freewalk((pagetable_t)child);
 288       pagetable[i] = 0;
 289     } else if(pte & PTE_V){
 290       panic("freewalk: leaf");
 291     }
 292   }
 293   kfree((void*)pagetable);
 294 }
 295 
 296 // Free user memory pages,
 297 // then free page-table pages.
 298 void
 299 uvmfree(pagetable_t pagetable, uint64 sz)
 300 {
 301   if(sz > 0)
 302     uvmunmap(pagetable, 0, PGROUNDUP(sz)/PGSIZE, 1);
 303   freewalk(pagetable);
 304 }
 305 
 306 // Given a parent process's page table, copy
 307 // its memory into a child's page table.
 308 // Copies both the page table and the
 309 // physical memory.
 310 // returns 0 on success, -1 on failure.
 311 // frees any allocated pages on failure.
 312 int
 313 uvmcopy(pagetable_t old, pagetable_t new, uint64 sz)
 314 {
 315   pte_t *pte;
 316   uint64 pa, i;
 317   uint flags;
 318   char *mem;
 319 
 320   for(i = 0; i < sz; i += PGSIZE){
 321     if((pte = walk(old, i, 0)) == 0)
 322       panic("uvmcopy: pte should exist");
 323     if((*pte & PTE_V) == 0)
 324       panic("uvmcopy: page not present");
 325     pa = PTE2PA(*pte);
 326     flags = PTE_FLAGS(*pte);
 327     if((mem = kalloc()) == 0)
 328       goto err;
 329     memmove(mem, (char*)pa, PGSIZE);
 330     if(mappages(new, i, PGSIZE, (uint64)mem, flags) != 0){
 331       kfree(mem);
 332       goto err;
 333     }
 334   }
 335   return 0;
 336 
 337  err:
 338   uvmunmap(new, 0, i / PGSIZE, 1);
 339   return -1;
 340 }
 341 
 342 // mark a PTE invalid for user access.
 343 // used by exec for the user stack guard page.
 344 void
 345 uvmclear(pagetable_t pagetable, uint64 va)
 346 {
 347   pte_t *pte;
 348   
 349   pte = walk(pagetable, va, 0);
 350   if(pte == 0)
 351     panic("uvmclear");
 352   *pte &= ~PTE_U;
 353 }
 354 
 355 // Copy from kernel to user.
 356 // Copy len bytes from src to virtual address dstva in a given page table.
 357 // Return 0 on success, -1 on error.
 358 int
 359 copyout(pagetable_t pagetable, uint64 dstva, char *src, uint64 len)
 360 {
 361   uint64 n, va0, pa0;
 362   pte_t *pte;
 363 
 364   while(len > 0){
 365     va0 = PGROUNDDOWN(dstva);
 366     if(va0 >= MAXVA)
 367       return -1;
 368     pte = walk(pagetable, va0, 0);
 369     if(pte == 0 || (*pte & PTE_V) == 0 || (*pte & PTE_U) == 0 ||
 370        (*pte & PTE_W) == 0)
 371       return -1;
 372     pa0 = PTE2PA(*pte);
 373     n = PGSIZE - (dstva - va0);
 374     if(n > len)
 375       n = len;
 376     memmove((void *)(pa0 + (dstva - va0)), src, n);
 377 
 378     len -= n;
 379     src += n;
 380     dstva = va0 + PGSIZE;
 381   }
 382   return 0;
 383 }
 384 
 385 // Copy from user to kernel.
 386 // Copy len bytes to dst from virtual address srcva in a given page table.
 387 // Return 0 on success, -1 on error.
 388 int
 389 copyin(pagetable_t pagetable, char *dst, uint64 srcva, uint64 len)
 390 {
 391   uint64 n, va0, pa0;
 392 
 393   while(len > 0){
 394     va0 = PGROUNDDOWN(srcva);
 395     pa0 = walkaddr(pagetable, va0);
 396     if(pa0 == 0)
 397       return -1;
 398     n = PGSIZE - (srcva - va0);
 399     if(n > len)
 400       n = len;
 401     memmove(dst, (void *)(pa0 + (srcva - va0)), n);
 402 
 403     len -= n;
 404     dst += n;
 405     srcva = va0 + PGSIZE;
 406   }
 407   return 0;
 408 }
 409 
 410 // Copy a null-terminated string from user to kernel.
 411 // Copy bytes to dst from virtual address srcva in a given page table,
 412 // until a '\0', or max.
 413 // Return 0 on success, -1 on error.
 414 int
 415 copyinstr(pagetable_t pagetable, char *dst, uint64 srcva, uint64 max)
 416 {
 417   uint64 n, va0, pa0;
 418   int got_null = 0;
 419 
 420   while(got_null == 0 && max > 0){
 421     va0 = PGROUNDDOWN(srcva);
 422     pa0 = walkaddr(pagetable, va0);
 423     if(pa0 == 0)
 424       return -1;
 425     n = PGSIZE - (srcva - va0);
 426     if(n > max)
 427       n = max;
 428 
 429     char *p = (char *) (pa0 + (srcva - va0));
 430     while(n > 0){
 431       if(*p == '\0'){
 432         *dst = '\0';
 433         got_null = 1;
 434         break;
 435       } else {
 436         *dst = *p;
 437       }
 438       --n;
 439       --max;
 440       p++;
 441       dst++;
 442     }
 443 
 444     srcva = va0 + PGSIZE;
 445   }
 446   if(got_null){
 447     return 0;
 448   } else {
 449     return -1;
 450   }
 451 }

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