改动:
//vm.c
void vmprint2(pagetable_t pagetable, int depth) {
for(int i = 0; i < 512; i++){
pte_t pte = pagetable[i];
if(pte & PTE_V){
// this PTE points to a lower-level page table.
uint64 child = PTE2PA(pte);
int j;
printf("..");
for (j = 1; j < depth; ++j) printf(" ..");
printf("%d: pte %p pa %p\n", i, pte, child);
if (depth < 3) vmprint2((pagetable_t)child, depth + 1);
}
}
}
void vmprint(pagetable_t pagetable) {
printf("page table %p\n", pagetable);
// there are 2^9 = 512 PTEs in a page table.
vmprint2(pagetable, 1);
}
//defs.h
void vmprint(pagetable_t);
//exec.c
...
if (p->pid == 1) vmprint(p->pagetable);
最终结果如下图所示:
在XV6原先的设计下,只有一张内核表,而每个进程都维护了一张自己的进程用户页表。
这样做的话有个问题,因为内核的页表不包含这些映射,进行系统调用时必须传入物理地址而不能传入虚拟地址,而本处要做的便是在每个用户进程内都维护一个内核页表,进程切换的时候在硬件维度就切换内核页表,分配时也要用这个内核页表。(也就是用新的内核页表替代用户页表)
改动:
//proc.h
// Per-process state
struct proc {
...
pagetable_t kernel_pagetable;// kernel page table
...
};
//vm.c
pagetable_t
proc_kernel_pagetable() {
pagetable_t kernel_page_table = (pagetable_t) kalloc();
memset(kernel_page_table, 0, PGSIZE);
// uart registers
mappages(kernel_page_table, UART0, PGSIZE, UART0, PTE_R | PTE_W);
// virtio mmio disk interface
mappages(kernel_page_table, VIRTIO0, PGSIZE, VIRTIO0, PTE_R | PTE_W);
// CLINT
// mappages(kernel_page_table, CLINT, 0x10000, CLINT, PTE_R | PTE_W);
// PLIC
mappages(kernel_page_table, PLIC, 0x400000, PLIC, PTE_R | PTE_W);
// map kernel text executable and read-only.
mappages(kernel_page_table, KERNBASE, (uint64)etext-KERNBASE, KERNBASE, PTE_R | PTE_X);
// map kernel data and the physical RAM we'll make use of.
mappages(kernel_page_table, (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_page_table, TRAMPOLINE, PGSIZE, (uint64)trampoline, PTE_R | PTE_X);
return kernel_page_table;
}
//vm.c
/*
* create a direct-map page table for the kernel.
*/
void
kvminit()
{
kernel_pagetable = proc_kernel_pagetable();
mappages(kernel_pagetable, CLINT, 0x10000, CLINT, PTE_R | PTE_W);
}
//proc.c
// initialize the proc table at boot time.
void
procinit(void)
{
struct proc *p;
initlock(&pid_lock, "nextpid");
for(p = proc; p < &proc[NPROC]; p++) {
initlock(&p->lock, "proc");
// Allocate a page for the process's kernel stack.
// Map it high in memory, followed by an invalid
// guard page.
// char *pa = kalloc();
// if(pa == 0)
// panic("kalloc");
// uint64 va = KSTACK((int) (p - proc));
// kvmmap(va, (uint64)pa, PGSIZE, PTE_R | PTE_W);
// p->kstack = va;
}
// kvminithart();
}
//proc.h
...
found:
p->pid = allocpid();
// Allocate a trapframe page.
if((p->trapframe = (struct trapframe *)kalloc()) == 0){
release(&p->lock);
return 0;
}
//Add seperated kernel page table
// allocate a kernel page for it
char *pa = kalloc();
if(pa == 0)
panic("kalloc");
p->kernel_pagetable = proc_kernel_pagetable();
if(p->kernel_pagetable == 0){
freeproc(p);
release(&p->lock);
return 0;
}
uint64 va = TRAMPOLINE - 2*PGSIZE;
mappages(p->kernel_pagetable, va, PGSIZE, (uint64)pa, PTE_R | PTE_W);
p->kstack = va;
// An empty user page table.
...
//proc.c
...
int found = 0;
for(p = proc; p < &proc[NPROC]; p++) {
acquire(&p->lock);
if(p->state == RUNNABLE) {
// Switch to chosen process. It is the process's job
// to release its lock and then reacquire it
// before jumping back to us.
p->state = RUNNING;
c->proc = p;
w_satp(MAKE_SATP(p->kernel_pagetable));
sfence_vma();
swtch(&c->context, &p->context);
kvminithart();
// Process is done running for now.
// It should have changed its p->state before coming back.
c->proc = 0;
found = 1;
}
release(&p->lock);
}
...
//vm.c
#include "spinlock.h"
#include "proc.h"
uint64
kvmpa(uint64 va)
{
uint64 off = va % PGSIZE;
pte_t *pte;
uint64 pa;
pte = walk(myproc()->kernel_pagetable, va, 0);//修改
if(pte == 0)
panic("kvmpa");
if((*pte & PTE_V) == 0)
panic("kvmpa");
pa = PTE2PA(*pte);
ret
//proc.c
static void
freeproc(struct proc *p)
{
if(p->trapframe)
kfree((void*)p->trapframe);
p->trapframe = 0;
if(p->pagetable)
proc_freepagetable(p->pagetable, p->sz);
if(p->kernel_pagetable)
proc_free_kernelpagetable(p->kstack, p->kernel_pagetable);
p->kernel_pagetable = 0;
...
//proc.c
void proc_free_kernelpagetable(uint64 kstack, pagetable_t pagetable) {
uvmunmap(pagetable, UART0, 1, 0);
uvmunmap(pagetable, VIRTIO0, 1, 0);
uvmunmap(pagetable, PLIC, 0x400000/PGSIZE, 0);
uvmunmap(pagetable, KERNBASE, ((uint64)etext-KERNBASE)/PGSIZE, 0);
uvmunmap(pagetable, (uint64)etext, (PHYSTOP-(uint64)etext)/PGSIZE, 0);
uvmunmap(pagetable, TRAMPOLINE, 1, 0);
uvmunmap(pagetable, kstack, 1, 1);
freewalk(pagetable);
}
//defs.h
void freewalk(pagetable_t);
pagetable_t proc_kernel_pagetable(void);
void proc_freepagetable(pagetable_t, uint64);
void proc_free_kernelpagetable(uint64, pagetable_t);
最终执行usertests,结果如下:
本题的目标是要用copyin_new 来取代copyin,用copyinstr_new取代copyinstr,其中需要在修改原用户页表时也同步修改新建的内核页表。
具体改动如下:
//proc.c
//copy from user page table to kernel page table
void pageCopyUser2Kernel(pagetable_t upagetable, pagetable_t kpagetable, uint64 va) {
pte_t *pte = walk(upagetable, va, 0);
pte_t *kernelPte = walk(kpagetable, va, 1);
*kernelPte = (*pte) & (~PTE_U);
}
//defs.h
void pageCopyUser2Kernel(pagetable_t, pagetable_t, uint64);
//proc.c
// Set up first user process.
void
userinit(void)
{
struct proc *p;
p = allocproc();
initproc = p;
// allocate one user page and copy init's instructions
// and data into it.
uvminit(p->pagetable, initcode, sizeof(initcode));
p->sz = PGSIZE;
pageCopyUser2Kernel(p->pagetable, p->kernel_pagetable, 0);
...
//proc.c
int
fork(void)
{
int i, pid;
struct proc *np;
struct proc *p = myproc();
// Allocate process.
if((np = allocproc()) == 0){
return -1;
}
// Copy user memory from parent to child.
if(uvmcopy(p->pagetable, np->pagetable, p->sz) < 0){
freeproc(np);
release(&np->lock);
return -1;
}
// Copy user page table to kernel page table.
int j;
for (j = 0; j < p->sz; j += PGSIZE) {
pageCopyUser2Kernel(np->pagetable, np->kernel_pagetable, j);
}
np->sz = p->sz;
...
//exec.c
int
exec(char *path, char **argv)
{
...
// Load program into memory.
for(i=0, off=elf.phoff; i<elf.phnum; i++, off+=sizeof(ph)){
if(readi(ip, 0, (uint64)&ph, off, sizeof(ph)) != sizeof(ph))
goto bad;
if(ph.type != ELF_PROG_LOAD)
continue;
if(ph.memsz < ph.filesz)
goto bad;
if(ph.vaddr + ph.memsz < ph.vaddr)
goto bad;
uint64 sz1;
if((sz1 = uvmalloc(pagetable, sz, ph.vaddr + ph.memsz)) == 0)
goto bad;
//新增此判断
if (sz1 >= PLIC) {
goto bad;
}
sz = sz1;
if(ph.vaddr % PGSIZE != 0)
goto bad;
if(loadseg(pagetable, ph.vaddr, ip, ph.off, ph.filesz) < 0)
goto bad;
}
...
int j;
uvmunmap(p->kernel_pagetable, 0, PGROUNDUP(p->sz), 0);
for (j = 0; j < sz; j += PGSIZE) {
pageCopyUser2Kernel(p->pagetable, p->kernel_pagetable, j);
}
...
//proc.c
static void
freeproc(struct proc *p)
{
if(p->trapframe)
kfree((void*)p->trapframe);
p->trapframe = 0;
if(p->kernel_pagetable)
proc_free_kernelpagetable(p->kstack, p->kernel_pagetable, p->sz);
...
void proc_free_kernelpagetable(uint64 kstack, pagetable_t pagetable, uint64 sz) {
uvmunmap(pagetable, UART0, 1, 0);
uvmunmap(pagetable, VIRTIO0, 1, 0);
uvmunmap(pagetable, PLIC, 0x400000/PGSIZE, 0);
uvmunmap(pagetable, KERNBASE, ((uint64)etext-KERNBASE)/PGSIZE, 0);
uvmunmap(pagetable, (uint64)etext, (PHYSTOP-(uint64)etext)/PGSIZE, 0);
uvmunmap(pagetable, TRAMPOLINE, 1, 0);
uvmunmap(pagetable, 0, PGROUNDUP(sz)/PGSIZE, 0);
uvmunmap(pagetable, kstack, 1, 1);
freewalk(pagetable);
}
uint64
sys_sbrk(void)
{
int addr;
int n, j;
struct proc* p = myproc();
if(argint(0, &n) < 0)
return -1;
addr = p->sz;
if (n + addr >= PLIC)
return -1;
if(growproc(n) < 0)
return -1;
if (n > 0) {
for (j = addr; j < addr + n; j += PGSIZE) {
pageCopyUser2Kernel(p->pagetable, p->kernel_pagetable, j);
}
} else {
for (j = addr - PGSIZE; j >= addr + n; j -= PGSIZE) {
uvmunmap(p->kernel_pagetable, j, 1, 0);
}
}
return addr;
}
//defs.h
int copyin_new(pagetable_t, char *, uint64, uint64);
int copyinstr_new(pagetable_t, char *, uint64, uint64);
int
copyin(pagetable_t pagetable, char *dst, uint64 srcva, uint64 len)
{
return copyinstr_new(pagetable, dst, srcva, max);
}
int
copyinstr(pagetable_t pagetable, char *dst, uint64 srcva, uint64 max)
{
return copyinstr_new(pagetable, dst, srcva, max);
}
输入make grade结果如下: