feat: docs & ARCH 2.2, 2.3, 2.4

This commit is contained in:
Faynot
2026-07-07 16:40:41 +03:00
parent c092a81331
commit 1bfde637d0
50 changed files with 5113 additions and 1823 deletions

115
kernel/docs/cpu/lapic.md Normal file
View File

@@ -0,0 +1,115 @@
# Local APIC: `lapic.rs`
## Аппаратная модель
**Local APIC** (Advanced Programmable Interrupt Controller) — это
встроенный в каждое ядро x86-64 контроллер прерываний.
```
Local APIC (MMIO, начиная с 0xFEE00_000)
┌──────────────────────────────┐
│ 0x020: IRR (In-Service Reg) │
│ 0x030: TMR (Trigger Mode) │
│ 0x080: EOI │ ← запись 0 = подтверждение прерывания
│ 0x0B0: LINT0/LINT1 │
│ 0x0E0: Timer │
│ 0x200: LVT Error │
│ 0x300: ICR (Interrupt Cmd) │ ← отправка межпроцессорного прерывания
│ 0x310: ICR_HIGH (APIC ID) │
└──────────────────────────────┘
```
## Регистры
| Смещение | Регистр | Назначение |
|----------|---------|------------|
| 0x0B0 | EOI | End Of Interrupt — подтверждение обработки |
| 0x300 | ICR (low) | Interrupt Command Register — отправка IPI |
| 0x310 | ICR (high) | Destination APIC ID |
## Инициализация
```rust
static LAPIC_VIRT_BASE: AtomicU64 = AtomicU64::new(0);
pub fn init() {
// LAPIC отображён bootloader'ом в HHDM по адресу 0xFEE00_000
LAPIC_VIRT_BASE.store(LAPIC_DEFAULT_BASE + get_hhdm(), Ordering::SeqCst);
}
```
LAPIC расположен по физическому адресу `0xFEE0_0000`. Ядро получает
виртуальный базовый адрес, добавляя HHDM offset.
## Доступ к регистрам
```rust
#[inline(always)]
fn write_lapic_reg(offset: u64, value: u32) {
let base = LAPIC_VIRT_BASE.load(Ordering::Relaxed);
if base == 0 { return; } // LAPIC ещё не инициализирован
unsafe { core::ptr::write_volatile((base + offset) as *mut u32, value) }
}
```
- `write_volatile` — запрещает компилятору оптимизировать обращение
(регистры MMIO).
- `Relaxed` ordering — для отладки/инициализации достаточно.
## Операции
### send_eoi() — конец прерывания
```rust
pub fn send_eoi() {
write_lapic_reg(LAPIC_EOI, 0); // 0x0B0
}
```
### broadcast_ipi_exclude_self(vector) — IPI всем, кроме себя
```rust
pub fn broadcast_ipi_exclude_self(vector: u8) {
// ICR[19:18] = 10b (All Excluding Self)
// ICR[14] = 1 (Assert)
// ICR[7:0] = vector
let icr_low = (2 << 18) | (1 << 14) | (vector as u32);
write_lapic_reg(LAPIC_ICR_LOW, icr_low);
}
```
Используется для TLB shootdown — необходимо разослать всем ядрам
(кроме текущего) IPI с вектором `TLB_SHOOTDOWN_VECTOR`.
### current_core_id() — определение текущего ядра
```rust
pub fn current_core_id() -> u32 {
// CPUID leaf 1: EBX[31:24] = Local APIC ID
let mut ebx: u32;
asm!(
"mov {tmp:r}, rbx", // спрятать rbx (резерв LLVM)
"mov eax, 1", "cpuid",
"mov {out:e}, ebx", // сохранить EBX
"mov rbx, {tmp:r}", // восстановить rbx
tmp = out(reg) _,
out = out(reg) ebx,
...
);
ebx >> 24
}
```
**RBX проблема:** LLVM резервирует RBX, поэтому его нужно сохранять
и восстанавливать вручную вокруг CPUID инструкции.
## Тонкости
1. **MMIO vs MSR**: LAPIC можно программировать через MSR (IA32_APIC_BASE)
и через MMIO. Bootloader (Limine) настраивает MMIO mapping в HHDM.
2. **Инициализация**: LAPIC уже включён bootloader'ом. `init()` просто
сохраняет виртуальный адрес.
3. **x2APIC**: не используется (в текущей версии — MMIO xAPIC).
4. **EOI**: обязателен после каждого прерывания от LAPIC (включая IPI).
5. **ICR запись**: после записи в ICR Low, шина APIC доставляет
прерывание. Запись блокирующая (ждёт готовности шины).