6.9 KiB
6.9 KiB
Обработчики прерываний и исключений: interrupts.rs
Концептуальная модель
Файл объединяет обработчики исключений (таблицу IDT) и межпроцессорные прерывания (TLB shootdown). Это связующий слой между аппаратурой (CPU exceptions, APIC) и программными подсистемами (VMM, Memory).
Структура обработчика (stub + Rust handler)
Каждый обработчик состоит из двух частей:
- Сборочный stub (global_asm): сохраняет контекст, вызывает Rust-функцию, восстанавливает контекст, iretq.
- Rust handler: собственно обработка.
Пример: Page Fault
[stack]
page_fault_stub: error_code ← CPU пушет
push rax ...регистры...
push rcx
...
mov rdi, [rsp + 15*8] ← error_code как аргумент
call rust_page_fault_handler ← вызов Rust
pop r15 восстановление
...
pop rax
add rsp, 8 ← убираем error_code
iretq ← возврат
Exception stubs (макрос)
macro_rules! exception_stub {
($name:ident, $handler:ident) => {
concat!(
".global ", stringify!($name), "\n",
stringify!($name), ":\n",
"push rax\npush rcx\n...push r15\n", // сохранение
"mov rdi, [rsp + 15*8]\n", // error_code
"call ", stringify!($handler), "\n",
"pop r15\n...pop rax\n", // восстановление
"add rsp, 8\n", // очистка error_code
"iretq\n",
)
};
}
Используется для:
page_fault_stub→rust_page_fault_handler(вектор 14)gpf_stub→rust_gpf_handler(вектор 13)double_fault_stub→rust_double_fault_handler(вектор 8)
TLB shootdown stub
global_asm!(
"tlb_shootdown_stub:",
"push rax\npush rcx\n...", // сохранение
"call rust_tlb_shootdown_handler",
"pop r15\n...pop rax\n", // восстановление
"iretq"
);
Early handlers — для начальной загрузки
До того как VMM и slab allocator готовы, ядро не может обрабатывать сложные исключения. Для векторов 0-31 генерируются ранние заглушки.
Генерация (макрос .altmacro)
.macro early_stub vec
.globl early_handler_\vec
.balign 16
early_handler_\vec:
push 0 /* dummy error code (если нет аппаратного) */
push \vec /* номер вектора */
jmp early_common
.endm
early_stub 0..early_stub 31 генерирует 32 обработчика.
early_common
early_common:
push rax\npush rcx\n... // сохранение всех GP-регистров
mov rdi, [rsp + 15*8] // vector number
mov rsi, [rsp + 16*8] // error code (или dummy 0)
call rust_early_exception_handler
// never returns
rust_early_exception_handler(vector, error_code)
pub extern "C" fn rust_early_exception_handler(vector: u64, _error_code: u64) -> ! {
serial::write_global(format_args!(
"\n!!! EARLY EXCEPTION !!! vector={} error_code={:#x}\nCPU halted.\n",
vector, _error_code
));
loop { asm!("cli; hlt"); }
}
Всегда HALT — раннее исключение фатально.
init_early_exceptions() — настройка IDT для ранней загрузки
pub fn init_early_exceptions() {
let idt = addr_of_mut!(IDT);
// Устанавливаем early_handler_N для векторов 0-31
for (v, &handler) in early_handlers.iter().enumerate() {
(*idt).set_handler(v as u8, handler);
}
// Переопределяем критические:
(*idt).set_handler(8, double_fault_stub); // #DF
(*idt).set_handler(13, gpf_stub); // #GPF
(*idt).set_handler(14, page_fault_stub); // #PF
(*idt).set_handler(TLB_SHOOTDOWN_VECTOR, tlb_shootdown_stub);
let ptr: &'static IDT = &*addr_of!(IDT);
ptr.load(); // lidt
}
init_idt() — перезагрузка после полной инициализации
pub fn init_idt() {
// Переустанавливаем только Page Fault и TLB Shootdown
// (их обработчики уже переключились на VMM-aware версии)
let idt = addr_of_mut!(IDT);
(*idt).set_handler(14, page_fault_stub);
(*idt).set_handler(TLB_SHOOTDOWN_VECTOR, tlb_shootdown_stub);
let ptr: &'static IDT = &*addr_of!(IDT);
ptr.load();
}
Обработчики исключений
rust_page_fault_handler(error_code)
pub extern "C" fn rust_page_fault_handler(error_code: u64) {
let fault_addr: u64;
asm!("mov {}, cr2", out(reg) fault_addr); // читаем CR2
let write = (error_code & 0x2) != 0; // fault на запись?
let present = (error_code & 0x1) != 0; // PTE был PRESENT?
let mut vmm_guard = KERNEL_SPACE.lock();
if let Some(space) = vmm_guard.as_mut() {
space.process_pending_revocations(); // обрабатываем отзывы
match space.handle_fault(fault_addr, write) {
Ok(_) => {} // обработано: iretq retry
Err(e) => panic!(...), // необработанный fault
}
} else {
panic!("Page fault before KERNEL_SPACE!");
}
}
rust_gpf_handler(error_code) и rust_double_fault_handler(error_code)
Оба — HALT с сообщением.
TLB Shootdown обработчик
pub extern "C" fn rust_tlb_shootdown_handler() {
crate::mem::vmm::handle_tlb_shootdown_ipi();
crate::cpu::lapic::send_eoi(); // подтверждаем LAPIC прерывание
}
Тайминги и безопасность
- TLB shootdown — IPI, требует минимальной задержки.
- Все операции в
handle_tlb_shootdown_ipi— простые и быстрые. - LAPIC EOI отправляется сразу после локального TLB flush.
Константы
pub const TLB_SHOOTDOWN_VECTOR: u8 = 0xFD;
Вектор 0xFD (253) — в диапазоне пользовательских прерываний (32-255), намеренно далеко от системных векторов 0-31.