Skip to content

Commit

Permalink
lab page tables all done
Browse files Browse the repository at this point in the history
  • Loading branch information
slk000 committed Jul 14, 2022
1 parent 5da5523 commit e3df478
Show file tree
Hide file tree
Showing 6 changed files with 140 additions and 32 deletions.
10 changes: 10 additions & 0 deletions answers-pgtbl.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
levels pte pa user mode privilege
..0: pte 0x0000000021fda801 pa 0x0000000087f6a000 V
.. ..0: pte 0x0000000021fda401 pa 0x0000000087f69000 V
.. .. ..0: pte 0x0000000021fdac1f pa 0x0000000087f6b000 V R W X U <--- text & data
.. .. ..1: pte 0x0000000021fda00f pa 0x0000000087f68000 V R W X <--- guard page
.. .. ..2: pte 0x0000000021fd9c1f pa 0x0000000087f67000 V R W X U <--- user stack
..255: pte 0x0000000021fdb401 pa 0x0000000087f6d000 V
.. ..511: pte 0x0000000021fdb001 pa 0x0000000087f6c000 V
.. .. ..510: pte 0x0000000021fdd807 pa 0x0000000087f76000 V R W
.. .. ..511: pte 0x0000000020001c0b pa 0x0000000080007000 V R X
9 changes: 8 additions & 1 deletion kernel/defs.h
Original file line number Diff line number Diff line change
Expand Up @@ -169,10 +169,12 @@ pagetable_t uvmcreate(void);
void uvminit(pagetable_t, uchar *, uint);
uint64 uvmalloc(pagetable_t, uint64, uint64);
uint64 uvmdealloc(pagetable_t, uint64, uint64);
uint64 kvmdealloc(pagetable_t, uint64, uint64);
#ifdef SOL_COW
#else
int uvmcopy(pagetable_t, pagetable_t, uint64);
#endif
int kptcopy(pagetable_t, pagetable_t, uint64, uint64);
void uvmfree(pagetable_t, uint64);
void uvmunmap(pagetable_t, uint64, uint64, int);
void uvmclear(pagetable_t, uint64);
Expand All @@ -181,7 +183,12 @@ 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);
pagetable_t uktblinit(struct proc *);
pagetable_t uktblinit(pagetable_t);

// vmcopyin.c
int statscopyin(char *, int);
int copyin_new(pagetable_t, char *, uint64, uint64);
int copyinstr_new(pagetable_t, char *, uint64, uint64);

// plic.c
void plicinit(void);
Expand Down
10 changes: 10 additions & 0 deletions kernel/exec.c
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ exec(char *path, char **argv)

if((pagetable = proc_pagetable(p)) == 0)
goto bad;
// do not need to allocate a new kernel pagetable,
// only need to unmap old entries in the previous one.(before exec() return)

// Load program into memory.
for(i=0, off=elf.phoff; i<elf.phnum; i++, off+=sizeof(ph)){
Expand All @@ -48,6 +50,8 @@ exec(char *path, char **argv)
goto bad;
if(ph.vaddr + ph.memsz < ph.vaddr)
goto bad;
if(ph.vaddr + ph.memsz >= PLIC)
goto bad;
uint64 sz1;
if((sz1 = uvmalloc(pagetable, sz, ph.vaddr + ph.memsz)) == 0)
goto bad;
Expand Down Expand Up @@ -116,6 +120,12 @@ exec(char *path, char **argv)
p->trapframe->sp = sp; // initial stack pointer
proc_freepagetable(oldpagetable, oldsz);

// unmap the mappings which are copied from user's pagetable (vaddr 0~user proc size),
// do not touch the kernel part(do not create new kernel pagetable)
// in case of destroying it.
uvmunmap(p->kpagetable, 0, PGROUNDUP(oldsz)/PGSIZE, 0);
kptcopy(p->pagetable, p->kpagetable, 0, p->sz);

if(p->pid==1) vmprint(p->pagetable);
return argc; // this ends up in a0, the first argument to main(argc, argv)

Expand Down
68 changes: 47 additions & 21 deletions kernel/proc.c
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ allocproc(void)
release(&p->lock);
return 0;
}
uktblinit(p);
uktblinit(p->kpagetable);
char *pa = kalloc();
if(pa == 0)
panic("kalloc");
Expand Down Expand Up @@ -158,25 +158,30 @@ freeproc(struct proc *p)
if(p->pagetable)
proc_freepagetable(p->pagetable, p->sz);
p->pagetable = 0;
p->sz = 0;
p->pid = 0;
p->parent = 0;
p->name[0] = 0;
p->chan = 0;
p->killed = 0;
p->xstate = 0;
// free kernel stack

// free kernel stack first before freeing kpagetable
if(p->kstack) {
uvmunmap(p->kpagetable, p->kstack, 1, 1);
}
p->kstack = 0;

// free kernel page table without freeing physical memory
if(p->kpagetable) {
// pagetable_t tmp = p->kpagetable;
proc_freekpagetable(p->kpagetable);
// vmprint(tmp);
}

