feat: docs & ARCH 2.2, 2.3, 2.4
This commit is contained in:
115
kernel/docs/cpu/idt.md
Normal file
115
kernel/docs/cpu/idt.md
Normal file
@@ -0,0 +1,115 @@
|
||||
# Interrupt Descriptor Table: `idt.rs`
|
||||
|
||||
## Аппаратная модель
|
||||
|
||||
IDT (Interrupt Descriptor Table) — это таблица из 256 entry (по 16 байт каждая),
|
||||
которая сообщает CPU, куда передавать управление при прерываниях и исключениях.
|
||||
|
||||
```
|
||||
IDT:
|
||||
┌──────┬──────────────────────────────────────────────────────┐
|
||||
│ 0 │ #DE — Divide Error │
|
||||
│ 1 │ #DB — Debug │
|
||||
│ 2 │ #NMI — Non-Maskable Interrupt │
|
||||
│ 3 │ #BP — Breakpoint │
|
||||
│ 4 │ #OF — Overflow │
|
||||
│ 5 │ #BR — Bound Range Exceeded │
|
||||
│ 6 │ #UD — Undefined Opcode │
|
||||
│ 7 │ #NM — Device Not Available │
|
||||
│ 8 │ #DF — Double Fault │
|
||||
│ 9 │ #MF — Coprocessor Segment Overrun │
|
||||
│ 10 │ #TS — Invalid TSS │
|
||||
│ 11 │ #NP — Segment Not Present │
|
||||
│ 12 │ #SS — Stack-Segment Fault │
|
||||
│ 13 │ #GP — General Protection Fault │
|
||||
│ 14 │ #PF — Page Fault │
|
||||
│ 15-31│ Reserved / CPU exceptions │
|
||||
│ 32-255│ User-defined (hardware interrupts) │
|
||||
└──────┴──────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
Загрузка IDT: инструкция `lidt [idtr_ptr]`, где `idtr_ptr` — это
|
||||
6-байтовая структура `IdtPtr`:
|
||||
```
|
||||
IdtPtr:
|
||||
┌──────────┬──────────┐
|
||||
│ limit:16 │ base:48 │
|
||||
└──────────┴──────────┘
|
||||
```
|
||||
|
||||
## IdtEntry — 16-байтовая запись
|
||||
|
||||
```rust
|
||||
#[repr(C, packed)]
|
||||
pub struct IdtEntry {
|
||||
offset_low: u16, // Бит 0:15 адреса обработчика
|
||||
selector: u16, // Селектор сегмента кода (0x28 для ядра)
|
||||
ist: u8, // Interrupt Stack Table
|
||||
type_attr: u8, // Тип вентиля + флаги
|
||||
offset_mid: u16, // Бит 16:31 адреса обработчика
|
||||
offset_high: u32, // Бит 32:63 адреса обработчика
|
||||
ignore: u32, // Зарезервировано
|
||||
}
|
||||
```
|
||||
|
||||
**type_attr:**
|
||||
- Бит 7: Present (должен быть 1)
|
||||
- Бит 6-5: DPL (Descriptor Privilege Level)
|
||||
- Бит 4: Reserved (0)
|
||||
- Бит 3-0: Gate Type (0xE = Interrupt Gate, 0xF = Trap Gate)
|
||||
|
||||
`set_handler(handler, selector, flags)`:
|
||||
```rust
|
||||
pub fn set_handler(&mut self, handler: u64, selector: u16, flags: u8) {
|
||||
self.offset_low = handler as u16;
|
||||
self.selector = selector;
|
||||
self.ist = 0;
|
||||
self.type_attr = flags | 0x80; // Present bit forced on
|
||||
self.offset_mid = (handler >> 16) as u16;
|
||||
self.offset_high = (handler >> 32) as u32;
|
||||
}
|
||||
```
|
||||
|
||||
## InterruptDescriptorTable — 256 entry
|
||||
|
||||
```rust
|
||||
pub struct InterruptDescriptorTable {
|
||||
entries: [IdtEntry; 256], // 256 × 16 = 4096 байт
|
||||
}
|
||||
```
|
||||
|
||||
**Методы:**
|
||||
- `set_handler(vector, handler)` — устанавливает обработчик с
|
||||
селектором 0x28 (GDT code segment) и type_attr 0x8E
|
||||
(Interrupt Gate, Ring 0, Present).
|
||||
- `load()` — `lidt` инструкция.
|
||||
|
||||
```rust
|
||||
pub unsafe fn load(&'static self) {
|
||||
let ptr = IdtPtr {
|
||||
limit: (size_of::<Self>() - 1) as u16, // 4095
|
||||
base: self as *const _ as u64,
|
||||
};
|
||||
asm!("lidt [{}]", in(reg) &ptr);
|
||||
}
|
||||
```
|
||||
|
||||
## Глобальная IDT
|
||||
|
||||
```rust
|
||||
pub static mut IDT: InterruptDescriptorTable = InterruptDescriptorTable::new();
|
||||
```
|
||||
|
||||
`static mut` — потому что IDT модифицируется в ранней инициализации,
|
||||
до включения прерываний. Потенциально может быть заменён на `static`
|
||||
с `UnsafeCell`.
|
||||
|
||||
## Детали конфигурации
|
||||
|
||||
- **Selector**: `0x28` — это GDT entry для ring 0 code segment
|
||||
(дескриптор 5, 5 × 8 = 0x28). Селектор сегмента кода в long mode.
|
||||
- **Type 0x8E**: `1000_1110` = бит 7 (Present) + бит 3:0 = 1110
|
||||
(Interrupt Gate, 32-bit). В 64-bit режиме все вентили — 64-bit,
|
||||
флаг 0xE остаётся корректным.
|
||||
- **IST**: 0 — не используем Interrupt Stack Table (один стек для
|
||||
всех обработчиков).
|
||||
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.
|
||||
106
kernel/docs/cpu/introduction.md
Normal file
106
kernel/docs/cpu/introduction.md
Normal file
@@ -0,0 +1,106 @@
|
||||
# CPU подсистема: концептуальная модель
|
||||
|
||||
## Состав и ответственность
|
||||
|
||||
CPU подсистема отвечает за:
|
||||
|
||||
1. **Обработку прерываний и исключений** — IDT, обработчики.
|
||||
2. **Исключения ранней загрузки** — пока ядро ещё не полностью инициализировано.
|
||||
3. **TLB Shootdown** — межпроцессорное прерывание для синхронизации TLB.
|
||||
4. **Local APIC** — программируемый контроллер прерываний.
|
||||
|
||||
```
|
||||
CPU Subsystem
|
||||
┌─────────────────────────────────────────────────────────┐
|
||||
│ CPU Core #0 │
|
||||
│ │
|
||||
│ ┌──────────────┐ ┌─────────────────────────┐ │
|
||||
│ │ LAPIC │ │ IDT │ │
|
||||
│ │ │ │ │ │
|
||||
│ │ ICR ────────┼────────► 0: #DE (Divide Error) │ │
|
||||
│ │ EOI │ │ 1: #DB (Debug) │ │
|
||||
│ │ (timer) │ │ ... │ │
|
||||
│ │ │ │ 8: #DF (Double Fault) │ │
|
||||
│ │ │ │ 13: #GP (GPF) │ │
|
||||
│ │ │ │ 14: #PF (Page Fault) │ │
|
||||
│ │ │ │ ... │ │
|
||||
│ │ │ │ 0xFD: TLB Shootdown │ │
|
||||
│ └──────────────┘ └─────────────────────────┘ │
|
||||
└─────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
## Модули
|
||||
|
||||
| Файл | Компонент | Функция |
|
||||
|------|-----------|---------|
|
||||
| `idt.rs` | IDT структуры | Определение IdtEntry, IdtPtr, InterruptDescriptorTable |
|
||||
| `interrupts.rs` | Обработчики | Early handlers, Page Fault, Double Fault, GPF, TLB shootdown |
|
||||
| `lapic.rs` | Local APIC | Инициализация LAPIC, EOI, broadcast IPI |
|
||||
|
||||
## Порядок инициализации
|
||||
|
||||
```
|
||||
kmain()
|
||||
│
|
||||
├── cpu::interrupts::init_early_exceptions()
|
||||
│ └── IDT для векторов 0-31 + TLB shootdown (0xFD)
|
||||
│
|
||||
├── ... (PMM, LAPIC, Page tables) ...
|
||||
│
|
||||
├── cpu::interrupts::init_idt()
|
||||
│ └── Перезагрузка IDT с полными обработчиками
|
||||
│
|
||||
├── STI (разрешение прерываний)
|
||||
│
|
||||
└── ... (работа с прерываниями)
|
||||
```
|
||||
|
||||
## Обработка исключений: два этапа
|
||||
|
||||
### Этап 1: Early (до VMM)
|
||||
|
||||
На раннем этапе загрузки (до настройки page tables и heap) ядро
|
||||
не может обрабатывать сложные исключения. Для векторов 0-31
|
||||
устанавливаются `early_handler_N`, которые:
|
||||
|
||||
1. Пушат вектор и (возможно) dummy error code.
|
||||
2. Переходят в `early_common`.
|
||||
3. Сохраняют все регистры.
|
||||
4. Вызывают `rust_early_exception_handler(vector, error_code)`.
|
||||
5. Паникуют (HALT).
|
||||
|
||||
### Этап 2: Полный (после VMM)
|
||||
|
||||
После инициализации VMM три критических исключения получают
|
||||
полноценные обработчики:
|
||||
|
||||
- **Page Fault (#PF, вектор 14)**: попытка обработать (COW, lazy).
|
||||
- **Double Fault (#DF, вектор 8)**: HALT с сообщением.
|
||||
- **General Protection Fault (#GPF, вектор 13)**: HALT с сообщением.
|
||||
- **TLB Shootdown (вектор 0xFD)**: межпроцессорный TLB сброс.
|
||||
|
||||
## TLB Shootdown: модель
|
||||
|
||||
При изменении page tables на одном ядре, TLB других ядер устаревает.
|
||||
Протокол:
|
||||
|
||||
```
|
||||
CPU 0 (инициатор) CPU 1 (мишень)
|
||||
│ │
|
||||
├── local_tlb_flush_asid() │
|
||||
├── SHOOTDOWN_LOCK.lock() │
|
||||
├── SHOOTDOWN_ASID = asid │
|
||||
├── SHOOTDOWN_ACK = 0 │
|
||||
├── LAPIC: broadcast IPI │
|
||||
│ (вектор TLB_SHOOTDOWN_VECTOR) ───► прерывание!
|
||||
│ ├── handle_tlb_shootdown_ipi()
|
||||
│ ├── local_tlb_flush_asid()
|
||||
│ ├── SHOOTDOWN_ACK |= 1 << core
|
||||
│ ├── LAPIC::send_eoi()
|
||||
│ └── iretq
|
||||
│ │
|
||||
├── spin_loop() ◄────────────────────┤
|
||||
│ (ждёт ACK от всех ядер) │
|
||||
├── SHOOTDOWN_LOCK.unlock() │
|
||||
└── continue │
|
||||
```
|
||||
115
kernel/docs/cpu/lapic.md
Normal file
115
kernel/docs/cpu/lapic.md
Normal 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 доставляет
|
||||
прерывание. Запись блокирующая (ждёт готовности шины).
|
||||
Reference in New Issue
Block a user