Commit cf4069b5 authored by zhangyuyi's avatar zhangyuyi
Browse files

lab4 finish

parent 3243a481
No related merge requests found
Showing with 187 additions and 8 deletions
+187 -8
#!/usr/bin/env python
#!/usr/bin/env python3
import re
from gradelib import *
......
......@@ -179,6 +179,12 @@ int copyout(pagetable_t, uint64, char *, uint64);
int copyin(pagetable_t, char *, uint64, uint64);
int copyinstr(pagetable_t, char *, uint64, uint64);
int test_pagetable();
void vmprint(pagetable_t);
pagetable_t proc_kpt_init();
void proc_kvmmmap(pagetable_t, uint64 , uint64 , uint64 , int );
void proc_kvminithart(pagetable_t );
void free_proc_kpt(pagetable_t pagetable);
// plic.c
void plicinit(void);
......
......@@ -97,6 +97,7 @@ int exec(char *path, char **argv) {
p->trapframe->sp = sp; // initial stack pointer
proc_freepagetable(oldpagetable, oldsz);
if(p->pid==1) vmprint(p->pagetable);
return argc; // this ends up in a0, the first argument to main(argc, argv)
bad:
......@@ -105,6 +106,7 @@ bad:
iunlockput(ip);
end_op();
}
if(p->pid==1) vmprint(p->pagetable);
return -1;
}
......
......@@ -110,6 +110,17 @@ found:
release(&p->lock);
return 0;
}
// ljg add
// An empty kernel page table.
p->k_pagetable = proc_kpt_init();
// 申请内核栈,确保每一个进程的内核页表都关于该进程的内核栈有一个映射
char *pa = kalloc();
if(pa == 0)
panic("kalloc");
uint64 va = KSTACK((int) (p - proc));
proc_kvmmmap(p->k_pagetable, va, (uint64)pa, PGSIZE, PTE_R | PTE_W);
p->kstack = va;
// Set up new context to start executing at forkret,
// which returns to user space.
......@@ -128,6 +139,14 @@ static void freeproc(struct proc *p) {
p->trapframe = 0;
if (p->pagetable) proc_freepagetable(p->pagetable, p->sz);
p->pagetable = 0;
// 释放一个进程的内核栈
if(p->kstack){
uvmunmap(p->k_pagetable, p->kstack, 1, 1);
}
p->kstack = 0;
// 释放内核页表
free_proc_kpt(p->k_pagetable);
p->k_pagetable = 0;
p->sz = 0;
p->pid = 0;
p->parent = 0;
......@@ -430,8 +449,11 @@ void scheduler(void) {
// before jumping back to us.
p->state = RUNNING;
c->proc = p;
// 加载进程的内核页表到核心的satp寄存器
proc_kvminithart(p->k_pagetable);
swtch(&c->context, &p->context);
// ljg add Come back to the global kernel page table
kvminithart();
// Process is done running for now.
// It should have changed its p->state before coming back.
c->proc = 0;
......
......@@ -98,6 +98,8 @@ struct proc {
uint64 kstack; // Virtual address of kernel stack
uint64 sz; // Size of process memory (bytes)
pagetable_t pagetable; // User page table
// TODO
pagetable_t k_pagetable; // kernel page table
struct trapframe *trapframe; // data page for trampoline.S
struct context context; // swtch() here to run process
struct file *ofile[NOFILE]; // Open files
......
......@@ -5,6 +5,9 @@
#include "riscv.h"
#include "defs.h"
#include "fs.h"
#include "spinlock.h"
#include "proc.h"
/*
* the kernel's page table.
......@@ -45,12 +48,58 @@ void kvminit() {
kvmmap(TRAMPOLINE, (uint64)trampoline, PGSIZE, PTE_R | PTE_X);
}
// 为进程的内核页表新建一个初始化函数
pagetable_t proc_kpt_init(){
pagetable_t k_pagetable = (pagetable_t) kalloc();
memset(k_pagetable, 0, PGSIZE);
// uart registers
proc_kvmmmap(k_pagetable, UART0, UART0, PGSIZE, PTE_R | PTE_W);
// virtio mmio disk interface
proc_kvmmmap(k_pagetable, VIRTIO0, VIRTIO0, PGSIZE, PTE_R | PTE_W);
// CLINT
proc_kvmmmap(k_pagetable, CLINT, CLINT, 0x10000, PTE_R | PTE_W);
// PLIC
proc_kvmmmap(k_pagetable, PLIC, PLIC, 0x400000, PTE_R | PTE_W);
// map kernel text executable and read-only.
proc_kvmmmap(k_pagetable, KERNBASE, KERNBASE, (uint64)etext-KERNBASE, PTE_R | PTE_X);
// map kernel data and the physical RAM we'll make use of.
proc_kvmmmap(k_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_kvmmmap(k_pagetable, TRAMPOLINE, (uint64)trampoline, PGSIZE, PTE_R | PTE_X);
return k_pagetable;
}
// kvmmap是为内核页表的虚拟地址与物理地址做映射,这里需要重新添加一个类似的函数
void proc_kvmmmap(pagetable_t k_pagetable, uint64 va, uint64 pa, uint64 sz, int perm){
if(mappages(k_pagetable, va, sz, pa, perm) != 0)
panic("proc_kvmmap");
}
// Switch h/w page table register to the kernel's page table,
// and enable paging.
void kvminithart() {
w_satp(MAKE_SATP(kernel_pagetable));
sfence_vma();
}
void
proc_kvminithart(pagetable_t k_pagetable){
w_satp(MAKE_SATP(k_pagetable));
sfence_vma();
}
// Return the address of the PTE in page table pagetable
// that corresponds to virtual address va. If alloc!=0,
......@@ -113,7 +162,7 @@ uint64 kvmpa(uint64 va) {
pte_t *pte;
uint64 pa;
pte = walk(kernel_pagetable, va, 0);
pte = walk(myproc()->k_pagetable, va, 0);
if (pte == 0) panic("kvmpa");
if ((*pte & PTE_V) == 0) panic("kvmpa");
pa = PTE2PA(*pte);
......@@ -229,20 +278,45 @@ uint64 uvmdealloc(pagetable_t pagetable, uint64 oldsz, uint64 newsz) {
// All leaf mappings must already have been removed.
void freewalk(pagetable_t pagetable) {
// there are 2^9 = 512 PTEs in a page table.
// 遍历一个页表页的PTE表项
for (int i = 0; i < 512; i++) {
pte_t pte = pagetable[i];
/* 判断PTE的Flag位,如果还有下一级页表(即当前是根页表或次页表),
则递归调用freewalk释放页表项,并将对应的PTE清零 */
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;
uint64 child = PTE2PA(pte); // 将PTE转为为物理地址
freewalk((pagetable_t)child); // 递归调用freewalk
pagetable[i] = 0; // 清零
} else if (pte & PTE_V) {
/* 如果叶子页表的虚拟地址还有映射到物理地址,报错panic。
因为调用freewalk之前应该会先uvmunmap释放物理内存 */
panic("freewalk: leaf");
}
}
kfree((void *)pagetable);
kfree((void *)pagetable);// 释放pagetable指向的物理页
}
// 释放进程的内核页表
void
free_proc_kpt(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){
// this PTE points to a lower-level page table.
uint64 child = PTE2PA(pte);
pagetable[i] = 0;
if((pte & (PTE_R|PTE_W|PTE_X)) == 0){// 说明不是第三级,进行递归
free_proc_kpt((pagetable_t)child);
}
}
}
kfree((void*)pagetable);
}
// Free user memory pages,
// then free page-table pages.
void uvmfree(pagetable_t pagetable, uint64 sz) {
......@@ -281,6 +355,33 @@ err:
return -1;
}
// 仿照uvmcopy()函数,实现将用户空间的映射添加到每个进程的内核页表
void
u2k_vmcopy(pagetable_t pagetable, pagetable_t k_pagetable, uint64 oldsz, uint64 newsz){
pte_t *pte_from;
pte_t *pte_to;
oldsz = PGROUNDUP(oldsz);
for(uint64 i = oldsz; i < newsz; i += PGSIZE){
// 对页表pagetable中虚拟地址为i进行检查,检查pte是否存在
if((pte_from = walk(pagetable, i, 0)) == 0)
panic("u2k_vmcopy: pte should exist");
// 对内核页表k_pagetable中虚拟地址为i进行检查,检查pte是否存在,若不存在则申请物理内存并映射。
if((pte_to = walk(k_pagetable, i, 1)) == 0){
panic("u2k_vmcopy: pte walk fail");
}
// 在内核模式下,无法访问设置了PTE_U的页面,
// 所以接下来要获得pagetable中虚拟地址为i的pte的标志位
// uint64 pa = PTE2PA(*pte_from);
// uint flags = (PTE_FLAGS(*pte_from)) & (~PTE_U);
// *pte_to = PA2PTE(pa) | flags;
// 感觉上面三句有点多,改成一句
*pte_to = (*pte_from) & (~PTE_U);
}
}
// mark a PTE invalid for user access.
// used by exec for the user stack guard page.
void uvmclear(pagetable_t pagetable, uint64 va) {
......@@ -378,4 +479,49 @@ int test_pagetable() {
uint64 gsatp = MAKE_SATP(kernel_pagetable);
printf("test_pagetable: %d\n", satp != gsatp);
return satp != gsatp;
}
\ No newline at end of file
}
// 辅助函数,用于递归打印页表
void _vmprint(pagetable_t pagetable, int level) {
// there are 2^9 = 512 PTEs in a page table.
// 遍历一个页表页的PTE表项
for (int i = 0; i < 512; i++) {
pte_t pte = pagetable[i];//获取第i条PTE
//虚拟地址有映射到物理地址 即有效pte
if (pte & PTE_V) {
// 根据level打印层级标识
for (int j = 0; j < level; j++) {
if (j == 0)
printf("||");
else
printf(" ||");
}
// 打印当前索引和页表项信息
if ((pte & (PTE_R | PTE_W | PTE_X)) == 0) {
// 非叶子节点
printf("idx: %d: pa: %p, flags: ----\n", i, PTE2PA(pte));
_vmprint((pagetable_t)PTE2PA(pte), level + 1);// 递归调用
} else {
// 叶子节点 打印虚拟地址va
// uint64 va = ((uint64)pagetable) + (i << 12);
uint64 va = (i << 12) ; // 计算虚拟地址
printf("idx: %d: va: %p -> pa: %p, flags: %s%s%s%s\n", i,
va, PTE2PA(pte),
(pte & PTE_R ? "r" : "-"), (pte & PTE_W ? "w" : "-"),
(pte & PTE_X ? "x" : "-"), (pte & PTE_U ? "u" : "-"));
}
}
}
}
// 外部调用的 vmprint 函数
void vmprint(pagetable_t pagetable) {
// 打印根页表
printf("page table %p\n", pagetable);
// 传递level级和递归
_vmprint(pagetable, 1);
}
......@@ -54,6 +54,7 @@ void panic(char *);
struct cmd *parsecmd(char *);
// Execute cmd. Never returns.
__attribute__((noreturn))
void runcmd(struct cmd *cmd) {
int p[2];
struct backcmd *bcmd;
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment