在 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
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。