#include "param.h" #include "types.h" #include "memlayout.h" #include "elf.h" #include "riscv.h" #include "defs.h" /* * the kernel's page table. */ pagetable_t kernel_pagetable; extern char etext[]; // kernel.ld sets this to end of kernel code. extern char trampstart[]; // trampoline.S /* * create a direct-map page table for the kernel and * turn on paging. called early, in supervisor mode. * the page allocator is already initialized. */ void kvminit() { kernel_pagetable = (pagetable_t) kalloc(); memset(kernel_pagetable, 0, PGSIZE); // uart registers mappages(kernel_pagetable, UART0, PGSIZE, UART0, PTE_R | PTE_W); // map kernel text executable and read-only. mappages(kernel_pagetable, KERNBASE, (uint64)etext-KERNBASE, KERNBASE, PTE_R | PTE_X); // map kernel data and the physical RAM we'll make use of. mappages(kernel_pagetable, (uint64)etext, PHYSTOP-(uint64)etext, (uint64)etext, PTE_R | PTE_W); // map the trampoline for trap entry/exit to // the highest virtual address in the kernel. mappages(kernel_pagetable, TRAMPOLINE, PGSIZE, (uint64)trampstart, PTE_R | PTE_X); kvmswitch(); } // Switch h/w page table register to the kernel's page table, // and enable paging. void kvmswitch(void) { w_satp(MAKE_SATP(kernel_pagetable)); } // Return the address of the PTE in page table pagetable // that corresponds to virtual address va. If alloc!=0, // create any required page table pages. // // The risc-v Sv39 scheme has three levels of page table // pages. A page table page contains 512 64-bit PTEs. // A 64-bit virtual address is split into five fields: // 39..63 -- must be zero. // 30..38 -- 9 bits of level-2 index. // 21..39 -- 9 bits of level-1 index. // 12..20 -- 9 bits of level-0 index. // 0..12 -- 12 bits of byte offset within the page. static pte_t * walk(pagetable_t pagetable, const void *va, int alloc) { if((uint64)va >= MAXVA) panic("walk"); for(int level = 2; level > 0; level--) { pte_t *pte = &pagetable[PX(level, va)]; if(*pte & PTE_V) { pagetable = (pagetable_t)PTE2PA(*pte); } else { if(!alloc || (pagetable = (pde_t*)kalloc()) == 0) return 0; memset(pagetable, 0, PGSIZE); *pte = PA2PTE(pagetable) | PTE_V; } } return &pagetable[PX(0, va)]; } // Create PTEs for virtual addresses starting at va that refer to // physical addresses starting at pa. va and size might not // be page-aligned. void mappages(pagetable_t pagetable, uint64 va, uint64 size, uint64 pa, int perm) { char *a, *last; pte_t *pte; a = (char*)PGROUNDDOWN(va); last = (char*)PGROUNDDOWN(va + size - 1); for(;;){ if((pte = walk(pagetable, a, 1)) == 0) panic("mappages: walk"); if(*pte & PTE_V) panic("remap"); *pte = PA2PTE(pa) | perm | PTE_V; if(a == last) break; a += PGSIZE; pa += PGSIZE; } } // Remove mappings from a page table. The mappings in // the given range must exist. Optionally free the // physical memory. void unmappages(pagetable_t pagetable, uint64 va, uint64 size, int do_free) { char *a, *last; pte_t *pte; uint64 pa; a = (char*)PGROUNDDOWN(va); last = (char*)PGROUNDDOWN(va + size - 1); for(;;){ if((pte = walk(pagetable, a, 0)) == 0) panic("unmappages: walk"); if((*pte & PTE_V) == 0) panic("unmappages: not mapped"); if(PTE_FLAGS(*pte) == PTE_V) panic("unmappages: not a leaf"); if(do_free){ pa = PTE2PA(*pte); kfree((void*)pa); } *pte = 0; if(a == last) break; a += PGSIZE; pa += PGSIZE; } } // create an empty user page table. pagetable_t uvmcreate() { pagetable_t pagetable; pagetable = (pagetable_t) kalloc(); if(pagetable == 0) panic("uvmcreate: out of memory"); memset(pagetable, 0, PGSIZE); return pagetable; } // Load the user initcode into address 0 of pagetable, // for the very first process. // sz must be less than a page. void uvminit(pagetable_t pagetable, char *src, uint sz) { char *mem; if(sz >= PGSIZE) panic("inituvm: more than a page"); mem = kalloc(); memset(mem, 0, PGSIZE); mappages(pagetable, 0, PGSIZE, (uint64)mem, PTE_W|PTE_R|PTE_X|PTE_U); memmove(mem, src, sz); } // Deallocate user pages to bring the process size from oldsz to // newsz. oldsz and newsz need not be page-aligned, nor does newsz // need to be less than oldsz. oldsz can be larger than the actual // process size. Returns the new process size. int uvmdealloc(pagetable_t pagetable, uint64 oldsz, uint64 newsz) { if(newsz >= oldsz) return oldsz; unmappages(pagetable, newsz, oldsz - newsz, 1); return newsz; } // Recursively free page table pages. // All leaf mappings must already have been removed. static void freewalk(pagetable_t pagetable) { // there are 2^9 = 512 PTEs in a page table. for(int i = 0; i < 512; i++){ pte_t pte = pagetable[i]; if((pte & PTE_V) && (pte & (PTE_R|PTE_W|PTE_X)) == 0){ // this PTE points to a lower-level page table. uint64 child = PTE2PA(pte); freewalk((pagetable_t)child); pagetable[i] = 0; } else if(pte & PTE_V){ // XXX trampoline pages... panic("freewalk: leaf"); } } kfree((void*)pagetable); } // Free user memory pages, // then free page table pages. void uvmfree(pagetable_t pagetable, uint64 sz) { unmappages(pagetable, 0, sz, 1); freewalk(pagetable); } // Given a parent process's page table, copy // its memory into a child's page table. // Copies both the page table and the // physical memory. void uvmcopy(pagetable_t old, pagetable_t new, uint64 sz) { pte_t *pte; uint64 pa, i; uint flags; char *mem; for(i = 0; i < sz; i += PGSIZE){ if((pte = walk(old, (void *) i, 0)) == 0) panic("copyuvm: pte should exist"); if((*pte & PTE_V) == 0) panic("copyuvm: page not present"); pa = PTE2PA(*pte); flags = PTE_FLAGS(*pte); if((mem = kalloc()) == 0) panic("uvmcopy: kalloc failed"); memmove(mem, (char*)pa, PGSIZE); mappages(new, i, PGSIZE, (uint64)mem, flags); } }