7.7 KiB
Виртуальная память (VMM)
Файл: src/mem/vmm.rs
Управление адресными пространствами: PML4 + VMA списки, lazy demand paging, copy-on-write, shared mappings, ASID/PCID, TLB shootdown IPI.
Глобальные статики
| Переменная | Тип | Назначение |
|---|---|---|
ACTIVE_CPUS_MASK |
AtomicU64 |
Битовая маска активных CPU (core 0 по умолчанию) |
SHOOTDOWN_LOCK |
Locked<()> |
Блокировка TLB shootdown IPI |
SHOOTDOWN_ASID |
AtomicU16 |
ASID, подлежащий сбросу |
SHOOTDOWN_ACK |
AtomicU64 |
Битовая маска подтверждений удалённых ядер |
INVPCID_SUPPORTED |
AtomicBool |
Поддержка инструкции INVPCID |
ASID_ALLOC |
Locked<AsidAllocator> |
Аллокатор ASID/PCID |
KERNEL_SPACE |
Locked<Option<AddressSpace>> |
Адресное пространство ядра |
Типы ошибок: VmError
OutOfMemory— нет свободной физической памятиRegionOverlap— VMA перекрывается с существующейRegionNotFound— VMA не найденаInvalidAlignment— неверное выравниваниеInvalidRange— некорректный диапазонPermissionDenied— нет прав доступаUnexpectedFault— page fault в не-lazy, не-COW регионеNonCanonical— неканонический адресAsidExhausted— кончились ASID
VmaBacking — типы подложки
pub enum VmaBacking {
Anonymous(Vec<Option<PhysAddr>>), // per-page tracking: None = ещё не выделена
Physical(PhysAddr), // фиксированный физический диапазон (MMIO, DMA)
Shared { owner_cap: u64, phys_base: PhysAddr }, // zero-copy borrow
}
VmaFlags (bitflags)
| Флаг | Бит | Описание |
|---|---|---|
READ |
0 | Чтение |
WRITE |
1 | Запись |
EXEC |
2 | Исполнение |
USER |
3 | Доступ из userspace |
LAZY |
4 | Demand paging (фрейм при page fault) |
SHARED |
5 | Разделяемая память |
PINNED |
6 | Защита от revoke |
NOCACHE |
7 | Отключение кэширования |
MMIO |
8 | Memory-mapped I/O |
COW |
9 | Copy-on-Write |
to_page_flags() -> PageTableFlags: преобразует VmaFlags в аппаратные флаги. PRESENT всегда включён, NO_EXECUTE если не EXEC.
AsidAllocator
Битовая карта на 4096 ASID/PCID (128 × u32).
- ASID 0 (ядро) и ASID 4095 (зарезервирован) — заняты постоянно
alloc() -> Option<u16>— двухпроходный с пропуском целых слов (амортизированное O(1))free(asid)— сброс бита
VmaRegion
pub struct VmaRegion {
virt_start: VirtAddr,
virt_end: VirtAddr,
flags: VmaFlags,
cap_token: u64,
backing: VmaBacking,
}
Методы: contains(addr), size(), pages().
AddressSpace
pub struct AddressSpace {
asid: u16,
pml4_phys: PhysAddr,
regions: Vec<VmaRegion>, // sorted by virt_start, non-overlapping
hhdm: u64,
}
Создание
| Метод | Описание |
|---|---|
new(hhdm) |
Выделяет PML4 (занулённый), аллоцирует ASID |
from_active(pml4_phys, asid, hhdm) |
Обёртка для существующей PML4 (ядро, ASID=0) |
map_region(virt, phys, size, flags, cap_token) -> Result<VirtAddr>
- Валидация выравнивания (4 KiB) и проверка перекрытий (
check_overlap) - Создание
VmaBacking:- LAZY →
Anonymous(vec![None; page_count]) - Physical (если
physне None и нет LAZY) →Physical(phys) - Eager anonymous: цикл по страницам,
pmm_alloc() + zero + map
- LAZY →
- Вставка в
regionsс сохранением сортировки (insert_sorted)
map_shared(virt, phys_base, page_count, flags, owner_cap)
Eager маппинг физического диапазона без владения фреймами (не освобождаются при unmap).
handle_fault(fault_addr, write) -> Result<(), VmError>
Обработка page fault:
find_idx(fault_addr)— бинарный поиск региона- Проверка прав: write fault в WRITE-регионе
- COW: если флаг COW и пишем:
- Выделяет новый фрейм
- Копирует содержимое старого
- Обновляет PTE: убирает COW, добавляет WRITABLE
- LAZY: если флаг LAZY:
- Выделяет фрейм, зануляет, маппит
- Обновляет
Vec<Option<PhysAddr>>
- Иначе:
Err(UnexpectedFault)
unmap_region(virt) -> Result<(), VmError>
find_idx(virt)→ регионdo_unmap(region): анмаппит PTE, освобождает фреймы если Anonymous- Удаляет регион из списка
tlb_flush_asid(self.asid)
revoke_by_token(cap_token)
Анмаппит все VMA с совпадающим cap_token (кроме PINNED). Сливает TLB.
process_pending_revocations(&mut self)
- Дренирует
MMU_REVOCATION_QUEUE(pop в цикле) - Для каждого токена →
revoke_by_token(token) - TLB flush если была хотя бы одна ревокация
clone_for_fork(child_cap_token) -> Result<Self, VmError>
Создание дочернего адресного пространства (fork):
- Physical/Shared: маппинг напрямую
- Anonymous: COW setup:
- Убирает WRITABLE из PTE родителя
- Устанавливает COW флаг в обоих PTEs
inc_ref_frame()для всех фреймов
translate(virt) -> Option<PhysAddr>
Аппаратный walk страничной таблицы (через PageTable::translate).
activate()
unsafe: запись CR3 = pml4_phys | asid | (1<<63). Бит 63 = NOFLUSH — сохраняет PCID-тегированные записи TLB.
Drop
- Анмаппит все регионы
- Освобождает PML4 фрейм через
free_frame - Возвращает ASID в пул
TLB Management
local_tlb_flush_asid(asid)
- INVPCID type 1 если поддерживается (single-context flush)
- Иначе
tlb_flush_all()
tlb_flush_asid(asid)
local_tlb_flush_asid(asid)— локальный сброс- Устанавливает глобальные статики:
SHOOTDOWN_ASID = asid,SHOOTDOWN_ACK = 0 broadcast_ipi_exclude_self(TLB_SHOOTDOWN_VECTOR)— IPI всем остальным ядрам- Spin-loop:
while SHOOTDOWN_ACK != ACTIVE_CPUS_MASK { hlt(); }
tlb_flush_all()
Читает CR3 → перезаписывает без бита 63 → полный сброс всех non-global entries.
handle_tlb_shootdown_ipi()
local_tlb_flush_asid(SHOOTDOWN_ASID.load())- Устанавливает свой бит в
SHOOTDOWN_ACK(черезfetch_or)
init_cpu_features()
CPUID leaf 7, проверка EBX[10] (INVPCID). Сохраняет в INVPCID_SUPPORTED.
init_kernel_space(pml4_phys, hhdm)
Регистрирует PML4 ядра как KERNEL_SPACE с ASID 0.