feat: docs & ARCH 2.2, 2.3, 2.4
This commit is contained in:
200
kernel/docs/cpu/interrupts.md
Normal file
200
kernel/docs/cpu/interrupts.md
Normal file
@@ -0,0 +1,200 @@
|
||||
# Обработчики прерываний и исключений: `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.
|
||||
Reference in New Issue
Block a user