在 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

putao
5 声望0 粉丝

推动世界向前发展,改善民生。