实验地址:Lab2
Exercise 5. Fill in the missing code in mem_init() after the call to check_page().
Your code should now pass the check_kern_pgdir() and check_page_installed_pgdir() checks.
在mem_init()
函数里面添加相关代码,使之能够通过check_kern_pgdir()
和check_page_installed_pgdir()
里面的测试用例。
解答如下:
//
// Map 'pages' read-only by the user at linear address UPAGES
// Permissions:
// - the new image at UPAGES -- kernel R, user R
// (ie. perm = PTE_U | PTE_P)
// - pages itself -- kernel RW, user NONE
// Your code goes here:
boot_map_region(kern_pgdir,UPAGES,ROUNDUP((npages*sizeof(struct PageInfo)),PGSIZE),PADDR((void*)pages),PTE_U|PTE_P);
boot_map_region(kern_pgdir,(uintptr_t)pages,ROUNDUP((npages*sizeof(struct PageInfo)),PGSIZE),PADDR((void*)pages),PTE_W|PTE_P);
将UPAGES
和pages
这两个虚拟地址同时映射到PADDR(pages)
这个物理地址中,只不过由于添加的权限不同,USER只能够访问UPAGES
而不能够访问pages
。
//
// Use the physical memory that 'bootstack' refers to as the kernel
// stack. The kernel stack grows down from virtual address KSTACKTOP.
// We consider the entire range from [KSTACKTOP-PTSIZE, KSTACKTOP)
// to be the kernel stack, but break this into two pieces:
// * [KSTACKTOP-KSTKSIZE, KSTACKTOP) -- backed by physical memory
// * [KSTACKTOP-PTSIZE, KSTACKTOP-KSTKSIZE) -- not backed; so if
// the kernel overflows its stack, it will fault rather than
// overwrite memory. Known as a "guard page".
// Permissions: kernel RW, user NONE
// Your code goes here:
boot_map_region(kern_pgdir,KSTACKTOP-KSTKSIZE,KSTKSIZE,(physaddr_t)(PTE_ADDR(PADDR(bootstack))),PTE_W|PTE_P);
将KSTACKTOP
映射到bootstack
指向的物理地址中。需要注意的是,bootstack
本身是一个虚拟地址,需要进行物理地址变换。
//
// Map all of physical memory at KERNBASE.
// Ie. the VA range [KERNBASE, 2^32) should map to
// the PA range [0, 2^32 - KERNBASE)
// We might not have 2^32 - KERNBASE bytes of physical memory, but
// we just set up the mapping anyway.
// Permissions: kernel RW, user NONE
// Your code goes here:
boot_map_region(kern_pgdir,KERNBASE,ROUNDUP(((long long)1<<32)-KERNBASE,PGSIZE),0,PTE_W|PTE_P);
将KERNBASE
之上的虚拟地址映射到0
开始的物理地址空间,可能当前没有那么多物理地址,但是我们先进行映射。
需要注意的是boot_map_region()
函数不要写错:
static void
boot_map_region(pde_t *pgdir, uintptr_t va, size_t size, physaddr_t pa, int perm)
{
// Fill this function in
uintptr_t now_va;
uintptr_t now_pa;
int page_num = ROUNDUP(size,PGSIZE)/PGSIZE;
pte_t *pte;
now_va = va;
now_pa = pa;
for (int i=0; i<page_num; i++)
{
//下面两行是错误的代码,应该写在后面
//now_va += PGSIZE;
//now_pa += PGSIZE;
pte = pgdir_walk(pgdir,(void*)now_va,true);
if (!pte)
panic("in function boot_map_region:pte is null\n");
*pte = PTE_ADDR(now_pa) | perm | PTE_P;
now_va += PGSIZE;
now_pa += PGSIZE;
}
}
运行make;make qemu
得到如下结果:
qemu -drive file=obj/kern/kernel.img,index=0,media=disk,format=raw -serial mon:stdio -gdb tcp::26000 -D qemu.log
6828 decimal is 15254 octal!
Physical memory: 131072K available, base = 640K, extended = 130432K
check_page_free_list() succeeded!
check_page_alloc() succeeded!
check_page() succeeded!
check_kern_pgdir() succeeded!
check_page_free_list() succeeded!
check_page_installed_pgdir() succeeded!
Welcome to the JOS kernel monitor!
Type 'help' for a list of commands.
可以看到全部通过。最后进行make grade
评分:
running JOS: (1.1s)
Physical page allocator: OK
Page management: OK
Kernel page directory: OK
Page management 2: OK
Score: 70/70
下面对后面的思考题进行回答。
2.大致的表格如下
Entry | Base Virtual Address | Points to (logically): |
---|---|---|
UPAGES | 0xef000000 | pages |
KSTACKTOP-STKSIZE | 0xf0000000-STKSIZE | bootstack |
UVPT | 0xef400000 | kern_pgdir |
3.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?
答:由于用户和内核是运行在同一地址空间的,为了防止用户代码更改内核数据的情况,需要对内核代码数据区进行相关的保护。在软件层面,我们在kern_pgdir
里面将ULIM
之上的虚拟空间地址的页表项不添加PTE_U
,使用户不能够访问;在硬件层面,当用户访问ULIM
之上的空间时,CPU发现PTE_U
位为0
,就会报错。这样一来,就保护了内核的数据和代码。
4.What is the maximum amount of physical memory that this operating system can support? Why?
答:在硬件层面上,80386有32根地址线,能够寻址4G的物理地址空间,但是这不等于我们的系统就能够寻址4G的空间。内核用UPAGES
开始的PTSIZE
空间来存储物理页的信息。每个物理页信息占sizeof(struct PageInfo)=8B
的大小,所以共PTSIZE/8
个物理页,又每个物理页大小为4K
,所以总的大小为(PTSIZE/8)*4K=2G
5.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?
答:这个题目意思没太理解清楚。如果是说pages的话,那么就是npages*sizeof(struct PageInfo);如果是说kern_pgdir的话,那么就是PGSIZE这么大。
6.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?
答:在开启了分页之后,由于[0,4MB)和[KERNBASE,KERNBASE+4MB)都是映射到物理地址[0,4MB)的,所以在开启分页到跳转到高地址之间的语句能够正常执行。如果不把虚拟地址[0,4MB)映射到物理地址[0,4MB),就会出现崩溃的情况。执行跳转语句jmp *%eax
后,EIP到高低址空间执行。
Challenge略。
END.