xv6, line by line
kernel/vm.c

kernel/vm.c

C · 491 lines · annotated 100% · kernel · upstream

About this file

Everything xv6 does with page tables: building them, changing them, copying them, freeing them, and moving data between the kernel and a process’s memory through them.

The hardware’s side. With Sv39 paging on, every address the hart uses is a virtual address of 39 bits, split as 9 + 9 + 9 + 12:

 38      30 29      21 20      12 11          0
+----------+----------+----------+-------------+
|  L2 idx  |  L1 idx  |  L0 idx  |   offset    |
+----------+----------+----------+-------------+

The hardware starts at the root page-table page named by satp, uses L2 idx to pick one of its 512 PTEs, follows that PTE to a second page, uses L1 idx there, follows again, uses L0 idx in the third page, and finds the leaf PTE: its physical page number, plus the 12-bit offset, is the physical address. For example the trampoline address 0x3ffffff000 has indices 255, 511, 511 and offset 0. The macros PX, PTE2PA and PA2PTE in kernel/riscv.h do the same bit arithmetic, and walk repeats the whole lookup in software.

Two kinds of page table.

  • One kernel page table, kernel_pagetable, built once by kvmmake and used by every hart whenever it runs kernel code. It is a direct map: RAM and devices appear at virtual addresses equal to their physical addresses, so the kernel can turn any physical address into a pointer. User memory does not appear in it.
  • One user page table per process (p->pagetable), built by proc_pagetable, kexec and uvmalloc. It maps the program’s memory from address 0 up, with the U bit set, plus the trampoline page and trapframe pages at the top (see the user memory layout). It is installed only while that process runs in user mode.

Because the kernel cannot use user addresses directly, copyin, copyout and copyinstr translate them in software, through the process’s page table, and copy through the direct map. Those three, and the page-fault handler in kernel/trap.c, also call vmfault, which allocates the pages of memory grown lazily by sbrklazy.

Function names follow a pattern: kvm... works on the kernel page table, uvm... on a user one.

Read before: kernel/riscv.h (the PTE macros), kernel/memlayout.h (the address map), kernel/kalloc.c. Read next: kernel/proc.c and kernel/exec.c, which build user address spaces with these functions.

