diff --git a/.PLAN.md.swp b/.PLAN.md.swp
deleted file mode 100644
index 68d93f4..0000000
Binary files a/.PLAN.md.swp and /dev/null differ
diff --git a/kernel/docs/README.md b/kernel/docs/README.md
deleted file mode 100644
index 46e8804..0000000
--- a/kernel/docs/README.md
+++ /dev/null
@@ -1,19 +0,0 @@
-# Документация ядра LISA
-
-LISA v0.1.0 — capability-based микроядро для x86-64, написанное на Rust (nightly).
-
-## Разделы
-
-| Раздел | Описание |
-|---|---|
-| [Архитектура](architecture.md) | Общая архитектура и взаимосвязи подсистем |
-| [Точка входа](main.md) | `kmain()`, инициализация, FramebufferDisplay, тесты |
-| [Физическая память](memory-management.md) | BitmapPMM, BuddyAllocator, Page Tables, адресация |
-| [Виртуальная память](vmm.md) | AddressSpace, VMA, COW, Lazy, TLB shootdown, ASID/PCID |
-| [Акторы памяти](pmactor.md) | PMActor, PMRouter, BuddyAllocator, асинхронные запросы |
-| [Capability system](capabilities.md) | CNode, Capability, mint/revoke, KernelObject |
-| [Прерывания](interrupts.md) | IDT, обработчики page fault / TLB shootdown, LAPIC |
-| [Консоль и отладка](console.md) | TTY, Serial, макросы логирования |
-| [Аллокатор кучи](allocator.md) | SlabAllocator, Locked, GlobalAlloc |
-| [Очередь отзыва](events.md) | RevocationQueue, MMU уведомления |
-| [Сборочная система](build-system.md) | Cargo, linker scripts, GNUmakefile, toolchain |
diff --git a/kernel/docs/allocator.md b/kernel/docs/allocator.md
deleted file mode 100644
index 9e722d1..0000000
--- a/kernel/docs/allocator.md
+++ /dev/null
@@ -1,105 +0,0 @@
-# Аллокатор кучи
-
-**Файл**: `src/mem/allocator.rs`
-
-Slab-аллокатор со spinlock-синхронизацией и fallback bump-аллокацией для больших блоков.
-
----
-
-## Примитив синхронизации: `Locked`
-
-Самодельный spinlock (альтернатива `spin::Mutex`).
-
-```rust
-pub struct Locked {
- inner: UnsafeCell,
- lock: AtomicBool,
-}
-```
-
-- `new(inner)` — создаёт с unlocked состоянием
-- `lock() -> LockedGuard<'_, A>` — CAS-цикл на `AtomicBool` (спин-ожидание)
-
-### LockedGuard
-
-- `Deref`/`DerefMut` — доступ к внутренним данным
-- `Drop` — `store(false, Release)` — освобождение блокировки
-
-`unsafe impl Sync for Locked` — разработчик гарантирует корректность.
-
----
-
-## SlabAllocator
-
-```rust
-pub struct SlabAllocator {
- list_heads: [Option<&'static mut ListNode>; 9], // slab free list
- large_block_free: Option<&'static mut LargeBlockNode>, // free list для >2048
- heap_start: usize,
- heap_end: usize,
- next_bump: usize,
-}
-```
-
-### Slab классы
-
-```rust
-const BLOCK_SIZES: &[usize] = &[8, 16, 32, 64, 128, 256, 512, 1024, 2048];
-```
-
-9 классов — степени двойки от 8 до 2048.
-
-### ListNode / LargeBlockNode
-
-```rust
-struct ListNode {
- next: Option<&'static mut ListNode>,
-}
-
-struct LargeBlockNode {
- size: usize,
- next: Option<&'static mut LargeBlockNode>,
-}
-```
-
-Односвязные списки свободных блоков.
-
-### `init(&mut self, start, size)`
-
-Устанавливает границы кучи: `heap_start = start`, `heap_end = start + size`, `next_bump = start`.
-
-### `list_index(layout) -> Option`
-
-Находит индекс наименьшего slab-класса, покрывающего `max(size, align)`.
-
-Пример: layout size=20, align=8 → блок 20 > 16, следующий 32 → index 3 (32 байта).
-
-### `fallback_alloc(&mut self, layout) -> *mut u8`
-
-1. Проверяет `large_block_free` — если есть подходящий блок, отдаёт его
-2. Иначе bump-аллокация: выравнивание, проверка `next_bump + size <= heap_end`, возврат `next_bump`
-3. При OOM возвращает null
-
----
-
-## Глобальный аллокатор
-
-```rust
-#[global_allocator]
-pub static ALLOCATOR: Locked;
-```
-
-### `GlobalAlloc::alloc(&self, layout) -> *mut u8`
-
-1. Lock allocator
-2. Если `layout.size()` влезает в slab класс:
- - Находит `list_index`
- - Если free list не пуст: pop голову, вернуть указатель
- - Если пуст: `fallback_alloc(layout)` но с размером slab-блока (не `layout.size()`)
-3. Для блоков > 2048: сразу `fallback_alloc(layout)`
-
-### `GlobalAlloc::dealloc(&self, ptr, layout)`
-
-1. Lock allocator
-2. Для slab-sized: push обратно в slab free list как ListNode
-3. Для больших: push в large_block_free как LargeBlockNode
diff --git a/kernel/docs/architecture.md b/kernel/docs/architecture.md
deleted file mode 100644
index 37630ad..0000000
--- a/kernel/docs/architecture.md
+++ /dev/null
@@ -1,56 +0,0 @@
-# Архитектура ядра LISA
-
-LISA — **capability-based микроядро**. Вся физическая память управляется через систему capability (прав доступа к объектам). Ядро использует загрузчик **Limine** (протокол Limine boot protocol).
-
-## Общая схема
-
-```
-kmain()
- ├── BitmapPMM.init() ── глобальный менеджер фреймов
- ├── LAPIC.init() ── доступ к Local APIC
- ├── PageTable::activate() ── включение страничной памяти
- ├── SlabAllocator.init() ── куча ядра
- ├── VMM::init_kernel_space() ── адресное пространство ядра
- ├── IDT::load() ── обработчики прерываний
- ├── PMRouter::init() ── 65536 каналов
- ├── CNode (cap тест) ── insert → mint → revoke
- │ └── RevocationQueue.push() ── уведомление VMM
- └── PMActor тест
- ├── PMRouter.alloc_channel()
- ├── PMActor.submit_request()
- ├── PMActor.process_messages()
- └── BuddyAllocator (внутри PMActor)
-```
-
-## Поток ревокации
-
-```
-CNode::revoke()
- → RevocationQueue.push(token_sig)
- → (page fault handler)
- → process_deferred_mmu_events()
- → AddressSpace::process_pending_revocations()
- → RevocationQueue.pop()
- → AddressSpace::revoke_by_token(token) // анмаппинг VMA
- → TLB flush
-```
-
-## Поток PMActor запроса
-
-```
-1. PMRouter::alloc_channel() → получаем channel_id
-2. PMActor::submit_request(Allocate { channel_id, ... })
-3. (актор обрабатывает) PMActor::process_messages()
-4. PMActor::handle_allocate() → BuddyAllocator::alloc_pages()
-5. PMRouter::route_responses([PMResponse { channel_id, result }])
-6. PMRouter::wait_for_response(channel_id) → spin до READY
-7. Возврат результата клиенту
-```
-
-## Ключевые принципы
-
-- **Lock-free очереди** — MPSC кольцевые буферы везде, где возможен контеншен (PMActorQueue, RevocationQueue, PMRouter channels)
-- **Double-write logging** — каждое сообщение дублируется в фреймбуфер (консоль) и последовательный порт
-- **HHDM** — Higher Half Direct Map: весь физическая память отображена 1:1 в верхней половине адресного пространства через HHDM offset
-- **ASID/PCID** — аппаратная изоляция TLB между адресными пространствами
-- **Распределённое управление памятью** — PMM выделяет только сырые фреймы, PMActor управляет диапазонами через BuddyAllocator
diff --git a/kernel/docs/boot/introduction.md b/kernel/docs/boot/introduction.md
new file mode 100644
index 0000000..ef11509
--- /dev/null
+++ b/kernel/docs/boot/introduction.md
@@ -0,0 +1,152 @@
+# Процесс загрузки: концептуальная модель
+
+## Цепочка загрузки
+
+```
+Питание включено
+ │
+ ▼
+CPU reset vector (0xFFFFFFF0)
+ │
+ ▼
+UEFI firmware / BIOS
+ │
+ ▼
+Limine bootloader ─────────────────────────────┐
+ │ │
+ │ 1. Переводит CPU в 64-bit long mode │
+ │ 2. Настраивает page tables (identity map) │
+ │ 3. Загружает ядро по физическому адресу │
+ │ 4. Настраивает HHDM │
+ │ 5. Заполняет Limine requests │
+ │ 6. Передаёт управление на kmain │
+ │ │
+ ▼ │
+kmain() (точка входа) │
+ │ │
+ ... инициализация ... │
+ │ │
+ ▼ │
+HCF (останов CPU) ─────────────────────────────┘
+```
+
+## Limine Boot Protocol
+
+Elyz использует [Limine](https://github.com/limine-bootloader/limine) —
+современный bootloader с открытым исходным кодом.
+
+Статические `Limine requests` сообщают bootloader'у, что нужно ядру:
+
+| Request | Назначение |
+|---------|------------|
+| `FramebufferRequest` | Получить framebuffer для графического вывода |
+| `MemoryMapRequest` | Получить карту физической памяти |
+| `HhdmRequest` | Получить HHDM offset |
+| `ExecutableAddressRequest` | Получить физический/виртуальный адрес ядра |
+| `BaseRevision` | Проверить совместимость с Limine |
+
+### Размещение в секциях
+
+Requests размещаются между специальными маркерами в секции `.data`:
+
+```rust
+#[used]
+#[unsafe(link_section = ".requests_start_marker")]
+static _START_MARKER: RequestsStartMarker = RequestsStartMarker::new();
+
+// ... все requests ...
+
+#[used]
+#[unsafe(link_section = ".requests_end_marker")]
+static _END_MARKER: RequestsEndMarker = RequestsEndMarker::new();
+```
+
+Линкер скрипт сохраняет эти секции:
+```ld
+.data : {
+ *(.data .data.*)
+ KEEP(*(.requests_start_marker))
+ KEEP(*(.requests))
+ KEEP(*(.requests_end_marker))
+} :data
+```
+
+## Точка входа — kmain
+
+Линкер скрипт: `ENTRY(kmain)`.
+
+```rust
+#[unsafe(no_mangle)]
+unsafe extern "C" fn kmain() -> ! { ... }
+```
+
+Атрибуты:
+- `no_mangle` — сохраняет имя `kmain` (линкер ищет именно его).
+- `extern "C"` — C ABI (Linux x86-64 calling convention: RDI, RSI, ...).
+- `unsafe` — на этапе инициализации все операции потенциально опасны.
+- `-> !` — kmain никогда не возвращается (HALT).
+
+## Build system
+
+### GNUmakefile
+
+```makefile
+KARCH ?= x86_64
+RUST_TARGET ?= $(KARCH)-unknown-none
+RUST_PROFILE ?= dev
+
+all:
+ RUSTFLAGS="-C relocation-model=static" \
+ cargo build --target $(RUST_TARGET) --profile $(RUST_PROFILE)
+ cp target/$(RUST_TARGET)/$(RUST_PROFILE_SUBDIR)/kernel .
+```
+
+- `relocation-model=static`: ядро не использует динамическую перелокацию.
+- `--target x86_64-unknown-none`: bare-metal target (без ОС).
+- Результат копируется в `kernel/` (корень).
+
+### build.rs — linker script
+
+```rust
+fn main() {
+ let arch = std::env::var("CARGO_CFG_TARGET_ARCH").unwrap();
+ println!("cargo:rustc-link-arg=-Tlinker-{arch}.ld");
+ println!("cargo:rerun-if-changed=linker-{arch}.ld");
+}
+```
+
+Подставляет правильный linker script для архитектуры.
+
+### rust-toolchain.toml
+
+```toml
+[toolchain]
+channel = "nightly"
+targets = ["x86_64-unknown-none"]
+```
+
+Требуется nightly Rust из-за:
+- `#![no_std]`, `#![no_main]`.
+- `core::arch::global_asm!`, `core::arch::asm!`.
+- `const { ... }` в инициализации констант.
+
+## Память: расположение после загрузки
+
+```
+Физическая память:
+┌───────────────────────┐ 0x0
+│ Reserved │
+├───────────────────────┤
+│ Usable (free) │ ← входит в mmap entries
+├───────────────────────┤
+│ Kernel image │ ← загружен Limine
+├───────────────────────┤
+│ PMM metadata │ ← bitmap + ref_counts + L1
+├───────────────────────┤
+│ ... (usable) │
+└───────────────────────┘ max_addr
+
+Виртуальная память (Higher Half):
+0xFFFF_8000_0000_0000 ─── HHDM (вся физическая память 1:1)
+0xFFFF_9000_0000_0000 ─── Kernel heap (8 MiB)
+```
diff --git a/kernel/docs/boot/linker.md b/kernel/docs/boot/linker.md
new file mode 100644
index 0000000..052317e
--- /dev/null
+++ b/kernel/docs/boot/linker.md
@@ -0,0 +1,151 @@
+# Линкер-скрипты: архитектурные детали
+
+## Назначение
+
+Линкер-скрипты управляют расположением секций ELF-образа ядра.
+Для каждой архитектуры — свой скрипт, но все они следуют одной схеме.
+
+## Общая структура
+
+```
+OUTPUT_FORMAT(...) ← формат ELF (зависит от архитектуры)
+ENTRY(kmain) ← точка входа
+
+PHDRS ← сегменты (program headers)
+{
+ text PT_LOAD;
+ rodata PT_LOAD;
+ data PT_LOAD;
+}
+
+SECTIONS ← расположение секций
+{
+ . = 0xffffffff80000000; ← база higher half
+
+ .text : { *(.text .text.*) } :text
+
+ . = ALIGN(MAXPAGESIZE);
+ .rodata : { *(.rodata .rodata.*) } :rodata
+
+ . = ALIGN(MAXPAGESIZE);
+ .data : {
+ *(.data .data.*)
+ KEEP(*(.requests_start_marker))
+ KEEP(*(.requests))
+ KEEP(*(.requests_end_marker))
+ } :data
+
+ .bss : { *(.bss .bss.*) *(COMMON) } :data
+
+ /DISCARD/ : { *(.eh_frame*) *(.note .note.*) }
+}
+```
+
+## Детали
+
+### OUTPUT_FORMAT
+
+| Архитектура | Формат |
+|-------------|--------|
+| x86_64 | `elf64-x86-64` |
+| aarch64 | `elf64-littleaarch64` |
+| riscv64 | `elf64-littleriscv` |
+| loongarch64 | `elf64-loongarch` |
+
+### Базовый адрес: 0xFFFFFFFF80000000
+
+Ядро размещается в **higher half** — верхней 2 GiB виртуального
+адресного пространства. Это стандартная практика для x86-64:
+
+```
+0x0000_0000_0000_00000 ─── user space (не используется ядром)
+0xFFFF_8000_0000_00000 ─── kernel space (higher half)
+0xFFFF_FFFF_FFFF_FFFF ─── конец
+```
+
+Любой адрес в области 0xFFFF800000000000 — 0xFFFFFFFFFFFFFFFF корректен;
+0xFFFFFFFF80000000 выбран как начало typical higher half региона.
+
+### PHDRS: PT_LOAD сегменты
+
+Bootloader загружает только PT_LOAD сегменты. Их три:
+
+1. **text**: код + inline-константы.
+2. **rodata**: неизменяемые данные (строки, таблицы).
+3. **data**: изменяемые данные + BSS.
+
+### Section alignment
+
+```ld
+. = ALIGN(CONSTANT(MAXPAGESIZE));
+```
+
+MAXPAGESIZE = 0x1000 (4 KiB). Каждая секция начинается с новой страницы,
+что даёт bootloader'у правильные MMU permissionы:
+- text = read + execute (no write)
+- rodata = read (no write, no execute)
+- data = read + write (no execute)
+
+### Limine Requests в .data
+
+```ld
+KEEP(*(.requests_start_marker))
+KEEP(*(.requests))
+KEEP(*(.requests_end_marker))
+```
+
+- `KEEP` — запрещает линкеру выбрасывать эти секции (dead code elimination).
+- `.requests_start_marker` и `.requests_end_marker` — маркеры границ.
+- Bootloader сканирует память между ними, чтобы найти requests.
+
+### BSS
+
+```ld
+.bss : {
+ *(.bss .bss.*)
+ *(COMMON)
+} :data
+```
+
+- BSS — неинициализированные глобальные переменные.
+- Занимает место в виртуальной памяти, но не в ELF-файле.
+- Bootloader обнуляет BSS перед передачей управления.
+
+### DISCARD
+
+```ld
+/DISCARD/ : {
+ *(.eh_frame*)
+ *(.note .note.*)
+}
+```
+
+- `.eh_frame*` — исключительные фреймы C++/Rust unwinding.
+- `.note.*` — ELF notes.
+- Не нужны bare-metal ядру, могут вызвать проблемы.
+
+## Архитектурные различия
+
+### RISC-V
+
+```ld
+.data : {
+ *(.data .data.*)
+ KEEP(*(.requests_start_marker))
+ KEEP(*(.requests))
+ KEEP(*(.requests_end_marker))
+ *(.sdata .sdata.*) ← RISC-V: small data
+} :data
+
+.bss : {
+ *(.sbss .sbss.*) ← RISC-V: small BSS
+ *(.bss .bss.*)
+ *(COMMON)
+} :data
+```
+
+RISC-V имеет `.sdata`/`.sbss` секции для small data (GP-relative addressing).
+
+### AArch64 и LoongArch64
+
+Идентичны x86_64, за исключением OUTPUT_FORMAT.
diff --git a/kernel/docs/build-system.md b/kernel/docs/build-system.md
deleted file mode 100644
index 069522e..0000000
--- a/kernel/docs/build-system.md
+++ /dev/null
@@ -1,134 +0,0 @@
-# Сборочная система
-
----
-
-## Cargo.toml
-
-**Файл**: `Cargo.toml`
-
-```toml
-[package]
-name = "LISA"
-version = "0.1.0"
-edition = "2024"
-
-[lib]
-# н/д — только бинарный крейт
-
-[[bin]]
-name = "kernel"
-path = "src/main.rs"
-
-[dependencies]
-limine = "0.5" # Limine boot protocol
-embedded-graphics = "0.8" # 2D фреймбуфер рисование
-bitflags = "2.11.0" # bitflags макрос
-
-[profile.dev]
-panic = "abort" # без раскрутки стека
-
-[profile.release]
-panic = "abort"
-```
-
----
-
-## build.rs
-
-**Файл**: `build.rs`
-
-```rust
-fn main() {
- let arch = std::env::var("CARGO_CFG_TARGET_ARCH").unwrap();
- println!("cargo:rustc-link-arg=-Tlinker-{}.ld", arch);
- println!("cargo:rerun-if-changed=linker-{}.ld", arch);
-}
-```
-
-Передаёт линкер-скрипт в зависимости от архитектуры: `-Tlinker-x86_64.ld`, `-Tlinker-aarch64.ld`, etc.
-
----
-
-## Linker скрипты
-
-### Общая структура (все 4 архитектуры)
-
-- **Entry**: `kmain`
-- **Base address**: `0xFFFFFFFF80000000` — высшие 2 GiB, по спецификации Limine
-- **Program headers**: `PT_LOAD` для `.text`, `.rodata`, `.data`
-
-### Секции
-
-```
-SECTIONS {
- .text : { *(.text .text.*) } → код
- .rodata : { *(.rodata .rodata.*) } → только чтение (page-aligned)
- .data : {
- *(.requests_start_marker) → Limine requests
- *(.requests)
- *(.requests_end_marker)
- *(.data .data.*)
- }
- .bss : { *(.bss .bss.*) } → нули
- /DISCARD/ : { *(.eh_frame*) *(.note.*) }
-}
-```
-
-### Архитектурные различия
-
-| Архитектура | OUTPUT_FORMAT | Особенности |
-|---|---|---|
-| x86-64 | `elf64-x86-64` | Стандартный |
-| aarch64 | `elf64-littleaarch64` | Стандартный |
-| riscv64 | `elf64-littleriscv` | `.data` + `.sdata`, `.bss` + `.sbss` |
-| loongarch64 | `elf64-loongarch` | Стандартный |
-
----
-
-## GNUmakefile
-
-**Файл**: `GNUmakefile`
-
-### Переменные
-
-```makefile
-OUTPUT := kernel
-KARCH ?= x86_64
-RUST_TARGET := $(KARCH)-unknown-none
-# riscv64 -> riscv64gc-unknown-none-elf
-RUST_PROFILE ?= dev
-```
-
-### Цели
-
-| Цель | Действие |
-|---|---|
-| `all` | `RUSTFLAGS="-C relocation-model=static" cargo build --target ...` + копирование `target/.../kernel` → `./kernel` |
-| `clean` | `cargo clean` + rm `./kernel` |
-| `distclean` | То же, что clean |
-
-`relocation-model=static` — запрещает позиционно-независимый код (ядро загружается по фиксированному адресу).
-
----
-
-## rust-toolchain.toml
-
-**Файл**: `rust-toolchain.toml`
-
-```toml
-[toolchain]
-channel = "nightly"
-targets = ["x86_64-unknown-none"]
-# aarch64-unknown-none, riscv64gc-unknown-none-elf, loongarch64-unknown-none (для будущего)
-```
-
----
-
-## .gitignore
-
-**Файл**: `.gitignore`
-
-```
-/kernel # бинарный файл ядра
-/target # артефакты сборки Cargo
-```
diff --git a/kernel/docs/capabilities.md b/kernel/docs/capabilities.md
deleted file mode 100644
index 987dffd..0000000
--- a/kernel/docs/capabilities.md
+++ /dev/null
@@ -1,147 +0,0 @@
-# Система Capability
-
----
-
-## Дескрипторы Capability
-
-**Файл**: `src/cap/descriptor.rs`
-
-### Relation
-
-```rust
-pub enum Relation {
- Strong, // владение (исключительный доступ)
- Borrow, // заимствование (временный доступ)
- Transfer, // передача (владение перешло)
-}
-```
-
-### CapObject
-
-```rust
-pub enum CapObject {
- Empty, // null (слот свободен)
- Memory { phys: PhysAddr, size_pages: usize }, // регион физической памяти
- CNode { phys: PhysAddr, slots: usize }, // узел capability (таблица)
- PMActor { id: u64 }, // ссылка на PMActor
-}
-```
-
-### CapRights (bitflags, u8)
-
-```rust
-READ = 1 << 0
-WRITE = 1 << 1
-EXECUTE = 1 << 2
-GRANT = 1 << 3 // разрешение на mint (порождение потомков)
-```
-
-### Capability
-
-```rust
-pub struct Capability {
- pub object: CapObject, // ссылка на объект
- pub rights: CapRights, // права доступа
- pub relation: Relation, // тип отношений
- pub token_sig: u64, // уникальный токен для отзыва (MMU)
-}
-```
-
-- `empty()` — возвращает Capability с `CapObject::Empty`
-- `is_valid() -> bool` — `object != Empty`
-
----
-
-## Объекты ядра
-
-**Файл**: `src/cap/object.rs`
-
-### ObjectType
-
-```rust
-pub enum ObjectType {
- Untyped, // сырая нетипизированная память
- Frame, // выделенный физический фрейм
- CNode, // узел capability
- ThreadBlock, // блок управления потоком
- PageTable, // страница таблицы
-}
-```
-
-### KernelObject
-
-```rust
-pub struct KernelObject {
- pub phys_addr: PhysAddr,
- pub size_bits: u8, // размер как степень двойки
- pub obj_type: ObjectType,
- pub ref_count: AtomicUsize, // атомарный счётчик ссылок
- pub owner_id: u64, // ID владельца
-}
-```
-
-- `add_ref()` — atomic increment (Relaxed ordering)
-- `release() -> bool` — atomic decrement (Release). Возвращает true, если счётчик достиг 0
-
----
-
-## CNode
-
-**Файл**: `src/cap/mod.rs`
-
-Узел capability, хранит массив слотов с индивидуальной блокировкой.
-
-### CNodeSlot
-
-```rust
-struct CNodeSlot {
- cap: Capability,
- parent_idx: Option, // индекс родителя (для дерева) — None у корневых
-}
-```
-
-### CNode
-
-```rust
-pub struct CNode {
- slots: Vec>,
-}
-```
-
-### `new(size) -> Self`
-
-Создаёт `size` пустых слотов с `parent_idx = None`.
-
-### `insert(slot, cap) -> Result<(), &str>`
-
-Проверка границ, сохранение Capability в слоте.
-
-### `mint(src, dest, relation, rights) -> Result<(), &str>`
-
-**Порождение дочерней capability с урезанными правами:**
-
-1. **Lock ordering**: блокировка слотов по возрастанию индекса (deadlock prevention)
-2. Source-слот не пуст, имеет GRANT право
-3. `child_rights = parent_rights & requested_rights` — дочерние права не могут превышать родительские
-4. Копирует Capability, устанавливает новые права, relation, `parent_idx`
-5. Ошибка при `src == dest`
-
-### `revoke(slot_idx) -> Result<(), &str>`
-
-**Каскадный отзыв с уведомлением VMM:**
-
-1. `revoke_internal(slot_idx)` — рекурсивно находит и уничтожает всех потомков
-2. Уничтожает саму Capability в слоте (Empty)
-3. Пушит `token_sig` в `MMU_REVOCATION_QUEUE` (кроме 0 и `0xDEAD_BEEF`)
-4. **Паника** при переполнении очереди
-
-### `revoke_internal(slot_idx)`
-
-Приватная рекурсия:
-1. Сканирует все слоты в поисках `parent_idx == slot_idx`
-2. Для каждого потомка: рекурсивный вызов
-3. Затем уничтожает свой слот (Empty)
-
-### `get_cap(slot) -> Option`
-
-Возвращает копию Capability.
diff --git a/kernel/docs/capability/cnode.md b/kernel/docs/capability/cnode.md
new file mode 100644
index 0000000..765aabb
--- /dev/null
+++ b/kernel/docs/capability/cnode.md
@@ -0,0 +1,146 @@
+# CNode: таблица capability: `mod.rs`
+
+## Концептуальная модель
+
+**CNode** (Capability Node) — это массив слотов, каждый из которых
+может хранить один capability. Это аналог файловой таблицы в Unix,
+но для capabilities.
+
+```
+CNode {
+ slots: Vec>
+}
+
+CNodeSlot {
+ cap: Capability,
+ parent_idx: Option, // индекс родительского слота
+}
+```
+
+## Инициализация
+
+```rust
+pub fn new(size: usize) -> Self {
+ let mut slots = Vec::with_capacity(size);
+ for _ in 0..size {
+ slots.push(Locked::new(CNodeSlot {
+ cap: Capability::empty(),
+ parent_idx: None,
+ }));
+ }
+ Self { slots }
+}
+```
+
+Все слоты изначально пустые (`Capability::empty()`).
+
+## Операции
+
+### insert(slot, cap) — вставка
+
+```rust
+pub fn insert(&self, slot: usize, cap: Capability) -> Result<(), &'static str> {
+ if slot >= self.slots.len() { return Err("Index out of bounds"); }
+ let mut s = self.slots[slot].lock();
+ s.cap = cap;
+ Ok(())
+}
+```
+
+Простая вставка без проверки (перезаписывает существующий).
+
+### mint(src, dest, relation, rights) — создание потомка
+
+```rust
+pub fn mint(&self, src: usize, dest: usize, relation: Relation, rights: CapRights) -> Result<(), &'static str>
+```
+
+**Валидация:**
+- src и dest в пределах массива.
+- src != dest (дедлок не имеет смысла, но блокировка была бы корректна).
+- src содержит валидный capability.
+- src имеет GRANT.
+
+**Процесс:**
+1. Захват блокировок в порядке возрастания индекса (lock ranking).
+2. Вычисление `final_rights = src.rights & rights`.
+3. Копирование Capability в dest с новыми правами и relation.
+4. Установка `parent_idx = Some(src)`.
+
+### revoke(slot_idx) — отзыв
+
+```rust
+pub fn revoke(&self, slot_idx: usize) -> Result<(), &'static str>
+```
+
+Каскадное удаление:
+
+```
+revoke_internal(slot_idx):
+ │
+ ├── 1. Поиск потомков:
+ │ for i in 0..slots.len():
+ │ if slots[i].parent_idx == Some(slot_idx):
+ │ revoke_internal(i) ← рекурсивно!
+ │
+ ├── 2. Уничтожение себя:
+ │ cap = Capability::empty()
+ │ parent_idx = None
+ │ token = old_token_sig
+ │
+ └── 3. Отправка token в очередь:
+ if token != 0 && token != 0xDEAD_BEEF:
+ MMU_REVOCATION_QUEUE.push(token)
+```
+
+**Почему нет блокировок при рекурсии?**
+- На каждом шаге проверка `is_child` захватывает и отпускает блокировку.
+- Рекурсивный вызов происходит **после** освобождения блокировки.
+- Это предотвращает взаимоблокировки.
+
+**Фильтр токенов:**
+- `token == 0`: пустой/невалидный capability.
+- `token == 0xDEAD_BEEF`: сырой Untyped (для тестов/отладки).
+- Эти токены не отправляются в очередь (бессмысленно).
+
+### get_cap(slot) — чтение
+
+```rust
+pub fn get_cap(&self, slot: usize) -> Option {
+ self.slots.get(slot).map(|s| s.lock().cap)
+}
+```
+
+## Lock Ranking — детали
+
+```rust
+let (src_slot, dest_slot) = if src < dest {
+ _guard_low = self.slots[src].lock(); // меньший → первый
+ _guard_high = self.slots[dest].lock(); // больший → второй
+} else {
+ _guard_low = self.slots[dest].lock();
+ _guard_high = self.slots[src].lock();
+};
+```
+
+**Причина:** если поток A делает mint(5, 10), а поток B делает mint(10, 5),
+без lock ranking они могут взаимно заблокироваться:
+- A: lock(5) → ждёт lock(10)
+- B: lock(10) → ждёт lock(5)
+
+С lock ranking:
+- A: lock(5) → lock(10)
+- B: lock(5) → ждёт... → дождался → lock(10)
+
+## Интеграция с событиями
+
+После `revoke()`, токен попадает в `MMU_REVOCATION_QUEUE` (см.
+`events.rs`). VMM обрабатывает очередь в `process_pending_revocations()`.
+
+## Особенности реализации
+
+1. **Vec>**: каждый слот — отдельная spinlock-ячейка.
+ Это позволяет параллельно читать разные слоты.
+2. **`pub slots`**: прямой доступ к слоту возможен (для тестов).
+3. **panic на overflow**: если очередь отзыва переполнена — паника.
+ Это критический сбой подсистемы ресурсов.
diff --git a/kernel/docs/capability/descriptors.md b/kernel/docs/capability/descriptors.md
new file mode 100644
index 0000000..17fed8e
--- /dev/null
+++ b/kernel/docs/capability/descriptors.md
@@ -0,0 +1,115 @@
+# Дескрипторы capability: `descriptor.rs`
+
+## Назначение
+
+Файл определяет базовые типы capability-системы:
+что такое capability, какие бывают объекты, права и отношения.
+
+## CapObject — что представляет capability
+
+```rust
+pub enum CapObject {
+ Empty, // Пустой слот
+ Memory { phys: PhysAddr, size_pages: usize }, // Фрейм памяти
+ CNode { phys: PhysAddr, slots: usize }, // Другой CNode
+ PMActor { id: u64 }, // PM Actor
+}
+```
+
+### Memory
+
+Capability на физическую память:
+- `phys` — физический адрес начала.
+- `size_pages` — размер в страницах.
+
+### CNode
+
+Capability на другой CNode:
+- `phys` — физический адрес CNode.
+- `slots` — количество слотов.
+
+### PMActor
+
+Capability на PMActor:
+- `id` — уникальный идентификатор актора.
+
+### Empty
+
+Слот пуст. `is_valid()` возвращает `false`.
+
+## CapRights — права доступа
+
+```rust
+bitflags! {
+ pub struct CapRights: u8 {
+ const READ = 1 << 0; // 0x01 — чтение
+ const WRITE = 1 << 1; // 0x02 — запись
+ const EXECUTE = 1 << 2; // 0x04 — исполнение
+ const GRANT = 1 << 3; // 0x08 — разрешение на mint
+ }
+}
+```
+
+**Правила:**
+- Права дочернего capability = `родительские_права & запрошенные_права`.
+- Нельзя расширить права: если родитель не имеет GRANT, mint невозможен.
+- `CapRights::all()` = R | W | X | G = 0x0F.
+
+## Relation — тип связи
+
+```rust
+pub enum Relation {
+ Strong, // Владелец — сильная ссылка (объект жив, пока есть Strong)
+ Borrow, // Заёмщик — временный доступ
+ Transfer, // Передача — владение переходит без возможности отзыва
+}
+```
+
+- **Strong**: capability владеет объектом. При revoke, объект может
+ быть уничтожен или возвращён пулу.
+- **Borrow**: capability предоставляет временный доступ.
+ При revoke родителя, borrow-потомки тоже отзываются.
+- **Transfer**: полная передача владения. Используется при IPC.
+
+## Capability — полный дескриптор
+
+```rust
+#[derive(Debug, Clone, Copy)]
+pub struct Capability {
+ pub object: CapObject, // Целевой объект
+ pub rights: CapRights, // Права доступа
+ pub relation: Relation, // Тип связи
+ pub token_sig: u64, // Уникальный подписывающий токен
+}
+```
+
+### token_sig — назначение
+
+- Уникальный 64-битный идентификатор capability.
+- Используется для:
+ 1. **Отзыва**: при revoke, token_sig отправляется в MMU_REVOCATION_QUEUE.
+ 2. **Идентификации в VMM**: VMA хранят `cap_token == token_sig`.
+ 3. **Отладки**: каждый capability можно однозначно отследить.
+
+### Методы
+
+```rust
+impl Capability {
+ pub const fn empty() -> Self {
+ Self {
+ object: CapObject::Empty,
+ rights: CapRights::empty(),
+ relation: Relation::Borrow,
+ token_sig: 0,
+ }
+ }
+
+ pub fn is_valid(&self) -> bool {
+ !matches!(self.object, CapObject::Empty)
+ }
+}
+```
+
+`token_sig = 0` зарезервирован для пустых/невалидных capability.
+`token_sig = 0xDEAD_BEEF` используется для сырых Untyped
+(не отправляется в MMU_REVOCATION_QUEUE, см. `cap/mod.rs`).
diff --git a/kernel/docs/capability/introduction.md b/kernel/docs/capability/introduction.md
new file mode 100644
index 0000000..a307a19
--- /dev/null
+++ b/kernel/docs/capability/introduction.md
@@ -0,0 +1,126 @@
+# Capability-система: концептуальная модель
+
+## Философия
+
+**Capability** (дескриптор возможности) — это **неподделываемый токен**,
+дающий право выполнить определённую операцию над определённым объектом.
+
+В традиционных ОС (Linux, Windows) доступ контролируется через:
+- PID + UID/GID + проверка при каждом системном вызове.
+- MMU: page tables определяют, что отображено, но не кто отобразил.
+
+В модели capabilities:
+- **Если у вас нет capability — у вас нет доступа.**
+- Capability хранятся в CNode — защищённой таблице, доступной только ядру.
+- Capability можно создавать только от родительского capability
+ (иерархия наследования).
+- Права можно только **урезать** (mint), но не расширить.
+- Capability можно **отозвать** (revoke), что уничтожает его
+ и всех его потомков.
+
+## Основные понятия
+
+```
+Capability {
+ object: CapObject, // на что указывает (Memory, CNode, PMActor...)
+ rights: CapRights, // права (R, W, X, G)
+ relation: Relation, // Strong (владеет), Borrow (заём), Transfer
+ token_sig: u64, // уникальный идентификатор (для revoke)
+}
+
+Relation {
+ Strong: владеет объектом (capability владеет памятью)
+ Borrow: заём — временный доступ без права распоряжаться
+ Transfer: передача — владение переходит получателю
+}
+
+CapRights {
+ READ = 0x1,
+ WRITE = 0x2,
+ EXECUTE = 0x4,
+ GRANT = 0x8, // разрешение создавать дочерние capability
+}
+```
+
+## Иерархия и отзыв
+
+```
+CNode (массив слотов)
+┌────────┬────────┬────────┬────────┐
+│ slot 0 │ slot 1 │ slot 2 │ slot 3 │ ...
+├────────┼────────┼────────┼────────┤
+│ cap │ cap │ cap │ cap │
+│ parent:│ parent:│ parent:│ parent:│
+│ None │ Some(0)│ Some(0)│ Some(1)│
+└────────┴────────┴────────┴────────┘
+ │
+ ┌────────┴────────┐
+ ▼ ▼
+ slot 1 slot 2
+ (mint from 0) (mint from 0)
+
+revoke(0) → slot 0 уничтожается
+ → рекурсивно: slot 1, slot 2 тоже уничтожаются
+ → token_sig slot 0 отправляется в MMU_REVOCATION_QUEUE
+ → VMM обработает отзыв при следующем page fault
+```
+
+### Mint — создание дочернего capability
+
+```rust
+cnode.mint(src, dest, relation, rights)
+```
+
+- `src` — исходный слот (должен иметь GRANT).
+- `dest` — целевой слот (должен быть пустым).
+- `relation` — как наследник связан с родителем.
+- `rights` — права наследника (∩ с правами родителя).
+
+### Revoke — отзыв capability
+
+```rust
+cnode.revoke(slot_idx)
+```
+
+1. Рекурсивно находит всех потомков и уничтожает их.
+2. Уничтожает сам capability.
+3. Отправляет `token_sig` в глобальную `MMU_REVOCATION_QUEUE`.
+4. VMM при следующем page fault обрабатывает все накопленные отзывы.
+
+## Lock Ranking — предотвращение дедлоков
+
+В `mint()` захватываются две блокировки (src и dest). Чтобы избежать
+инверсии блокировок, используется строгий порядок:
+
+```rust
+let (src_slot, dest_slot) = if src < dest {
+ // Захватываем меньший индекс первым
+ _guard_low = self.slots[src].lock();
+ _guard_high = self.slots[dest].lock();
+} else {
+ _guard_low = self.slots[dest].lock();
+ _guard_high = self.slots[src].lock();
+};
+```
+
+## Интеграция с VMM
+
+При отзыве capability, VMM должна аннулировать все VMA, связанные
+с отозванным токеном. Для этого:
+
+1. `revoke()` пушит `token_sig` в lock-free очередь.
+2. При page fault: `process_pending_revocations()` дренирует очередь.
+3. VMA с `cap_token == token_sig` удаляются из AddressSpace.
+4. TLB flush для синхронизации MMU.
+
+## Использование в kmain()
+
+```rust
+let root_cnode = CNode::new(256);
+// Вставка capability на фрейм
+root_cnode.insert(0, mem_cap).unwrap();
+// Mint с урезанными правами
+root_cnode.mint(0, 10, Relation::Borrow, R | W).unwrap();
+// Revoke — отзыв всех потомков
+root_cnode.revoke(0);
+```
diff --git a/kernel/docs/capability/objects.md b/kernel/docs/capability/objects.md
new file mode 100644
index 0000000..d24696b
--- /dev/null
+++ b/kernel/docs/capability/objects.md
@@ -0,0 +1,73 @@
+# Ядерные объекты: `object.rs`
+
+## Концептуальная модель
+
+`KernelObject` — это ref-counted представление объекта ядра.
+В отличие от `Capability` (которая указывает на объект), `KernelObject`
+— это сам объект с подсчётом ссылок.
+
+## Структура
+
+```rust
+pub struct KernelObject {
+ pub phys_addr: PhysAddr, // физический адрес объекта
+ pub size_bits: u8, // размер в битах (2^size_bits)
+ pub obj_type: ObjectType, // тип объекта
+ pub ref_count: AtomicUsize, // счётчик ссылок
+ pub owner_id: u64, // ID владельца
+}
+```
+
+## ObjectType — классификация объектов
+
+```rust
+pub enum ObjectType {
+ Untyped, // Сырая память без типа
+ Frame, // Фрейм (4KiB страница)
+ CNode, // Capability Node
+ ThreadBlock, // Блок управления потоком (TCB)
+ PageTable, // Таблица страниц
+}
+```
+
+## Ref-counting
+
+```rust
+impl KernelObject {
+ pub fn add_ref(&self) {
+ self.ref_count.fetch_add(1, Ordering::Relaxed);
+ // ^ Relaxed: нас не волнует порядок других операций
+ // при увеличении счётчика. Только атомарность.
+ }
+
+ pub fn release(&self) -> bool {
+ self.ref_count.fetch_sub(1, Ordering::Release) == 1
+ // ^ Release: все операции до release видны тому,
+ // кто Acquire-читает ref_count.
+ // Возвращает true, если это была последняя ссылка
+ // (объект должен быть уничтожен).
+ }
+}
+```
+
+## Текущее состояние
+
+`KernelObject` определён, но пока не используется активно.
+Capability-система в текущей версии работает напрямую с `CapObject`
+и `PhysAddr`, без обёртки в `KernelObject`.
+
+Планируемое использование:
+- При выделении памяти через PMActor: создаётся KernelObject.
+- Capability ссылается на KernelObject через ID/индекс.
+- Когда последняя Strong capability удалена → KernelObject
+ уничтожается → память возвращается.
+
+## Отличие CapObject vs KernelObject
+
+| Характеристика | CapObject | KernelObject |
+|---------------|-----------|--------------|
+| Роль | Что capability представляет | Сам объект в памяти ядра |
+| Ref-count | Нет | AtomicUsize |
+| Хранение | В CNode slot | В отдельной таблице |
+| Типы | Memory, CNode, PMActor | Untyped, Frame, CNode, ThreadBlock, PageTable |
+| Связь | CapObject.Memory.phys = KernelObject.phys_addr | — |
diff --git a/kernel/docs/console.md b/kernel/docs/console.md
deleted file mode 100644
index 06c4ec6..0000000
--- a/kernel/docs/console.md
+++ /dev/null
@@ -1,132 +0,0 @@
-# Консоль и отладка
-
----
-
-## TTY — PSF2 терминал
-
-**Файл**: `src/tty.rs`
-
-Шрифтовой терминал на основе PSF2 (PC Screen Font v2). Это **реально используемая консоль** в `kmain`.
-
-### Psf2Header — `#[repr(C, packed)]`
-
-```rust
-struct Psf2Header {
- magic: u32, // магическое число PSF2
- version: u32,
- header_size: u32,
- flags: u32,
- num_glyphs: u32, // количество глифов
- bytes_per_glyph: u32,
- height: u32, // высота шрифта в пикселях
- width: u32, // ширина шрифта в пикселях
-}
-```
-
-### Console
-
-```rust
-pub struct Console<'a> {
- framebuffer: &'a Framebuffer<'a>,
- font: &'static [u8], // сырые данные PSF2
- x: usize, // курсор X (в пикселях)
- y: usize, // курсор Y (в пикселях)
- fg_color: u32, // цвет текста (0xFFFFFF = белый)
- bg_color: u32, // цвет фона (0x000000 = чёрный)
-}
-```
-
-### Методы
-
-| Метод | Описание |
-|---|---|
-| `new(framebuffer, font)` | Создаёт консоль, курсор в (0,0) |
-| `set_color(fg)` | Устанавливает цвет текста |
-| `clear()` | Зануляет фреймбуфер, сбрасывает курсор |
-| `header()` | Возвращает ссылку на Psf2Header |
-| `scroll()` | Сдвигает фреймбуфер на высоту шрифта, зануляет низ |
-| `draw_glyph(glyph_index, x, y)` | Рисует глиф: сканирует bitmap шрифта, пишет `fg_color` для установленных битов |
-| `write_char(c)` | Обрабатывает `\n`, word wrap, поиск глифа (0 для отсутствующих), скролл |
-| `write_str(s)` | Реализация `fmt::Write` — итерация по символам |
-
-`draw_glyph` — ключевой метод: строки шрифта — это побитовое представление, каждый бит — один пиксель. Если бит установлен → пишется `fg_color`, иначе пропускается.
-
----
-
-## Serial — последовательный порт
-
-**Файл**: `src/debug/serial.rs`
-
-Драйвер UART 16550 на COM1.
-
-### SerialPort
-
-```rust
-pub struct SerialPort(pub u16); // номер порта
-```
-
-### Инициализация UART (`init()`)
-
-```rust
-port + 1 = 0x00 // отключение прерываний
-port + 3 = 0x80 // DLAB = 1 (доступ к делителю)
-port + 0 = 0x03 // делитель младший байт (~38400 бод)
-port + 1 = 0x00 // делитель старший байт
-port + 3 = 0x03 // 8N1: 8 бит, no parity, 1 stop bit
-port + 2 = 0xC7 // FIFO enable, clear, 14-byte threshold
-port + 4 = 0x0B // DTR + RTS (data terminal ready)
-```
-
-### Методы
-
-| Метод | Описание |
-|---|---|
-| `is_transmit_empty() -> bool` | Проверка бита 5 Line Status Register |
-| `send(data: u8)` | Ждёт `is_transmit_empty()`, затем пишет в порт |
-| `write_str(s)` (fmt::Write) | Поcимвольная отправка |
-
-### Функции
-
-| Функция | Описание |
-|---|---|
-| `init_global()` | Инициализирует COM1, сохраняет в `SERIAL_PORT` |
-| `write_global(args: Arguments)` | Пишет форматированную строку в serial |
-| `outb(port, val)` | `unsafe`: инструкция `out` (write I/O port) |
-| `inb(port) -> u8` | `unsafe`: инструкция `in` (read I/O port) |
-
----
-
-## Логирование
-
-**Файл**: `src/debug.rs`
-
-### LogLevel
-
-```rust
-pub enum LogLevel {
- Info, // зелёный: ANSI \x1b[32m, RGB 0x00FF00
- Warn, // жёлтый: ANSI \x1b[33m, RGB 0xFFFF00
- Error, // красный: ANSI \x1b[31m, RGB 0xFF0000
-}
-```
-
-### Макросы
-
-#### `log!(console, level, module, $($arg)*)`
-
-Формат: `[ LOG ] | \n`
-
-Двойной вывод:
-- **Console**: цветной `[ LOG ]` (по LogLevel) + белый ` | `
-- **Serial**: ANSI-цветной `[ LOG ]` + ` | ` + ANSI reset
-
-#### `info!(console, module, ...)`
-Обёртка над `log!` с `LogLevel::Info`. Зелёный `[ LOG ]`.
-
-#### `warn!(console, module, ...)`
-Обёртка над `log!` с `LogLevel::Warn`. Жёлтый `[ LOG ]`.
-
-#### `error!(console, module, ...)`
-Обёртка над `log!` с `LogLevel::Error`. Красный `[ LOG ]`.
-
-Все макросы экспортируются (`#[macro_export]`) и доступны из любого модуля.
diff --git a/kernel/docs/cpu/idt.md b/kernel/docs/cpu/idt.md
new file mode 100644
index 0000000..d486ec2
--- /dev/null
+++ b/kernel/docs/cpu/idt.md
@@ -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::() - 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 (один стек для
+ всех обработчиков).
diff --git a/kernel/docs/cpu/interrupts.md b/kernel/docs/cpu/interrupts.md
new file mode 100644
index 0000000..9d500d8
--- /dev/null
+++ b/kernel/docs/cpu/interrupts.md
@@ -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.
diff --git a/kernel/docs/cpu/introduction.md b/kernel/docs/cpu/introduction.md
new file mode 100644
index 0000000..5072b8c
--- /dev/null
+++ b/kernel/docs/cpu/introduction.md
@@ -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 │
+```
diff --git a/kernel/docs/cpu/lapic.md b/kernel/docs/cpu/lapic.md
new file mode 100644
index 0000000..82ff79d
--- /dev/null
+++ b/kernel/docs/cpu/lapic.md
@@ -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 доставляет
+ прерывание. Запись блокирующая (ждёт готовности шины).
diff --git a/kernel/docs/debug/introduction.md b/kernel/docs/debug/introduction.md
new file mode 100644
index 0000000..5ef4992
--- /dev/null
+++ b/kernel/docs/debug/introduction.md
@@ -0,0 +1,88 @@
+# Подсистема отладки: концептуальная модель
+
+## Два канала вывода
+
+Ядро имеет два параллельных канала для отладки:
+
+1. **Экранный (framebuffer console)** — через `tty::Console`.
+ - Использует PSF2-шрифты.
+ - Цветной вывод (зелёный/жёлтый/красный для Info/Warn/Error).
+ - Медленнее, но визуально нагляднее.
+
+2. **Serial port (COM1)** — через `debug::serial`.
+ - Текстовый вывод с ANSI escape codes.
+ - Работает через QEMU/KVM serial console.
+ - Быстрее, может быть перенаправлен в файл.
+
+## LogLevel — уровни логирования
+
+```rust
+pub enum LogLevel {
+ Info, // Зелёный на экране, зелёный в serial
+ Warn, // Жёлтый
+ Error, // Красный
+}
+```
+
+Каждый уровень имеет:
+- `serial_color_code()` — ANSI escape code для serial.
+- `console_color()` — RGB значение для framebuffer.
+
+## Макросы
+
+```rust
+// Основной макрос
+log!(console, level, module, format_args...)
+
+// Специализированные
+info!(console, module, format_args...)
+warn!(console, module, format_args...)
+error!(console, module, format_args...)
+```
+
+**Формат вывода на экран:**
+```
+[ LOG ] |
+```
+
+**Формат в serial:**
+```
+GREEN[ LOG] RESET |
+```
+
+## Цветовое кодирование
+
+| Уровень | Экран (RGB) | Serial (ANSI) |
+|---------|-------------|---------------|
+| Info | 0x00FF00 | `\x1b[32m` (green) |
+| Warn | 0xFFFF00 | `\x1b[33m` (yellow) |
+| Error | 0xFF0000 | `\x1b[31m` (red) |
+| Текст | 0xFFFFFF (white) | `\x1b[0m` (reset) |
+
+## Использование в kmain()
+
+```rust
+// После инициализации serial
+debug::serial::init_global();
+
+// После инициализации console
+info!(console, "BOOT", "LIS4 Kernel Starting...");
+info!(console, "MEM", "BitmapPMM initialized.");
+info!(console, "LAPIC", "Local APIC initialized.");
+```
+
+## Архитектура
+
+```
+┌──────────────┐ ┌───────────────────┐
+│ kmain() │────►│ log!() macro │
+└──────────────┘ └────────┬──────────┘
+ │
+ ┌──────────────┼──────────────┐
+ ▼ ▼ ▼
+ ┌──────────┐ ┌──────────┐ ┌──────────┐
+ │ Экран │ │ Serial │ │ Паника │
+ │ Console │ │ COM1 │ │ Handler │
+ │ (tty.rs) │ │(serial.rs)│ │(main.rs) │
+ └──────────┘ └──────────┘ └──────────┘
+```
diff --git a/kernel/docs/debug/serial.md b/kernel/docs/debug/serial.md
new file mode 100644
index 0000000..c0d2da7
--- /dev/null
+++ b/kernel/docs/debug/serial.md
@@ -0,0 +1,110 @@
+# Serial Port драйвер: `serial.rs`
+
+## Назначение
+
+Драйвер последовательного порта (UART 16550, COM1) для отладочного
+вывода. Позволяет видеть сообщения ядра через QEMU serial console,
+minicom, screen и т.д.
+
+## Аппаратная модель
+
+COM1 расположен по портам ввода-вывода `0x3F8`-`0x3FF`:
+
+| Порт | Регистр | Назначение |
+|------|---------|------------|
+| 0x3F8 | DATA | Чтение/запись данных |
+| 0x3F9 | IER | Interrupt Enable |
+| 0x3FA | IIR/FCR | Interrupt ID / FIFO Control |
+| 0x3FB | LCR | Line Control |
+| 0x3FC | MCR | Modem Control |
+| 0x3FD | LSR | Line Status |
+| 0x3FE | MSR | Modem Status |
+
+## Инициализация
+
+```rust
+pub unsafe fn init() -> Self {
+ let port = Self::COM1; // 0x3F8
+ outb(port + 1, 0x00); // IER = 0 (disable interrupts)
+ outb(port + 3, 0x80); // LCR DLAB=1 (enable baud rate programming)
+ outb(port + 0, 0x03); // Divisor LSB = 3 (38400 baud)
+ outb(port + 1, 0x00); // Divisor MSB = 0
+ outb(port + 3, 0x03); // LCR = 8N1 (8 bits, No parity, 1 stop)
+ outb(port + 2, 0xC7); // FCR = enable FIFO, clear, 14-byte threshold
+ outb(port + 4, 0x0B); // MCR = DTR+RTS+OUT2 (enable IRQ + handshake)
+ SerialPort(port)
+}
+```
+
+### Детали конфигурации
+
+1. **IER = 0**: отключаем прерывания UART (TODO: включить для RX).
+2. **DLAB = 1**: разрешаем программирование делителя бода.
+3. **Divisor = 3**: при тактовой 1.8432 MHz → 115200 / 3 = 38400 бод.
+4. **LCR = 0x03**: 8N1 — 8 бит данных, нет чётности, 1 стоп-бит.
+5. **FCR = 0xC7**: enable FIFO, clear both FIFOs, trigger at 14 bytes.
+6. **MCR = 0x0B**: DTR=1, RTS=1, OUT2=1 (необходимо для IRQ на ISA шине).
+
+## Отправка байта
+
+```rust
+fn is_transmit_empty(&self) -> bool {
+ unsafe { (inb(self.0 + 5) & 0x20) != 0 } // LSR bit 5 = Transmitter Holding Register Empty
+}
+
+pub fn send(&self, data: u8) {
+ while !self.is_transmit_empty() {} // Ждём, пока UART готов
+ unsafe { outb(self.0, data); }
+}
+```
+
+## fmt::Write реализация
+
+```rust
+impl core::fmt::Write for SerialPort {
+ fn write_str(&mut self, s: &str) -> core::fmt::Result {
+ for byte in s.bytes() { self.send(byte); }
+ Ok(())
+ }
+}
+```
+
+## Глобальный экземпляр
+
+```rust
+static SERIAL_PORT: Locked