[6.s081]Lab Page Tables
Overview
This lab teaches us the memory layout of xv6, how the kernel and user page table works and how to allocate pages when creating a new process. https://pdos.csail.mit.edu/6.S081/2021/labs/pgtbl.html
Speed up system calls
We can allocate a dedicated page when creating a process and write some data into it in the kernel mode for speeding up the system calls. This way we don't need to copy in/out the data between kernel and user space when do the system calls. Kind of trade space for time efficiency.
Allocate a Page when Creating the Process
static struct proc*
allocproc(void)
{
// ... some code
// Allocate a usyscall page.
if((p->usyscall = (uint64)kalloc()) == 0) {
freeproc(p);
release(&p->lock);
return 0;
}
u.pid = p->pid;
*(struct usyscall *)p->usyscall = u;
// ... some code
}
Free the Memory when Destroying a Process
static void
freeproc(struct proc *p)
{
// ... some code
if(p->usyscall)
kfree((void*)p->usyscall);
// ... some code
}
Map the Page when Initializing the Page Table
pagetable_t
proc_pagetable(struct proc *p)
{
// ... some code
if(mappages(pagetable, USYSCALL, PGSIZE, p->usyscall, PTE_R | PTE_W | PTE_U) < 0) {
uvmunmap(pagetable, TRAMPOLINE, 1, 0);
uvmunmap(pagetable, TRAPFRAME, 1, 0);
uvmfree(pagetable, 0);
}
return pagetable;
}
Print a page table
Traverse the page table like walk()
or freewalk()
function:
// vm.c
void
walk_print(pagetable_t page, int level)
{
for(int i = 0; i < 512; ++i) {
pte_t pte = page[i];
// child PTE exist and is also a PTE
if((pte & PTE_V) && (pte & (PTE_R | PTE_W | PTE_X)) == 0) {
uint64 child = PTE2PA(pte);
for(int j = 0; j < level; ++j) {
printf(".. ");
}
printf("..");
printf("%d: pte %p pa %p\n", i, pte, child);
walk_print((pagetable_t)child, level+1);
} else if(pte & PTE_V) {
uint64 child = PTE2PA(pte);
for(int j = 0; j < level; ++j) {
printf(".. ");
}
printf("..");
printf("%d: pte %p pa %p\n", i, pte, child);
}
}
}
void vmprint(pagetable_t pagetable)
{
printf("page table %p\n", pagetable);
walk_print(pagetable, 0);
}
Detecting which pages have been accessed
Iterate all the pages one by one to check the PTE_A bit, and remember to reset it after you get a postive results:
int
sys_pgaccess(void)
{
// lab pgtbl: your code here.
uint64 fva;
int pnum;
uint64 abits;
int res = 0;
pte_t* pte_addr;
pte_t pte;
// read the arguments from the stack
if(argaddr(0, &fva) < 0) {
return -1;
}
if(argint(1, &pnum) < 0) {
return -1;
}
if(argaddr(2, &abits) < 0) {
return -1;
}
pagetable_t pagetable = myproc()->pagetable;
for(int i = 0; i < pnum; ++i) {
pte_addr = walk(pagetable, fva, 0);
pte = *pte_addr;
if(pte & PTE_A) {
(*pte_addr) = pte & ~(PTE_A);
res |= (1 << i);
}
fva += PGSIZE;
}
if(copyout(pagetable, abits, (char *)&res, sizeof(res))<0) {
return -1;
}
return 0;
}