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

104 lines
4.7 KiB
Markdown
Raw Permalink 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.
# Адресация и трансляция: `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: глобальное состояние
```rust
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 гарантирует, что даже если в будущем
инструкции будут переупорядочены, значение будет видно всем ядрам.
## Код и абстракция
```rust
// 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.