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への割り当ては、さらに何をやっているのか良く分からない。