本文记录了 Lab: page tables 的实验过程

Lab: page tables

在这次实验中,我们将学习页表并对其进行修改,以简化将数据从用户空间复制到内核空间的函数。

在编写代码之前,阅读 xv6 book 的第三章的内容,以及下面的代码文件

  • kern/memlayout.h:内存的布局
  • kern/vm.c:包含了大多数操作虚拟内存的代码(重要)
  • kernel/kalloc.c:包含了分配以及释放物理内存的代码

在开始实验之前,将分支转换到 pgtbl

1
2
3
git fetch
git checkout pgtbl
make clean

实验要求

编写一个函数,输出页表的内容。只需要打印进程 ID 为 1 的那个进程的页表内容即可。

实验思路

直接根据实验的步骤走,vmprint 的实现参考 kernel/vm.c 中的 freewalk 函数中的代码。

实验步骤

  1. kernel/defs.h 声明函数原型
1
2
3
// vm.c
...
void vmprint(pagetable_t);
  1. kernel/exec.c 文件中的 exec 函数的 return argc; 前添加
1
2
3
4
5
// print the first process's page table before return argc
if (p->pid==1){
printf("page table %p\n", p->pagetable);
vmprint(p->pagetable);
}
  1. kernel/vm.c 添加 vmprint 的实现
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
void
print(pagetable_t pagetable, int level){
// 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)){
uint64 child = PTE2PA(pte);

for(int l = 0; l < level; l++){
printf("..");
if(l != level - 1) printf(" ");
}
printf("%d: pte %p pa %p\n", i, pte, child);

// this PTE points to a lower-level page table.
if((pte & (PTE_R|PTE_W|PTE_X)) == 0)
print((pagetable_t)child, level + 1);
}
}
}
// implemention the function vmprint
void
vmprint(pagetable_t pagetable)
{
print(pagetable, 1);
}

A kernel page table per process (hard)

关于内核页表的描述

​ Xv6 只有一个内核页表,每当在内核中执行时就会使用这个内核页表。内核页表存放的是对物理地址的直接映射,因此对于内核虚拟地址 x 来说,它会直接映射到物理地址 x。Xv6 为每个进程的用户地址空间维护了一个单独的页表,这个页表只包含该进程从虚拟地址 0 开始的用户内存的映射。由于内核页表并不包含这些映射,所以用户地址在内核中是无效的。

​ 因此,当内核需要使用由系统调用传进来的用户指针(例如,传给 write() 的缓冲区指针)时。首先,内核必须将该指针转换称物理地址。

​ 这个 task 需要给每个进程添加一个单独的内核页表,两个 task 的目的是让每个进程都拥有一个同时映射了用户内存区和内核内存区的页表。这样进程在进入内核后,可以直接在自己这个内核页表中的用户内存区和内核内存区之间传递数据,不需要经过页表切换。

实验要求

修改内核代码,使得每个进程在内核中运行时使用进程本身的内核页表副本。

  • 修改 struct proc,为每个进程维护一个内核页表(与 kernel_pagetable 保持一致)
  • 修改 scheduler 的代码,使得当改变进程时,同时改变内核页表
  • 每个进程的内核页表应该与现有的全局内核页表相同

然后按照 hints 中的步骤做(首先确保你理解了 vm.c 中的代码)

实验思路

  1. vm.c
  • 实现函数 int ukvmmap(pagetable_t, uint64, uint64, uint64, int); ,它的功能与 kvmmap 功能类似,只不过它是在进程的内核页表设置 va -> pa 的映射关系
  • 实现一个与 kvminit 函数功能相同的函数 pagetable_t ukpgtb();,相当于将全局内核页表的副本存放在进程内核页表中
  • 实现函数 void ukfreepgtb(pagetable_t);,释放进程的内核页表。它的具体实现可以参考 freewalk 函数
  1. proc.c 文件中修改代码实现完整的功能

实验步骤

根据 hints

  1. kernel/defs.h 声明函数原型
