2 ポイント 投稿者 GN⁺ 2025-10-26 | 1件のコメント | WhatsAppで共有
  • コンピュータの電源ボタンを押した瞬間からLinuxカーネルが実行されるまでの過程を、段階ごとに説明した技術解説
  • CPUがリアルモード(real mode) で起動し、プロテクトモード(protected mode)ロングモード(long mode) に移行する過程を具体的に扱う
  • BIOS/UEFIファームウェアブートローダー(GRUB)カーネルの展開とアドレス再配置など、各段階の役割と動作原理を詳細に記述
  • メモリマッピング、割り込み、ページテーブル、kASLR など、カーネル初期化に必要な中核概念を簡潔な例とともに説明
  • Linuxブートの内部メカニズムを理解することで、システムアーキテクチャ、セキュリティ、性能最適化に関する洞察を提供

Part 1 — 電源ボタンからカーネルの最初の実行まで

  • 電源ボタンを押すと、CPUはリアルモード(real mode) にリセットされ、最初の命令を実行する

    • リアルモードは8086時代から存在する単純なアドレス体系で、セグメント(segment)オフセット(offset) を組み合わせて物理アドレスを計算する
    • 例: physical_address = (segment << 4) + offset
    • CPUはリセット後、0xFFFFFFF0 アドレス(リセットベクタ)へジャンプし、ファームウェアに制御を渡す
  • レジスタ(register) はCPU内部の超高速ストレージスロットで、CS(コードセグメント)、IP(命令ポインタ)などがある

    • CSは現在のコード位置、IPは次に実行する命令を指す

BIOSとUEFI

  • BIOS は旧来のファームウェアで、POST(電源投入時自己診断)の後にブート順序を確認し、起動可能なディスクを探索する
    • 起動可能なディスクは、先頭512バイトセクタの末尾が 0x55AA で示される
    • BIOSはこのセクタを 0x7C00 アドレスへコピーしてからジャンプし、実行する
  • UEFI は現代的な代替技術で、ファイルシステムを直接理解し、より大きなブートプログラムを読み込める
    • BIOSと異なり「最初のセクタ」という制約がなく、OSへより豊富なシステム情報を渡せる

ブートローダー(bootloader)

  • ブートローダー はカーネルをメモリに読み込み、実行準備を整えるプログラム
    • 代表的には GRUB が使われ、設定ファイルを読み、カーネルと初期RAMディスク(initrd)をメモリへロードする
    • カーネルファイルは、リアルモード用の小さなセットアッププログラム圧縮されたカーネル本体 で構成される
    • GRUBはカーネル位置、コマンドライン、initrd位置などの情報を setup header 構造体に記録した後、カーネルのセットアップコードへジャンプする

セットアッププログラム(setup code)

  • カーネル実行前に予測可能な作業空間を作る役割を果たす
    • セグメントレジスタ(CS, DS, SS)を揃え、direction flag をクリアしてメモリコピーが一定に動作するよう設定する
    • スタック(stack) を作成し、関数呼び出し時の一時データを保存する
    • BSS領域(初期値0で始まるべきグローバル変数領域)を0で初期化する
  • earlyprintk オプションがあれば、シリアルポートを設定して初期デバッグメッセージを出力できる
  • ファームウェアにRAMマップ(e820) を要求し、使用可能なメモリ領域と予約領域を把握する
  • すべての準備が終わると、最初のC関数である main を呼び出し、その後モード移行段階へ入る

割り込み(interrupt)

  • 割り込み はCPUが現在の作業を一時停止し、緊急処理を行うメカニズム
    • キー入力、タイマーなどのハードウェアイベントが代表例
    • マスク可能割り込み は一時的に遮断でき、NMI(Non-Maskable Interrupt) は常に処理される
    • モード移行中は予期しない割り込みを防ぐため、一時的に遮断する

Part 2 — リアルモードから32ビット、そして64ビットへ

  • 現代のLinuxはx86_64アーキテクチャのロングモード(long mode) で動作する
    • リアルモード → プロテクトモード → ロングモードの順に段階的な移行が必要

プロテクトモード(protected mode)

  • 1980年代の限界を超えるために導入された32ビットモードで、2つの中核構造を持つ
    • GDT(Global Descriptor Table) : セグメントの開始アドレス、サイズ、権限を定義する
      • Linuxはフラットモデル(flat model) を使い、32ビット空間全体を1つの連続領域として単純化する
    • IDT(Interrupt Descriptor Table) : 割り込み発生時に呼び出すハンドラのアドレスを保存する
      • ブート中は最小限のIDTだけをロードし、カーネル初期化後に完全なIDTを設置する

