pop_tf()
によるジャンプで、仮想アドレスで言う所の0x2ac8
へのジャンプとなる。これによりユーザモードに移るわけだが、この時のアドレス計算を見てみよう。ジャンプ先は0x2ac8
(仮想アドレス)なので、以下のような計算となる。
SATP
が0x8000000000080004
なので、0x8000_4000が仮想アドレス計算の起点となっている(これはページテーブルの起点:pt
の場所となっている )0x80004000
はpt[0]
の起点なので、以下のコードによりpt[1]
の先頭へのジャンプとなっている。0x8000_5000
へのページテーブルのジャンプとなっている。
0x80005000
はpt[1]
の先頭なので、pt[3]
へのジャンプとなっている。- ところが
pt[3]
は初期化されていないので、ページテーブル例外が発生する。
ページテーブル例外を処理するのは、handle_trap()
である。さらにページテーブル例外のためのhandle_fault()
を呼び出す。
このhandle_fault()
はmedeleg
によりスーパーバイザーモードで処理される。スーパーバイザーモードの例外での飛び先はstvec
により指定されている。
void handle_trap(trapframe_t* tf) { if (tf->cause == CAUSE_USER_ECALL) /* ... 中略 ... */ else if (tf->cause == CAUSE_ILLEGAL_INSTRUCTION) /* ... 中略 ... */ else if (tf->cause == CAUSE_FETCH_PAGE_FAULT || tf->cause == CAUSE_LOAD_PAGE_FAULT || tf->cause == CAUSE_STORE_PAGE_FAULT) handle_fault(tf->badvaddr, tf->cause); else assert(!"unexpected exception"); pop_tf(tf); }
stvec
の計算方法は以下の通り。pa2kva()
を使っているのでkernel_l2pt()
を経由してジャンプする仕組みになっている。
write_csr(stvec, pa2kva(trap_entry));
handle_fault()
の仕組みは以下のようになっている。
すでにページが割り当てられていない場合には、新たなページをfreelist
から割り当て、PTEにそのページを設定する。
さらにそこから先のuser_mapping
への割り当ては、さらに何をやっているのか良く分からない。