vm.c char* uva2ka(pde_t *pgdir, char *uva)

トップページ
jupiteroak.hatenablog.com


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

char* uva2ka(pde_t *pgdir, char *uva)
{
  pte_t *pte;

  pte = walkpgdir(pgdir, uva, 0);
  if((*pte & PTE_P) == 0)
    return 0;
  if((*pte & PTE_U) == 0)
    return 0;
  return (char*)P2V(PTE_ADDR(*pte));
}

uva2ka関数は、ページディレクトリpgdirの設定に基づいて、仮想アドレスuvaが所属するページの先頭アドレスを、xv6がメモリ割り当てで使用している仮想アドレス(カーネル空間における仮想アドレス)に、変換します。
(ページ(仮想アドレスの範囲)とページフレーム(物理アドレスの範囲)をマッピングさせる処理では、walkpgdir関数がページフレームを確保するためにkalloc関数を呼び出しますが、このkalloc関数の戻り値が「xv6がメモリ割り当てで使用している仮想アドレス」です。その後、mappages関数がマッピング設定の処理を行い、「xv6がメモリ割り当てで使用している仮想アドレス(kalloc関数の戻り値)」とは異なる仮想アドレスが物理アドレスマッピングされます。)

引数 pde_t *pgdir
仮想アドレスuvaの設定に関わるページディレクトリの先頭アドレスです。

引数 char *uva
変換対象となる先頭アドレスが所属するページ(4KBサイズの仮想アドレス範囲)と同じページ(4KBサイズの仮想アドレス範囲)に所属する仮想アドレスです。

戻り値 (char*)P2V(PTE_ADDR(*pte)) または 0
xv6がメモリ割り当てで使用している仮想アドレス(カーネル空間における仮想アドレス)が戻り値となります。
仮想アドレスuvaに対応しているページフレームが存在しない場合や、仮想アドレスuvaに対応しているページフレームにユーザープロセスがアクセスできない場合は、0が戻り値となります。


処理の内容

pte(ページテーブルエントリ)を取得する

pte = walkpgdir(pgdir, uva, 0);

walkpgdir関数を呼び出して、仮想アドレスuvaに対応しているpte(ページテーブルエントリ)を取得します。

pte(ページテーブルエントリ)がページフレームを参照していない場合は処理を終了する

if((*pte & PTE_P) == 0)
    return 0;

pteをPTE_P(#define PTE_P 0x001)でマスク処理することにより、pteのbit0(ページテーブルエントリのPフラグ)を取り出しています。*pte & PTE_Pが真となる場合→pteのbit0(ページテーブルエントリのPフラグ)が0→pte(ページテーブルエントリ)が参照しているページフレームが存在しない場合は、戻り値を0にして処理を終了します。

pte(ページテーブルエントリ)が参照しているページフレームにユーザープロセスがアクセスできない場合は処理を終了する

if((*pte & PTE_U) == 0)
    return 0;

pteをPTE_U(#define PTE_U 0x004)でマスク処理することにより、pteのbit2(ページテーブルエントリのU/Sフラグ)を取り出しています。*pte & PTE_Uが真となる場合→pteのbit2(ページテーブルエントリのU/Sフラグ)が0→pte(ページテーブルエントリ)が参照しているページフレームにユーザープロセスがアクセスできない場合は、戻り値を0にして処理を終了します。

ページフレームの先頭アドレス(物理アドレス)をページの先頭アドレス(仮想アドレス)に変換してリターンする

return (char*)P2V(PTE_ADDR(*pte));

ページフレームの先頭アドレス(物理アドレス)をページの先頭アドレス(仮想アドレス)に変換した値をリターンします。
PTE_ADDRマクロを使って、pte(ページテーブルエントリ)が参照しているページフレームの先頭アドレスを取得し、さらにP2Vマクロを使って、取得したページフレームの先頭アドレスPTE_ADDR(*pte)をページの先頭アドレスP2V(PTE_ADDR(*pte))に変換します。