Edited at

Writing an OS in Rust (Second Edition) #2


Writing an OS in Rust (Second Edition)第二章

https://os.phil-opp.com/minimal-rust-kernel/

これをやった記録


buildターゲットを変更する

cargo build時に--targetフラグを用いてターゲットが指定できる.ターゲットはjsonで記述できる.osnoneにしてpanic-strategabortにする.これでpanic時の挙動が変更できる.またリンカをlldに変更する.次にredzonedisableにする.

{

"llvm-target": "x86_64-unknown-none",
"data-layout": "e-m:e-i64:64-f80:128-n8:16:32:64-S128",
"arch": "x86_64",
"target-endian": "little",
"target-pointer-width": "64",
"target-c-int-width": "32",
"os": "none",
"executables": true,
"linker-flavor": "ld.lld",
"linker": "rust-lld",
"panic-strategy": "abort",
"disable-redzone": true,
"features": "-mmx,-sse,+soft-float"
}


Red Zone

https://os.phil-opp.com/red-zone/

Red Zoneは呼び出された関数がスタックポインタを調整しなくても自由に使える128byteの領域である.これはSystem V ABIで定義されている.x86ならローカル変数を用いるなら以下のようにスタックポインタ(esp)から確保する分の領域を引くことにより調整を行うが,x64ではする必要がない.

push ebp

mov ebp, esp <- 調整1
sub esp, 0x30 <-調整2

この2つの調整用の命令を省略できてうれしいのかはわからないが省略できるのがRed Zone

しかしRed Zoneには弱点がある.

それは例外発生時とハードウェア割り込み時である.

Red Zoneを使用しているときに例外処理が起こった場合,スタックポインタが調整されてない場合,Red Zoneの値が上書きれてしまう.スタックポインタ調整しておけば引いた分はうわがきされることはない.

カーネルでこのようなバグをうめこむのは面倒なので今回はRed Zoneは無効にしておく.


カーネルを書く

RustはLLVM基盤を用いているので,Linuxっぽい規則で書かなければならない.

#![no_std]

#![no_main]

// LLVMによりLinuxっぽい規則で
#[no_mangle]
pub extern "C" fn _start() -> ! {
loop {}
}

use core::panic::PanicInfo;
#[panic_handler]
fn panic(_info: &PanicInfo) -> ! {
loop{}
}

早速ビルドしてみるが失敗する.

☻  cargo build --target x86_64-weed.json

Compiling weed-os v0.1.0 ()
error[E0463]: can't find crate for `core`
|
= note: the `x86_64-weed-6876261073273951800` target may not be installed

error: aborting due to previous error

For more information about this error, try `rustc --explain E0463`.
error: Could not compile `weed-os`.

To learn more, run the command again with --verbose.

以下の2つのcrateがたりないようだ

まずcoreはバイナリで提供されるので,カスタムターゲット向けにビルドしなければならない.

まずは以下のコマンドでcorecompiler_builtinsallocをカスタムターゲット向けにクロスコンパイルするcargoのwrapperであるcargo-xbuildをインストールする.

cargo install cargo-xbuild

次にcargo-xbuildでビルドする.

cargo xbuild --target x86_64-weed.json

Compiling core v0.0.0 (~/.rustup/toolchains/nightly-x86_64-apple-darwin/lib/rustlib/src/rust/src/libcore)
Finished release [optimized] target(s) in 22.36s
Compiling compiler_builtins v0.1.0 (~/.rustup/toolchains/nightly-x86_64-apple-darwin/lib/rustlib/src/rust/src/libcompiler_builtins)
Finished release [optimized] target(s) in 1.98s
Compiling alloc v0.0.0 (/var/folders/17/v3jy0y6d66312zs7lkw0y88h0000gn/T/xargo.FpfzYTuSaCbG)
Finished release [optimized] target(s) in 3.79s
Compiling weed-os v0.1.0 (~/weed/weed-os)
Finished dev [unoptimized + debuginfo] target(s) in 0.19s

このとおりcorecompiler_builtinsallocと自作のプログラムがビルドされる.


VGA text modeを使ってHello, World!

おなじみのアドレス0xb8000に書き込むことでVGA text modeで文字列が出力できる.

これから簡単なドライバを書く.

static HELLO: &[u8] = b"Hello, World!";

#[no_mangle]
pub extern "C" fn _start() -> ! {
let vga_buffer = 0xb8000 as *mut u8;
for (i, &byte) in HELLO.iter().enumerate() {
unsafe {
*vga_buffer.offset(i as isize * 2) = byte;
*vga_buffer.offset(i as isize * 2 + 1) = 0xb;
}
}
loop {}
}