モード移行過程

  • セットアップコードはまず 割り込み無効化PICチップ停止A20ライン有効化数値演算コプロセッサ初期化 を実行する
    • A20ラインは1MBアドレスのラップアラウンド問題を解決するための歴史的な仕組み
  • 最小限のGDTとIDTをロードした後、CR0レジスタのPEビット を設定して far jump を行う
    • これによりプロテクトモードへ入り、セグメントとスタックポインタを新しいアドレス体系に合わせて再設定する

制御レジスタ(control registers)

  • CR0: プロテクトモードを有効化
  • CR3: ページテーブルの最上位アドレスを保存
  • CR4: PAEなどの拡張機能を有効化

ロングモード(long mode)移行の準備

  • 64ビットモードへ移行するには、2つの条件が必要
    • ページング(paging) の有効化: 仮想アドレスと物理アドレスの間のマッピングを行う
    • EFERレジスタのLME(Long Mode Enable) ビットの設定
  • ページテーブルは4KB単位のページをマッピングするが、初期ブート時には2MB単位の identity map で単純に構成する

ページング有効化手順

  • PAE 機能をCR4で有効にし、低位アドレス領域を2MB単位でカバーする最小限のページテーブルを作成する
  • 最上位テーブルのアドレスをCR3に記録した後、ページングを有効化する
  • EFERのLMEビットをセットし、64ビットコードへジャンプして ロングモードへ移行 する
  • アドレスとレジスタが64ビット幅へ拡張された状態で、カーネル実行の準備が整う

Part 3 — カーネルの展開、アドレス修正、そして自己移動

  • ここでCPUは64ビットモードにあり、メモリ上には圧縮されたカーネルイメージ が存在する
    • 小さな64ビットスタブ(stub)がカーネルを展開し、アドレスを調整する役割を担う

初期整理と安全装置の設定

  • スタブは自分の実際の実行位置を計算し、カーネルが重なる場合は 自己コピー(self-relocation) によって安全な位置へ移動する
  • 自身の BSS領域を初期化 し、簡易IDT をロードする(ページフォルトとNMIハンドラを含む)
    • ページフォルトが発生した場合は、欠けているマッピングをただちに追加して復旧する
  • カーネル、ブートパラメータ、コマンドラインバッファなど、必要な領域に対する identityマッピング を作成する

カーネルの展開

  • extract_kernel 関数が実行され、カーネルの圧縮を展開する
    • gzip、xz、zstd、lzo など、さまざまな圧縮アルゴリズムをサポート
    • 展開後は ELFヘッダ を読み、コード/データセクションを正しいアドレスへコピーする
  • カーネルがビルドされたアドレスと実際のロードアドレスが異なる場合は 再配置(relocation) を行う
    • アドレスを含む命令やポインタを修正し、実際のメモリ位置に合わせる
  • すべての準備が終わると start_kernel 関数 へジャンプし、本格的なカーネル初期化が始まる

カーネルの自己移動(kASLR)

  • kASLR (Kernel Address Space Layout Randomization) は、カーネルの物理・仮想アドレスをランダム化して攻撃の難易度を高める
    • ブート時に2つのベース(base)をランダムに選ぶ
      • 物理ベース: カーネルが実際に配置されるRAMアドレス
      • 仮想ベース: カーネルが使用する仮想アドレスの開始点
  • 選択過程
    • ブートローダー、initrd、コマンドラインバッファなど、保護すべき領域の一覧 を作成する
    • ファームウェアのメモリマップを走査し、十分に大きい空き領域を探す
    • ハードウェア乱数命令などから得た エントロピー でランダムなスロットを選ぶ
  • 適切な領域がなければデフォルトアドレスに戻り、nokaslr オプションを指定するとランダム化を無効化する

