104 lines
4.7 KiB
Markdown
104 lines
4.7 KiB
Markdown
# Адресация и трансляция: `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.
|