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;
}
这里注意判断存在引用或本来就是空闲界面时,不释放页面
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
,插入时分三种情况
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
否则之后会出错
这里显然是虚拟地址
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 |
通过权限控制,设置U/S = 0
可阻止用户程序访问内核内存
由于PAGES
的大小为4MB
而一个PageInfo
的大小为8Byte
,所以最多有524288
个页面,即最大内存为524288 * 4KB = 2GB
无法拥有最大内存,管理内存的开销为1
个一级页表与1024
个二级页表,再加上4MB
的PAGES
,即 1025 * 4KB + 4MB = 6100KB
在entry.S
中,指令
jmp *%eax
执行之后eip
位于KERNBASE
之上,在设置页表后还有一小段指令是运行在低地址的,可以运行的原因是页表中同时也把虚拟地址[0, 4MB)
映射到了物理地址[0, 4MB)
,这是必须的,不然会找不到地址
我的实现代码如下
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
位
首先在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]