p->kpagetable = 0;
p->sz = 0;
p->pid = 0;
p->parent = 0;
p->name[0] = 0;
p->chan = 0;
p->killed = 0;
p->xstate = 0;


p->state = UNUSED;
}

Expand Down Expand Up @@ -227,18 +232,18 @@ proc_freepagetable(pagetable_t pagetable, uint64 sz)
void
proc_freekpagetable(pagetable_t kpagetable)
{
// unmap first
uvmunmap(kpagetable, UART0, 1, 0);
uvmunmap(kpagetable, VIRTIO0, 1, 0);
uvmunmap(kpagetable, CLINT, 0x10000 / PGSIZE, 0);
uvmunmap(kpagetable, PLIC, 0x400000 / PGSIZE, 0);
uvmunmap(kpagetable, KERNBASE, (PHYSTOP - KERNBASE) / PGSIZE, 0);
// is trapframe necessary for every kernel pagetable?
uvmunmap(kpagetable, TRAPFRAME, 1, 0); // don't forget unmap this since I use proc_pagetable() to create the pagetable where maps TRAPFRAME
uvmunmap(kpagetable, TRAMPOLINE, 1, 0);

// free the pagetable itself, not the kernel memory.
uvmfree(kpagetable, 0);
// there are 2^9 = 512 PTEs in a page table.
for(int i = 0; i < 512; i++){
pte_t pte = kpagetable[i];
if(pte & PTE_V){
kpagetable[i] = 0;
if((pte & (PTE_R|PTE_W|PTE_X)) == 0){
uint64 child = PTE2PA(pte);
proc_freekpagetable((pagetable_t)child);
}
}
}
kfree((void*)kpagetable);
}

// a user program that calls exec("/init")
Expand Down Expand Up @@ -266,6 +271,7 @@ userinit(void)
// and data into it.
uvminit(p->pagetable, initcode, sizeof(initcode));
p->sz = PGSIZE;
mappages(p->kpagetable, 0, PGSIZE, walkaddr(p->pagetable, 0), PTE_W|PTE_R|PTE_X);

// prepare for the very first "return" from kernel to user.
p->trapframe->epc = 0; // user program counter
Expand All @@ -289,11 +295,21 @@ growproc(int n)

sz = p->sz;
if(n > 0){
if(sz + n >= PLIC){
return -1;
}
// if sz+n do not exceed the same page, uvmalloc will do nothing.
if((sz = uvmalloc(p->pagetable, sz, sz + n)) == 0) {
return -1;
}
// USE p->sz, NOT sz
if(kptcopy(p->pagetable, p->kpagetable, p->sz, p->sz+n) < 0){
uvmdealloc(p->pagetable, sz, p->sz); // remember to dealloc user memory when kptcopy fails
return -1;
}
} else if(n < 0){
sz = uvmdealloc(p->pagetable, sz, sz + n);
kvmdealloc(p->kpagetable, p->sz, p->sz+n);
}
p->sz = sz;
return 0;
Expand Down Expand Up @@ -323,6 +339,16 @@ fork(void)

np->parent = p;

// printf("old kpgtbl: %p new :%p\n", p->kpagetable, np->kpagetable);
// printf("dump of old kpgtbl:\n"); vmprint(p->kpagetable);
// Copy user pagetable into kernel pagetable
// if(kptcopy(p->pagetable, np->kpagetable, 0, np->sz) < 0){ // <- VERY BAD
if(kptcopy(np->pagetable, np->kpagetable, 0, np->sz) < 0){
freeproc(np);
release(&np->lock);
return -1;
}

// copy saved user registers.
*(np->trapframe) = *(p->trapframe);

Expand Down
74 changes: 64 additions & 10 deletions kernel/vm.c
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ kvminit()
kvmmap(VIRTIO0, VIRTIO0, PGSIZE, PTE_R | PTE_W);

// CLINT
// lab pgtbl-simplify: "After the kernel has booted, that address is 0xC000000 in xv6, the address of the PLIC registers"
// what about CLINT? unmap in process's kernel pagetable, keep it in the global kernel pagetable
kvmmap(CLINT, CLINT, 0x10000, PTE_R | PTE_W);

