Lab2实验报告
Execrise 1
static void *boot_alloc(uint32_t n)
{
static char *nextfree;
char *result;
if (!nextfree) {
extern char end[];
nextfree = ROUNDUP((char *) end, PGSIZE);
}
result = nextfree;
nextfree = nextfree + ROUNDUP(n, PGSIZE);
return result;
}
nextfree
指向下一个空闲字节,而起始时,这个空闲字节位于内核程序的.bss
段后面
这里分配内存的方法就是简单地移动并返回指针
void mem_init(void)
{
// 缺少的部分
pages = (struct PageInfo *) boot_alloc(npages * sizeof(struct PageInfo));
memset(pages, 0, npages * sizeof(struct PageInfo));
}
分配npages * sizeof(struct PageInfo)
个字节用于存放物理页面信息
void page_init(void)
{
size_t left_i = PGNUM(IOPHYSMEM);
size_t right_i = PGNUM(PADDR(pages + npages));
for (size_t i = 1; i < npages; i++) {
if (left_i > i || i > right_i) {
pages[i].pp_link = page_free_list;
page_free_list = &pages[i];
}
}
}
在初始化物理页面信息时,注意如下已被占有的物理页面不能加入空闲链表
IDT
存放的位置,即第0
个页面IO
映射内存,即0xA0000
到0x100000
- 用于存放物理页面信息的内存,即
pages
到pages+npages
struct PageInfo *page_alloc(int alloc_flags)
{
if (page_free_list == NULL) {
return NULL;
}
struct PageInfo *pp = page_free_list;
page_free_list = page_free_list -> pp_link;
pp -> pp_link = NULL;
if (alloc_flags & ALLOC_ZERO) {
memset(page2kva(pp), 0, PGSIZE);
}
return pp;
}
这里注意不要增加引用计数,并需要判断没用空闲界面的情况
void page_free(struct PageInfo *pp)
{
if (pp -> pp_ref || pp -> pp_link) {
panic("free error!");
}
pp -> pp_link = page_free_list;
page_free_list = pp;
}
这里注意判断存在引用或本来就是空闲界面时,不释放页面
Execrise 4
pte_t *pgdir_walk(pde_t *pgdir, const void *va, int create)
{
pde_t pde = pgdir[PDX(va)];
if (!(pde & PTE_P)) {
if (!create) {
return NULL;
}
struct PageInfo *pp = page_alloc(true);
if (!pp) {
return NULL;
}
(pp -> pp_ref)++;
pgdir[PDX(va)] = page2pa(pp) | PTE_U | PTE_P | PTE_W;
return (pte_t *) page2kva(pp) + PTX(va);
}
return (pte_t *) KADDR(PTE_ADDR(pde)) + PTX(va);
}
当一级页表内不存在va
对应的表项时,需要根据create
判断是否要为其分配一个物理页面用作二级页表
这里需要设置权限,由于一级页表和二级页表都有权限控制,所以一般的做法是,放宽一级页表的权限,主要由二级页表来控制权限
还要注意,一级页表中存放的地址是物理地址,而返回的必须是虚拟地址且必须去掉权限位
static void boot_map_region(pde_t *pgdir, uintptr_t va, size_t size, physaddr_t pa, int perm)
{
for (uintptr_t end = va + size; va != end; pa += PGSIZE, va+= PGSIZE) {
pte_t *pte = pgdir_walk(pgdir, (void *) va, true);
*pte = pa | perm | PTE_P;
}
}
这个函数用于之后将一块虚拟地址映射到物理地址
struct PageInfo *page_lookup(pde_t *pgdir, void *va, pte_t **pte_store)
{
pte_t *pte = pgdir_walk(pgdir, va, false);
if (!pte || !(*pte & PTE_P)) {
return NULL;
}
if (pte_store != NULL) {
*pte_store = pte;
}
return pa2page(PTE_ADDR(*pte));
}
注意判断不存在表项,或表项内容不可用时,pte_store
用于存放指向表项的指针
void page_remove(pde_t *pgdir, void *va)
{
pte_t *pte;
struct PageInfo *pp = page_lookup(pgdir, va, &pte);
if (pp) {
page_decref(pp);
*pte = 0;
tlb_invalidate(pgdir, va);
}
}
这里需要将表项内容清零,并且清除TLB
中对应的内容
int page_insert(pde_t *pgdir, struct PageInfo *pp, void *va, int perm)
{
pte_t *pte = pgdir_walk(pgdir, va, true);
if (!pte) {
return -E_NO_MEM;
}
if (*pte & PTE_P) {
if (PTE_ADDR(*pte) == page2pa(pp)) {
*pte = page2pa(pp) | perm | PTE_P;
return 0;
}
page_remove(pgdir, va);
}
++(pp -> pp_ref);
*pte = page2pa(pp) | perm | PTE_P;
return 0;
}
分配页面失败时返回-E_NO_MEM
,插入时分三种情况
- 插入位置不存在页面,直接插入即可
- 插入位置存在不相同的页面,先移除该页面再插入
- 插入位置存在相同页面,设置权限即可
Execrise 5
void mem_init(void)
{
// 缺少的部分
boot_map_region(kern_pgdir, UPAGES, ROUNDUP(npages * sizeof(struct PageInfo), PGSIZE), PTSIZE, PTE_U | PTE_P);
boot_map_region(kern_pgdir, KSTACKTOP-KSTKSIZE, KSTKSIZE, PADDR(bootstack), PTE_W | PTE_P);
boot_map_region(kern_pgdir, KERNBASE, 1 << 28, 0, PTE_W | PTE_P);
}
这里需要设置PTE_W
否则之后会出错
Question
- Assuming that the following JOS kernel code is correct, what type should variable x have, uintptr_t or physaddr_t?
这里显然是虚拟地址
- What entries (rows) in the page directory have been filled in at this point? What addresses do they map and where do they point? In other words, fill out this table as much as possible:
Entry | Base Virtual Address | Points to (logically) |
---|---|---|
1023 | 0x003be000 | Page table for top 4MB of phys memory |
… | … | Page table for top 4MB of phys memory |
961 | 0x003fc000 | Page table for top 4MB of phys memory |
960 | 0x003ff000 | Page table for top 4MB of phys memory |
959 | 0x003fe000 | Kernel Stack & Invalid Memory |
958 | NULL | NULL |
957 | 0x00118000 | Page Table |
956 | 0x003fd000 | Read-Only PAGES |
955 | NULL | NULL |
… | … | NULL |
0 | NULL | NULL |
- We have placed the kernel and user environment in the same address space. Why will user programs not be able to read or write the kernel’s memory? What specific mechanisms protect the kernel memory?
通过权限控制,设置U/S = 0
可阻止用户程序访问内核内存
- What is the maximum amount of physical memory that this operating system can support? Why?
由于PAGES
的大小为4MB
而一个PageInfo
的大小为8Byte
,所以最多有524288
个页面,即最大内存为524288 * 4KB = 2GB
- How much space overhead is there for managing memory, if we actually had the maximum amount of physical memory? How is this overhead broken down?
无法拥有最大内存,管理内存的开销为1
个一级页表与1024
个二级页表,再加上4MB
的PAGES
,即 1025 * 4KB + 4MB = 6100KB
- Revisit the page table setup in kern/entry.S and kern/entrypgdir.c. Immediately after we turn on paging, EIP is still a low number (a little over 1MB). At what point do we transition to running at an EIP above KERNBASE? What makes it possible for us to continue executing at a low EIP between when we enable paging and when we begin running at an EIP above KERNBASE? Why is this transition necessary?
在entry.S
中,指令
jmp *%eax
执行之后eip
位于KERNBASE
之上,在设置页表后还有一小段指令是运行在低地址的,可以运行的原因是页表中同时也把虚拟地址[0, 4MB)
映射到了物理地址[0, 4MB)
,这是必须的,不然会找不到地址
Challenge
- We consumed many physical pages to hold the page tables for the KERNBASE mapping. Do a more space-efficient job using the PTE_PS (“Page Size”) bit in the page directory entries.
我的实现代码如下
void mem_init(void)
{
// boot_map_region(kern_pgdir, KERNBASE, 1 << 28, 0, PTE_W | PTE_P);
// 开启 cr4 PSE 位
uint32_t cr4;
cr4 = rcr4();
cr4 |= CR4_PSE;
lcr4(cr4);
// 设置 PDE
uintptr_t va = KERNBASE;
physaddr_t pa = 0;
for (size_t i = 0; i != 64; ++i) {
kern_pgdir[PDX(va)] = pa | PTE_W | PTE_P | PTE_PS;
va += PTSIZE;
pa += PTSIZE;
}
}
首先注释掉先前对于KERNBASE
以上的boot_map_region()
,由于要使用PDE
的PS
位,所以需要先开启CR4
的PSE
位,然后便是每个PED
表项对应4MB
的内存,所以无需再分配二级页表
原本4KB
的页面大小,需要64
个PDE
表项和64 * 1024
个PTE
表项,也就是大约256KB
现在4MB
的页面大小,需要64
个PDE
表项,也就是256B
注意,需要注释掉check_kern_pgdir()
,因为他会检查PTE
的相关信息,然而这部分没有PTE
在qemu
虚拟机中,输入指令info pg
,下面为截取片段
[f0000-f43ff] PDE[3c0-3d0] --SDA---WP 00000-043ff
[f4400-fffff] PDE[3d1-3ff] --S-----WP 04400-0ffff
可以看到,映射成功了,其中S
就是PS
位
- Extend the JOS kernel monitor with commands
首先在command
数组中,加入showmappings
,结果如下
static struct Command commands[] = {
{ "help", "Display this list of commands", mon_help },
{ "kerninfo", "Display information about the kernel", mon_kerninfo },
{ "showmappings", "Display information about physical page mappings", mon_showmappings }
};
接下来便是实现showmappings
函数
int
mon_showmappings(int args, char **argv, struct Trapframe *tf)
{
char flag[1 << 8] = {
[0] = '-',
[PTE_W] = 'W',
[PTE_U] = 'U',
[PTE_A] = 'A',
[PTE_D] = 'D',
[PTE_PS] = 'S'
};
char *arg1 = argv[1];
char *arg2 = argv[2];
char *arg3 = argv[3];
char *endptr;
if (arg1 == NULL || arg2 == NULL || arg3) {
cprintf("we need exactly two arguments!\n");
return 0;
}
uintptr_t va_l = strtol(arg1, &endptr, 16);
if (*endptr) {
cprintf("argument's format error!\n");
return 0;
}
uintptr_t va_r = strtol(arg2, &endptr, 16);
if (*endptr) {
cprintf("argument's format error!\n");
return 0;
}
if (va_l > va_r) {
cprintf("the first argument should not larger than the second argument!\n");
return 0;
}
pde_t *pgdir = (pde_t *) PGADDR(PDX(UVPT), PDX(UVPT), 0); // 这里直接用 kern_pgdir 也可以
cprintf(" va range entry flag pa range \n");
cprintf("---------------------------------------------------------------\n");
while (va_l <= va_r) {
pde_t pde = pgdir[PDX(va_l)];
if (pde & PTE_P) {
char bit_w = flag[pde & PTE_W];
char bit_u = flag[pde & PTE_U];
char bit_a = flag[pde & PTE_A];
char bit_d = flag[pde & PTE_D];
char bit_s = flag[pde & PTE_PS];
pde = PTE_ADDR(pde);
if (va_l < KERNBASE) {
cprintf("[%08x - %08x]", va_l, va_l + PTSIZE - 1);
cprintf(" PDE[%03x] --%c%c%c--%c%cP\n", PDX(va_l), bit_s, bit_d, bit_a, bit_u, bit_w);
pte_t *pte = (pte_t *) (pde + KERNBASE);
for (size_t i = 0; i != 1024 && va_l <= va_r; va_l += PGSIZE, ++i) {
if (pte[i] & PTE_P) {
bit_w = flag[pte[i] & PTE_W];
bit_u = flag[pte[i] & PTE_U];
bit_a = flag[pte[i] & PTE_A];
bit_d = flag[pte[i] & PTE_D];
bit_s = flag[pte[i] & PTE_PS];
cprintf(" |-[%08x - %08x]", va_l, va_l + PGSIZE - 1);
cprintf(" PTE[%03x] --%c%c%c--%c%cP", i, bit_s, bit_d, bit_a, bit_u, bit_w);
cprintf(" [%08x - %08x]\n", PTE_ADDR(pte[i]), PTE_ADDR(pte[i]) + PGSIZE - 1);
}
}
continue;
}
cprintf("[%08x - %08x]", va_l, va_l + PTSIZE - 1, PDX(va_l));
cprintf(" PDE[%03x] --%c%c%c--%c%cP", PDX(va_l), bit_s, bit_d, bit_a, bit_u, bit_w);
cprintf(" [%08x - %08x]\n", pde, pde + PTSIZE - 1);
if (va_l == 0xffc00000) {
break;
}
}
va_l += PTSIZE;
}
return 0;
}
刚开始简单的判断参数正确性
之后便是简单地从页表中读取相关信息并打印到屏幕上,需要注意4KB
与4MB
的情况有所不同,因为4MB
页面不存在PTE
运行结果如下
K> showmappings ef400000 f0000000
va range entry flag pa range
---------------------------------------------------------------
[ef400000 - ef7fffff] PDE[3bd] ----A--U-P
|-[ef7bc000 - ef7bcfff] PTE[3bc] -------UWP [003fd000 - 003fdfff]
|-[ef7bd000 - ef7bdfff] PTE[3bd] ----A--U-P [00118000 - 00118fff]
|-[ef7bf000 - ef7bffff] PTE[3bf] -------UWP [003fe000 - 003fefff]
|-[ef7c0000 - ef7c0fff] PTE[3c0] --SDA---WP [00000000 - 00000fff]
|-[ef7c1000 - ef7c1fff] PTE[3c1] --SDA---WP [00400000 - 00400fff]
|-[ef7c2000 - ef7c2fff] PTE[3c2] --SDA---WP [00800000 - 00800fff]
|-[ef7c3000 - ef7c3fff] PTE[3c3] --SDA---WP [00c00000 - 00c00fff]
|-[ef7c4000 - ef7c4fff] PTE[3c4] --SDA---WP [01000000 - 01000fff]
|-[ef7c5000 - ef7c5fff] PTE[3c5] --SDA---WP [01400000 - 01400fff]
|-[ef7c6000 - ef7c6fff] PTE[3c6] --SDA---WP [01800000 - 01800fff]
|-[ef7c7000 - ef7c7fff] PTE[3c7] --SDA---WP [01c00000 - 01c00fff]
|-[ef7c8000 - ef7c8fff] PTE[3c8] --SDA---WP [02000000 - 02000fff]
|-[ef7c9000 - ef7c9fff] PTE[3c9] --SDA---WP [02400000 - 02400fff]
|-[ef7ca000 - ef7cafff] PTE[3ca] --SDA---WP [02800000 - 02800fff]
|-[ef7cb000 - ef7cbfff] PTE[3cb] --SDA---WP [02c00000 - 02c00fff]
|-[ef7cc000 - ef7ccfff] PTE[3cc] --SDA---WP [03000000 - 03000fff]
|-[ef7cd000 - ef7cdfff] PTE[3cd] --SDA---WP [03400000 - 03400fff]
|-[ef7ce000 - ef7cefff] PTE[3ce] --SDA---WP [03800000 - 03800fff]
|-[ef7cf000 - ef7cffff] PTE[3cf] --SDA---WP [03c00000 - 03c00fff]
|-[ef7d0000 - ef7d0fff] PTE[3d0] --SDA---WP [04000000 - 04000fff]
|-[ef7d1000 - ef7d1fff] PTE[3d1] --S-----WP [04400000 - 04400fff]
|-[ef7d2000 - ef7d2fff] PTE[3d2] --S-----WP [04800000 - 04800fff]
|-[ef7d3000 - ef7d3fff] PTE[3d3] --S-----WP [04c00000 - 04c00fff]
|-[ef7d4000 - ef7d4fff] PTE[3d4] --S-----WP [05000000 - 05000fff]
|-[ef7d5000 - ef7d5fff] PTE[3d5] --S-----WP [05400000 - 05400fff]
|-[ef7d6000 - ef7d6fff] PTE[3d6] --S-----WP [05800000 - 05800fff]
|-[ef7d7000 - ef7d7fff] PTE[3d7] --S-----WP [05c00000 - 05c00fff]
|-[ef7d8000 - ef7d8fff] PTE[3d8] --S-----WP [06000000 - 06000fff]
|-[ef7d9000 - ef7d9fff] PTE[3d9] --S-----WP [06400000 - 06400fff]
|-[ef7da000 - ef7dafff] PTE[3da] --S-----WP [06800000 - 06800fff]
|-[ef7db000 - ef7dbfff] PTE[3db] --S-----WP [06c00000 - 06c00fff]
|-[ef7dc000 - ef7dcfff] PTE[3dc] --S-----WP [07000000 - 07000fff]
|-[ef7dd000 - ef7ddfff] PTE[3dd] --S-----WP [07400000 - 07400fff]
|-[ef7de000 - ef7defff] PTE[3de] --S-----WP [07800000 - 07800fff]
|-[ef7df000 - ef7dffff] PTE[3df] --S-----WP [07c00000 - 07c00fff]
|-[ef7e0000 - ef7e0fff] PTE[3e0] --S-----WP [08000000 - 08000fff]
|-[ef7e1000 - ef7e1fff] PTE[3e1] --S-----WP [08400000 - 08400fff]
|-[ef7e2000 - ef7e2fff] PTE[3e2] --S-----WP [08800000 - 08800fff]
|-[ef7e3000 - ef7e3fff] PTE[3e3] --S-----WP [08c00000 - 08c00fff]
|-[ef7e4000 - ef7e4fff] PTE[3e4] --S-----WP [09000000 - 09000fff]
|-[ef7e5000 - ef7e5fff] PTE[3e5] --S-----WP [09400000 - 09400fff]
|-[ef7e6000 - ef7e6fff] PTE[3e6] --S-----WP [09800000 - 09800fff]
|-[ef7e7000 - ef7e7fff] PTE[3e7] --S-----WP [09c00000 - 09c00fff]
|-[ef7e8000 - ef7e8fff] PTE[3e8] --S-----WP [0a000000 - 0a000fff]
|-[ef7e9000 - ef7e9fff] PTE[3e9] --S-----WP [0a400000 - 0a400fff]
|-[ef7ea000 - ef7eafff] PTE[3ea] --S-----WP [0a800000 - 0a800fff]
|-[ef7eb000 - ef7ebfff] PTE[3eb] --S-----WP [0ac00000 - 0ac00fff]
|-[ef7ec000 - ef7ecfff] PTE[3ec] --S-----WP [0b000000 - 0b000fff]
|-[ef7ed000 - ef7edfff] PTE[3ed] --S-----WP [0b400000 - 0b400fff]
|-[ef7ee000 - ef7eefff] PTE[3ee] --S-----WP [0b800000 - 0b800fff]
|-[ef7ef000 - ef7effff] PTE[3ef] --S-----WP [0bc00000 - 0bc00fff]
|-[ef7f0000 - ef7f0fff] PTE[3f0] --S-----WP [0c000000 - 0c000fff]
|-[ef7f1000 - ef7f1fff] PTE[3f1] --S-----WP [0c400000 - 0c400fff]
|-[ef7f2000 - ef7f2fff] PTE[3f2] --S-----WP [0c800000 - 0c800fff]
|-[ef7f3000 - ef7f3fff] PTE[3f3] --S-----WP [0cc00000 - 0cc00fff]
|-[ef7f4000 - ef7f4fff] PTE[3f4] --S-----WP [0d000000 - 0d000fff]
|-[ef7f5000 - ef7f5fff] PTE[3f5] --S-----WP [0d400000 - 0d400fff]
|-[ef7f6000 - ef7f6fff] PTE[3f6] --S-----WP [0d800000 - 0d800fff]
|-[ef7f7000 - ef7f7fff] PTE[3f7] --S-----WP [0dc00000 - 0dc00fff]
|-[ef7f8000 - ef7f8fff] PTE[3f8] --S-----WP [0e000000 - 0e000fff]
|-[ef7f9000 - ef7f9fff] PTE[3f9] --S-----WP [0e400000 - 0e400fff]
|-[ef7fa000 - ef7fafff] PTE[3fa] --S-----WP [0e800000 - 0e800fff]
|-[ef7fb000 - ef7fbfff] PTE[3fb] --S-----WP [0ec00000 - 0ec00fff]
|-[ef7fc000 - ef7fcfff] PTE[3fc] --S-----WP [0f000000 - 0f000fff]
|-[ef7fd000 - ef7fdfff] PTE[3fd] --S-----WP [0f400000 - 0f400fff]
|-[ef7fe000 - ef7fefff] PTE[3fe] --S-----WP [0f800000 - 0f800fff]
|-[ef7ff000 - ef7fffff] PTE[3ff] --S-----WP [0fc00000 - 0fc00fff]
[efc00000 - efffffff] PDE[3bf] -------UWP
|-[efff8000 - efff8fff] PTE[3f8] --------WP [0010d000 - 0010dfff]
|-[efff9000 - efff9fff] PTE[3f9] --------WP [0010e000 - 0010efff]
|-[efffa000 - efffafff] PTE[3fa] --------WP [0010f000 - 0010ffff]
|-[efffb000 - efffbfff] PTE[3fb] --------WP [00110000 - 00110fff]
|-[efffc000 - efffcfff] PTE[3fc] --------WP [00111000 - 00111fff]
|-[efffd000 - efffdfff] PTE[3fd] --------WP [00112000 - 00112fff]
|-[efffe000 - efffefff] PTE[3fe] --------WP [00113000 - 00113fff]
|-[effff000 - efffffff] PTE[3ff] --------WP [00114000 - 00114fff]
[f0000000 - f03fffff] PDE[3c0] --SDA---WP [00000000 - 003fffff]