MIT 6.828 学习笔记4 Lab2实验报告

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/scnu20142005027/article/details/51292123

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映射内存,即0xA00000x100000
  • 用于存放物理页面信息的内存,即pagespages+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个二级页表,再加上4MBPAGES,即 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(),由于要使用PDEPS位,所以需要先开启CR4PSE位,然后便是每个PED表项对应4MB的内存,所以无需再分配二级页表
原本4KB的页面大小,需要64PDE表项和64 * 1024PTE表项,也就是大约256KB
现在4MB的页面大小,需要64PDE表项,也就是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;
}

刚开始简单的判断参数正确性
之后便是简单地从页表中读取相关信息并打印到屏幕上,需要注意4KB4MB的情况有所不同,因为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]

猜你喜欢

转载自blog.csdn.net/scnu20142005027/article/details/51292123