4.7 KiB
4.7 KiB
Адресация и трансляция: 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()— проверка выравнивания на 4KiBalign_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.