OS自作をRustに切り替えるかはまだ模索中だけれど、まずはx86-pc上で「Hello x86!!」を表示するカーネルこと始めをしてみた。
やりたいこと
- OSとカーネルはGitリポジトリを別にする
- IBM PC/AT互換機上(QEMU)で動作させる
- ブートにGNU GRUBを使用する
- カーネルのファイル形式はELFを使用する
環境を作る
nightly版rustをインストールしておく
参考:付録G: Rustの作られ方と“Nightly Rust”
$ rustup install nightly
ディレクトリを作る
MochiOSというプロジェクトディレクトリ配下に作っていく。
とりあえず、OSで使用するカーネルは別プロジェクトとしたいので、depsディレクトリ内にkernelプロジェクトcargoで作る。
また、kernelはnightly版rustを使用する前提にしておく。
$ mkdir MochiOS
$ cd MochiOS
$ git init
$ mkdir deps
$ cd deps
$ touch .gitkeep
$ cargo new kernel
$ cd kernel
$ rustup override set nightly
OSプロジェクトではdeps配下のディレクトリを管理したくないので、.gitignore、deps/.gitkeepを作成しておく。
# .gitignore
/deps/*
今は作成しないけど、deps配下の各サブプロジェクトはクローンしてくるシェルスクリプトを作る予定。
「Hello x86!!」を表示するコードを書く
標準ライブラリの無効化、Panic処理の実装、VGAのテキストバッファに文字列「Hello x86!!」の書込みを行って無限ループ。
// MochiOS/deps/kernel/src/main.rs
#![no_std]
#![no_main]
use core::panic::PanicInfo;
const VGA_BUFFER: *mut u8 = 0xB8000 as *mut u8;
#[panic_handler]
fn panic( _info: &PanicInfo ) -> !
{
loop {}
}
#[unsafe( no_mangle )]
pub extern "C" fn _start() -> !
{
let str = b"Hello x86!!";
for ( idx, &byte ) in str.iter().enumerate() {
unsafe {
*VGA_BUFFER.add( idx * 2 ) = byte;
*VGA_BUFFER.add( idx * 2 + 1 ) = 0x0F;
}
}
loop {}
}
multibootヘッダを書く
これ書いとけばGRUBがカーネルをロードしてくれるってよ。(自前でローダ用意しなくていいから楽だなぁ)
// MochiOS/deps/kernel/src/multiboot.rs
#[repr( C ) ]
pub struct MultibootHeader {
magic : u32,
flags : u32,
checksum: i32,
}
#[unsafe( link_section = ".multiboot" )]
#[unsafe( no_mangle )]
pub static _MULTIBOOT_HEADER: MultibootHeader = MultibootHeader {
magic : 0x1BADB002,
flags : 0,
checksum: -( 0x1BADB002 as i32 ),
};
ファイル追加したのでmain.rsにモジュール宣言を追記。
// MochiOS/deps/kernel/src/main.rs
module multiboot
コンパイルする
ターゲットファイルを作る
MochiOS/deps/kernel/src/x86_32.json(JSONってコメント書けないからパス表示を欄外に・・・)
{
"llvm-target" : "i686-unknown-none",
"data-layout" : "e-m:e-p:32:32-p270:32:32-p271:32:32-p272:64:64-i128:128-f64:32:64-f80:32-n8:16:32-S128",
"target-pointer-width": 32,
"arch" : "x86",
"vendor" : "unknown",
"os" : "none",
"linker" : "rust-lld",
"linker-flavor" : "ld.lld",
"disable-redzone" : true,
"panic-strategy" : "abort"
}
リンカスクリプトを書く
// MochiOS/deps/kernel/src/x86_32.lds
ENTRY( _start )
MEMORY {
KERNEL : ORIGIN = 0x00100000, LENGTH = 0x100000
}
SECTIONS {
.multiboot : {
KEEP( *( .multiboot ) )
} > KERNEL
.text : {
*( .text* )
} > KERNEL
.rodata : {
*( .rodata* )
} > KERNEL
.data : {
*( .data* )
} > KERNEL
.bss : {
*( .bss* )
*( COMMON )
} > KERNEL
}
cargoの設定を書く
# MochiOS/deps/kernel/.cargo/config.toml
[unstable]
build-std = [ "core" ]
json-target-spec = true
[build]
target = "./src/x86_32.json"
[target.'cfg( all( target_arch = "x86" ) )']
rustflags = [ "-C", "link-arg=-Tsrc/x86_32.lds" ]
実行する
イメージファイルのディレクトリを作る
$ cd MochiOS
$ mkdir -p iso/boot/grub
カーネルを置く
$ copy deps/kernel/target/x86_32/debug/kernel iso/boot/
grub.confを作る
# MochiOS/iso/boot/grub/grub.cfg
set timeout=0
set default=0
menuentry "MochiOS" {
multiboot /boot/kernel
boot
}
イメージファイルを作る
$ grub-mkrescue -o mochios.iso iso/
QEMUで実行する
デスクトップ環境でなはないので、VNCサーバを立ち上げて別PCからリモート接続。
$ qemu-system-i386 -cdrom mochios.iso -vnc :0

できた。
手こずりpoints
ldスクリプトが反映されない
試行錯誤中にreadelf -a kernelして確認すると.multibootセクションが作られていなくて。
原因は、cargo君がファイル依存関係でldスクリプトを知らないので、更新されてもコンパイルし直しが発生しなかった。
なので、下記コマンドで対応。
$ cargo clean
$ cargo build
(っていうかtargetディレクトリの容量がえげつないなrust・・・coreとかbinutlもクロス先用にコンパイルしているからなんだろうけど)
流石に、手で毎回clean打つのはめんどくさいので、ldスクリプトファイルをcargo君に監視してもらうようにした。
# MochiOS/deps/kernel/Cargo.toml
[package]
build = "build.rs"
# MochiOS/deps/kernel/build.rs
fn main()
{
println!( "cargo::rerun-if-changed=./src/x86_32.lds" );
}
grub-rescue実行してもISOファイルが生成されない
下記インストールで対応
$ sudo apt install grub-pc-bin xorriso
- grub-pc-binは、「DebianやUbuntuなどのLinux環境において、BIOSベースのPCでGRUB 2ブートローダを使用するために必要な、GRUBのコアイメージ(モジュール)群を収録したバイナリパッケージ」だそう。
- xorriso(えぐぞりっそ)は、「ISO 9660(Rock Ridge拡張対応)イメージの作成、操作、およびCD/DVD/BDメディアへの書き込みをコマンドラインで行う多機能ツール」だそう。
0 件のコメント:
コメントを投稿