# 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.1–1.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.7–1.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` — O(количество блоков этого порядка). При 10 000 блоков порядка 0, free() занимает тысячи итераций. **Решение**: Перейти на **intrusive linked list** через сами страницы: первые 8 байт неиспользуемой страницы хранят `next` указатель. Даёт O(1) удаление. Или хеш-таблицу: `HashMap` (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, children: Vec, // явный список наследников } ``` Отзыв — проход по дереву без сканирования всей таблицы. --- ### 🏗️ 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`, `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 1–6 готово │▸▸ │ ARCH 3 HHDM global │▸▸ │ PERF 16 word-level PMM │ │ ✅ MED 1.7–1.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.*