From cf4069b5303168987f99a1880f823b2bfc35b1a1 Mon Sep 17 00:00:00 2001
From: zhangyuyi <zyy040613@163.com>
Date: Fri, 15 Nov 2024 18:28:59 +0800
Subject: [PATCH] lab4 finish

---
 grade-lab-pgtbl |   2 +-
 kernel/defs.h   |   6 ++
 kernel/exec.c   |   2 +
 kernel/proc.c   |  24 +++++++-
 kernel/proc.h   |   2 +
 kernel/vm.c     | 158 ++++++++++++++++++++++++++++++++++++++++++++++--
 user/sh.c       |   1 +
 7 files changed, 187 insertions(+), 8 deletions(-)

diff --git a/grade-lab-pgtbl b/grade-lab-pgtbl
index 0a032096..c56f5774 100755
--- a/grade-lab-pgtbl
+++ b/grade-lab-pgtbl
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 
 import re
 from gradelib import *
diff --git a/kernel/defs.h b/kernel/defs.h
index 59f7aae5..48b4f5c3 100644
--- a/kernel/defs.h
+++ b/kernel/defs.h
@@ -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);
diff --git a/kernel/exec.c b/kernel/exec.c
index 7b8a5241..cc26b363 100644
--- a/kernel/exec.c
+++ b/kernel/exec.c
@@ -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;
 }
 
diff --git a/kernel/proc.c b/kernel/proc.c
index 292ccb81..d0292132 100644
--- a/kernel/proc.c
+++ b/kernel/proc.c
@@ -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;
diff --git a/kernel/proc.h b/kernel/proc.h
index 9c16ea72..a1b752c9 100644
--- a/kernel/proc.h
+++ b/kernel/proc.h
@@ -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
diff --git a/kernel/vm.c b/kernel/vm.c
index b794885c..2a8ca7c1 100644
--- a/kernel/vm.c
+++ b/kernel/vm.c
@@ -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);
+}
+
diff --git a/user/sh.c b/user/sh.c
index 13c7d7c8..6f1b5a8e 100644
--- a/user/sh.c
+++ b/user/sh.c
@@ -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;
-- 
GitLab