201 lines
6.9 KiB
Markdown
201 lines
6.9 KiB
Markdown
# Обработчики прерываний и исключений: `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.
|