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]]。