6.828: Operating System Engineering (47)

ええと、直前エントリな理解だと

  • pgdir_walk でページフレームを一つ確保して page table entry にする
  • page_insert では page_walk で新規に確保した pte を適切に処理
    • page ディスクリプタの値に合わせた値を PTX(va) の位置に格納
    • permission の設定
    • 要件な記述の理解がイマイチ

とりあえず

だらだらと pgdir_walk から実装検討。なんとなく以下がでっち上がる。

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)];

    if (pte == NULL) {
        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;
    
}

例えば以下な試験に通るのかどうか。

    // check that pgdir_walk returns a pointer to the pte
    ptep = KADDR(PTE_ADDR(boot_pgdir[PDX(PGSIZE)]));
    assert(pgdir_walk(boot_pgdir, (void*)PGSIZE, 0) == ptep+PTX(PGSIZE));

これ、PDX(0x0) も PDX(PGSIZE) も同じ値なので 0x0 で page_insert した後は ptep は存在しとるはずなのですが、なんとなく return しなきゃいけないナニを間違えているような気がします。試験そのまんまな書き方してやろうかな。

    return pte + PTX(va);

試験に合わせて手続き定義を書く、ってのはどうなんだろ。でもある意味試験が仕様なので、楽っちゃ楽。

page_insert 手続き。なんとなーくざっくりなソレが以下?

int
page_insert(pde_t *pgdir, struct Page *pp, void *va, int perm) 
{
    // Fill this function in
    pte_t *pte = pgdir_walk(pgdir, va, ~0);
    if (pte == NULL)
        return -E_NO_MEM;

    *pte = page2pa(pp);
    *pte = *pte | perm | PTE_P;

    return 0;
}

コメントに要件が列挙されてます。

  • If there is already a page mapped at 'va', it should be page_remove()d.
  • If necessary, on demand, a page table should be allocated and inserted into 'pgdir'.
  • pp->pp_ref should be incremented if the insertion succeeds.
  • The TLB must be invalidated if a page was formerly present at 'va'.

ええと、スデに va に map されている page があるかどうかってどうやって調べるのかな。page_lookup ?
コメントが以下です。

// Return the page mapped at virtual address 'va'.
// If pte_store is not zero, then we store in it the address
// of the pte for this page.  This is used by page_remove and
// can be used to verify page permissions for syscall arguments,
// but should not be used by most callers.
//
// Return NULL if there is no page mapped at va.
//
// Hint: the TA solution uses pgdir_walk and pa2page.

と、いうことは

pte_t *pte;
if (page_lookup(boot_pgdir, va, &pte) != NULL)

な時の pte を page_remove に渡すの? page_remove のプロトタイプてきにはビンゴなカンジがしますがどうなのか。

void
page_remove(pde_t *pgdir, void *va)

先に page_lookup と page_remove を作らんと駄目だな。

とりあえず

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 ? pte : pa2page(pte);
}

微妙だけどプロトタイプってことでご容赦下さい。

page_remove

コメントが以下。

// Unmaps the physical page at virtual address 'va'.
// If there is no physical page at that address, silently does nothing.
//
// Details:
//   - The ref count on the physical page should decrement.
//   - The physical page should be freed if the refcount reaches 0.
//   - The pg table entry corresponding to 'va' should be set to 0.
//     (if such a PTE exists)
//   - The TLB must be invalidated if you remove an entry from
//     the pg dir/pg table.
//
// Hint: The TA solution is implemented using page_lookup,
// 	tlb_invalidate, and page_decref.

あら、tlb_invalidate とか page_decref とかが云々ってあるな。こいつらは実装済みって理解で良いのかな。
とりあえず pte と va の関係って何だったか、と言いつつ pgdir_walk の実装を再確認。あら? page_lookup 使えとか書いてあるな。
とりあえず以下を無理矢理でっち上げてみました。

void
page_remove(pde_t *pgdir, void *va)
{
    // Fill this function in
    pte_t *pte;
    struct Page *page = page_lookup(pgdir, va, &pte);

    page_decref(page);
    tlb_invalidate(pgdir, va);
}

とりあえず、検証は別途ってことで。あと、boot_map_segment 手続きの実装も必要ですが、今日は限界。リキが残ってれば試験します。