6.828: Operating System Engineering (48)

なんとなく make qemu してみたらコンパイルでコケた。

pte_t *
pgdir_walk(pde_t *pgdir, const void *va, int create)
{
    // Fill this function in
    struct Page *page;
    pte_t *pte = pgdir[PDX(va)];

型が違うぞと。しかも良く見たらなんとなく微妙な雰囲気。

うーん

見れば見るほど昨晩でっち上げた pgdir_walk 手続きは微妙さ満点。てか、コンパイルが通らん時点でダウトっちゃダウトなのですが。
で、修正検討してみたのですが、とりあえずこうして

pte_t *
pgdir_walk(pde_t *pgdir, const void *va, int create)
{
    // Fill this function in
    struct Page *page;
    pte_t *pte;
    pde_t *pde = &pgdir[PDX(va)];

    if (!(*pde & PTE_P))
    	return NULL;

ええと、残りはこれで良いのかな。

    pte = (pte_t *)PTE_ADDR(*pde);

    if (pte == NULL || (!(*pte & PTE_P))) {
        if (create == 0)
            return NULL;
        
        if (page_alloc(&page) == -E_NO_MEM)
            return NULL;

        page->pp_ref = 1;
        memset(page2pa(page), 0, PGSIZE);
        pte[PTX(va)] = page2pa(page);
    }

    return pte + PTX(va);
    
}

うええ、気持ち悪い。扱ってるのがリニアアドレスなのか物理アドレスなのか分からん状態だし。上は物理アドレスな形になってます。なってないかも。
ええと、pte[PTX(va)] には phys addr が格納されているので、大丈夫か。
で、それを使う page_lookup 手続きが以下なカンジで修正すりゃ良いのかな。

struct Page *
page_lookup(pde_t *pgdir, void *va, pte_t **pte_store)
{
    // Fill this function in
    pte_t *pte = pgdir_walk(pgdir, va, 0);
    
    return pte == NULL ? NULL : pa2page((physaddr_t)*pte);
}

これでコンパイルはパスしたのですが、試験失敗。以下な assert でダウト認定。

    assert(page_insert(boot_pgdir, pp1, 0x0, 0) == 0);

よくよく考えてみたら page_insert は未完成。でも上の試験にはパスして欲しかったなぁ。てか色々見てみるに実装が非常に微妙であることが判明。
そもそも

    pte = (pte_t *)PTE_ADDR(*pde);

なソレが NULL なんだからそっちに値を入れなきゃ駄目でしょ的ソレ。で、printf やらなにやらを微妙に使って、パスする実装になったのですが、今度は

    assert(check_va2pa(boot_pgdir, 0x0) == page2pa(pp1));

な試験にパスしません。check_va2pa な実装をマネて以下な試験にパスしてはおりますが、

    // free pp0 and try again: pp0 should be used for page table
    page_free(pp0);
    assert(page_insert(boot_pgdir, pp1, 0x0, 0) == 0);
    assert(PTE_ADDR(boot_pgdir[0]) == page2pa(pp0));
    assert(check_va2pa(boot_pgdir, 0x0) == page2pa(pp1));
    assert(pp1->pp_ref == 1);
    assert(pp0->pp_ref == 1);

page_insert はまだ修正の余地あり。

その後

現実トウヒ気味にちょっとづつ進めて以下な試験にはパスしてます。

    // should be able to map pp2 at PGSIZE because pp0 is already allocated for page table
    assert(page_insert(boot_pgdir, pp2, (void*) PGSIZE, 0) == 0);
    assert(check_va2pa(boot_pgdir, PGSIZE) == page2pa(pp2));
    assert(pp2->pp_ref == 1);

    // should be no free memory
    assert(page_alloc(&pp) == -E_NO_MEM);

    // should be able to map pp2 at PGSIZE because it's already there
    assert(page_insert(boot_pgdir, pp2, (void*) PGSIZE, 0) == 0);
    assert(check_va2pa(boot_pgdir, PGSIZE) == page2pa(pp2));
    assert(pp2->pp_ref == 1);

いっちゃん最後の assert でオチてます。page_insert の実装が完了してないのが原因だと思われます。見直しというかコメントの通りに書いてないので。
とりあえず現状の pgdir_walk 手続きは以下なカンジ。

pte_t *
pgdir_walk(pde_t *pgdir, const void *va, int create)
{
    // Fill this function in
    struct Page *page;
    pte_t *pte, ret;
    pde_t *pde = &pgdir[PDX(va)];

    pte = (pte_t *)PTE_ADDR(*pde);

    if (pte == NULL) {
        if (create == 0) {
            return NULL;
        }
        
        if (page_alloc(&page) == -E_NO_MEM) {
            return NULL;
        }

        page->pp_ref = 1;
        memset((void *)page2pa(page), 0, PGSIZE);

        *pde = page2pa(page);
        *pde |= PTE_P;

        pte = (pte_t *)PTE_ADDR(*pde);
        *pte |= PTE_P;
    }

    return (pte_t *)KADDR(PTE_ADDR(pte)) + PTX(va);
}

どうもまだ KADDR を取り出して戻すあたりのイメージが微妙。