Files
Elyz/kernel/docs/boot/introduction.md
2026-07-07 16:40:41 +03:00

5.4 KiB
Raw Blame History

Процесс загрузки: концептуальная модель

Цепочка загрузки

Питание включено
    │
    ▼
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 — современный bootloader с открытым исходным кодом.

Статические Limine requests сообщают bootloader'у, что нужно ядру:

Request Назначение
FramebufferRequest Получить framebuffer для графического вывода
MemoryMapRequest Получить карту физической памяти
HhdmRequest Получить HHDM offset
ExecutableAddressRequest Получить физический/виртуальный адрес ядра
BaseRevision Проверить совместимость с Limine

Размещение в секциях

Requests размещаются между специальными маркерами в секции .data:

#[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();

Линкер скрипт сохраняет эти секции:

.data : {
    *(.data .data.*)
    KEEP(*(.requests_start_marker))
    KEEP(*(.requests))
    KEEP(*(.requests_end_marker))
} :data

Точка входа — kmain

Линкер скрипт: ENTRY(kmain).

#[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

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

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

[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)