# Обработчики прерываний и исключений: `interrupts.rs` ## Концептуальная модель Файл объединяет **обработчики исключений** (таблицу IDT) и **межпроцессорные прерывания** (TLB shootdown). Это связующий слой между аппаратурой (CPU exceptions, APIC) и программными подсистемами (VMM, Memory). ## Структура обработчика (stub + Rust handler) Каждый обработчик состоит из двух частей: 1. **Сборочный stub** (global_asm): сохраняет контекст, вызывает Rust-функцию, восстанавливает контекст, iretq. 2. **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 (макрос) ```rust 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 ```rust 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`) ```asm .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` ```asm 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)` ```rust 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 для ранней загрузки ```rust 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() — перезагрузка после полной инициализации ```rust 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)` ```rust 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 обработчик ```rust 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. ## Константы ```rust pub const TLB_SHOOTDOWN_VECTOR: u8 = 0xFD; ``` Вектор 0xFD (253) — в диапазоне пользовательских прерываний (32-255), намеренно далеко от системных векторов 0-31.