割り込み - kentakozuka/yetos GitHub Wiki

mermaid-diagram-2022-07-28-073924

mermaid
sequenceDiagram
    participant input as Input
    participant cpu as CPU
    participant ram as RAM
    participant os as OS
    os->>cpu: Request to allocate IDT
    cpu->>ram: Allocate IDT
    input->>cpu: Send interrupt event
    cpu->>os: Run function
    os->>cpu: Return interrupt function
Loading
  • 事前準備
    • イベント発生時に実行する割り込みハンドラを準備
    • 割り込みハンドラをIDT(割り込み記述子テーブル)に登録
  • イベント発生時
    • ハードウェア(場合によってはソフトウェア)がイベントをCPUに通知
    • CPUは現在の処理を中断し、イベントの種類に応じて登録された割り込みハンドラに処理を移す
    • 割り込みハンドラの処理が終わると、中断していた処理を再開

割り込み処理の終了

割り込み処理が終わった時点でCPUに対して、処理の終了を伝える必要がある。 0xfee00000 から fxfee00400 までの1024バイトの範囲にはメインメモリには配置されておらず、CPUのレジスタが置かれている。ここに書き込むことで割り込み処理の終了をCPUに伝える処理が実行される。

void NotifyEndOfInterrupt() {
  volatile auto end_of_interrupt = reinterpret_cast<uint32_t *>(0xfee000b0);
  *end_of_interrupt = 0;
}
  • volatile 修飾子をつけることでC++の最適化の対象から外す。これをつけないとコンパイラは *end_of_interrupt = 0; によって書き込んだ値が他の場所で利用されないため、メモリ書き込み命令を省く可能性がある

割り込みハンドラ

usb::xhci::Controller *xhc;

__attribute__((interrupt)) void IntHandlerXHCI(InterruptFrame *frame) {
  main_queue->Push(Message{Message::kInterruptXHCI});
  NotifyEndOfInterrupt();
}
  • __attribute__((interrupt)) で割り込みハンドラだということをコンパイラに伝える

割り込みベクタ

  • 割り込みを種類別に 割り込みベクタ(割り込み要因番号)を割り振って管理する
  • 固定で番号が振られているものと独自に番号を決めてよいものがある
  • x86-64では 0-255のいずれかになる。

割り込み記述子

  • Interrupt Descriptor
  • 割り込みについての詳細情報が入っている
Screen Shot 2022-07-25 at 7 43 04

リトルエンディアンなので、右から読む

フィールド 説明
offset_low, offset_middle, offset_high 割り込みハンドラのアドレスを設定する。3つ合わせて64ビットアドレスを指定する。
segment_selector 割り込みハンドラを実行する際のコードセグメント(実行可能コードが置いてあるメモリ区画).
attr 割り込み記述子の属性を設定する。typeは記述子の種別(14 or 15)。
descriptor_privilege Descriptor Privilege Level(DPL)。割り込みハンドラの実行権限(0-3)。
interrupt_stack_table IST。はみかん本では常に0。
present 記述子の有効フラグ。

割り込み記述子テーブル(IDT)

  • IDT: Interrupt Descriptor Table
  • 割り込みベクタと割り込みハンドラを対応付けるテーブル
  • 割り込み記述子を256個並べた配列
  • メインメモリ上に配置する
  • 割り込みハンドラをIDTに登録することで、実際に割り込みが発生した際にハンドラが呼び出される
Screen Shot 2022-07-27 at 8 53 27

MSI割り込み

  • Message Signaled Interruptの略。
  • 特定のメモリアドレス(Message Address)に対して32bitの値(Message Data)をを書き込む
  • メモリアドレスと値のフォーマットはCPU側の仕様で規定されている

x86-64の場合は以下

Screen Shot 2022-07-28 at 7 18 09

割り込みまとめ

  • 割り込みを扱う為には、割り込みハンドラ、割り込み記述子、割り込み発生源の設定が必要
  • 割り込みハンドラは __attribute__((interrupt))をつけ、処理の最後に End of Interruptレジスタへの値の書き込みをする
  • 割り込み記述子はメインメモリ上に作成したIDTの1つの要素で、割り込みハンドラのアドレスや各種属性を設定する
  • IDTは最大256個の配列。それぞれ0-255の割り込みベクタに対応する
  • IDTの先頭アドレス、及び大きさをlidt命令によりCPUに登録する
  • xHCIではMSI(MSI-X)方式で割り込みを発生させるため、Message Address 及び Message Dataの設定をする
⚠️ **GitHub.com Fallback** ⚠️