4.9 KiB
4.9 KiB
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 (один стек для всех обработчиков).