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, 0x400000, 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. va and size might not
 140 // be page-aligned. Returns 0 on success, -1 if walk() couldn't
 141 // allocate a needed page-table page.
 142 int
 143 mappages(pagetable_t pagetable, uint64 va, uint64 size, uint64 pa, int perm)
 144 {
 145   uint64 a, last;
 146   pte_t *pte;
 147 
 148   if(size == 0)
 149     panic("mappages: size");
 150   
 151   a = PGROUNDDOWN(va);
 152   last = PGROUNDDOWN(va + size - 1);
 153   for(;;){
 154     if((pte = walk(pagetable, a, 1)) == 0)
 155       return -1;
 156     if(*pte & PTE_V)
 157       panic("mappages: remap");
 158     *pte = PA2PTE(pa) | perm | PTE_V;
 159     if(a == last)
 160       break;
 161     a += PGSIZE;
 162     pa += PGSIZE;
 163   }
 164   return 0;
 165 }
 166 
 167 // Remove npages of mappings starting from va. va must be
 168 // page-aligned. The mappings must exist.
 169 // Optionally free the physical memory.
 170 void
 171 uvmunmap(pagetable_t pagetable, uint64 va, uint64 npages, int do_free)
 172 {
 173   uint64 a;
 174   pte_t *pte;
 175 
 176   if((va % PGSIZE) != 0)
 177     panic("uvmunmap: not aligned");
 178 
 179   for(a = va; a < va + npages*PGSIZE; a += PGSIZE){
 180     if((pte = walk(pagetable, a, 0)) == 0)
 181       panic("uvmunmap: walk");
 182     if((*pte & PTE_V) == 0)
 183       panic("uvmunmap: not mapped");
 184     if(PTE_FLAGS(*pte) == PTE_V)
 185       panic("uvmunmap: not a leaf");
 186     if(do_free){
 187       uint64 pa = PTE2PA(*pte);
 188       kfree((void*)pa);
 189     }
 190     *pte = 0;
 191   }
 192 }
 193 
 194 // create an empty user page table.
 195 // returns 0 if out of memory.
 196 pagetable_t
 197 uvmcreate()
 198 {
 199   pagetable_t pagetable;
 200   pagetable = (pagetable_t) kalloc();
 201   if(pagetable == 0)
 202     return 0;
 203   memset(pagetable, 0, PGSIZE);
 204   return pagetable;
 205 }
 206 
 207 // Load the user initcode into address 0 of pagetable,
 208 // for the very first process.
 209 // sz must be less than a page.
 210 void
 211 uvmfirst(pagetable_t pagetable, uchar *src, uint sz)
 212 {
 213   char *mem;
 214 
 215   if(sz >= PGSIZE)
 216     panic("uvmfirst: more than a page");
 217   mem = kalloc();
 218   memset(mem, 0, PGSIZE);
 219   mappages(pagetable, 0, PGSIZE, (uint64)mem, PTE_W|PTE_R|PTE_X|PTE_U);
 220   memmove(mem, src, sz);
 221 }
 222 
 223 // Allocate PTEs and physical memory to grow process from oldsz to
 224 // newsz, which need not be page aligned.  Returns new size or 0 on error.
 225 uint64
 226 uvmalloc(pagetable_t pagetable, uint64 oldsz, uint64 newsz, int xperm)
 227 {
 228   char *mem;
 229   uint64 a;
 230 
 231   if(newsz < oldsz)
 232     return oldsz;
 233 
 234   oldsz = PGROUNDUP(oldsz);
 235   for(a = oldsz; a < newsz; a += PGSIZE){
 236     mem = kalloc();
 237     if(mem == 0){
 238       uvmdealloc(pagetable, a, oldsz);
 239       return 0;
 240     }
 241     memset(mem, 0, PGSIZE);
 242     if(mappages(pagetable, a, PGSIZE, (uint64)mem, PTE_R|PTE_U|xperm) != 0){
 243       kfree(mem);
 244       uvmdealloc(pagetable, a, oldsz);
 245       return 0;
 246     }
 247   }
 248   return newsz;
 249 }
 250 
 251 // Deallocate user pages to bring the process size from oldsz to
 252 // newsz.  oldsz and newsz need not be page-aligned, nor does newsz
 253 // need to be less than oldsz.  oldsz can be larger than the actual
 254 // process size.  Returns the new process size.
 255 uint64
 256 uvmdealloc(pagetable_t pagetable, uint64 oldsz, uint64 newsz)
 257 {
 258   if(newsz >= oldsz)
 259     return oldsz;
 260 
 261   if(PGROUNDUP(newsz) < PGROUNDUP(oldsz)){
 262     int npages = (PGROUNDUP(oldsz) - PGROUNDUP(newsz)) / PGSIZE;
 263     uvmunmap(pagetable, PGROUNDUP(newsz), npages, 1);
 264   }
 265 
 266   return newsz;
 267 }
 268 
 269 // Recursively free page-table pages.
 270 // All leaf mappings must already have been removed.
 271 void
 272 freewalk(pagetable_t pagetable)
 273 {
 274   // there are 2^9 = 512 PTEs in a page table.
 275   for(int i = 0; i < 512; i++){
 276     pte_t pte = pagetable[i];
 277     if((pte & PTE_V) && (pte & (PTE_R|PTE_W|PTE_X)) == 0){
 278       // this PTE points to a lower-level page table.
 279       uint64 child = PTE2PA(pte);
 280       freewalk((pagetable_t)child);
 281       pagetable[i] = 0;
 282     } else if(pte & PTE_V){
 283       panic("freewalk: leaf");
 284     }
 285   }
 286   kfree((void*)pagetable);
 287 }
 288 
 289 // Free user memory pages,
 290 // then free page-table pages.
 291 void
 292 uvmfree(pagetable_t pagetable, uint64 sz)
 293 {
 294   if(sz > 0)
 295     uvmunmap(pagetable, 0, PGROUNDUP(sz)/PGSIZE, 1);
 296   freewalk(pagetable);
 297 }
 298 
 299 // Given a parent process's page table, copy
 300 // its memory into a child's page table.
 301 // Copies both the page table and the
 302 // physical memory.
 303 // returns 0 on success, -1 on failure.
 304 // frees any allocated pages on failure.
 305 int
 306 uvmcopy(pagetable_t old, pagetable_t new, uint64 sz)
 307 {
 308   pte_t *pte;
 309   uint64 pa, i;
 310   uint flags;
 311   char *mem;
 312 
 313   for(i = 0; i < sz; i += PGSIZE){
 314     if((pte = walk(old, i, 0)) == 0)
 315       panic("uvmcopy: pte should exist");
 316     if((*pte & PTE_V) == 0)
 317       panic("uvmcopy: page not present");
 318     pa = PTE2PA(*pte);
 319     flags = PTE_FLAGS(*pte);
 320     if((mem = kalloc()) == 0)
 321       goto err;
 322     memmove(mem, (char*)pa, PGSIZE);
 323     if(mappages(new, i, PGSIZE, (uint64)mem, flags) != 0){
 324       kfree(mem);
 325       goto err;
 326     }
 327   }
 328   return 0;
 329 
 330  err:
 331   uvmunmap(new, 0, i / PGSIZE, 1);
 332   return -1;
 333 }
 334 
 335 // mark a PTE invalid for user access.
 336 // used by exec for the user stack guard page.
 337 void
 338 uvmclear(pagetable_t pagetable, uint64 va)
 339 {
 340   pte_t *pte;
 341   
 342   pte = walk(pagetable, va, 0);
 343   if(pte == 0)
 344     panic("uvmclear");
 345   *pte &= ~PTE_U;
 346 }
 347 
 348 // Copy from kernel to user.
 349 // Copy len bytes from src to virtual address dstva in a given page table.
 350 // Return 0 on success, -1 on error.
 351 int
 352 copyout(pagetable_t pagetable, uint64 dstva, char *src, uint64 len)
 353 {
 354   uint64 n, va0, pa0;
 355 
 356   while(len > 0){
 357     va0 = PGROUNDDOWN(dstva);
 358     pa0 = walkaddr(pagetable, va0);
 359     if(pa0 == 0)
 360       return -1;
 361     n = PGSIZE - (dstva - va0);
 362     if(n > len)
 363       n = len;
 364     memmove((void *)(pa0 + (dstva - va0)), src, n);
 365 
 366     len -= n;
 367     src += n;
 368     dstva = va0 + PGSIZE;
 369   }
 370   return 0;
 371 }
 372 
 373 // Copy from user to kernel.
 374 // Copy len bytes to dst from virtual address srcva in a given page table.
 375 // Return 0 on success, -1 on error.
 376 int
 377 copyin(pagetable_t pagetable, char *dst, uint64 srcva, uint64 len)
 378 {
 379   uint64 n, va0, pa0;
 380 
 381   while(len > 0){
 382     va0 = PGROUNDDOWN(srcva);
 383     pa0 = walkaddr(pagetable, va0);
 384     if(pa0 == 0)
 385       return -1;
 386     n = PGSIZE - (srcva - va0);
 387     if(n > len)
 388       n = len;
 389     memmove(dst, (void *)(pa0 + (srcva - va0)), n);
 390 
 391     len -= n;
 392     dst += n;
 393     srcva = va0 + PGSIZE;
 394   }
 395   return 0;
 396 }
 397 
 398 // Copy a null-terminated string from user to kernel.
 399 // Copy bytes to dst from virtual address srcva in a given page table,
 400 // until a '\0', or max.
 401 // Return 0 on success, -1 on error.
 402 int
 403 copyinstr(pagetable_t pagetable, char *dst, uint64 srcva, uint64 max)
 404 {
 405   uint64 n, va0, pa0;
 406   int got_null = 0;
 407 
 408   while(got_null == 0 && max > 0){
 409     va0 = PGROUNDDOWN(srcva);
 410     pa0 = walkaddr(pagetable, va0);
 411     if(pa0 == 0)
 412       return -1;
 413     n = PGSIZE - (srcva - va0);
 414     if(n > max)
 415       n = max;
 416 
 417     char *p = (char *) (pa0 + (srcva - va0));
 418     while(n > 0){
 419       if(*p == '\0'){
 420         *dst = '\0';
 421         got_null = 1;
 422         break;
 423       } else {
 424         *dst = *p;
 425       }
 426       --n;
 427       --max;
 428       p++;
 429       dst++;
 430     }
 431 
 432     srcva = va0 + PGSIZE;
 433   }
 434   if(got_null){
 435     return 0;
 436   } else {
 437     return -1;
 438   }
 439 }

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