1
2
3
4
// vm.c
...
pagetable_t ukpgtb();
void ukfreepgtb(pagetable_t);
  1. kernel/vm.c 中实现函数 ukpgtb, ukfreepgtb,修改 kvmpa 中的页表
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
#include "spinlock.h"
#include "proc.h"

uint64
kvmpa(uint64 va)
{
// ...
// 修改页表为进程的内核页表
pte = walk(myproc()->kernel_pgtb, va, 0);
// ...
}


/*
* use function of mappages build map for process's kernel page table
*/
int
ukvmmap(pagetable_t pagetable, uint64 va, uint64 pa, uint64 sz, int perm)
{
if(mappages(pagetable, va, sz, pa, perm) != 0)
return -1;
return 0;
}
/*
* make a new page table for the kernel.
*/
pagetable_t
ukpgtb()
{
// the same as kvminit()
pagetable_t kernel_pgtb = (pagetable_t) kalloc();
memset(kernel_pgtb, 0, PGSIZE);

ukvmmap(kernel_pgtb, UART0, UART0, PGSIZE, PTE_R | PTE_W);
ukvmmap(kernel_pgtb, VIRTIO0, VIRTIO0, PGSIZE, PTE_R | PTE_W);
ukvmmap(kernel_pgtb, CLINT, CLINT, 0x10000, PTE_R | PTE_W);
ukvmmap(kernel_pgtb, PLIC, PLIC, 0x400000, PTE_R | PTE_W);
ukvmmap(kernel_pgtb, KERNBASE, KERNBASE, (uint64)etext-KERNBASE, PTE_R | PTE_X);
ukvmmap(kernel_pgtb, (uint64)etext, (uint64)etext, PHYSTOP-(uint64)etext, PTE_R | PTE_W);
ukvmmap(kernel_pgtb, TRAMPOLINE, (uint64)trampoline, PGSIZE, PTE_R | PTE_X);

return kernel_pgtb;
}
/*
* free the process's kernel page table
*/
void
ukfreepgtb(pagetable_t pagetable)
{
for(int i = 0; i < 512; i++){
pte_t pte = pagetable[i];
if(pte & PTE_V){
pagetable[i] = 0;
if((pte & (PTE_R|PTE_W|PTE_X)) == 0){
uint64 child = PTE2PA(pte);
ukfreepgtb((pagetable_t)child);
}
}
}
kfree((void*)pagetable);
}
  1. kernel/proc.c 中修改某些函数的代码

procinit 中初始化 kstack 的代码移至 allocproc,然后在进程的内核页表对它建立映射

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
static struct proc*
allocproc(void)
{
// ...
// produce a kernel page table for a new process
p->kernel_pgtb = ukpgtb();
if(p->kernel_pgtb == 0){
freeproc(p);
release(&p->lock);
return 0;
}
// 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));
p->kstack = va;
// process's kernel page table has a mapping for that process's kernel stack
if(ukvmmap(p->kernel_pgtb, p->kstack, (uint64)pa, PGSIZE, PTE_R | PTE_W) != 0){
freeproc(p);
release(&p->lock);
return 0;
}
// ...
}
// ...
static void
freeproc(struct proc *p)
{
// ...
// 释放进程的内核栈和内核页表
if(p->kstack){
uvmunmap(p->kernel_pgtb, p->kstack, 1, 1);
}
p->kstack = 0;
if(p->kernel_pgtb)
ukfreepgtb(p->kernel_pgtb);
p->kernel_pgtb = 0;
// ...
}
// ...
void
scheduler(void)
{
// ...
// load process's kernel page table
w_satp(MAKE_SATP(p->kernel_pgtb));
sfence_vma();
swtch(&c->context, &p->context);
// change back to global kernel page table
kvminithart();
// ...
}

测试

1
2
make qemu
usertests

Simplify copyin/copyinstr (hard)

