RISC-Vでマイクロカーネルの夢
リンカスクリプト
AT(Address at)指定すると、実際にオブジェクトを配置するセクションとリンクの際に使用するアドレスのセクションを分けることができる。
例えば"ram AT > rom"ならリンク時はram上にあるものとしてアドレスを計算し、出力バイナリではromセクションに配置する。
QEMU virtには仮想flushがあるらしいので、後々OS本体をromに載せたいね
RISC-V のブートシーケンス
QEMUのvirtはリセット時0x80000000にジャンプするとあるが、デバッグトレースを追うと0x1000から開始しmthirdレジスタ(CPUのコア番号?)を初期化してから0x80000000にジャンプしている。バイナリを逆アセンブリしても0x1000に配置されているコードはないから、ROMにブートローダー的なものがハードコーティングされてるんだろう。
例外/割り込み ハンドリング
risc-vのトラップベクタテーブルはCSRレジスタ指定の可変式
トラップベクタテーブルをアセンブリで書こうとすると関数的に書かざるを得ず意味論的にナンセンスなので、ベクタテーブル本体は関数ポインタテーブルの初期化式で生成する。
void (*const trap_vector_table[]) (void) = {
...,
}
riscv全ての割り込みに対して単一のエントリに制御を移すダイレクトベクタ方式なる割り込み方式があって、そっちを使えば割り込み発生時のレジスタ退避・復旧のコードを共通化できるのでそのほうがよくね?と思ったが、単に関数呼び出しの場合はパーマネントレジスタのみ考慮すればいいが、即座に割り込み発生元に制御が戻らない場合(タイマ割込みでプリエンティブマルチタスクする等)は作業レジスタや関数引数レジスタも考慮する必要があるため、コンテキストスイッチのオーバーヘッドを最小化するためにはベクタ方式でトラップする必要がある。ゆーて退避・復旧のasmを関数化してインライン展開すれば対して面倒でもないのでわざわざ書く必要がなかった。<-アホ
普通に間違えてた。riscvは割り込み時に割り込みベクタテーブルに制御が移るだけで、ベクタテーブルの関数を実行してくれるわけではない。結局アセンブリで関数チックに書かないといけないってことね。ちゃんと考えればわかることなのに何だったんだこの時間。