vm.c static pte_t * walkpgdir(pde_t *pgdir, const void *va, int alloc)
トップページ
jupiteroak.hatenablog.com
vm.c
https://github.com/mit-pdos/xv6-public/blob/master/vm.c#L35
static pte_t * walkpgdir(pde_t *pgdir, const void *va, int alloc) { pde_t *pde; pte_t *pgtab; pde = &pgdir[PDX(va)]; if(*pde & PTE_P){ pgtab = (pte_t*)P2V(PTE_ADDR(*pde)); } else { if(!alloc || (pgtab = (pte_t*)kalloc()) == 0) return 0; // Make sure all those PTE_P bits are zero. memset(pgtab, 0, PGSIZE); // The permissions here are overly generous, but they can // be further restricted by the permissions in the page table // entries, if necessary. *pde = V2P(pgtab) | PTE_P | PTE_W | PTE_U; } return &pgtab[PTX(va)];
walkpgdir関数は、引数vaで指定された仮想アドレスに対応しているPTE(ページテーブルエントリ)を取得します。
引数 pde_t *pgdir
ページディレクトリの先頭アドレスです。
引数 const void *va
取得したいPTE(ページテーブルエントリ)に対応している仮想アドレスです。
引数 int alloc
仮想アドレスvaに対応しているPTE(ページテーブルエントリ)が存在しなかった場合に、ページテーブルを作成するかどうかを指定します。
1を設定した場合、ページテーブルを作成します。
0を設定した場合、ページテーブルを作成しません。
戻り値 pte_t * &pgtab[PTX(va)] または 0
仮想アドレスvaに対応しているPTE(ページテーブルエントリ)のアドレスです。
仮想アドレスvaに対応しているPTE(ページテーブルエントリ)が存在しない、かつ、ページテーブルを作成しなかった場合は、0が戻り値となります。
処理の内容
- PDE(ページディレクトリエントリ)を取得する
- ページテーブルが既に作成されている場合
- ページテーブルが未だ作成されていない場合
- 仮想アドレスvaに対応しているPTE(ページテーブルエントリ)のアドレスを戻り値としてリターンする
PDE(ページディレクトリエントリ)を取得する
pde = &pgdir[PDX(va)];
引数pgdirが指定するページディレクトリから、仮想アドレスvaに対応しているpde(ページディレクトリエントリ)を取得します。
ページディレクトリのアドレスpgdirとインデックスの値PDX(va)を組み合わせることにより、仮想アドレスvaに対応しているpde(ページディレクトリエントリ)を指定することができます。仮想アドレスvaのbit31-22がそのままページディレクトリにおけるインデックスの値になるので、PDXマクロを使って仮想アドレスvaからインデックスの値を取得します。
ページテーブルが既に作成されている場合
PDE(ページディレクトリエントリ)が参照しているページテーブルのアドレスを取得する
if(*pde & PTE_P){ pgtab = (pte_t*)P2V(PTE_ADDR(*pde)); }
*pde & PTE_Pが真となる場合→*pde(ページディレクトリエントリ)のbit0(Pフラグ)が1の場合は、*pde(ページディレクトリエントリ)が参照しているページテーブルのアドレスを取得します。
*pde & PTE_Pについて
*pdeの値(32bit)をPTE_P(#define PTE_P 0x001)でマスク処理することにより、*pde(ページディレクトリエントリ)のbit0(Pフラグ)を取り出しています。*pde(ページディレクトリエントリ)のbit0(Pフラグ)が1の時、*pde(ページディレクトリエントリ)が参照しているページテーブルが存在することを示しています。
ページテーブルが未だ作成されていない場合
ページテーブルを作成してからページテーブルのアドレスを取得する
*pde & PTE_Pが偽となる場合→*pde(ページディレクトリエントリ)のbit0(Pフラグ)が0の場合→*pde(ページディレクトリエントリ)が参照しているページテーブルが存在しない場合は、ページテーブルを作成してからページテーブルの仮想アドレスを取得します。
ページテーブルを作成する
} else { if(!alloc || (pgtab = (pte_t*)kalloc()) == 0) return 0;
kalloc関数を呼び出して、ページテーブルとして利用する4KBのメモリ領域を割り当てます。
メモリ領域の割り当てに成功した場合は、そのメモリ領域の先頭アドレス(仮想アドレス)をpgtabに保存します。
メモリ領域の割り当てに失敗した場合、または、引数allocのフラグが0の場合は、0を戻り値として処理を終了します。
全てのPTE(ページテーブルエントリ)のPフラグを0にする
memset(pgtab, 0, PGSIZE);
PTE(ページテーブルエントリ)のbit0(Pフラグ)が0の時は、PTE(ページテーブルエントリ)がページフレームを参照していないことを示しています。ページテーブルを作成した時点ではどのPTE(ページテーブルエントリ)もページフレームを参照していないので、全てのPTE(ページテーブルエントリ)のbit0(Pフラグ)を0にします。そのために、memset関数を呼び出してページテーブルのメモリ領域4KB(#define PGSIZE 4096)を0で初期化します。
PDE(ページディレクトリエントリ)を再設定する
*pde = V2P(pgtab) | PTE_P | PTE_W | PTE_U;
V2P(pgtab)(ページテーブルの先頭物理アドレス)、PTE_P(#define PTE_P 0x001)、PTE_W(#define PTE_W 0x002)、PTE_U(#define PTE_U 0x004)の論理和を*pde(ページディレクトリエントリ)に設定します。
これによって、*pde(ページディレクトリエントリ)のbit31-12にページテーブルの先頭物理アドレスV2P(pgtab)のbit31-12が、Pフラグ(bit0)に1(#define PTE_P 0x001→0b 0001)が、R/Wフラグ(bit1)に1(#define PTE_W 0x002→0b 0010)が、U/Sフラグ(bit2)に1(#define PTE_U 0x004→0b 0100)が、それぞれ格納されます。
*pde(ページディレクトリエントリ)のbit0(Pフラグ)が1の時は、*pde(ページディレクトリエントリ)が参照しているページテーブルが存在することを示しています。
*pde(ページディレクトリエントリ)のbit1(R/Wフラグ)が1の時は、ページフレームが読み書き可能であることを示しています。
*pde(ページディレクトリエントリ)のbit2(U/Sフラグ)が1の時は、ユーザープロセスがページフレームにアクセスできることを示しています。
仮想アドレスvaに対応しているPTE(ページテーブルエントリ)のアドレスを戻り値としてリターンする
return &pgtab[PTX(va)];
仮想アドレスvaに対応しているPTE(ページテーブルエントリ)のアドレスを戻り値としてリターンします。
ページテーブルのアドレスpgtabとインデックスの値PTX(va)を組み合わせることにより、仮想アドレスvaに対応しているPTE(ページテーブルエントリ)を指定することができます。仮想アドレスvaのbit21-12がそのままページテーブルにおけるインデックスの値になるので、PTXマクロを使って仮想アドレスvaからインデックスの値を取得します。