vm.c static int mappages(pde_t *pgdir, void *va, uint size, uint pa, int perm)

トップページ
jupiteroak.hatenablog.com


vm.c
https://github.com/mit-pdos/xv6-public/blob/master/vm.c#L60

static int mappages(pde_t *pgdir, void *va, uint size, uint pa, int perm)
{
  char *a, *last;
  pte_t *pte;

  a = (char*)PGROUNDDOWN((uint)va);
  last = (char*)PGROUNDDOWN(((uint)va) + size - 1);
  for(;;){
    if((pte = walkpgdir(pgdir, a, 1)) == 0)
      return -1;
    if(*pte & PTE_P)
      panic("remap");
    *pte = pa | perm | PTE_P;
    if(a == last)
      break;
    a += PGSIZE;
    pa += PGSIZE;
  }
  return 0;
}

mappages関数は、引数pgdirで指定されたページディレクトリに、仮想アドレスva~va+size-1と物理アドレスpapa+size-1のマッピングを設定します。

引数 pde_t *pgdir
ページディレクトリの先頭アドレスです。

引数 void *va
マッピング対象となる仮想アドレス範囲における先頭アドレスです。

引数 uint size
マッピング対象となる仮想アドレス範囲(物理アドレス範囲)のサイズ(バイト単位)です。

引数 uint pa
マッピング対象となる物理アドレス範囲における先頭アドレスです。

引数 int perm
PTE(ページテーブルエントリ)に設定するパラメータです。

戻り値 0 または -1
マッピングに成功した場合は、0が戻り値となります。
マッピングに失敗した場合→マッピングの途中でPTE(ページテーブルエントリ)を取得できなかった場合は、-1が戻り値とまります。


処理の内容

アドレスを4KB境界に切り下げる

a = (char*)PGROUNDDOWN((uint)va);
last = (char*)PGROUNDDOWN(((uint)va) + size - 1);

PGROUNDDOWNマクロを使って、仮想アドレスvaを4KB境界のアドレス値(0x1000の倍数値)に切り下げ、仮想アドレスaを算出します。
PGROUNDDOWNマクロを使って、仮想アドレス((uint)va) + size - 1を4KB境界のアドレス値(0x1000の倍数値)に切り下げ、仮想アドレスlastを算出します。
仮想アドレスaは、マッピング対象となる仮想アドレス範囲において、先頭にあるページ(4KBのアドレス範囲)の先頭アドレスです。
仮想アドレスlastは、マッピング対象となる仮想アドレス範囲において、最後尾にあるページ(4KBのアドレス範囲)の先頭アドレスです。

仮想アドレス範囲a~a+size-1と物理アドレス範囲papa+size-1のマッピングを設定する

walkpgdir関数を呼び出して、仮想アドレスaに対応しているPTE(ページテーブルエントリ)を取得し、取得したPTE(ページテーブルエントリ)に物理アドレスpaを登録することで、ページ(4KBの仮想アドレス範囲)とページフレーム(4KBの物理アドレス範囲)のマッピングをつくることができます。これをfor文で繰り返すことにより、仮想アドレス範囲a~a+size-1と物理アドレス範囲papa+size-1のマッピングを設定します。

仮想アドレスに対応しているPTE(ページテーブルエントリ)を取得する

for(;;){
  if((pte = walkpgdir(pgdir, a, 1)) == 0)
        return -1;
  if(*pte & PTE_P)
        panic("remap");

1つ目のif文の処理では、walkpgdir関数を呼び出して、仮想アドレスaに対応しているPTE(ページテーブルエントリ)を取得しています。
(pte = walkpgdir(pgdir, a, 1)) == 0 が真となる場合→PTE(ページテーブルエントリ)を取得できた場合は、pteにPTE(ページテーブルエントリ)のアドレスが格納されます。(pte = walkpgdir(pgdir, a, 1)) == 0 が偽となる場合→PTE(ページテーブルエントリ)を取得できなかった場合は、-1を戻り値として処理を終了します。
2つ目のif文の処理では、*pte(ページテーブルエントリ)をPTE_P(#define PTE_P 0x001)でマスク処理することにより、*pte(ページテーブルエントリ)のbit0(Pフラグ)を取り出しています。*pde & PTE_Pが真となる場合→*pte(ページテーブルエントリ)のbit0(Pフラグ)が1→*pte(ページテーブルエントリ)が参照しているページフレームが存在する場合は、panic関数を呼び出してメッセージを出力します。

取得したPTE(ページテーブルエントリ)にページフレームの先頭アドレスを登録する

*pte = pa | perm | PTE_P;

ページフレームの先頭アドレスpa、引数perm、PTE_Pマクロ(#define PTE_P 0x001)の論理和を*pte(ページテーブルエントリ)に設定します。
これによって、*pte(ページテーブルエントリ)のbit31-12にページフレームの先頭アドレスpaのbit31-12が、所定のフィールド・フラグにpermが、Pフラグ(bit0)に1(#define PTE_P 0x001→0b0001)が、それぞれ格納されます。
pte(ページテーブルエントリ)にページフレームの先頭アドレスpaを登録したことにより、仮想アドレスa~a+0x0FFF(4KBの仮想アドレス範囲→ページ)と物理アドレスpapa+0x0FFF(4KBの物理アドレス範囲→ページフレーム)のマッピングをつくることができました。

ループの脱出・継続を判定する

if(a == last)
      break;
a += PGSIZE;
pa += PGSIZE;

a == last が真となる場合→マッピング対象となる仮想アドレス範囲において仮想アドレスaが最後尾にあるページ(4KBの仮想アドレス範囲)の先頭アドレスlastと同じ値になった場合は、ループを脱出します。
そうではない場合は、仮想アドレスaと物理アドレスpaを4KB分(#define PGSIZE 4096)インクリメントして、ループを継続します。