RISC-Vのテストパタン(riscv-tests)をパスさせるにあたり、RISC-Vの持つ仮想化の方式について勉強する必要が生じた。
これを機に、RISC-Vの仮想化の方式についてまとめてみよう。
RISC-Vの持つ仮想化のモード
RISC-Vには、オペレーションモードに応じていくつかの仮想化方式がある。 これらは、Privileged Architecture マニュアルに説明がされている。
http://riscv.org/spec/riscv-compressed-spec-v1.9.pdf
表3.3がRISC-Vの持つアドレス仮想化方式の一覧だ。
この中で、今回riscv-testsのVMモードで主に利用されているのは、Sv32とSv39のようだ。これらについてまとめてみよう。
RISC-Vはvmの立ち上げで何が置きているのか
riscv-testsのvm.cを見てみよう。vm_bootという関数が記述されている。
void vm_boot(long test_addr, long seed) { ... // map kernel to uppermost megapage l1pt[PTES_PER_PT-1] = ((pte_t)kernel_l2pt >> PGSHIFT << PTE_PPN_SHIFT) | PTE_V | PTE_TYPE_TABLE; kernel_l2pt[PTES_PER_PT-1] = ((pte_t)kernel_l3pt >> PGSHIFT << PTE_PPN_SHIFT) | PTE_V | PTE_TYPE_TABLE; // map user to lowermost megapage l1pt[0] = ((pte_t)user_l2pt >> PGSHIFT << PTE_PPN_SHIFT) | PTE_V | PTE_TYPE_TABLE; user_l2pt[0] = ((pte_t)user_l3pt >> PGSHIFT << PTE_PPN_SHIFT) | PTE_V | PTE_TYPE_TABLE; write_csr(sptbr, l1pt); ... // virtual memory off; set user mode upon eret clear_csr(mstatus, MSTATUS_VM | MSTATUS_PRV1); // virtual memory to Sv39 set_csr(mstatus, (long)VM_SV39 << __builtin_ctzl(MSTATUS_VM)); ...
l1ptは仮想化テーブルのベースアドレスになっており、このベースアドレスはsptbrレジスタに格納されている。 このテストベンチでは、l1ptは0xa000となっており、sptbrが0xa000に設定されている。 また、mstatusのVMビットをSV39に設定することで、仮想アドレスから物理アドレスへの変換がSV39の方式で実行されることが分かる。
また、物理アドレスと仮想アドレスの変換は、以下のpa2kvmで定義されているようだ。
#define pa2kva(pa) ((void*)(pa) - MEGAPAGE_SIZE)
では、次にSV39、SV32などの変換方式がどのようになっているのかを確認していこう。
変換の概要は下記の図のようになっている。これはSv32の変換方式だが、Sv39では、この変換のレベルが一つ上がったり、変換するアドレスの幅が変わっているだけで、基本的なところは同一になっている。
- i=LEVELS-1 (LEVEL=2)からスタートする。つまり、上位の変換ブロックから変換を始める。
- va.vpn[i]×PTSIZE(=4)+sptbr のアドレスを計算し(これが変換テーブルのアドレスとなる。つまり、va.vpn[i]をインデックスとして変換テーブルを参照している)、ページテーブルエントリ(=pte)を参照する。
- pte.Vビットを確認し、0であれば無効テーブルエントリなのでアドレスエラーを出力する。
- pte.type が2以上であれば、テーブルの参照の終端まで行ったことになるので、ステップ5に進む。ここでpte.typeの意味は以下のように定義されているようだ (Privileged Reference Manual参照)。
つまり、Type=0 or 1であれば次のテーブルへの参照となるが、それ以外は最終的なページテーブルとなる。 ここでテーブルのアクセスエラーを確認する。 Type=0,1のときは次のアドレスを計算する。a=pte.ppn×PAGESIZEとし(つまりベースアドレスを更新する)、再度ステップ2に進む。また、i<0となるとこれもテーブル不良のためアドレスエラーとなる。
ステップ5まで来たといことは、i>=0の間にページテーブルの終端に到達したことを意味する。ここでpte.typeのアクセス権限を確認し、アクセス権限が足りなければエラーとなる。 そうでなければ、以下の方針でアドレス変換を実行する。
オフセットはそのまま利用する。 pa.pgoff = va.pgoff
- i>0(=つまり、インデックスの参照を0まで使い切らない)場合はi-1から0まで(つまり到達しなかったレベル)は、仮想アドレスをそのまま物理アドレスとして利用する。 pa.ppn[i-1:0]=va.vpn[i-1:0]。
- それ以外のインデックスはページテーブルを参照して変換する。pa.ppn[LEVELS-1:i] = pte.ppn[LEVELS-1:i]
さて、次はこれを実装していこう。そしていよいよテストパタンを通すぞ!