x86-64 CPU が例外・割り込みを受けたときの制御移行の仕組み。[[concepts/blog-os-rust-kernel]] の CPU 例外章の核を抽出した概念ページ。[[concepts/bare-metal-rust-freestanding]] の上で実装する。

例外の分類

割り込みで例外ハンドラを呼ぶ。x86 には約20種類の例外があり、代表例:

  • page fault: 未マップ/権限違反のメモリアクセス。[[concepts/virtual-memory-mapping-strategies]] と直結。
  • invalid opcode, general protection fault(アクセス違反系)。
  • double fault: 例外ハンドラ実行中の例外、またはハンドラ未登録時に発生。
  • triple fault: double fault ハンドラ処理中に更に例外 → 致命的(CPU リセット/再起動ループ)。ハンドラ未登録だと page fault → double fault → triple fault と連鎖し、QEMU が boot ループでチカチカする。

IDT (Interrupt Descriptor Table)

  • CPU に IDT を登録しておくと、例外番号に対応するエントリのハンドラへ自動でジャンプする。
  • 例外発生時の流れ: 命令ポインタ(rip)と RFLAGS をスタックに push → IDT エントリを参照 → 割り込みゲートならハードウェア割り込みを無効化 → 指定 GDT セレクタを CS にロード → ハンドラへ jmp。

x86-interrupt 呼出規約

  • 例外は関数呼び出しに似るが「いつでも発生しうる」点が異なる。通常の呼出規約では scratch(caller-saved)レジスタ が破壊される前提だが、例外は任意命令間で割り込むため caller 側に退避コードが無い。
  • そこで extern "x86-interrupt" を付けると、上書きされる全レジスタ(preserved/scratch 両方)を自動でバックアップ・復元するコードが生成される。
  • 割り込みスタックフレーム: sp を16バイトアラインし、旧 sp / RFLAGS / rip / cs / (あれば)エラーコードを push してからハンドラを呼ぶ。x86-interrupt がこの一連を隠蔽する。
  • int3 (= 0xcc) はブレークポイント例外を発生させ、デバッガの一時停止に使われる。[[concepts/ptrace-syscall-tracing]] の breakpoint セットと同根。

GDT / TSS / IST によるスタックオーバーフロー対策

  • ガードページ(スタック底の特別なページ)でスタックオーバーフローを page fault として検出。だが割り込みスタックフレームを壊れたスタックに push しようとして失敗 → double → triple fault になる。
  • 解決: IST (Interrupt Stack Table) — 例外専用スタック(最大7本)の sp 配列を用意し、特定例外は別スタックに切り替えて処理する。
  • TSS (Task State Segment): 64bit ではタスク固有情報を持たなくなり、特権スタックテーブルと IST の2つを保持する構造体。IST は TSS の一部。
  • GDT (Global Descriptor Table): 歴史的にはセグメンテーション用だが、64bit でも kernel/user モード設定と TSS ロードのために必要。CPU へ教える手順は CS 再ロード → load_tss → IDT エントリ更新(double_fault に IST index を割当)。

関連

  • 実装支援 crate x86_64 / bootloader[[entities/rust-osdev-tooling]]
  • ページング各論は [[concepts/xv6-paging]] / [[concepts/virtual-memory-mapping-strategies]]