Files
Elyz/kernel/docs/memory/address-translation.md
2026-07-07 16:40:41 +03:00

4.7 KiB
Raw Blame History

Адресация и трансляция: address.rs

Концептуальная модель

В x86-64 MMU транслирует виртуальные адреса (48 бит, канонические) в физические (до 52 бит) через 4-уровневую иерархию page tables.

Ядро использует Higher-Half Direct Map (HHDM) — область виртуальной памяти, где вся физическая память отображена 1:1 с фиксированным смещением.

Физический адрес 0x0
  │
  │ + HHDM_OFFSET (например, 0xFFFF8000_0000_0000)
  ▼
Виртуальный адрес 0xFFFF8000_0000_0000

Физический адрес: PhysAddr(pub u64)

Представляет собой физический адрес. Это newtype над u64 — строгая типизация предотвращает случайное смешивание физических и виртуальных адресов.

Методы:

  • to_virt() — преобразует в виртуальный адрес через HHDM: VirtAddr(self.0 + HHDM_OFFSET)
  • is_aligned() — проверка выравнивания на 4KiB
  • align_down() / align_up() — выравнивание вниз/вверх до границы 4KiB

Виртуальный адрес: VirtAddr(pub u64)

Методы:

  • to_phys() — обратное преобразование: вычитает HHDM, возвращает None если адрес ниже HHDM (не канонический для физической памяти)
  • as_ptr<T>() — получить *const T (для чтения)
  • as_mut_ptr<T>() — получить *mut T (для записи)

HHDM Offset: глобальное состояние

static HHDM_OFFSET: AtomicU64 = AtomicU64::new(0);
  • init_hhdm(offset: u64) — вызывается в kmain() сразу после получения ответа от bootloader'а. Сохраняет offset через Ordering::Release.
  • get_hhdm() -> u64 — читает offset через Ordering::Relaxed (высокая производительность, на этой архитектуре атомарность гарантирована).

Почему Atomic?

HHDM инициализируется однократно до включения прерываний и создания дополнительных потоков. Atomic гарантирует, что даже если в будущем инструкции будут переупорядочены, значение будет видно всем ядрам.

Код и абстракция

// address.rs (полный код)
static HHDM_OFFSET: AtomicU64 = AtomicU64::new(0);

pub fn init_hhdm(offset: u64) {
    HHDM_OFFSET.store(offset, Ordering::Release);
    // ^ Release: все предыдущие записи становятся видимы для
    // всех acquire-операций на HHDM_OFFSET
}

pub fn get_hhdm() -> u64 {
    HHDM_OFFSET.load(Ordering::Relaxed)
    // ^ Relaxed: в однопоточном контексте и при инициализации
    // до включения прерываний это безопасно и максимально быстро
}

#[repr(transparent)]
pub struct PhysAddr(pub u64);
// ^ repr(transparent): гарантирует, что PhysAddr имеет то же
// представление в памяти, что и u64 — важно для FFI и передачи
// в ассемблерные инструкции (mov cr3, rax)

impl PhysAddr {
    pub fn to_virt(self) -> VirtAddr {
        VirtAddr(self.0 + get_hhdm())
        // ^ Концептуально: физический адрес + HHDM смещение даёт
        // виртуальный адрес, по которому этот физический фрейм
        // отображён в higher half
    }
}

impl VirtAddr {
    pub fn as_mut_ptr<T>(self) -> *mut T {
        self.0 as *mut T
        // ^ Прямая интерпретация числа как указателя.
        // Безопасность на стороне вызывающего кода.
    }
}

Где используется

  • PhysAddr::to_virt() — в paging.rs для доступа к Page Table entries, в pmm.rs для обнуления фреймов, в buddy.rs для intrusive list.
  • VirtAddr::as_mut_ptr() — в paging.rs для создания ссылок на PTE.
  • init_hhdm() — в kmain() при старте.
  • get_hhdm() — в lapic.rs для вычисления LAPIC_base.