ポインタを使ってunsafeブロック内でデータを格納している.

もちろんRustの安全性を享受するためには,なるべくunsafeブロックを避けなければならない.

後の章でより適したドライバを書くらしい.

このコードは0xb8000をミュータブルなu8へのポインタとしてvga_bufferを宣言している.

offsetメソッドでポインタのアドレスを計算している.isizeはアーキテクチャ依存の符号なし整数で,今回は64bitの大きさ.


ブートイメージを作成する

今回はブートローダは既存のものを使う.

[dependencies]

bootloader = "0.3.4"

Cargo.tomlに依存ライブラリを追加する.

またカーネルとブートローダを最終的に結合してやる必要があるが,cargoにはそのような機能がない.

したがってbootimageを使ってビルドする.

bootimageはkernelとbootloaderを組み合わせてビルドできるツールである.

やることは単純で,kernelをELF形式でビルド,bootloaderをバイナリでビルド.

bootloaderの後ろにkernelをくっつけて終わり.

bootloaderは起動後,kernelのELFを読み込みパースして,仮想メモリ空間にマッピングする.

ページングの設定が終われば,あとはスタックを準備して_start関数にjumpする.

チュートリアルが終わったらUEFIブートにしようと思っているし,ブートローダは書いたことがあるので今回は既存のものを喜んで使う.

cargo install bootimage --version "^0.5.0"

bootimageをインストールしたらビルドする.

bootimage build --target x86_64-weed.json

Building kernel
Compiling libc v0.2.44
Compiling unicode-width v0.1.5
Compiling remove_dir_all v0.5.1
Compiling os_bootinfo v0.2.1
Compiling bit_field v0.9.0
Compiling usize_conversions v0.2.0
Compiling ux v0.1.3
Compiling zero v0.1.2
Compiling bitflags v1.0.4
Compiling font8x8 v0.2.4
Compiling getopts v0.2.18
Compiling xmas-elf v0.6.2
Compiling pulldown-cmark v0.0.3
Compiling rand v0.4.3
Compiling tempdir v0.3.7
Compiling skeptic v0.5.0
Compiling fixedvec v0.2.3
Compiling x86_64 v0.2.14
Compiling bootloader v0.3.8
Compiling weed-os v0.1.0 (~/weed/weed-os)
Finished dev [unoptimized + debuginfo] target(s) in 20.25s
Building bootloader v0.3.8
Compiling core v0.0.0 (~/.rustup/toolchains/nightly-x86_64-apple-darwin/lib/rustlib/src/rust/src/libcore)
Finished release [optimized + debuginfo] target(s) in 23.60s
Compiling compiler_builtins v0.1.0 (~/.rustup/toolchains/nightly-x86_64-apple-darwin/lib/rustlib/src/rust/src/libcompiler_builtins)
Finished release [optimized + debuginfo] target(s) in 2.14s
Compiling alloc v0.0.0 (/var/folders/17/v3jy0y6d66312zs7lkw0y88h0000gn/T/xargo.CQqUXYKkSXbw)
Finished release [optimized + debuginfo] target(s) in 3.96s
Downloaded libc v0.2.43
Downloaded ux v0.1.2
Compiling unicode-width v0.1.5
Compiling libc v0.2.43
Compiling remove_dir_all v0.5.1
Compiling zero v0.1.2
Compiling usize_conversions v0.2.0
Compiling ux v0.1.2
Compiling bitflags v1.0.4
Compiling bit_field v0.9.0
Compiling os_bootinfo v0.2.1
Compiling font8x8 v0.2.4
Compiling getopts v0.2.18
Compiling xmas-elf v0.6.2
Compiling rand v0.4.3
Compiling pulldown-cmark v0.0.3
Compiling tempdir v0.3.7
Compiling skeptic v0.5.0
Compiling fixedvec v0.2.3
Compiling x86_64 v0.2.14
Compiling bootloader v0.3.8 (~/.cargo/registry/src/github.com-1ecc6299db9ec823/bootloader-0.3.8)
Finished release [optimized + debuginfo] target(s) in 21.79s
Creating disk image at target/x86_64-weed/debug/bootimage-weed-os.bin

ビルドが終わったら,qemuで動かす.

qemu-system-x86_64 -drive format=raw,file=target/x86_64-weed/debug/bootimage-weed-os.bin

おわり