Rustで自作OS 4日目
Tags: rust
Published: 2021-07-05 14:48:02

第3章 boot loaderをやっていく。まずはデバッグで使うQEMU Monitorの有効化。起動時のオプションに`-monitor stdio`をつければ、プロンプトが返ってきて操作ができる。

```
QEMU 5.2.0 monitor - type 'help' for more information
(qemu) info registers
iininfinfoinfo info rinfo reinfo reginfo regiinfo regisinfo registinfo registeinfo registerinfo registers
RAX=0000000000000000 RBX=0000000000000000 RCX=0000000000000000 RDX=0000000007ed5e20
RSI=0000000007233018 RDI=0000000007233018 RBP=0000000007234418 RSP=0000000007ec1360
R8 =0000000006211630 R9 =0000000000001000 R10=0000000007e00000 R11=0000000007ed6ea0
R12=0000000007ec1588 R13=0000000007ec1598 R14=0000000007ec1708 R15=0000000000000000
RIP=000000000620f72f RFL=00000202 [-------] CPL=0 II=0 A20=1 SMM=0 HLT=0
ES =0030 0000000000000000 ffffffff 00cf9300 DPL=0 DS   [-WA]
CS =0038 0000000000000000 ffffffff 00af9a00 DPL=0 CS64 [-R-]
SS =0030 0000000000000000 ffffffff 00cf9300 DPL=0 DS   [-WA]
DS =0030 0000000000000000 ffffffff 00cf9300 DPL=0 DS   [-WA]
FS =0030 0000000000000000 ffffffff 00cf9300 DPL=0 DS   [-WA]
GS =0030 0000000000000000 ffffffff 00cf9300 DPL=0 DS   [-WA]
LDT=0000 0000000000000000 0000ffff 00008200 DPL=0 LDT
TR =0000 0000000000000000 0000ffff 00008b00 DPL=0 TSS64-busy
GDT=     0000000007bee698 00000047
IDT=     000000000782e018 00000fff
CR0=80000033 CR2=0000000000000000 CR3=0000000007c01000 CR4=00000668
DR0=0000000000000000 DR1=0000000000000000 DR2=0000000000000000 DR3=0000000000000000 
DR6=00000000ffff0ff0 DR7=0000000000000400
EFER=0000000000000500
FCW=037f FSW=0000 [ST=0] FTW=00 MXCSR=00001f80
FPR0=0000000000000000 0000 FPR1=0000000000000000 0000
FPR2=0000000000000000 0000 FPR3=0000000000000000 0000
FPR4=0000000000000000 0000 FPR5=0000000000000000 0000
FPR6=0000000000000000 0000 FPR7=0000000000000000 0000
XMM00=00000000069940180000000000000000 XMM01=00000000000000000000000000000000
XMM02=00000000000000000000000000000000 XMM03=00000000000000000000000000000000
XMM04=00000000000000000000000000000000 XMM05=00000000000000000000000000000000
XMM06=00000000000000000000000000000000 XMM07=00000000000000000000000000000000
XMM08=00000000000000000000000000000000 XMM09=00000000000000000000000000000000
XMM10=00000000000000000000000000000000 XMM11=00000000000000000000000000000000
XMM12=00000000000000000000000000000000 XMM13=00000000000000000000000000000000
XMM14=00000000000000000000000000000000 XMM15=00000000000000000000000000000000
(qemu)
```

次に、既存のUEFIアプリをboot loaderとして、haltするだけの最小のカーネルを作ってロードする。boot loaderはプロジェクトのルートに`main.rs`として配置しているので、カーネルはsrcディレクトリを作ってそこに置く。

rustにもexternがある。externなしだとABIがRustのものになるが、`extern "C"`するとSystem ABIになる。ドキュメントにはFFI用途と書いてあるが、自作OS用途でも大丈夫なはず。

ちなみにNightlyだと`llvm_asm`もあるらしい。
https://doc.rust-lang.org/unstable-book/library-features/llvm-asm.html

ここからはloaderとkernelを分けて作るために、[cargoのworkspace機能](https://doc.rust-lang.org/cargo/reference/workspaces.html)を使うことにした。
[StackOverflow](https://stackoverflow.com/a/53139624/15289527)によると、[tokio](https://github.com/tokio-rs/tokio/blob/master/Cargo.toml)が参考になるらしい。

workspaceでtagetがrootに一個だけなのは仕様らしい。
https://doc.rust-lang.org/book/ch14-03-cargo-workspaces.html
> The workspace has one target directory at the top level for the compiled artifacts to be placed into; the adder package doesn’t have its own target directory. Even if we were to run cargo build from inside the adder directory, the compiled artifacts would still end up in add/target rather than add/adder/target. Cargo structures the target directory in a workspace like this because the crates in a workspace are meant to depend on each other. If each crate had its own target directory, each crate would have to recompile each of the other crates in the workspace to have the artifacts in its own target directory. By sharing one target directory, the crates can avoid unnecessary rebuilding.


freestandingオプションを探してたら、ちょうどそれのサイトを見つけた。
https://os.phil-opp.com/ja/freestanding-rust-binary/
必要なことは全部ここに書いてあった。

> Rust コンパイラが _start という名前の関数を実際に出力するように、#[no_mangle] attributeを用いて名前修飾を無効にします。

> 戻り値の型である ! は関数が発散している、つまり値を返すことができないことを意味しています。エントリポイントはどの関数からも呼び出されず、OS またはブートローダから直接呼び出されるので、これは必須です。なので、値を返す代わりに、エントリポイントは例えば OS の exit システムコールを呼び出します。

と思ったんだけど、`x86_64-efl`をターゲットにしたカーネルのコンパイルができなかったので今日はここまで。