实验要求

  • copyin() 中直接调用 copyin_new();在 copyinstr() 中直接调用 copyinstr_new()
  • fork(), exec(), sark() 中,内核会改变进程的用户内存映射,以同样的方式同时改变进程的内核页表
  • userinit() 中,将进程的用户页表映射到它的内核页表上
  • 在进程的内核页表中,对于需要的用户地址,应该为 PTEs 设置什么访问类型?(内核模式下不能访问 PTE_U 类型的页表)=> 将 PTEs 的 PTE_U 设为 0
  • 进程的内存空间大小不能超过 PLIC

实验思路

  • copyin_new()copyinstr_new() 已经在 vmcopyin.c 中实现了,直接调用即可
  • 关键的是要实现一个函数:它会将进程的用户页表映射到进程的内核页表
  • 修改 exec(), userinit(), growproc(), fork() 中的代码,使得当进程的页表变化时,进程的内核页表也会更新

实验步骤

  1. kernel/defs.h 声明函数原型
1
2
3
4
5
// vm.c
int ukmap(pagetable_t, pagetable_t, uint64, uint64);
// vmcopy.c
int copyin_new(pagetable_t, char *, uint64, uint64);
int copyinstr_new(pagetable_t, char *, uint64, uint64);
  1. 修改 kernel/vm.c 中的 copyin()copyinstr() 函数,实现 ukmap
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
int
copyin(pagetable_t pagetable, char *dst, uint64 srcva, uint64 len)
{
// pagetable wasn't used
return copyin_new(pagetable, dst, srcva, len);
}
int
copyinstr(pagetable_t pagetable, char *dst, uint64 srcva, uint64 max)
{
// pagetable wasn't used
return copyinstr_new(pagetable, dst, srcva, max);
}


/*
* when kernel changes a process's user mapping,
* changes the process's kernel page table in the same
*/
int
ukmap(pagetable_t upagetable, pagetable_t ukpagetable, uint64 oldsz, uint64 newsz)
{
pte_t *upte, *kpte;
uint64 i;
// can not exceed PLIC
if((newsz < oldsz) || PGROUNDDOWN(newsz) >= PLIC)
return -1;

for(i = PGROUNDDOWN(oldsz); i < newsz; i += PGSIZE){
if((upte = walk(upagetable, i, 0)) == 0)
panic("ukmap: pte should exist");
if((kpte = walk(ukpagetable, i, 1)) == 0)
panic("ukmap: walk error");
*kpte = (*upte) & (~PTE_U); // mark page can be access in kernel
}
return 0;
}
  1. 修改 exec 函数
1
2
3
4
5
6
7
8
9
int
exec(char *path, char **argv)
{
// ...
// 在进程的内核页表添加进程页表的映射
if(ukmap(p->pagetable, p->kernel_pgtb, 0, p->sz) < 0)
goto bad;
// ...
}
  1. 修改 kernel/proc.c 中的 fork(), growproc(), userinit() 函数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
void
userinit(void)
{
// ...
p->sz = PGSIZE;
// (+)
ukmap(p->pagetable, p->kernel_pgtb, 0, p->sz);
// ...
}

int
growproc(int n)
{
// ...
if(n > 0){
if((sz = uvmalloc(p->pagetable, sz, sz + n)) == 0) {
return -1;
}
if(ukmap(p->pagetable, p->kernel_pgtb, sz - n, sz) == -1)
return -1;
} else if(n < 0){
sz = uvmdealloc(p->pagetable, sz, sz + n);
if(sz != PGSIZE)
uvmunmap(p->kernel_pgtb, PGROUNDUP(p->sz), (PGROUNDUP(sz) - PGROUNDUP(p->sz)) / PGSIZE, 0);
}
// ...
}

int
fork(void)
{
np->sz = p->sz;
// ...
if(ukmap(np->pagetable, np->kernel_pgtb, 0, np->sz) == -1){
freeproc(np);
release(&np->lock);
return -1;
}
// ...
}

测试

1
make grade

