Rustで自作OSを起動しようとして、真っ黒画面と殴り合っている話(途中経過)
※ 本記事では Working Set 昇格の内部評価ロジックそのものを断定するものではない。
あくまで観測可能な挙動(WS / PageFault の推移)から、
「アクセスの継続性(滞在時間)が影響しているように見える」
という傾向を示したものである。
本記事の検証は筆者の環境・条件での観測結果であり、
製品の不具合や仕様を断定するものではありません。
同様の結果が再現されるかは環境差があります。
はじめに
Rust で OS を書く、いわゆる Rust OS 開発に手を出している。
目的はシンプルで、
QEMU 上で
自作カーネルを起動して
画面に「何か」を表示する
──ただそれだけ。
……のはずだった。
結果から言うと、
画面は真っ黒、時々一瞬で消える、たまに SMM と例外ログが洪水のように流れる。
今回はその 「まだ完成していない段階」までの記録をまとめる。
開発環境
OS: Windows
Rust: nightly
target: x86_64-unknown-none
bootloader: 0.9.33
実行環境: QEMU (qemu-system-x86_64)
参考: Writing an OS in Rust 系資料
最初の目標
まずはこれ。
VGA テキストモード(0xb8000)
画面に文字を1文字でも出す
HI
これが出れば勝ち。
🔥 panic が教えてくれたこと
― RustOS 最初の“本当のエラー” ―
OS 開発を進めていく中で、最初に画面を赤く染めたのがこのエラーだった。

panicked at src/page_table.rs:105:25:
failed to map segment starting at Page(4KiB) …
PageAlreadyMapped(PhysFrame(4KiB))
最初にこれを見たときは、正直こう思った。
「またどこかおかしいことをしてるな」
でも後から振り返ると、これは**単なるクラッシュではなく、RustOS が“自分で自分を守った瞬間”**だった。
🧨 panic は「暴走を止めるブレーキ」
この panic は、Rust のランタイムが出しているものではない。
自分で書いた OS コードの中で発火している panicだ。
意味は単純で、でも重い。
すでに割り当て済みの物理フレームに
再度マッピングしようとした
それを「異常」として検出し
panic で即座に止めた
もし panic がなければどうなっていたか。
👉 ページテーブル破壊
👉 メモリの二重使用
👉 原因不明の暴走
👉 デバッグ不能な闇
panic は敵ではなく、最後の安全装置だった。
🧠 「PageAlreadyMapped」が意味する設計ミス
このエラーが示しているのは、
単なる実装ミスではなく 設計レベルの問題。
仮想アドレスと物理アドレスの対応関係
ブート時にどこまでマッピングするか
すでに bootloader が確保している領域
OS 側が追加で確保しようとしている領域
これらの境界が曖昧なまま進めていたことが原因だった。
つまりこの panic は、
「今のまま進むと、必ず後で詰む」
と教えてくれていた。
🔴 panic が出たからこそ、次に進めた
panic を無理やり消すこともできた。
unwrap() を消すことも、Result を無視することもできた。
でもそれをしなかった。
代わりに、
どこでマップしているのか
すでにマップ済みかどうか
bootloader の責務はどこまでか
を 真正面から考えることになった。
結果として、
ページングの理解が一段深くなり
QEMU の -d int を使うようになり
CPU 例外ログが読めるようになった
panic は「失敗」ではなく、OS開発者としてのレベルが1段上がった合図だった。
最初の壁:ビルドは通る、でも画面は真っ黒
cargo +nightly bootimage
は通る。
qemu-system-x86_64.exe -drive format=raw,file=bootimage-rustos.bin
も起動する。
でも画面は真っ黒。
落ちていない。
再起動もしない。
何も映らない。
画面が一瞬で消える問題
オプションを追加。
-no-reboot
→ 一瞬で消えなくなった
→ つまり例外は起きている
さらに:
-d int
地獄のようなログが流れる
QEMU から吐き出されるログ(一部抜粋):
check_exception old: 0xffffffff new 0xe
RIP=0000000000000000
CR2=0000000000000000
Page Fault
RIP = 0
IDT 未設定
そのまま Double Fault
👉 完全に「OS 初期化途中で死んでる」状態

BootInfo に framebuffer が無い問題
「じゃあ framebuffer で描画しよう」と思い、
boot_info.framebuffer
を書いたら即エラー。
error[E0609]: no field `framebuffer` on type `BootInfo`
原因
bootloader 0.9.x には framebuffer は無い
feature でも存在しない
VGA か vga_320x200 を使うしかない
👉 情報の世代差トラップ
lazy_static / spin が std を要求して死ぬ
さらに進めると:
error[E0463]: can't find crate for `std`
原因:
x86_64-unknown-none
no_std
lazy_static が内部で extern crate std
👉 OS カーネルで std は使えない
「真っ黒=失敗」ではないと気づく
ここで大事な気づき。
真っ黒でも QEMU は生きている
-no-reboot を付けると止まる
例外ログが出る=カーネルは実行されている
つまり、
「表示されていない」だけで「起動していない」わけではない
現在地(正直な状態)
✅ bootimage は作れている
✅ QEMU は起動している
✅ カーネルには入っている
❌ VGA 書き込みが見えていない
❌ entry_point / 初期化周りを調整中
❌ IDT 未設定で例外即死
ここまでやって分かったこと
Rust OS は「1行ズレるだけで無音で死ぬ」
世代違いの記事は平気で嘘を書く
真っ黒画面は「最初の関門」
QEMU の -no-reboot は神
ログを読む力がすべて
次にやること(自分用メモ)
entry_point!(kernel_main) の再確認
linker.ld を一旦捨てる
VGA 0xb8000 への直書き最小コードに戻す
IDT を最低限用意する
framebuffer に逃げない(まだ早い)
おわりに
まだ完成していない。
でも、この段階の記録は未来の自分と誰かを確実に救う。
Rust で OS を書くのは、
「動いたら勝ち」じゃない。
「なぜ動かないか説明できたら勝ち」
続きは、
画面に H が出た日にまた書く。