// PLIC
Expand All @@ -53,33 +55,34 @@ kvminit()
* create user kernel pagetable
*/
pagetable_t
uktblinit(struct proc *p)
uktblinit(pagetable_t kpagetable)
{
// p->kpagetable = uvmcreate();

// uart registers
uvmmap(p->kpagetable, UART0, UART0, PGSIZE, PTE_R | PTE_W);
uvmmap(kpagetable, UART0, UART0, PGSIZE, PTE_R | PTE_W);

// virtio mmio disk interface
uvmmap(p->kpagetable, VIRTIO0, VIRTIO0, PGSIZE, PTE_R | PTE_W);
uvmmap(kpagetable, VIRTIO0, VIRTIO0, PGSIZE, PTE_R | PTE_W);

// CLINT
uvmmap(p->kpagetable, CLINT, CLINT, 0x10000, PTE_R | PTE_W);
// lab pgtbl-simplify: "After the kernel has booted, that address is 0xC000000 in xv6, the address of the PLIC registers"
// what about CLINT? unmap in process's kernel pagetable, keep it in the global kernel pagetable
// uvmmap(kpagetable, CLINT, CLINT, 0x10000, PTE_R | PTE_W);

// PLIC
uvmmap(p->kpagetable, PLIC, PLIC, 0x400000, PTE_R | PTE_W);
uvmmap(kpagetable, PLIC, PLIC, 0x400000, PTE_R | PTE_W);

// map kernel text executable and read-only.
uvmmap(p->kpagetable, KERNBASE, KERNBASE, (uint64)etext-KERNBASE, PTE_R | PTE_X);
uvmmap(kpagetable, KERNBASE, KERNBASE, (uint64)etext-KERNBASE, PTE_R | PTE_X);

// map kernel data and the physical RAM we'll make use of.
uvmmap(p->kpagetable, (uint64)etext, (uint64)etext, PHYSTOP-(uint64)etext, PTE_R | PTE_W);
uvmmap(kpagetable, (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.
// TRAMPOLINE is mapped in kernel/proc.c:proc_pagetable()

return p->kpagetable;
return kpagetable;
}

// Switch h/w page table register to the kernel's page table,
Expand Down Expand Up @@ -200,8 +203,10 @@ mappages(pagetable_t pagetable, uint64 va, uint64 size, uint64 pa, int perm)
for(;;){
if((pte = walk(pagetable, a, 1)) == 0)
return -1;
if(*pte & PTE_V)
if(*pte & PTE_V){
// printf("map va%p -> pa%p in tbl %p\n",a,pa,pagetable);
panic("remap");
}
*pte = PA2PTE(pa) | perm | PTE_V;
if(a == last)
break;
Expand Down Expand Up @@ -313,6 +318,21 @@ uvmdealloc(pagetable_t pagetable, uint64 oldsz, uint64 newsz)
return newsz;
}

// shrink the kpagetable without freeing physical memory
// copied from uvmdealloc
uint64
kvmdealloc(pagetable_t kpagetable, uint64 oldsz, uint64 newsz)
{
if(newsz >= oldsz)
return oldsz;

if(PGROUNDUP(newsz) < PGROUNDUP(oldsz)){
int npages = (PGROUNDUP(oldsz) - PGROUNDUP(newsz)) / PGSIZE;
uvmunmap(kpagetable, PGROUNDUP(newsz), npages, 0); // 0 = not deallocate memory
}

return newsz;
}
// Recursively free page-table pages.
// All leaf mappings must already have been removed.
void
Expand Down Expand Up @@ -379,6 +399,34 @@ uvmcopy(pagetable_t old, pagetable_t new, uint64 sz)
return -1;
}

// copy pagetable from ENTIRE (vaddr ranged in begin&end) user pagetable into user's kernel pagetable
// begin & end: do not need to be aligned. copy pagetable entries that convering vaddr from begin to end
// code copied from uvmcopy()
int
kptcopy(pagetable_t pagetable, pagetable_t kpagetable, uint64 begin, uint64 end)
{
pte_t *pte;
uint64 pa, va = PGROUNDUP(begin);
uint flags;

for(; va < end; va += PGSIZE){
if((pte = walk(pagetable, va, 0)) == 0)
panic("kptcopy: pte should exist");
if((*pte & PTE_V) == 0)
panic("kptcopy: page not present");
pa = PTE2PA(*pte);
flags = PTE_FLAGS(*pte) & (~PTE_U); // unset PTE_U, to allow kernel access this page
if(mappages(kpagetable, va, PGSIZE, pa, flags) != 0){
goto err;
}
}
return 0;

err:
// pay attention to the range being unmapped.
uvmunmap(kpagetable, PGROUNDUP(begin), (va-PGROUNDUP(begin)) / PGSIZE, 0);
return -1;
}
// mark a PTE invalid for user access.
// used by exec for the user stack guard page.
void
Expand Down Expand Up @@ -423,6 +471,8 @@ copyout(pagetable_t pagetable, uint64 dstva, char *src, uint64 len)
int
copyin(pagetable_t pagetable, char *dst, uint64 srcva, uint64 len)
{
return copyin_new(0, dst, srcva, len);
/*
uint64 n, va0, pa0;
while(len > 0){
Expand All @@ -440,6 +490,7 @@ copyin(pagetable_t pagetable, char *dst, uint64 srcva, uint64 len)
srcva = va0 + PGSIZE;
}
return 0;
*/
}

// Copy a null-terminated string from user to kernel.
Expand All @@ -449,6 +500,8 @@ copyin(pagetable_t pagetable, char *dst, uint64 srcva, uint64 len)
int
copyinstr(pagetable_t pagetable, char *dst, uint64 srcva, uint64 max)
{
return copyinstr_new(0, dst, srcva, max);
/*
uint64 n, va0, pa0;
int got_null = 0;
Expand Down Expand Up @@ -483,6 +536,7 @@ copyinstr(pagetable_t pagetable, char *dst, uint64 srcva, uint64 max)
} else {
return -1;
}
*/
}

/*
Expand Down
1 change: 1 addition & 0 deletions time.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
24

0 comments on commit e3df478

Please sign in to comment.