Diff

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
diff --git a/kernel/defs.h b/kernel/defs.h
index a73b4f7..a2b3b03 100644
--- a/kernel/defs.h
+++ b/kernel/defs.h
@@ -178,6 +178,14 @@ uint64 walkaddr(pagetable_t, uint64);
int copyout(pagetable_t, uint64, char *, uint64);
int copyin(pagetable_t, char *, uint64, uint64);
int copyinstr(pagetable_t, char *, uint64, uint64);
+void vmprint(pagetable_t); // define prototype for vmprint
+int ukvmmap(pagetable_t, uint64, uint64, uint64, int);
+pagetable_t ukpgtb();
+void ukfreepgtb(pagetable_t);
+int ukmap(pagetable_t, pagetable_t, uint64, uint64);
+// vmcopy.c
+int copyin_new(pagetable_t, char *, uint64, uint64);
+int copyinstr_new(pagetable_t, char *, uint64, uint64);

// plic.c
void plicinit(void);
diff --git a/kernel/exec.c b/kernel/exec.c
index 0e8762f..00cdf0e 100644
--- a/kernel/exec.c
+++ b/kernel/exec.c
@@ -115,7 +115,13 @@ exec(char *path, char **argv)
p->trapframe->epc = elf.entry; // initial program counter = main
p->trapframe->sp = sp; // initial stack pointer
proc_freepagetable(oldpagetable, oldsz);
-
+ if(ukmap(p->pagetable, p->kernel_pgtb, 0, p->sz) == -1)
+ goto bad;
+ // print the first process's page table before return argc
+ if (p->pid == 1){
+ printf("page table %p\n", p->pagetable);
+ vmprint(p->pagetable);
+ }
return argc; // this ends up in a0, the first argument to main(argc, argv)

bad:
diff --git a/kernel/proc.c b/kernel/proc.c
index dab1e1d..2ce7c6d 100644
--- a/kernel/proc.c
+++ b/kernel/proc.c
@@ -30,18 +30,8 @@ procinit(void)
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();
+ // kvminithart();
}

// Must be called with interrupts disabled,
@@ -121,6 +111,28 @@ found:
return 0;
}

+ // produce a kernel page table for a new process
+ p->kernel_pgtb = ukpgtb();
+ if(p->kernel_pgtb == 0){
+ freeproc(p);
+ release(&p->lock);
+ return 0;
+ }
+ // 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));
+ p->kstack = va;
+ // process's kernel page table has a mapping for that process's kernel stack
+ if(ukvmmap(p->kernel_pgtb, p->kstack, (uint64)pa, PGSIZE, PTE_R | PTE_W) != 0){
+ freeproc(p);
+ release(&p->lock);
+ return 0;
+ }
+
// Set up new context to start executing at forkret,
// which returns to user space.
memset(&p->context, 0, sizeof(p->context));
@@ -142,6 +154,15 @@ freeproc(struct proc *p)
if(p->pagetable)
proc_freepagetable(p->pagetable, p->sz);
p->pagetable = 0;
+
+ if(p->kstack){
+ uvmunmap(p->kernel_pgtb, p->kstack, 1, 1);
+ }
+ p->kstack = 0;
+ if(p->kernel_pgtb)
+ ukfreepgtb(p->kernel_pgtb);
+ p->kernel_pgtb = 0;
+
p->sz = 0;
p->pid = 0;
p->parent = 0;
@@ -220,7 +241,7 @@ userinit(void)
// and data into it.
uvminit(p->pagetable, initcode, sizeof(initcode));
p->sz = PGSIZE;
-
+ ukmap(p->pagetable, p->kernel_pgtb, 0, p->sz);
// prepare for the very first "return" from kernel to user.
p->trapframe->epc = 0; // user program counter
p->trapframe->sp = PGSIZE; // user stack pointer
@@ -246,8 +267,12 @@ growproc(int n)
if((sz = uvmalloc(p->pagetable, sz, sz + n)) == 0) {
return -1;
}
+ if(ukmap(p->pagetable, p->kernel_pgtb, sz - n, sz) < 0)
+ return -1;
} else if(n < 0){
sz = uvmdealloc(p->pagetable, sz, sz + n);
+ if(sz != PGSIZE)
+ uvmunmap(p->kernel_pgtb, PGROUNDUP(p->sz), (PGROUNDUP(sz) - PGROUNDUP(p->sz)) / PGSIZE, 0);
}
p->sz = sz;
return 0;
@@ -274,7 +299,11 @@ fork(void)
return -1;
}
np->sz = p->sz;
-
+ if(ukmap(np->pagetable, np->kernel_pgtb, 0, np->sz) == -1){
+ freeproc(np);
+ release(&np->lock);
+ return -1;
+ }
np->parent = p;

