在 Sv39 模式下,虛擬地址是 39 位,分為三級頁表來管理:
第一級頁表(頁目錄):負責最高的 9 位虛擬地址。
第二級頁表(頁中間目錄):負責中間的 9 位虛擬地址。
第三級頁表(頁表):負責最低的 9 位虛擬地址。
9 位虛擬地址可以表示2的9次方=512個條目。
512條目×8字節/條目=4096字節=4KB
假設我們有一個頁表,包含多個條目,每個條目佔用 8 字節:
計算偏移量
假設我們要訪問頁表中的第 5 個條目(Entry 5),它的索引是 5。為了計算這個條目在頁表中的實際地址,我們需要將索引乘以每個條目的大小(8 字節)。
索引:5
每個條目大小:8 字節
偏移量:5 * 8 = 40(十六進制是 0x28)
左移3位:5 << 3 = 5 2^3 = 5 8 = 40
最後,我們將計算出的偏移量加到頁表基地址上,得到實際的內存地址:
頁表基地址:0x80000000
偏移量:0x28
實際地址:0x80000000 + 0x28 = 0x80000028
.section .text
.global _start
# 程序入口
_start:
# 初始化棧指針
la sp, stack_top
# 調用內存映射函數
la a0, page_table_base # 頁表基地址
li a1, 0x80000000 # 物理地址起始
li a2, 0x80000000 # 虛擬地址起始
li a3, 0x80400000 # 虛擬地址結束(4MB)
li a4, 0xF # 保護標誌 (RWX)
call map_identity
# 啓用 MMU
la a0, page_table_base
call enable_mmu
# 無限循環,防止程序退出
1: j 1b
# 棧區定義
.section .bss
.balign 16
stack:
.space 4096 * 4
stack_top:
# 頁表定義
.section .data
.balign 4096
page_table_base:
.space 4096 * 3 # 頁目錄、頁中間目錄和頁表
# 函數:物理內存到虛擬內存的恆等映射
map_identity:
# 保存寄存器
addi sp, sp, -16
sd ra, 8(sp)
sd t0, 0(sp)
# 初始化頁表基地址
la t0, page_table_base
1: # 循環開始
# 計算虛擬地址的頁目錄索引
srli t1, a2, 30 # 取高9位作為頁目錄索引
andi t1, t1, 0x1FF # 保留低9位
# 計算頁中間目錄索引
srli t2, a2, 21 # 取高9位作為頁中間目錄索引
andi t2, t2, 0x1FF # 保留低9位
# 計算頁表索引
srli t3, a2, 12 # 取高9位作為頁表索引
andi t3, t3, 0x1FF # 保留低9位
# 計算頁表條目地址
slli t1, t1, 3 # 頁目錄索引左移3位
add t1, t0, t1 # 加上頁表基地址
ld t1, 0(t1) # 讀取頁目錄條目
# 檢查頁目錄條目是否存在
andi t4, t1, 1 # 檢查有效位
beqz t4, create_pmd # 如果不存在,創建頁中間目錄
# 計算頁中間目錄地址
slli t2, t2, 3 # 頁中間目錄索引左移3位
add t2, t1, t2 # 加上頁目錄基地址
ld t2, 0(t2) # 讀取頁中間目錄條目
# 檢查頁中間目錄條目是否存在
andi t4, t2, 1 # 檢查有效位
beqz t4, create_pte # 如果不存在,創建頁表
# 計算頁表地址
slli t3, t3, 3 # 頁表索引左移3位
add t3, t2, t3 # 加上頁中間目錄基地址
ld t3, 0(t3) # 讀取頁表條目
# 設置頁表條目
or t3, a1, a4 # 將物理地址和保護標誌合併
sd t3, 0(t3) # 寫入頁表條目
# 更新地址
addi a2, a2, 0x1000 # 虛擬地址加上頁大小
addi a1, a1, 0x1000 # 物理地址加上頁大小
bltu a2, a3, 1b # 如果虛擬地址小於結束地址,繼續循環
# 恢復寄存器並返回
ld ra, 8(sp)
ld t0, 0(sp)
addi sp, sp, 16
ret
# 到此時,t0/t1/t2/t3 都使用了。
create_pmd:
# 創建頁中間目錄
li t4, 0x1000 # 分配一頁內存
slli t4, t4, 12 # 左移12位,得到物理頁號
or t4, t4, 1 # 設置有效位
sd t4, 0(t1) # 寫入頁目錄條目
j 1b # 返回循環
create_pte:
# 創建頁表
li t4, 0x1000 # 分配一頁內存
slli t4, t4, 12 # 左移12位,得到物理頁號
or t4, t4, 1 # 設置有效位
sd t4, 0(t2) # 寫入頁中間目錄條目
j 1b # 返回循環
# 函數:啓用 MMU
enable_mmu:
# 設置 satp 寄存器
srli a2, a0, 12 # 將頁表基地址右移12位
li t0, 8 # Sv39 模式
slli t0, t0, 60 # 左移60位,放置到 MODE 字段
or a2, a2, t0 # 合併 MODE 和 PPN
csrw satp, a2 # 寫入 satp 寄存器
sfence.vma # 刷新 TLB
ret