vm.c pde_t* copyuvm(pde_t *pgdir, uint sz)
トップページ
jupiteroak.hatenablog.com
vm.c
https://github.com/mit-pdos/xv6-public/blob/master/vm.c#L315
pde_t* copyuvm(pde_t *pgdir, uint sz) { pde_t *d; pte_t *pte; uint pa, i, flags; char *mem; if((d = setupkvm()) == 0) return 0; for(i = 0; i < sz; i += PGSIZE){ if((pte = walkpgdir(pgdir, (void *) i, 0)) == 0) panic("copyuvm: pte should exist"); if(!(*pte & PTE_P)) panic("copyuvm: page not present"); pa = PTE_ADDR(*pte); flags = PTE_FLAGS(*pte); if((mem = kalloc()) == 0) goto bad; memmove(mem, (char*)P2V(pa), PGSIZE); if(mappages(d, (void*)i, PGSIZE, V2P(mem), flags) < 0) { kfree(mem); goto bad; } } return d; bad: freevm(d); return 0; }
copyuvm関数は、引数pgdirで指定されるページディレクトリから仮想アドレス0~0+sz-1に関連する設定をコピーし、新規にページディレクトリを作成します。
引数 pde_t *pgdir
コピー元となるページディレクトリの先頭アドレスです。
引数 uint sz
仮想アドレス範囲のサイズ(バイト単位)です。
戻り値 pde_t *d または 0
新規に作成したページディレクトリの先頭アドレスです。
ページディレクトリの作成に失敗した場合は、0が戻り値となります。
処理の内容
- 新規にページディレクトリを作成する
- 仮想アドレス0~0+sz-1に対応するページディレクトリとページテーブルの設定を新規に作成したページディレクトリにコピーする
- 新規に作成したページディレクトリの先頭アドレスを戻り値としてリターン
- メモリ領域を解放する
新規にページディレクトリを作成する
if((d = setupkvm()) == 0) return 0;
setupkvm関数を呼び出して、カーネル空間のマッピングのみが設定されたページディレクトリを作成します。
ページディレクトリの作成に失敗した場合は、戻り値を0として処理を終了します。
仮想アドレス0~0+sz-1に対応するページディレクトリとページテーブルの設定を新規に作成したページディレクトリにコピーする
for(i = 0; i < sz; i += PGSIZE){ if((pte = walkpgdir(pgdir, (void *) i, 0)) == 0) panic("copyuvm: pte should exist"); if(!(*pte & PTE_P)) panic("copyuvm: page not present"); pa = PTE_ADDR(*pte); flags = PTE_FLAGS(*pte); if((mem = kalloc()) == 0) goto bad; memmove(mem, (char*)P2V(pa), PGSIZE); if(mappages(d, (void*)i, PGSIZE, V2P(mem), flags) < 0) { kfree(mem); goto bad; } }
引数pgdirで指定されたページディレクトリからPTE(ページテーブルエントリ)を取得する
if((pte = walkpgdir(pgdir, (void *) i, 0)) == 0) panic("copyuvm: pte should exist");
walkpgdir関数を呼び出して、仮想アドレスiに対応しているpte(ページテーブルエントリ)を取得します。
取得したPTE(ページテーブルエントリ)がページテーブルを参照していることを確認する
if(!(*pte & PTE_P)) panic("copyuvm: page not present");
pteをPTE_P(#define PTE_P 0x001)でマスク処理することにより、pteのbit0(ページテーブルエントリのPフラグ)を取り出しています。!(*pde & PTE_P)が真となる場合→pteのbit0(ページテーブルエントリのPフラグ)が0→pte(ページテーブルエントリ)が参照しているページフレームが存在しない場合は、panic関数を呼び出してメッセージを出力します。
取得したPTE(ページテーブルエントリ)からページフレームの先頭アドレスを取得する
pa = PTE_ADDR(*pte);
PTE_ADDRマクロを使って、pte(ページテーブルエントリ)からページフレームの先頭アドレスpaを取得します。
取得したPTE(ページテーブルエントリ)から各種のパラメータを取得する
flags = PTE_FLAGS(*pte);
PTE_FLAGSマクロ絵お使って、pte(ページテーブルエントリ)に設定されている各種のパラメータ(Pフラグ、R/Wフラグ、U/Sフラグ、PWTフラグ、PCDフラグ、Aフラグ、Dフラグ)を取得します。
新規にページテーブルを作成する
if((mem = kalloc()) == 0) goto bad;
kalloc関数を呼び出して、ページフレームとして使用する4KBのメモリ領域を割り当てます。
(mem = kalloc()) == 0 が真となる場合→メモリ領域の割り当てに失敗した場合は、badラベルへジャンプします。
ページテーブルの内容をコピーする
memmove(mem, (char*)P2V(pa), PGSIZE);
memmove関数を呼び出して、既存のページテーブル(先頭アドレスP2V(pa)・サイズPGSIZE(#define PGSIZE 4096))から、新規に作成されたページテーブル(先頭アドレスmem・サイズPGSIZEのメモリ領域)へ、内容をコピーします。
新規に作成されたページディレクトリに仮想アドレスi~i+PGSIZE-1と物理アドレスV2P(mem)~V2P(mem)+PGSIZE-1のマッピングを設定する
if(mappages(d, (void*)i, PGSIZE, V2P(mem), flags) < 0) { kfree(mem); goto bad; }
mappages関数を呼び出して、新規に作成されたページディレクトリに、仮想アドレスi~i+PGSIZE-1と物理アドレスV2P(mem)~ V2P(mem)+PGSIZE-1 のマッピングを設定します。mappages(d, (void*)i, PGSIZE, V2P(mem), flags) < 0 が真となる場合→マッピングに失敗した場合は、kfree関数を呼び出してページフレーム(割り当てられていた4KBのメモリ領域)を解放します。最後に、badラベルへジャンプします。
新規に作成したページディレクトリの先頭アドレスを戻り値としてリターン
return d;
メモリ領域を解放する
bad: freevm(d); return 0;
badラベルへジャンプした場合は、freevm関数を呼び出して、ユーザー空間に関わる、ページディレクトリのメモリ領域、全てのページテーブルのメモリ領域、全てのページフレーム(4KBのメモリ領域)を解放します。最後に、0を戻り値として処理を終了します。