// copy saved user registers.
@@ -473,12 +502,16 @@ scheduler(void)
// before jumping back to us.
p->state = RUNNING;
c->proc = p;
+ // load process's kernel page table
+ w_satp(MAKE_SATP(p->kernel_pgtb));
+ sfence_vma();
swtch(&c->context, &p->context);

// Process is done running for now.
// It should have changed its p->state before coming back.
c->proc = 0;
-
+ // change back to global kernel page table
+ kvminithart();
found = 1;
}
release(&p->lock);
diff --git a/kernel/proc.h b/kernel/proc.h
index 9c16ea7..86a3fc4 100644
--- a/kernel/proc.h
+++ b/kernel/proc.h
@@ -103,4 +103,5 @@ struct proc {
struct file *ofile[NOFILE]; // Open files
struct inode *cwd; // Current directory
char name[16]; // Process name (debugging)
+ pagetable_t kernel_pgtb; // process's kernel page table
};
diff --git a/kernel/vm.c b/kernel/vm.c
index bccb405..76e843f 100644
--- a/kernel/vm.c
+++ b/kernel/vm.c
@@ -5,6 +5,8 @@
#include "riscv.h"
#include "defs.h"
#include "fs.h"
+#include "spinlock.h"
+#include "proc.h"

/*
* the kernel's page table.
@@ -132,7 +134,7 @@ kvmpa(uint64 va)
pte_t *pte;
uint64 pa;

- pte = walk(kernel_pagetable, va, 0);
+ pte = walk(myproc()->kernel_pgtb, va, 0);
if(pte == 0)
panic("kvmpa");
if((*pte & PTE_V) == 0)
@@ -379,23 +381,8 @@ copyout(pagetable_t pagetable, uint64 dstva, char *src, uint64 len)
int
copyin(pagetable_t pagetable, char *dst, uint64 srcva, uint64 len)
{
- uint64 n, va0, pa0;
-
- while(len > 0){
- va0 = PGROUNDDOWN(srcva);
- pa0 = walkaddr(pagetable, va0);
- if(pa0 == 0)
- return -1;
- n = PGSIZE - (srcva - va0);
- if(n > len)
- n = len;
- memmove(dst, (void *)(pa0 + (srcva - va0)), n);
-
- len -= n;
- dst += n;
- srcva = va0 + PGSIZE;
- }
- return 0;
+ // pagetable wasn't used
+ return copyin_new(pagetable, dst, srcva, len);
}

// Copy a null-terminated string from user to kernel.
@@ -405,38 +392,99 @@ copyin(pagetable_t pagetable, char *dst, uint64 srcva, uint64 len)
int
copyinstr(pagetable_t pagetable, char *dst, uint64 srcva, uint64 max)
{
- uint64 n, va0, pa0;
- int got_null = 0;
-
- while(got_null == 0 && max > 0){
- va0 = PGROUNDDOWN(srcva);
- pa0 = walkaddr(pagetable, va0);
- if(pa0 == 0)
- return -1;
- n = PGSIZE - (srcva - va0);
- if(n > max)
- n = max;
-
- char *p = (char *) (pa0 + (srcva - va0));
- while(n > 0){
- if(*p == '\0'){
- *dst = '\0';
- got_null = 1;
- break;
- } else {
- *dst = *p;
+ // pagetable wasn't used
+ return copyinstr_new(pagetable, dst, srcva, max);
+}
+void
+print(pagetable_t pagetable, int level){
+ // 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)){
+ uint64 child = PTE2PA(pte);
+ for(int l = 0; l < level; l++){
+ printf("..");
+ if(l != level + 1) printf(" ");
}
- --n;
- --max;
- p++;
- dst++;
+ printf("%d: pte %p pa %p\n", i, pte, child);
+ // this PTE points to a lower-level page table.
+ if((pte & (PTE_R|PTE_W|PTE_X)) == 0)
+ print((pagetable_t)child, level + 1);
}
-
- srcva = va0 + PGSIZE;
}
- if(got_null){
- return 0;
- } else {
+}
+// implemention the function vmprint
+void
+vmprint(pagetable_t pagetable)
+{
+ print(pagetable, 1);
+}
+/*
+ * use function of mappages build map for user's kernel page table
+ */
+int
+ukvmmap(pagetable_t pagetable, uint64 va, uint64 pa, uint64 sz, int perm)
+{
+ if(mappages(pagetable, va, sz, pa, perm) != 0)
return -1;
+ return 0;
+}
+/*
+ * make a new page table for the kernel
+ */
+pagetable_t
+ukpgtb()
+{
+ pagetable_t kernel_pgtb = (pagetable_t) kalloc();
+ memset(kernel_pgtb, 0, PGSIZE);
+ if(kernel_pgtb == 0) return 0;
+ // the same as kvminit()
+ ukvmmap(kernel_pgtb, UART0, UART0, PGSIZE, PTE_R | PTE_W);
+ ukvmmap(kernel_pgtb, VIRTIO0, VIRTIO0, PGSIZE, PTE_R | PTE_W);
+ ukvmmap(kernel_pgtb, CLINT, CLINT, 0x10000, PTE_R | PTE_W);
+ ukvmmap(kernel_pgtb, PLIC, PLIC, 0x400000, PTE_R | PTE_W);
+ ukvmmap(kernel_pgtb, KERNBASE, KERNBASE, (uint64)etext-KERNBASE, PTE_R | PTE_X);
+ ukvmmap(kernel_pgtb, (uint64)etext, (uint64)etext, PHYSTOP-(uint64)etext, PTE_R | PTE_W);
+ ukvmmap(kernel_pgtb, TRAMPOLINE, (uint64)trampoline, PGSIZE, PTE_R | PTE_X);
+ return kernel_pgtb;
+}
+/*
+ * free the process's kernel page table
+ */
+void
+ukfreepgtb(pagetable_t pagetable)
+{
+ for(int i = 0; i < 512; i++){
+ pte_t pte = pagetable[i];
+ if(pte & PTE_V){
+ pagetable[i] = 0;
+ if((pte & (PTE_R|PTE_W|PTE_X)) == 0){
+ uint64 child = PTE2PA(pte);
+ ukfreepgtb((pagetable_t)child);
+ }
+ }
+ }
+ kfree((void*)pagetable);
+}
+/*
+ * when kernel changes a process's user mapping,
+ * changes the process's kernel page table in the same
+ */
+int
+ukmap(pagetable_t upagetable, pagetable_t ukpagetable, uint64 oldsz, uint64 newsz)
+{
+ pte_t *upte, *kpte;
+ uint64 i;
+ // can not exceed PLIC
+ if(newsz < oldsz || (PGROUNDUP(newsz) >= PLIC))
+ return -1;
+
+ for(i = PGROUNDUP(oldsz); i < newsz; i += PGSIZE){
+ if((upte = walk(upagetable, i, 0)) == 0)
+ panic("ukmap: pte should exist");
+ if((kpte = walk(ukpagetable, i, 1)) == 0)
+ panic("ukmap: walk error");
+ *kpte = (*upte) & (~PTE_U); // mark page can be access in kernel
}
+ return 0;
}