用語まとめ

  • Hexadecimal(16進数) : 0x 接頭辞で表し、ハードウェアのビット構造やアラインメントに向いている
  • Register: CPU内部の一時記憶領域 (CS, DS, SS, IP, SP など)
  • Segment/Offset: リアルモードのアドレス計算方式 (segment * 16 + offset)
  • BIOS/UEFI: システム初期化とブートプログラムのロードを担うファームウェア
  • Bootloader(GRUB) : カーネルのロードとシステム情報の受け渡しを行う
  • Stack/BSS: 関数の一時保存領域と、0で初期化されたグローバル変数領域
  • Interrupt/NMI: ハードウェア・ソフトウェアイベントの処理メカニズム
  • GDT/IDT: セグメントおよび割り込み定義テーブル
  • A20 Line: 1MBアドレスのラップアラウンドを防ぐスイッチ
  • Protected Mode/Long Mode: 32ビットおよび64ビットの実行モード
  • Paging/Page Tables: 仮想アドレスと物理アドレス

1件のコメント

 
GN⁺ 2025-10-26
Hacker Newsのコメント
  • 本当に良い記事だった。数か月前に自分もLinuxのブートプロセスについて記事を書いたが、ディスクI/Oの観点(ディスク上に何があり、どう読み込まれるか)にもう少し焦点を当てていた。 自分の記事はBooting x86-64で読める
  • ページソースにこんなコードがあった。
    
      uwu
    
    
    これは何なんだろうという感じだった
    • まだ開発中の機能
  • UEFIはファームウェアが実装するインターフェースだ。つまり、ファームウェアそのものではなく、ファームウェアと対話するための標準インターフェースという意味だ。 「UEFIがマシンを起動する」という表現は少し用語として不正確だ。実際にはファームウェアがマシンを起動し、私たちはUEFIを通じてファームウェアと通信している。 この記事では現代のファームウェアの興味深い部分がかなり省略されている。たとえば ExitBootServices() を呼ぶ時点で、すでにlong modeに入っている。わざわざ real/protected モード移行の過程をたどる必要はない
    • こういう内容をさらに読める資料がどこかにないか気になる
  • 電源ボタンが本当にCPUを直接起動しているのか気になった。おそらくIntel Management Engine(またはAMDの類似機能)が常時動作していて、電源ボタンの信号を受けてCPUを起動する構造なのかもしれない
    • その役割はEmbedded Controller(EC) が担っているのだと思う。ブートローダーがCPUを立ち上げる前に動作する部分だ。 Chromebookではこの部分がcorebootブートローダーと一緒にオープンソースで公開されている。 関連文書はChromium EC Zephyr READMEで読める
  • この記事よりもう少し詳細なバージョンの記事を探している。マイクロプロセッサのデータシートを丸ごと読みたいわけではないが、UEFI/BIOS以前の段階まで、もう少し深く扱う資料があると嬉しい
  • 記事自体は面白いが、一方でなぜ16進数(HEX) の説明まで必要なのか疑問でもあった。このレベルの記事を読む人がそれを知らないだろうか、と思った。 もう一つ気になるのは、物理的な電源ボタンを押した瞬間にどうやってreset vectorにつながるのかという点だ。これはハードウェアと電子回路の魔法のような領域だ
    • HNはIT専門家だけが見る場所ではない。Linuxのブートプロセスに興味はあっても、16進数の概念をよく知らない人や忘れている人もいるだろう
  • テーマは興味深いが、初心者向けすぎるように感じた
    • 「電源が安定するとCPUはreal modeにリセットされる」といった説明を見ると、うちの祖母がこういうことを理解できるほど詳しい人なのだろうか、という気分になった
    • 大学でオーディエンス分析(audience analysis) を学んだが、どんな知識を既知として前提にするか、どの用語や略語を説明するかを判断するのは重要だ。技術ライティングではこれは一種の芸術だ
  • モバイルでは読みづらかった。文字色が薄すぎる
    • デスクトップブラウザでもスタイリングがいまひとつだった。FirefoxやFirefox Mobileのリーダーモードを使うと、ずっと読みやすくなる
    • 少し自虐的なデザインのようにも見えた
  • GRUBには触れられていたが、詳しくは扱われていなかった。 関連してはPixelbeatのディスク構造ドキュメントが役に立つ
  • この記事を見て、昔Facebookの技術面接を受けたことを思い出した。2010年ごろ、Production Engineer職の電話面接で、「Linuxサーバーのブートプロセスを説明してください」という質問を受けた。 さらに深く説明しろというヒント以外は何の助けもなかった。結局ダブリンには引っ越さなかったし、まあ surveillance capitalism がどうこうで、まだ酸っぱいブドウだったということだ