Files
Elyz/kernel/docs/cpu/idt.md
2026-07-07 16:40:41 +03:00

4.9 KiB
Raw Blame History

Interrupt Descriptor Table: idt.rs

Аппаратная модель

IDT (Interrupt Descriptor Table) — это таблица из 256 entry (по 16 байт каждая), которая сообщает CPU, куда передавать управление при прерываниях и исключениях.

IDT:
┌──────┬──────────────────────────────────────────────────────┐
│  0   │ #DE — Divide Error                                   │
│  1   │ #DB — Debug                                          │
│  2   │ #NMI — Non-Maskable Interrupt                        │
│  3   │ #BP — Breakpoint                                     │
│  4   │ #OF — Overflow                                       │
│  5   │ #BR — Bound Range Exceeded                           │
│  6   │ #UD — Undefined Opcode                               │
│  7   │ #NM — Device Not Available                           │
│  8   │ #DF — Double Fault                                   │
│  9   │ #MF — Coprocessor Segment Overrun                    │
│ 10   │ #TS — Invalid TSS                                    │
│ 11   │ #NP — Segment Not Present                            │
│ 12   │ #SS — Stack-Segment Fault                            │
│ 13   │ #GP — General Protection Fault                      │
│ 14   │ #PF — Page Fault                                    │
│ 15-31│ Reserved / CPU exceptions                            │
│ 32-255│ User-defined (hardware interrupts)                  │
└──────┴──────────────────────────────────────────────────────┘

Загрузка IDT: инструкция lidt [idtr_ptr], где idtr_ptr — это 6-байтовая структура IdtPtr:

IdtPtr:
┌──────────┬──────────┐
│ limit:16 │ base:48  │
└──────────┴──────────┘

IdtEntry — 16-байтовая запись

#[repr(C, packed)]
pub struct IdtEntry {
    offset_low:  u16,   // Бит 0:15 адреса обработчика
    selector:    u16,   // Селектор сегмента кода (0x28 для ядра)
    ist:         u8,    // Interrupt Stack Table
    type_attr:   u8,    // Тип вентиля + флаги
    offset_mid:  u16,   // Бит 16:31 адреса обработчика
    offset_high: u32,   // Бит 32:63 адреса обработчика
    ignore:      u32,   // Зарезервировано
}

type_attr:

  • Бит 7: Present (должен быть 1)
  • Бит 6-5: DPL (Descriptor Privilege Level)
  • Бит 4: Reserved (0)
  • Бит 3-0: Gate Type (0xE = Interrupt Gate, 0xF = Trap Gate)

set_handler(handler, selector, flags):

pub fn set_handler(&mut self, handler: u64, selector: u16, flags: u8) {
    self.offset_low  = handler as u16;
    self.selector    = selector;
    self.ist         = 0;
    self.type_attr   = flags | 0x80;  // Present bit forced on
    self.offset_mid  = (handler >> 16) as u16;
    self.offset_high = (handler >> 32) as u32;
}

InterruptDescriptorTable — 256 entry

pub struct InterruptDescriptorTable {
    entries: [IdtEntry; 256],  // 256 × 16 = 4096 байт
}

Методы:

  • set_handler(vector, handler) — устанавливает обработчик с селектором 0x28 (GDT code segment) и type_attr 0x8E (Interrupt Gate, Ring 0, Present).
  • load()lidt инструкция.
pub unsafe fn load(&'static self) {
    let ptr = IdtPtr {
        limit: (size_of::<Self>() - 1) as u16,   // 4095
        base: self as *const _ as u64,
    };
    asm!("lidt [{}]", in(reg) &ptr);
}

Глобальная IDT

pub static mut IDT: InterruptDescriptorTable = InterruptDescriptorTable::new();

static mut — потому что IDT модифицируется в ранней инициализации, до включения прерываний. Потенциально может быть заменён на static с UnsafeCell.

Детали конфигурации

  • Selector: 0x28 — это GDT entry для ring 0 code segment (дескриптор 5, 5 × 8 = 0x28). Селектор сегмента кода в long mode.
  • Type 0x8E: 1000_1110 = бит 7 (Present) + бит 3:0 = 1110 (Interrupt Gate, 32-bit). В 64-bit режиме все вентили — 64-bit, флаг 0xE остаётся корректным.
  • IST: 0 — не используем Interrupt Stack Table (один стек для всех обработчиков).