1#include "param.h"
2#include "types.h"
4#include "elf.h"
5#include "riscv.h"
6#include "defs.h"
7#include "spinlock.h"
8#include "proc.h"
9#include "fs.h"
11/*
12 * the kernel's page table.
13 */
16extern char etext[]; // kernel.ld sets this to end of kernel code.
18extern char trampoline[]; // trampoline.S
20// Make a direct-map page table for the kernel.
22kvmmake(void)
29 // uart registers
32 // virtio mmio disk interface
35 // PLIC
36 kvmmap(kpgtbl, PLIC, PLIC, 0x4000000, PTE_R | PTE_W);
38 // map kernel text executable and read-only.
41 // map kernel data and the physical RAM we'll make use of.
45 // map the trampoline for trap entry/exit to
46 // the highest virtual address in the kernel.
49 // allocate and map a kernel stack for each process.
52 return kpgtbl;
55// add a mapping to the kernel page table.
56// only used when booting.
57// does not flush TLB or enable paging.
58void
61 if (mappages(kpgtbl, va, sz, pa, perm) != 0)
62 panic("kvmmap");
65// Initialize the kernel_pagetable, shared by all CPUs.
66void
67kvminit(void)
72// Switch the current CPU's h/w page table register to
73// the kernel's page table, and enable paging.
74void
77 // wait for any previous writes to the page table memory to finish.
82 // flush stale entries from the TLB.
86// Return the address of the PTE in page table pagetable
87// that corresponds to virtual address va. If alloc!=0,
88// create any required page-table pages.
89//
90// The risc-v Sv39 scheme has three levels of page-table
91// pages. A page-table page contains 512 64-bit PTEs.
92// A 64-bit virtual address is split into five fields:
93// 39..63 -- must be zero.
94// 30..38 -- 9 bits of level-2 index.
95// 21..29 -- 9 bits of level-1 index.
96// 12..20 -- 9 bits of level-0 index.
97// 0..11 -- 12 bits of byte offset within the page.
101 if (va >= MAXVA)
102 panic("walk");
104 for (int level = 2; level > 0; level--) {
106 if (*pte & PTE_V) {
108 } else {
109 if (!alloc || (pagetable = (pde_t *)kalloc()) == 0)
110 return 0;
113 }
114 }
115 return &pagetable[PX(0, va)];
118// Look up a virtual address, return the physical address,
119// or 0 if not mapped.
120// Can only be used to look up user pages.
127 if (va >= MAXVA)
128 return 0;
131 if (pte == 0)
132 return 0;
133 if ((*pte & PTE_V) == 0)
134 return 0;
135 if ((*pte & PTE_U) == 0)
136 return 0;
138 return pa;
141// Create PTEs for virtual addresses starting at va that refer to
142// physical addresses starting at pa.
143// va and size MUST be page-aligned.
144// Returns 0 on success, -1 if walk() couldn't
145// allocate a needed page-table page.
146int
152 if ((va % PGSIZE) != 0)
153 panic("mappages: va not aligned");
155 if ((size % PGSIZE) != 0)
156 panic("mappages: size not aligned");
158 if (size == 0)
159 panic("mappages: size");
161 a = va;
163 for (;;) {
164 if ((pte = walk(pagetable, a, 1)) == 0)
165 return -1;
166 if (*pte & PTE_V)
167 panic("mappages: remap");
169 if (a == last)
170 break;
173 }
174 return 0;
177// create an empty user page table.
178// returns 0 if out of memory.
184 if (pagetable == 0)
185 return 0;
187 return pagetable;
190// Remove npages of mappings starting from va. va must be
191// page-aligned. It's OK if the mappings don't exist.
192// Optionally free the physical memory.
193void
199 if ((va % PGSIZE) != 0)
200 panic("uvmunmap: not aligned");
202 for (a = va; a < va + npages * PGSIZE; a += PGSIZE) {
203 if ((pte = walk(pagetable, a, 0)) == 0) // leaf page table entry allocated?
204 continue;
205 if ((*pte & PTE_V) == 0) // has physical page been allocated?
206 continue;
207 if (do_free) {
209 kfree((void *)pa);
210 }
211 *pte = 0;
212 }
215// Allocate PTEs and physical memory to grow a process from oldsz to
216// newsz, which need not be page aligned. Returns new size or 0 on error.
220 char *mem;
223 if (newsz < oldsz)
224 return oldsz;
227 for (a = oldsz; a < newsz; a += PGSIZE) {
229 if (mem == 0) {
231 return 0;
232 }
235 0) {
238 return 0;
239 }
240 }
241 return newsz;
244// Deallocate user pages to bring the process size from oldsz to
245// newsz. oldsz and newsz need not be page-aligned, nor does newsz
246// need to be less than oldsz. oldsz can be larger than the actual
247// process size. Returns the new process size.
251 if (newsz >= oldsz)
252 return oldsz;
257 }
259 return newsz;
262// Recursively free page-table pages.
263// All leaf mappings must already have been removed.
264void
267 // there are 2^9 = 512 PTEs in a page table.
268 for (int i = 0; i < 512; i++) {
270 if ((pte & PTE_V) && (pte & (PTE_R | PTE_W | PTE_X)) == 0) {
271 // this PTE points to a lower-level page table.
275 } else if (pte & PTE_V) {
276 panic("freewalk: leaf");
277 }
278 }
279 kfree((void *)pagetable);
282// Free user memory pages,
283// then free page-table pages.
284void
287 if (sz > 0)
292// Given a parent process's page table, copy
293// its memory into a child's page table.
294// Copies both the page table and the
295// physical memory.
296// returns 0 on success, -1 on failure.
297// frees any allocated pages on failure.
298int
304 char *mem;
306 for (i = 0; i < sz; i += PGSIZE) {
307 if ((pte = walk(old, i, 0)) == 0)
308 continue; // page table entry hasn't been allocated
309 if ((*pte & PTE_V) == 0)
310 continue; // physical page hasn't been allocated
313 if ((mem = kalloc()) == 0)
314 goto err;
315 memmove(mem, (char *)pa, PGSIZE);
316 if (mappages(new, i, PGSIZE, (uint64)mem, flags) != 0) {
318 goto err;
319 }
320 }
321 return 0;
324 uvmunmap(new, 0, i / PGSIZE, 1);
325 return -1;
328// mark a PTE invalid for user access.
329// used by exec for the user stack guard page.
330void
336 if (pte == 0)
337 panic("uvmclear");
338 *pte &= ~PTE_U;
341// Copy from kernel to user.
342// Copy len bytes from src to virtual address dstva in a given page table.
343// Return 0 on success, -1 on error.
344int
350 while (len > 0) {
352 if (va0 >= MAXVA)
353 return -1;
356 if (pa0 == 0) {
357 if ((pa0 = vmfault(pagetable, psz, va0, 0)) == 0) {
358 return -1;
359 }
360 }
363 // forbid copyout over read-only user text pages.
364 if ((*pte & PTE_W) == 0)
365 return -1;
367 n = PGSIZE - (dstva - va0);
368 if (n > len)
369 n = len;
370 memmove((void *)(pa0 + (dstva - va0)), src, n);
372 len -= n;
373 src += n;
375 }
376 return 0;
379// Copy from user to kernel.
380// Copy len bytes to dst from virtual address srcva in a given page table.
381// Return 0 on success, -1 on error.
382int
387 while (len > 0) {
390 if (pa0 == 0) {
391 if ((pa0 = vmfault(pagetable, psz, va0, 1)) == 0) {
392 return -1;
393 }
394 }
395 n = PGSIZE - (srcva - va0);
396 if (n > len)
397 n = len;
398 memmove(dst, (void *)(pa0 + (srcva - va0)), n);
400 len -= n;
401 dst += n;
403 }
404 return 0;
407// Copy a null-terminated string from user to kernel.
408// Copy bytes to dst from virtual address srcva in a given page table,
409// until a '\0', or max.
410// Return 0 on success, -1 on error.
411int
416 int got_null = 0;
418 while (got_null == 0 && max > 0) {
421 if (pa0 == 0) {
422 if ((pa0 = vmfault(pagetable, psz, va0, 1)) == 0) {
423 return -1;
424 }
425 }
426 n = PGSIZE - (srcva - va0);
427 if (n > max)
428 n = max;
430 char *p = (char *)(pa0 + (srcva - va0));
431 while (n > 0) {
432 if (*p == '\0') {
433 *dst = '\0';
435 break;
436 } else {
437 *dst = *p;
438 }
439 --n;
440 --max;
441 p++;
442 dst++;
443 }
446 }
447 if (got_null) {
448 return 0;
449 } else {
450 return -1;
451 }
454// allocate and map user memory if process is referencing a page
455// that was lazily allocated in sys_sbrk().
456// returns 0 if va is invalid or already mapped, or if
457// out of physical memory, and physical address if successful.
463 if (va >= psz)
464 return 0;
467 return 0;
468 }
470 if (mem == 0)
471 return 0;
472 memset((void *)mem, 0, PGSIZE);
474 kfree((void *)mem);
475 return 0;
476 }
477 return mem;
480int
484 if (pte == 0) {
485 return 0;
486 }
487 if (*pte & PTE_V) {
488 return 1;
489 }
490 return 0;