MIT 6.1810: page tables

警告
本文最后更新于 2023-03-16,文中内容可能已过时。

MIT 6.1810 中第三个 lab 的 solution

page tables

When each process is created, map one read-only page at USYSCALL (a virtual address defined in memlayout.h). At the start of this page, store a struct usyscall (also defined in memlayout.h), and initialize it to store the PID of the current process. For this lab, ugetpid() has been provided on the userspace side and will automatically use the USYSCALL mapping. You will receive full credit for this part of the lab if the ugetpid test case passes when running pgtbltest.

在kernel/proc.h中的proc结构体定义中新增一个变量:

1
struct usyscall *usc;        // stores Process ID to speed up getpid() syscall

在kernel/proc.c中的allocproc函数分配trapframe页的下面加上:

1
2
3
4
5
if ((p->usc = (struct usyscall *)kalloc()) == 0) {
    freeproc(p);
    release(&p->lock);
    return 0;
}
1
p->usc->pid = p->pid;

记得还得在freeproc中加上对应的free处理:

1
2
if(p->usc)
    kfree((void*)p->usc);

在proc_pagetable函数中添加分配USYSCALL页的处理逻辑。

1
2
3
4
5
6
if (mappages(pagetable, USYSCALL, PGSIZE,  (uint64)p->usc, PTE_R | PTE_U) < 0){
    uvmunmap(pagetable, TRAMPOLINE, 1, 0);
    uvmunmap(pagetable, TRAPFRAME, 1, 0);
    uvmfree(pagetable, 0);
    return 0;
}

在proc_freepagetable也加上对应的处理逻辑:

1
uvmunmap(pagetable, USYSCALL, 1, 0);

这些照着前面抄就完了()

Define a function called vmprint(). It should take a pagetable_t argument, and print that pagetable in the format described below. Insert if(p->pid==1) vmprint(p->pagetable) in exec.c just before the return argc, to print the first process’s page table. You receive full credit for this part of the lab if you pass the pte printout test of make grade.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
void
vmprint_core(pagetable_t pagetable, int depth)
{
  for(int i = 0; i < 512; i++) {
    pte_t pte = pagetable[i];
    if ((pte & PTE_V)) {
      for (int n = 1; n <= depth; n++)  printf(" ..");
      printf("%d: pte %p pa %p\n", i, pte, PTE2PA(pte));
      if (depth != 3) {
        uint64 child = PTE2PA(pte);
        vmprint_core((pagetable_t)child, depth + 1);
      }
    }
  }
}

// print detail of pagetable.
void
vmprint(pagetable_t pagetable)
{
  printf("page table %p\n", pagetable);
  vmprint_core(pagetable, 1);
}

Your job is to implement pgaccess(), a system call that reports which pages have been accessed. The system call takes three arguments. First, it takes the starting virtual address of the first user page to check. Second, it takes the number of pages to check. Finally, it takes a user address to a buffer to store the results into a bitmask (a datastructure that uses one bit per page and where the first page corresponds to the least significant bit). You will receive full credit for this part of the lab if the pgaccess test case passes when running pgtbltest.

和 lab2 不一样,这次的虽然需要实现一个系统调用,但是写 syscall 的那些准备工作(比如在user/user.h、user/usys.pl等文件写好syscall的信息)已经被写好了。

通过查看riscv-privileged文档的图4.18得知,PTE_A应该被设置为(1L « 6)。

1
#define PTE_A (1L << 6) // accessed

之后在kernel/sysproc.c中修改sys_pgaccess函数的内容:

 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
int
sys_pgaccess(void)
{
  // lab pgtbl: your code here.
  int pnum;
  uint64 addr;
  uint64 va;
  int pgmask = 0;
  struct proc *p = myproc();

  argaddr(0, &va);
  argint(1, &pnum);
  argaddr(2, &addr);

  if(pnum > 32)
    return -1;

  for(int i = 0; i < pnum; i++) {
    pte_t *pte = walk(p->pagetable, va, 0);
    if(*pte & PTE_A) {
      *pte &= ~PTE_A;
      pgmask |= (1 << i);
    }
    va += PGSIZE;
  }

  if(copyout(p->pagetable, addr, (char*)&pgmask, sizeof(pgmask)) == -1){
    return -1;
  }

  return 0;
}