Files
Elyz/PLAN.md
2026-07-06 00:25:49 +03:00

627 lines
25 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Elyz (LISA) Microkernel — Полный план оптимизации и полировки
> Анализ кода по состоянию на июнь 2026.
> Составлен на основе исходников: `kernel/src/`, linker-скрипты, Cargo.toml, Makefile.
---
## Содержание
1. [Условные обозначения](#условные-обозначения)
2. [Критические баги (не работает)](#ф-1-критические-баги-фаза-1)
3. [Средние баги (UB / утечки)](#ф-1-критические-баги-фаза-1)
4. [Проблемы архитектуры](#ф-2-архитектурный-рефакторинг-фаза-2)
5. [Code Quality & Naming](#ф-3-code-quality--naming-фаза-3)
6. [Производительность](#ф-4-производительность-фаза-4)
7. [Инфраструктура](#ф-5-инфраструктура-фаза-5)
8. [Полный пошаговый план (сортированный)](#полный-пошаговый-план-сортированный-по-приоритету)
9. [Roadmap](#roadmap)
---
## Условные обозначения
| Метка | Значение |
|-------|----------|
| 🔴 CRIT | Системный баг — крах, silent corruption, UB |
| 🟡 HIGH | Утечка памяти, логическая ошибка |
| 🟢 MED | Неоптимальность, технический долг |
| 🔵 LOW | Косметика, нейминг, кодстайл |
---
## Ф 1: Критические баги (Фаза 1 — CRIT 1.11.6 ✅ исправлены)
### 🔴 CRIT 1.6 — `tlb_flush_asid` не работает с >64 ядрами ✅ исправлено
**Файл**: `kernel/src/mem/vmm.rs:794-802`
**Проблема**: `1u64 << current_core` при `current_core >= 64` — UB (panic в debug, wrap в release).
**Решение**: Добавлена проверка `current_core < 64` перед shift. Для core >= 64 self-exclusion пропускается (core не входит в нижние 64 бита маски). В `handle_tlb_shootdown_ipi` аналогичная проверка предотвращает запись в маску по некорректному смещению.
```rust
let target_mask = if current_core < 64 {
active_cpus & !(1u64 << current_core)
} else {
active_cpus
};
```
**TODO**: При реализации SMP с >64 ядер — перейти на `AtomicU128` или динамический список APIC ID.
---
## Ф 1.5: Средние баги (Фаза 1.5 — MED 1.71.10 ✅ исправлены)
### 🟡 MED 1.7 — `unwrap_unchecked()` на Option без full fence ✅ исправлено
**Файл**: `kernel/src/mem/pm_router.rs:70-82`
**Проблема**: TOCTOU race между check `is_ready` и `unwrap_unchecked()`. Хотя `is_ready` никогда не сбрасывается, code smell.
**Решение**: Добавлен комментарий-инвариант, объясняющий почему TOCTOU безопасен (is_ready set-once, never cleared). Panic path вынесен в `#[cold]` функцию:
```rust
#[cold]
fn not_initialized() -> ! {
panic!("FATAL: PMRouter is accessed before initialization!")
}
```
---
### 🟡 MED 1.8 — `process_messages` не имеет квоты ✅ исправлено
**Файл**: `kernel/src/mem/pm_manages.rs:328-354`
**Проблема**: Дренирует всю очередь за один вызов. Если 10 000 сообщений, заблокирует ядро на неопределённое время.
**Решение**: Добавлен лимит `MAX_MESSAGES_PER_CALL = 64`. После обработки 64 сообщений вызов возвращает управление, позволяя другим компонентам ядра получить процессорное время.
---
### 🟡 MED 1.9 — Bitmap PMM инициализирует `ref_counts` в 1, потом маркирует used-фреймы ✅ исправлено
**Файл**: `kernel/src/mem/pmm.rs:49-76`
**Проблема**: `fill(0xFF)` + `fill(1)` для всех страниц, затем итерация free по USABLE. Лишняя работа.
**Решение**: Инвертирована логика: `fill(0)` для bitmap и ref_counts, затем итерация по **не-USABLE** записям для mark_used. Убрана лишняя запись в резервированные страницы.
---
### 🟡 MED 1.10 — Page fault handler берёт `KERNEL_SPACE.lock()` каждый раз ✅ исправлено
**Файл**: `kernel/src/cpu/interrupts.rs:109-148`
**Проблема**: Двойной захват `KERNEL_SPACE.lock()` — в `process_deferred_mmu_events()` и в `rust_page_fault_handler()`. На SMP каждый PF будет сериализован.
**Решение**: `process_deferred_mmu_events()` встроен в `rust_page_fault_handler` — блокировка захватывается один раз и удерживается для обеих операций. Добавлен TODO о переходе на `RwLock` для SMP.
---
## Ф 2: Архитектурный рефакторинг (Фаза 2)
### 🏗️ ARCH 2.1 — HHDM offset передаётся параметром в каждую функцию
**Проблема**: `hhdm: u64` тащится через все вызовы:
```rust
p4.map_region(virt, phys, size, flags, hhdm);
p4.map_page(virt, frame, flags, hhdm);
```
Загрязняет сигнатуры и не нужно (HHDM offset — константа после boot).
**Решение**: Глобальный `static HHDM_OFFSET: AtomicU64`:
```rust
pub fn init(offset: u64) { HHDM_OFFSET.store(offset, Ordering::Release); }
pub fn get() -> u64 { HHDM_OFFSET.load(Ordering::Relaxed) }
```
Убрать `hhdm` из параметров всех функций paging/vmm.
---
### 🏗️ ARCH 2.2 — Buddy allocator: O(n) поиск при free()
**Файл**: `kernel/src/mem/buddy.rs:246`
```rust
if let Some(pos) = self.free_lists[order].iter().position(|&b| b == buddy_idx) {
```
Линейный поиск по `Vec<usize>` — O(количество блоков этого порядка). При 10 000 блоков порядка 0, free() занимает тысячи итераций.
**Решение**: Перейти на **intrusive linked list** через сами страницы: первые 8 байт неиспользуемой страницы хранят `next` указатель. Даёт O(1) удаление.
Или хеш-таблицу: `HashMap<usize, ()>` (buddy_idx → exists).
---
### 🏗️ ARCH 2.3 — PMM Bitmap: O(n) allocation scan
**Файл**: `kernel/src/mem/pmm.rs:128-158`
Побайтовый (побитовый) scan всего bitmap в поиске нулевого бита. На системе с 64 GB RAM → 64M бит → до 8M итераций на аллокацию.
**Решение**: Использовать **tree bitmap** (buddy-style bitmap). Хранить дополнительный уровень с `u64` словами, где каждый бит = `all_ones(word)`. Позволяет за O(1) найти свободный блок через `ctz` / `clz`.
---
### 🏗️ ARCH 2.4 — Channel `wait_for_response` — busy-wait
**Файл**: `kernel/src/mem/pm_router.rs:130-137`
```rust
while channel.state.load(Ordering::Acquire) != STATE_READY {
core::hint::spin_loop();
}
```
100% CPU consumption в ожидании. Для реального асинхронного рантайма нужен yield-to-scheduler или waker.
**Решение**: Добавить `Waker` registration: поток регистрирует свой `Waker` в канале, а `route_responses` вызывает `wake()` при записи результата. Пока планировщика нет — хотя бы HLT-based ожидание.
---
### 🏗️ ARCH 2.5 — Capability revocation: O(n) рекурсия
**Файл**: `kernel/src/cap/mod.rs:97-137`
`revoke_internal()` сканирует **весь CNode** на каждом уровне рекурсии. С 256+ слотами и глубиной 10 → 2560 итераций.
**Решение**: Держать per-slot список children:
```rust
struct CNodeSlot {
cap: Capability,
parent: Option<usize>,
children: Vec<usize>, // явный список наследников
}
```
Отзыв — проход по дереву без сканирования всей таблицы.
---
### 🏗️ ARCH 2.6 — Нет SMP startup кода
`ACTIVE_CPUS_MASK = 1` (только BSP). TLB shootdown кидает IPI в пустоту. Нет:
- SIPI trampoline page
- Startup sequence для AP
- Per-CPU data areas
- Stack allocation для AP
**Решение**: Реализовать SIPI-последовательность:
1. Выделить страницу по адресу <1MB (real mode trampoline)
2. Записать startup код (выход из real mode → long mode → setup stack → jump to rust)
3. Отправить SIPI с вектором на эту страницу
4. AP просыпается, инициализирует LAPIC, регистрируется в `ACTIVE_CPUS_MASK`
---
### 🏗️ ARCH 2.7 — Вся память в HHDM identity-map при старте
**Файл**: `kernel/src/main.rs:194-201`
```rust
for entry in mmap_res.entries() {
p4.map_region(virt_hhdm, phys, entry.length, flags, hhdm_offset);
if entry.entry_type != RESERVED {
p4.map_region(VirtAddr(entry.base), phys, entry.length, flags, hhdm_offset);
}
}
```
Все *не-reserved* регионы identity-map'ятся по физическому адресу (second mapping). На системе с 64GB RAM и 4KB страницами = 16M calls к `map_page`. Каждый call:
- Аллоцирует до 3 страниц таблиц (P3, P2, P1)
- INVLPG на каждую 4KB страницу
- Время: ~часы при эмуляции, минуты при KVM
**Решение**: Использовать **2MB huge pages** (`PageTableFlags::HUGE_PAGE`) для identity map. 64GB → 32K entries вместо 16M. INVLPG заменяется на CR3 reload.
---
## Ф 3: Code Quality & Naming (Фаза 3)
### 🧹 CQ 3.1 — Dead code: FramebufferDisplay + Console (main.rs)
**Файл**: `kernel/src/main.rs:42-144`
Две структуры:
- `FramebufferDisplay` (реализует `DrawTarget` для `embedded-graphics`)
- `Console` (обёртка над `FramebufferDisplay` + `embedded-graphics`)
Никто не используется. Настоящий консольный вывод — `tty::Console`. При этом `embedded-graphics` висит в Cargo.toml как зависимость.
**Решение**: Удалить об struct + реализацию, убрать `embedded-graphics` из зависимостей.
---
### 🧹 CQ 3.2 — Unused imports в main.rs
**Файл**: `kernel/src/main.rs:6-16`
```rust
use core::fmt::{self, Write};
use alloc::vec::Vec;
use crate::mem::paging::{PageTable, PageTableFlags};
use crate::mem::address::{PhysAddr, VirtAddr};
use crate::cap::{Relation, CapRights, Capability, CapObject};
use crate::mem::pm_manages::{PMActor, PMRequest, PMResult};
```
Часть из них используется, часть — нет (например `fmt::self`, `PageTable`). После удаления dead code (3.1) ещё часть станет не нужна.
---
### 🧹 CQ 3.3 — `#[allow(dead_code)]` на весь mod vmm
**Файл**: `kernel/src/mem/vmm.rs:27`
```rust
#![allow(dead_code)]
```
Отключает warning для ВСЕГО модуля. 800+ строк кода с отключённой проверкой мёртвого кода. Означает что значительная часть VMM API не используется сейчас — надо либо использовать, либо убрать, либо маркировать `#[allow(dead_code)]` на конкретные единицы.
---
### 🧹 CQ 3.4 — Имя проекта: LISA / Elyz / kernel
| Где | Что написано |
|-----|-------------|
| `Cargo.toml` | `name = "LISA"` |
| `README.md` | "Elyz" |
| `limine.conf` | `Elyz (LISA KERNEL)` |
| `GNUmakefile` | `elyz-x86_64.iso` |
| `main.rs` | `LIS4` (LISA 4?) |
Три разных публичных имени.
---
### 🧹 CQ 3.5 — Странное имя файла: `pm_manages.rs`
Грамматически некорректно (глагол в имени файла). Основная структура — `PMActor`, файл должен называться `pm_actor.rs`.
---
### 🧹 CQ 3.6 — Мёртвые linker-скрипты
- `linker-aarch64.ld`
- `linker-riscv64.ld`
- `linker-loongarch64.ld`
Не используются (build.rs хардкодит `linker-x86_64.ld`). Ни один из этих таргетов не подключен в `rust-toolchain.toml`. Dead weight.
---
### 🧹 CQ 3.7 — Locked SpinLock без backoff
**Файл**: `kernel/src/mem/allocator.rs:27`
```rust
while self.lock.compare_exchange_weak(false, true, Ordering::Acquire, Ordering::Relaxed).is_err() {
core::hint::spin_loop();
}
```
`spin_loop()` на x86 — это `REP NOP` (PAUSE). Это правильно, но при длительном ожидании нужно экспоненциально увеличивать задержку.
---
### 🧹 CQ 3.8 — `log!` максрос не lock-free
При вызове `info!(...)` внутри макроса:
1. Вызывается `SerialPort::init()` (см. CRIT 1.3)
2. Пишет на экран через framebuffer
3. Пишет в serial
Если в PF handler вызвать `info!`, и PF произошёл из-за framebuffer DMA или serial IRQ — будет reentrancy. Надо маркировать как `#[inline(never)]` или добавить reentrancy guard.
---
## Ф 4: Производительность (Фаза 4)
### ⚡ PERF 4.1 — Bitmap PMM: word-level skip
**Файл**: `kernel/src/mem/pmm.rs:139`
```rust
if self.bitmap[byte_idx] == 0xFF { continue; }
```
Проверка на уровне байта — можно на уровне `u64` слова: `if word == u64::MAX { skip 8 bytes }`. В 8x меньше итераций.
---
### ⚡ PERF 4.2 — Buddy free: O(n) → O(1)
**Файл**: `kernel/src/mem/buddy.rs:246`
См. ARCH 2.2. Замена `Vec` на intrusive list — ключевой перф-фикс.
---
### ⚡ PERF 4.4 — INVLPG в bulk_map: batch flush
**Файл**: `kernel/src/mem/paging.rs:119-121`
```rust
unsafe {
asm!("invlpg [{}]", in(reg) virt.0, ...);
}
```
`map_region` вызывает `map_page` в цикле — каждая итерация делает INVLPG. 16M страниц = 16M INVLPG. CPU заливает TLBs и потом их же сбрасывает.
**Решение**: Убрать INVLPG из `map_page` (оставить вызывающему), сделать отдельную функцию `flush_range(virt, pages)` или вызов `invpcid` type-2 (flush-by-range).
**Но осторожно**: INVLPG нужен при модификации активной таблицы. Если identity-map ещё не загружена — можно без INVLPG.
---
### ⚡ PERF 4.5 — TLB shootdown: busy-wait → IPI + HLT
**Файл**: `kernel/src/mem/vmm.rs:809-811`
```rust
while SHOOTDOWN_ACK.load(Ordering::Acquire) & target_mask != target_mask {
spin_loop();
}
```
Пока нет AP — код мёртвый. Когда AP появятся, надо чтобы AP висели в `HLT` и просыпались от IPI с TLB_SHOOTDOWN_VECTOR.
---
### ⚡ PERF 4.6 — Page fault handler: lock-free VMA tree
**Файл**: `kernel/src/cpu/interrupts.rs:110`
`KERNEL_SPACE.lock()` — каждый PF сериализуется. На одноядерной системе — OK, на SMP — bottleneck.
**Решение**: `RWLock` на VMA region или lock-free RB-tree для VMA поиска.
---
## Ф 5: Инфраструктура (Фаза 5)
### 🛠️ INFRA 5.1 — SMP AP Startup Code
Отсутствует. Нужен:
- Startup trampoline в real mode (<1MB)
- Переход в protected → long mode
- Настройка GDT/IDT для AP
- Per-CPU stack
- Регистрация в `ACTIVE_CPUS_MASK`
---
### 🛠️ INFRA 5.2 — Мультиархитектурность
Есть linker-скрипты под aarch64, riscv64, loongarch64, но:
- `build.rs` хардкодит `linker-x86_64.ld`
- ASM в interrupts.rs только x86
- LAPIC — x86-specific
- Port IO — x86-specific
- IDT — x86-specific
Для портирования нужна архитектурная абстракция — traits:
```rust
trait Arch {
type PteFlags;
fn init_interrupts();
fn send_ipi(core_id: u32, vector: u8);
fn read_cycle_counter() -> u64;
}
```
---
### 🛠️ INFRA 5.3 — Unit-тесты для Buddy Allocator
Buddy allocator — идеальный кандидат для `#[cfg(test)]`:
- Проверка инвариантов после alloc/free sequence
- Fuzzing случайных порядков
- Проверка coalescing (граничные случаи)
- Проверка alignment
---
### 🛠️ INFRA 5.4 — Утилита для бинарного патчинга
Сейчас результирующий ELF надо руками objcopy'ить. Можно добавить Cargo alias или build.rs скрипт для автоматизации.
---
### 🛠️ INFRA 5.5 — GDB/QEMU debugging support
Добавить `.gdbinit` / `gdb.py` с:
- `target remote localhost:1234`
- Разгрузка символов из ELF
- Макросы для просмотра page table, capability, VMA regions
---
## Ф 6: Безопасность (сквозная)
### 🛡️ SEC 6.1 — Capability minting не проверяет `Relation` parent
**Файл**: `kernel/src/cap/mod.rs:64-77`
```rust
if !src_slot.cap.rights.contains(CapRights::GRANT) {
return Err("Insufficient rights to mint (Missing GRANT flag)");
}
```
Проверяет только GRANT флаг. Но не проверяет `relation`: если source — `Borrow`, можно ли mint? В текущей реализации — можно. Должен быть check:
```rust
if src_slot.cap.relation == Relation::Borrow {
return Err("Cannot mint from borrowed capability");
}
```
---
### 🛡️ SEC 6.2 — CapGuardToken не реализован
В дескрипторе есть упоминание `guards: Vec<CapGuardToken>`, `Borrow` relation и guard-токены, логика самих guard'ов не реализована. Если есть borrow, mint не должен позволять дальнейшее распространение — но это не проверяется.
---
### 🛡️ SEC 6.3 — Нет проверки прав на операции с PMM через capabilities
`BitmapPMM` полностью глобальный: любой код может вызвать `alloc_frame()`, `free_frame()` без проверки capability. Капы пока хранят токены и права, но PMM их не проверяет.
---
---
## Полный пошаговый план (сортированный по приоритету)
### Этап 1: Исправить критические баги (сейчас — не работает)
```
[X] 1. ✅ HIGH — Исправить target_mask для >64 core
Добавлена проверка current_core < 64 перед shift.
Estimate: ~10 мин
[ ] 2. 🔥 HIGH — Исправить ref_counts инициализацию
Вместо fill(1) + free, сделать fill(0) + mark_used.
Estimate: ~15 мин
```
### Этап 2: Архитектурный рефакторинг
```
[ ] 3. 🏗️ ARCH — Глобальный HHDM_OFFSET
AtomicU64 static, убрать hhdm из сигнатур.
Estimate: ~1 ч
[ ] 4. 🏗️ ARCH — Intrusive free-list в buddy allocator
O(1) free вместо O(n).
Estimate: ~2 ч
[ ] 5. 🏗️ ARCH — Tree bitmap для PMM
word-level scan с ctz.
Estimate: ~1.5 ч
[ ] 6. 🏗️ ARCH — HLT-based wait в router channel
Пока нет планировщика — HLT вместо spin.
Estimate: ~30 мин
[ ] 7. 🏗️ ARCH — Capability revoke без O(n) scan
Children list в слоте.
Estimate: ~1 ч
[ ] 8. 🏗️ ARCH — Identity-map через 2MB huge pages
map_region с HUGE_PAGE флагом для identity.
Estimate: ~1 ч
```
### Этап 3: Code Quality
```
[ ] 9. 🧹 CQ — Удалить dead code (FramebufferDisplay, Console, embedded-graphics)
+ неиспользуемые импорты.
Estimate: ~20 мин
[ ] 10. 🧹 CQ — Убрать #![allow(dead_code)] из vmm.rs
Маркировать конкретные единицы.
Estimate: ~15 мин
[ ] 11. 🧹 CQ — Удалить мёртвые linker-скрипты
aarch64/riscv64/loongarch64.ld.
Estimate: ~5 мин
[ ] 12. 🧹 CQ — Переименовать pm_manages.rs → pm_actor.rs
+ все референсы.
Estimate: ~10 мин
[ ] 13. 🧹 CQ — Вынести panic.rs из main.rs
Отдельный файл для panic_handler.
Estimate: ~10 мин
[ ] 14. 🧹 CQ — Синхронизировать имена: LISA → Elyz
Cargo.toml, main.rs ("LIS4"), README.
Estimate: ~15 мин
[ ] 15. 🧹 CQ — Locked spinlock с exponential backoff
PAUSE + цикл задержки.
Estimate: ~15 мин
```
### Этап 4: Производительность
```
[ ] 16. ⚡ PERF — Bitmap scan: word-level (u64) вместо byte-level
+ ctzl для поиска бита.
Estimate: ~30 мин
[ ] 17. ⚡ PERF — INVLPG-free bulk map
Убрать INVLPG из map_page, вызов flush_range после.
Estimate: ~30 мин
[ ] 18. ⚡ PERF — process_messages quota
Max N сообщений за вызов.
Estimate: ~15 мин
```
### Этап 5: Инфраструктура
```
[ ] 19. 🛠️ INFRA — SMP AP startup code
SIPI → trampoline → long mode → Rust.
Estimate: ~1 неделя
[ ] 20. 🛠️ INFRA — Unit tests для buddy allocator
#[cfg(test)] модуль.
Estimate: ~1 ч
[ ] 21. 🛠️ INFRA — GDB script + QEMU -s
.gdbinit для отладки.
Estimate: ~30 мин
[ ] 22. 🛠️ INFRA — Arch trait для x86_64
Подготовка к портированию.
Estimate: ~3-4 ч
```
### Этап 6: Безопасность
```
[ ] 23. 🛡️ SEC — Проверка Relation при mint
Запретить mint из Borrow.
Estimate: ~10 мин
[ ] 24. 🛡️ SEC — CapGuard реализация
Привязка guard-токенов к source slot при mint.
Estimate: ~45 мин
```
---
## Roadmap (визуально)
```
Неделя 1 Неделя 2 Неделя 3
┌─────────────────────────┐ ┌─────────────────────────┐ ┌─────────────────────────┐
│ ✅ CRIT 16 готово │▸▸ │ ARCH 3 HHDM global │▸▸ │ PERF 16 word-level PMM │
│ ✅ MED 1.71.10 │ │ ARCH 4 intrusive list │ │ PERF 17 INVLPG batch │
│ HIGH 2 ref_counts │ │ ARCH 5 tree bitmap │ │ INFRA 19 SMP startup │
│ │ │ CQ 9 clean dead code │ │ INFRA 20 unit tests │
│ │ │ CQ 11-15 rename/refact│ │ SEC 23-24 caps │
└─────────────────────────┘ └─────────────────────────┘ └─────────────────────────┘
↓ ↓ ↓
Kernel bootable и Код читаемый, Производительность:
логирует без крашей архитектура чище SMP + оптимизации
```
---
*Документ создан 29 июня 2026 на основе анализа исходного кода проекта Elyz.*