// 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..29 -- 9 bits of level-1 index. // 12..20 -- 9 bits of level-0 index. // 0..11 -- 12 bits of byte offset within the page. pte_t * walk(pagetable_t pagetable, uint64 va, int alloc) { if(va >= MAXVA) panic("walk");
// p->lock must be held when using these: enumprocstatestate;// Process state structproc *parent;// Parent process void *chan; // If non-zero, sleeping on chan int killed; // If non-zero, have been killed int xstate; // Exit status to be returned to parent's wait int pid; // Process ID
// these are private to the process, so p->lock need not be held. uint64 kstack; // Virtual address of kernel stack uint64 sz; // Size of process memory (bytes) uint64 kstack_pa; //kstack pa in kernel pagetable pagetable_t pagetable; // User page table pagetable_t kernel_pagetable; // kernel_pagetable structtrapframe *trapframe;// data page for trampoline.S structcontextcontext;// swtch() here to run process structfile *ofile[NOFILE];// Open files structinode *cwd;// Current directory char name[16]; // Process name (debugging) };
Early in the boot sequence, main calls kvminit (kernel/vm.c:22) to create the kernel’s page table. This call occurs before xv6 has enabled paging on the RISC-V, so addresses refer directly to physical memory. Kvminit first allocates a page of physical memory to hold the root page-table page.
// map kernel text executable and read-only. kvmmap(KERNBASE, KERNBASE, (uint64)etext-KERNBASE, PTE_R | PTE_X);
kvmmap((uint64)etext, (uint64)etext, PHYSTOP-(uint64)etext, PTE_R | PTE_W); // map kernel data and the phy return 0; in the kernel. kvmmap(TRAMPOLINE, (uint64)trampoline, PGSIZE, PTE_R | PTE_X); // map kernel data and the physical RAM we'll make use of.
// used for proc_kvminit // same as kvmmp void proc_kvmmap(pagetable_t proc_kernel_pagetable,uint64 va, uint64 pa, uint64 sz, int perm) { if(mappages(proc_kernel_pagetable, va, sz, pa, perm) != 0) panic("proc_kvmmap"); }
// used for porc to have a kernel pagetable // same as kvminit pagetable_tproc_kvminit() { pagetable_t proc_kernel_pagetable = (pagetable_t) kalloc(); if(proc_kernel_pagetable == (pagetable_t)0) return0; memset(proc_kernel_pagetable, 0, PGSIZE);
// map kernel text executable and read-only. proc_kvmmap(proc_kernel_pagetable, KERNBASE, KERNBASE, (uint64)etext-KERNBASE, PTE_R | PTE_X);
// map kernel data and the physical RAM we'll make use of. proc_kvmmap(proc_kernel_pagetable, (uint64)etext, (uint64)etext, PHYSTOP-(uint64)etext, PTE_R | PTE_W);
// map the trampoline for trap entry/exit to // the highest virtual address in the kernel. proc_kvmmap(proc_kernel_pagetable, TRAMPOLINE, (uint64)trampoline, PGSIZE, PTE_R | PTE_X); return proc_kernel_pagetable; }
Each process has two stacks: a user stack and a kernel stack ( p->kstack ). When the process is executing user instructions, only its user stack is in use, and its kernel stack is empty. When the process enters the kernel (for a system call or interrupt), the kernel code executes on the process’s kernel stack; while a process is in the kernel, its user stack still contains saved data, but isn’t ac- tively used. A process’s thread alternates between actively using its user stack and its kernel stack. The kernel stack is separate (and protected from user code) so that the kernel can execute even if a process has wrecked its user stack.
procinit (kernel/proc.c:26) , which is called from main , allocates a kernel stack for each pro- cess. It maps each stack at the virtual address generated by KSTACK , which leaves room for the invalid stack-guard pages. kvmmap adds the mapping PTEs to the kernel page table, and the call to kvminithart reloads the kernel page table into satp so that the hardware knows about the new PTEs.
// Allocate a page for the process's own kernel stack(p->kernel_pagetable). // Map it high in memory, followed by an invalid // guard page. //initlock(&p->lock, "proc"); uint64 va = p->kstack; uint64 pa = p->kstack_pa; if(pa == 0) { printf("%p", va); panic("allocproc: invalid pa\n"); } printf("%p\n", pa); //va = KSTACK(0); proc_kvmmap(p->kernel_pagetable ,va, (uint64)pa, PGSIZE, PTE_R | PTE_W);
void scheduler(void) { structproc *p; structcpu *c = mycpu(); c->proc = 0; for(;;){ // Avoid deadlock by ensuring that devices can interrupt. intr_on(); 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));//change the satp register to current process kernel pagetable sfence_vma(); swtch(&c->context, &p->context); kvminithart();// 当proc执行完之后,需要将全局kernel page table 转到 satp中 // Process is done running for now. // It should have changed its p->state before coming back. c->proc = 0; found = 1; } release(&p->lock); } #if !defined (LAB_FS) if(found == 0) { intr_on(); kvminithart(); asmvolatile("wfi"); } #else ; #endif } }
// kernel/proc.c // Grow or shrink user memory by n bytes. // Return 0 on success, -1 on failure. int growproc(int n) { uint sz; structproc *p = myproc();
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;
// prepare for the very first "return" from kernel to user. p->trapframe->epc = 0; // user program counter p->trapframe->sp = PGSIZE; // user stack pointer
// kernel/vm.c // Copy from user to kernel. // Copy len bytes to dst from virtual address srcva in a given page table. // Return 0 on success, -1 on error. int copyin(pagetable_t pagetable, char *dst, uint64 srcva, uint64 len) { return copyin_new(pagetable, dst, srcva, len); }
// Copy a null-terminated string from user to kernel. // Copy bytes to dst from virtual address srcva in a given page table, // until a '\0', or max. // Return 0 on success, -1 on error. int copyinstr(pagetable_t pagetable, char *dst, uint64 srcva, uint64 max) { return copyinstr_new(pagetable, dst, srcva, max); }
实验就完成啦^_^ 可以看一下实现的copynew_in,可以发现,已经取消翻译的那一步了。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
// Copy from user to kernel. // Copy len bytes to dst from virtual address srcva in a given page table. // Return 0 on success, -1 on error. int copyin_new(pagetable_t pagetable, char *dst, uint64 srcva, uint64 len) { structproc *p = myproc();