Sv32 のまとめ(RISC-V 仮想メモリ)
ページテーブルは難しいですよね。
自分なりに Sv32 を整理してAIに整理してもらって結局AIの文章になったものを共有します。
Sv32とは
Sv32 とは、32bit 仮想アドレスを
4KB (= 2^12 byte) ページ単位で区切り、
仮想アドレスと物理アドレスを対応付ける仮想メモリ方式です。
仮想アドレス(VA)は、ページテーブルエントリ(PTE)を介して
最終的に物理アドレス(PA)へ変換されます。
VA
↓
PTE
↓
PA
Sv32 ではページテーブルは 最大2階層 になります。
仮想アドレス構造(VA)
Sv32 の仮想アドレスは次のように分割されます。
VPN[1] | VPN[0] | offset
10bit 10bit 12bit
- VPN[1] : 1段目ページテーブルのインデックス
- VPN[0] : 2段目ページテーブルのインデックス
- offset : ページ内オフセット(4KB)
基本のページウォークは以下です。
- VPN[1] を使って level1 の PTE を参照
- VPN[0] を使って level0 の PTE を参照
- offset を使って最終 PA を生成
PTE(ページテーブルエントリ)の構造
Sv32 の PTE は 32bit で構成されます。
PPN[1] | PPN[0] | RSW | D | A | G | U | X | W | R | V
各フィールドの意味
- PPN[1], PPN[0] : 次に参照する物理ページ番号
- RSW : OS 用予約領域
- D : Dirty(書き込み済み)
- A : Accessed(参照済み)
- G : Global マッピング
- U : User モード許可
- X : 実行可能
- W : 書き込み可能
- R : 読み込み可能
- V : 有効ビット
ページウォークの判定手順
1. Vビットを確認
- V = 0 → 無効エントリ → ページフォルト
- V = 1 → 有効なので次へ進む
2. R/W/X を確認(leaf 判定)
R=W=X=0 → non-leaf(次のページテーブル)
それ以外 → leaf(最終ページ)
leaf の場合(最終ページ)
R/W/X のいずれかが 1 の場合、この PTE は最終ページを指します。
物理アドレスは次のように生成されます。
PA = (PPN << 12) | offset
PPN は PPN[1] と PPN[0] を連結した物理ページ番号です。
non-leaf の場合(次のページテーブルへ)
R=W=X=0 の場合、この PTE は次のページテーブルを指します。
next_table = pte.PPN << 12
pte2 = MEM[next_table + VPN[0] * 4]
つまり、
-
pte.PPN << 12: 次のページテーブルの物理アドレス -
VPN[0] * 4: PTE配列のインデックス(4byte/PTE)
として次の PTE を参照します。
Level1 で leaf になる場合(4MB メガページ)
途中(level1)で R/W/X のどれかが 1 になる場合もあります。
この場合は 4MB のメガページ として解釈されます。
そのときの物理アドレスは次のようになります。
PA = [ PPN[1] | VPN[0] | offset ]
- 上位ビット : PPN[1]
- 中位ビット : VPN[0]
- 下位ビット : offset
これにより 4MB の連続領域を1つのPTEでマッピングできます。
まとめ
Sv32 のページウォークは次の流れになります。
-
satp.PPNから root ページテーブルを取得 -
VPN[1]で level1 の PTE を参照 - leaf なら
PPN + offsetで PA を生成 - non-leaf なら次のページテーブルへ
-
VPN[0]で level0 の PTE を参照 - 最終的に
PPN + offsetで PA を生成
この仕組みによって、仮想アドレスから物理アドレスへの変換が実現されています。