ラズパイ２＋RustでOSはじめの一歩


大仰なタイトルにしてしまいましたが、
http://wiki.osdev.org/Raspberry_Pi_Bare_Bones
ここの内容の一部をRustに置き換えてみようというだけの記事です。
qemuを使うので実機がなくても大丈夫です。

開発環境はDebian testing(stretch)です。

# 準備

## qemuインストール

```bash
$ sudo aptitude install qemu-system-arm
```

## ツールチェインインストール

Debianにはgcc-arm-none-eabiパッケージが存在しているのでインストールするだけです。

```bash
$ sudo aptitude install build-essential gcc-arm-none-eabi
```

## Rustインストール

[rustup](https://www.rustup.rs/)を使います。

```bash
$ curl https://sh.rustup.rs -sSf | sh
$ source $HOME/.cargo/env
```

ビルドにはnightlyが必要です。

```bash
$ rustup default nightly
```

ターゲットにarm-none-eabiが無いので、替わりにarm-unknown-linux-gnueabiを使用します

```bash
$ rustup target add arm-unknown-linux-gnueabi
```

似たターゲット名にarm-unknown-linux-gnueabihfがありますが、こちらを使うとビルドエラーが出るので注意です。[^1]
[^1]: arm-unknown-linux-gnueabihfのhfはhard floatの略で、こちらを使う場合はCコードのコンパイルやリンク時に`-mfloat-abi=hard`等の指定が必要になってくると思われます

# まずはRust抜きで

http://wiki.osdev.org/Raspberry_Pi_Bare_Bones
ここの内容通りに進めていくだけです。
いちいちコマンド打たなくて良いようにMakefileを作りましょう。

```mf:Makefile
#!/usr/bin/make

CC = arm-none-eabi-gcc
CFLAGS =  -mcpu=arm1176jzf-s -fpic -ffreestanding -std=gnu99 -O2 -Wall -Wextra
ASM_FLAGS = -mcpu=arm1176jzf-s -fpic -ffreestanding
OBJ = boot.o kernel.o

kernel.elf: ${OBJ}
        ${CC} -T linker.ld -o $@ -ffreestanding -O2 -nostdlib ${OBJ}

boot.o: boot.S
        ${CC} ${ASM_FLAGS} -c $< -o $@

kernel.o: kernel.c
        ${CC} ${CFLAGS} -c $< -o $@

clean:
        rm -f *.o *.elf

.PHONY: clean
```

ビルドしてqemuで実行してみます。

```bash
$ make
$ qemu-system-arm -m 256 -M raspi2 -serial mon:stdio -kernel kernel.elf
Hello, kernel World!
```

# `kernel_main()`をRustにしてみる

いよいよRustを使ってみます。

```rust:main.rs
#![feature(lang_items)]
#![crate_type = "staticlib"]
#![no_std]

extern {
    fn uart_init();
    fn uart_puts(data: &'static str);
}

#[no_mangle]
pub extern fn kernel_main() {
    unsafe {uart_init();}
    unsafe {uart_puts("Hello, kernel World!\r\n");}

    loop{}
}

#[lang = "eh_personality"]
extern fn eh_personality() {}

#[lang = "panic_fmt"]
extern fn panic_fmt() -> ! { loop {} }

#[no_mangle]
pub extern fn __aeabi_unwind_cpp_pr0 () {}
```

stdlibは使わない(使えない)ため`#[no_std]`で無効にします
使用するCの関数は`extern`で宣言します
`kernel_main()`は外部から呼び出せるように`pub`と`#[no_mangle]`を付けます

kernel.cとMakefileを修正します。

```diff:kernel.c
@@ -112,19 +112,3 @@ unsigned char uart_getc() {
 void uart_puts(const char* str) {
     for (size_t i = 0; str[i] != '\0'; i++) uart_putc((unsigned char)str[i]);
 }
-
-#if defined(__cplusplus)
-extern "C" /* Use C linkage for kernel_main. */
-#endif
-    void
-    kernel_main(uint32_t r0, uint32_t r1, uint32_t atags) {
-    // Declare as unused
-    (void)r0;
-    (void)r1;
-    (void)atags;
-
-    uart_init();
-    uart_puts("Hello, kernel World!\r\n");
-
-    while (1) uart_putc(uart_getc());
-}
```

```diff:Makefile
@@ -3,7 +3,7 @@
 CC = arm-none-eabi-gcc
 CFLAGS =  -mcpu=arm1176jzf-s -fpic -ffreestanding -std=gnu99 -O2 -Wall -Wextra
 ASM_FLAGS = -mcpu=arm1176jzf-s -fpic -ffreestanding
-OBJ = boot.o kernel.o
+OBJ = boot.o kernel.o main.o

 kernel.elf: ${OBJ}
        ${CC} -T linker.ld -o $@ -ffreestanding -O2 -nostdlib ${OBJ}
@@ -14,6 +14,9 @@ boot.o: boot.S
 kernel.o: kernel.c
        ${CC} ${CFLAGS} -c $< -o $@

+main.o : main.rs
+       rustc --target arm-unknown-linux-gnueabi --emit=obj $<
+
 clean:
        rm -f *.o *.elf
```

ビルドして実行してみます。

```bash
$ make clean
$ make
$ $ qemu-system-arm -m 256 -M raspi2 -serial mon:stdio -kernel kernel.elf
Hello, kernel World!
```

無事実行できました。

# 参考

[Raspberry Pi Bare Bones](http://wiki.osdev.org/Raspberry_Pi_Bare_Bones)
[RustでベアメタルRaspberry PiのLチカ](http://keens.github.io/blog/2016/05/04/rustdebeametaruraspberry_pinolchika/)