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(ページディレクトリエントリ)を取得する

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(ページディレクトリエントリ)が参照しているページテーブルが存在することを示しています。

pgtab = (pte_t*)P2V(PTE_ADDR(*pde))について

*pde(ページディテクトリエントリ)が参照しているページテーブルの先頭アドレス(仮想アドレス)を取得します。
*pde(ページディレクトリエントリ)のbit31-12にはページテーブルのアドレス上位20bitが格納されているので、PTE_ADDRマクロを使って*pde(ページディレクトリエントリ)からページテーブルの先頭アドレスを取得します。取得したページテーブルの先頭アドレスは物理アドレスなので、P2Vマクロを使って、取得したページテーブルの先頭アドレスを仮想アドレスに変換します。

ページテーブルが未だ作成されていない場合

ページテーブルを作成してからページテーブルのアドレスを取得する

*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からインデックスの値を取得します。