最近在研究arm v8页表创建过程,顺带做了一个如下形式的页表,
// level 1 table, 4 entries:
// 0000 0000 - 3FFF FFFF, 1GB block, DDR
// 4000 0000 - 7FFF FFFF, 1GB block, DDR
// 8000 0000 - BFFF FFFF, 1GB block, DDR
// C000 0000 - FFFF FFFF, point to level2 tabel
//
// level 2 table, 512 entries:
// C000 0000 - DFFF FFFF, 256 entries, 512MB DDR, 2MB block
// E000 0000 - EFFF FFFF, 128 entries, 256MB OSPI0 flash, 2MB block
// F100 0000 - F11F FFFF, 1 entry, point to level 3_1
// F200 0000 - F21F FFFF, 1 entry, point to level 3_2
// F240 0000 - F25F FFFF, 1 entry, point to level 3_3
// F260 0000 - F27F FFFF, 1 entry, point to level 3_4
// F500 0000 - F500 7FFF, 1 entries, GIC 32kB, point to level 3_5
// F220 0000 - F23D FFFF, 1 entries, 2MB block, device, RNE_CFG1
// F400 0000 - F403 FFFF, 1 entries, 2MB block, normal memory, RNE_MEM, 1.5M acture, SRAM
下面是页表创建过程与详细注释,供后来者学习参考。
cpp
//----------------------------------------------------------------
// setup translation table
//
//----------------------------------------------------------------
#include "v8_mmu.h"
.text
.cfi_sections .debug_frame // put stack frame info into .debug_frame instead of .eh_frame
.global setup_ttb
.global ZeroBlock
.global __ttb0_l1
.global __ttb0_l2_ram
.global __ttb0_l3_ram_1
.global __ttb0_l3_ram_2
.global __ttb0_l3_ram_3
//----------------------------------------------------------------
// setup translation table
// level 1 table, 4 entries:
// 0000 0000 - 3FFF FFFF, 1GB block, DDR
// 4000 0000 - 7FFF FFFF, 1GB block, DDR
// 8000 0000 - BFFF FFFF, 1GB block, DDR
// C000 0000 - FFFF FFFF, point to level2 tabel
//
// level 2 table, 512 entries:
// C000 0000 - DFFF FFFF, 256 entries, 512MB DDR, 2MB block
// E000 0000 - EFFF FFFF, 128 entries, 256MB OSPI0 flash, 2MB block
// F100 0000 - F11F FFFF, 1 entry, point to level 3_1
// F200 0000 - F21F FFFF, 1 entry, point to level 3_2
// F240 0000 - F25F FFFF, 1 entry, point to level 3_3
// F260 0000 - F27F FFFF, 1 entry, point to level 3_4
// F500 0000 - F500 7FFF, 1 entries, GIC 32kB, point to level 3_5
// F220 0000 - F23D FFFF, 1 entries, 2MB block, device, RNE_CFG1
// F400 0000 - F403 FFFF, 1 entries, 2MB block, normal memory, RNE_MEM, 1.5M acture, SRAM
//----------------------------------------------------------------
.type setup_ttb, "function"
.cfi_startproc
setup_ttb:
//
// x21 = address of L1 tables
// x22 = address of L2 tables
// x23 = address of L3_1 tables
// x24 = address of L3_2 tables
// x25 = address of L3_3 tables
// x26 = address of L3_4 tables
// x27 = address of L3_5 tables
ldr x2, =0 // x2 = 0 (用于清零内存)
ldr x3, =0 // x3 = 0 (同上)
// 清零 L1 页表 (__ttb0_l1) (L1 页表(32 字节,4 个条目))
ldr x21, =__ttb0_l1 // x21 = L1 页表基地址
mov x0, x21 // x0 = 当前操作地址
mov x1, #(4 << 3) // x1 = 4个条目 * 8字节 = 32字节 (L1页表大小)
add x0, x0, x1 // x0 指向区域末尾 (预递减清零准备)
// can not call func ZeroBlock, not support nesting
loop_zero_0:
subs x1, x1, #16 // 每次处理16字节 (2个条目),更新计数器
stp x2, x3, [x0, #-16]! // 存储双零并递减地址: [x0-16] = (0,0), x0-=16
b.ne loop_zero_0 // 循环直到x1=0
// 清零 L2 页表 (__ttb0_l2_ram) (L2 页表(4096 字节,512 条目))
ldr x22, =__ttb0_l2_ram // x22 = L2 页表基地址
mov x1, #(512 << 3) // x1 = 512条目 * 8字节 = 4096字节 (标准2MB块大小)
mov x0, x22 // x0 = 当前操作地址
add x0, x0, x1 // x0 指向区域末尾
//循环使用 subs + b.ne 实现精确计数(当 x1 减至 0 时退出)
loop_zero_1:
subs x1, x1, #16 // 每次16字节
stp x2, x3, [x0, #-16]! // 存储双零并前移指针
b.ne loop_zero_1 // 循环
// 清零第一个 L3 页表 (__ttb0_l3_ram_1) (三个 L3 页表(各 4096 字节))
ldr x23, =__ttb0_l3_ram_1 // x23 = L3 页表1基地址
mov x1, #(512 << 3) // 4096字节 (标准4KB页表大小)
mov x0, x23
add x0, x0, x1 // 指向末尾
loop_zero_2:
subs x1, x1, #16
stp x2, x3, [x0, #-16]!
b.ne loop_zero_2
// 清零第二个 L3 页表 (__ttb0_l3_ram_2)
ldr x24, =__ttb0_l3_ram_2 // x24 = L3 页表2基地址
mov x1, #(512 << 3) // 4096字节
mov x0, x24
add x0, x0, x1 // 指向末尾
loop_zero_3:
subs x1, x1, #16
stp x2, x3, [x0, #-16]!
b.ne loop_zero_3
// 清零第三个 L3 页表 (__ttb0_l3_ram_3)
ldr x25, =__ttb0_l3_ram_3 // x25 = L3 页表3基地址
mov x1, #(512 << 3) // 4096字节
mov x0, x25
add x0, x0, x1 // 指向末尾
loop_zero_4:
subs x1, x1, #16
stp x2, x3, [x0, #-16]!
b.ne loop_zero_4
// 清零第4个 L3 页表 (__ttb0_l3_ram_4)
ldr x26, =__ttb0_l3_ram_4 // x26 = L3 页表4基地址
mov x1, #(512 << 3) // 4096字节
mov x0, x26
add x0, x0, x1 // 指向末尾
loop_zero_5:
subs x1, x1, #16
stp x2, x3, [x0, #-16]!
b.ne loop_zero_5
// 清零第5个 L3 页表 (__ttb0_l3_ram_5)
ldr x27, =__ttb0_l3_ram_5 // x26 = L3 页表4基地址
mov x1, #(512 << 3) // 4096字节
mov x0, x27
add x0, x0, x1 // 指向末尾
loop_zero_6:
subs x1, x1, #16
stp x2, x3, [x0, #-16]!
b.ne loop_zero_6
// 设置 L1 页表项配置
// 0000 0000 - 3FFF FFFF, 1GB block, DDR
// 4000 0000 - 7FFF FFFF, 1GB block, DDR
// 8000 0000 - BFFF FFFF, 1GB block, DDR
// 3 1G block, write to l1 table
//
ldr x1, =3 // x1 = 3 (需要配置的页表项数量)
ldr x2, =0x40000000 // x2 = 1GB 地址增量 (每个 L1 条目映射 1GB 空间)
// 构建 L1 块描述符属性值:
// - 物理地址基址: 0x00000000
// - 块描述符类型 (TT_S1_ATTR_BLOCK) TT_S1_ATTR_BLOCK:使用块描述符 (1GB/2MB 大页)
// - 内存属性索引 1 (MATTR=1) 普通内存,适用于ddr
// - 非安全状态 (NS)
// - 特权读写权限 (AP_RW_PL1)
// - 内部可共享 (SH_INNER)
// - 访问标志 (AF)
// - 非全局映射 (nG)
ldr x3, =(0x00000000 | \
TT_S1_ATTR_BLOCK | \
(1 << TT_S1_ATTR_MATTR_LSB) | \
TT_S1_ATTR_NS | \
TT_S1_ATTR_AP_RW_PL1 | \
TT_S1_ATTR_SH_INNER | \
TT_S1_ATTR_AF | \
TT_S1_ATTR_nG)
mov x4, x21 // x4 = L1 页表基址指针 (x21 来自前段代码)
#if 1 // enable after ddr ready
loop1:
str x3, [x4], #8 // 存储每次的x3描述符到 L1 页表并后移指针
add x3, x3, x2 // 物理地址增加 1GB (0x40000000)
subs x1, x1, #1 // 递减计数器
bne loop1 // 循环直到 3 个条目配置完成
#else
loop1:
add x4, x4, #8 // 仅移动指针 (不存储)
add x3, x3, x2 // 物理地址增加 1GB
subs x1, x1, #1
bne loop1
#endif
// 配置 L1 页表中 C0000000-FFFFFFFF 区域的条目
// C000 0000 - FFFF FFFF, point to level2 tabel, write to l1 table
orr x1, x22, #TT_S1_ATTR_PAGE // 将 L2 表基址(x22)与页表描述符属性组合。TT_S1_ATTR_PAGE:页表描述符 (指向下级页表)
str x1, [x4] // 存储到 L1 页表 (指向 L2 页表)
// 配置 L2 页表:DDR 区域 (C0000000-DFFFFFFF, 512MB)
// level 2 table: C000 0000 - DFFF FFFF, 256 entries, 512MB DDR, 2MB block
ldr x1, =256 // 256 个条目 (256 * 2MB = 512MB)
ldr x2, =0x200000 // x2 = 2MB 地址增量 (块大小)
// 构建 DDR 区域的 L2 块描述符:
// - 物理地址基址: 0xC0000000
// - 块描述符类型
// - 内存属性索引 1 (普通内存)
ldr x3, =(0xC0000000 | \
TT_S1_ATTR_BLOCK | \
(1 << TT_S1_ATTR_MATTR_LSB) | \
TT_S1_ATTR_NS | \
TT_S1_ATTR_AP_RW_PL1 | \
TT_S1_ATTR_SH_INNER | \
TT_S1_ATTR_AF | \
TT_S1_ATTR_nG)
mov x4, x22 // x4 = L2 页表基址指针 (x22)
loop2_ddr:
str x3, [x4], #8 // 存储每次的x3到描述符到 L2 页表并后移指针
add x3, x3, x2 // 物理地址增加 2MB
subs x1, x1, #1 // 递减计数器
bne loop2_ddr // 循环配置 256 个条目
// 配置 L2 页表:OSPI0 Flash 区域 (E0000000-EFFFFFFF, 256MB)
// level 2 table: E000 0000 - EFFF FFFF, 64 entries, 128MB OSPI0 flash, 2MB block
ldr x1, =128 // 128 个条目 (128 * 2MB = 256MB)
ldr x2, =0x200000 // 2MB 块大小
// 构建 OSPI Flash 的 L2 块描述符:
// - 物理地址基址: 0xE0000000
// - 内存属性索引TT_S1_ATTR_MATTR_LSB 2 (通常用于设备内存)
//AP_RW_PL1:特权读写权限
//SH_INNER:内部可共享
//AF:访问标志 (Access Flag)
//nG:非全局映射
ldr x3, =(0xE0000000 | \
TT_S1_ATTR_BLOCK | \
(2 << TT_S1_ATTR_MATTR_LSB) | \
TT_S1_ATTR_NS | \
TT_S1_ATTR_AP_RW_PL1 | \
TT_S1_ATTR_SH_INNER | \
TT_S1_ATTR_AF | \
TT_S1_ATTR_nG)
loop2_ospi0:
str x3, [x4], #8 // 继续在 L2 页表存储 (紧接 DDR 区域之后)
add x3, x3, x2 // 物理地址增加 2MB
subs x1, x1, #1 // 递减计数器
bne loop2_ospi0 // 循环配置 128 个条目
/*
0x00000000 ~ 0xc0000000 ----l1
0xc0000000 ~ 0xe0000000 ----l2
0xe0000000 ~ 0xe8000000 ----l2 DDR MEM
0xe8000000 ~ 0xf0000000 ----l2 DEVICE MEM
0xf2200000 ~ 0xf23fffff rne_cfg ----l2
0xf4000000 ~ 0xf403ffff rne_mem 256kB ----l2
0xf5000000 ~ 0xf5007fff GIC 32kB ----l2
0xf0000000 ~ 0xf0ffffff rsv
0xf1000000 ~ 0xf11fffff peri -----2MB : l2 -> l3 3_1
0xf2000000 ~ 0xf21fffff usb -----2MB : l2 -> l3 3_2
0xf2400000 ~ 0xf25fffff peri -----2MB : l2 -> l3 3_3
0xf2600000 ~ 0xf27fffff ddr gic noc acodec -----0xf2600000 ~ 0xf26fffff 1MB : l2 -> l3 3_4
// x21 = address of L1 tables
// x22 = address of L2 tables
// x23 = address of L3_1 tables
// x24 = address of L3_2 tables
// x25 = address of L3_3 tables
// x26 = address of L3_4 tables
地址转换:
L2 索引:ubfx 提取位[29:21] (#21, #9)
L3 索引:ubfx 提取位[20:12] (#12, #9)
页表项位置:基址 + 索引 × 8 (lsl #3)
*/
/*
虚拟地址 (x2):
31 21 12 0
┌────────┬────────┬────────┐
│ L2索引 │ L3索引 │ 页内偏移 │
└────────┴────────┴────────┘
9位 9位 12位
页表结构 (L2):
x22 → ┌───────────┐ 基址
│ 条目0 │
├───────────┤
│ 条目1 │
├───────────┤
│ ... │
├───────────┤
│ 条目480 │ ← x22 + 480×8
├───────────┤
│ ... │
└───────────┘
*/
// 0xf4000000 ~ 0xf403ffff 区域:2MB 块映射 (普通内存)
// F400 0000 - F403 FFFF, 1 entries, 2MB block, normal memory, RNE_MEM, 256KB acture, SRAM
// 物理地址 + 属性
ldr x1, =(0xF4000000 | \
TT_S1_ATTR_BLOCK | \
(1 << TT_S1_ATTR_MATTR_LSB) | \
TT_S1_ATTR_NS | \
TT_S1_ATTR_AP_RW_PL1 | \
TT_S1_ATTR_SH_INNER | \
TT_S1_ATTR_AF | \
TT_S1_ATTR_nG)
ldr x2, =0xF4000000 // 目标虚拟地址
ubfx x3, x2, #21, #9 // 提取 L2 索引 (位[29:21]) ?
str x1, [x22, x3, lsl #3] // 写入 L2 页表 (x22) ?
// 0xf2200000 ~ 0xf23fffff 区域:2MB 块映射 (设备内存)
// F220 0000 - F23F FFFF, 1 entries, 2MB block, device, RNE_CFG1
// 物理地址 + 属性
ldr x1, =(0xF2200000 | \
TT_S1_ATTR_BLOCK | \
(2 << TT_S1_ATTR_MATTR_LSB) | \
TT_S1_ATTR_NS | \
TT_S1_ATTR_AP_RW_PL1 | \
TT_S1_ATTR_SH_INNER | \
TT_S1_ATTR_AF | \
TT_S1_ATTR_nG)
ldr x2, =0xF2200000 // 目标虚拟地址
ubfx x3, x2, #21, #9 // 提取 L2 索引
str x1, [x22, x3, lsl #3] // 写入 L2 页表
// 设置 L2 页表项:0xf1000000 ~ 0xf11fffff 指向 L3 页表 (x23)
// level 2 table: 0xf1000000 ~ 0xf11fffff, 1 entry, point to level 3_1
orr x1, x23, #TT_S1_ATTR_TABLE // 组合 L3 基址和表描述符属性
ldr x2, =0xF1000000 // 虚拟地址范围起始
/*从 x2 的位 [21] 开始连续提取 9 位(位 [21] 到 [29]) 将提取的 9 位值存入 x3,高位补零扩展
在页表上下文中的意义:
在 ARM64 页表系统中: 虚拟地址位 [29:21] 对应 L2 页表索引, 9 位索引可寻址 512 个条目 (2⁹ = 512) , 该指令提取虚拟地址中的 L2 页表索引号*/
ubfx x3, x2, #21, #9 // 提取 L2 索引
str x1, [x22, x3, lsl #3] // 写入 L2 页表
// 设置 L2 页表项:0xf2000000 ~ 0xf21fffff 指向 L3 页表 (x24)
// level 2 table: 0xf2000000 ~ 0xf21fffff, 1 entry, point to level 3_2
orr x1, x24, #TT_S1_ATTR_TABLE // 组合 L3 基址和表描述符属性
ldr x2, =0xF2000000 // 虚拟地址范围起始
ubfx x3, x2, #21, #9 // 提取 L2 索引
str x1, [x22, x3, lsl #3] // 写入 L2 页表 //目标地址 = x22 + (x3 << 3)
// 设置 L2 页表项:0xf2400000 ~ 0xf25fffff 指向 L3 页表 (x25)
// level 2 table: 0xf2400000 ~ 0xf25fffff, 1 entry, point to level 3_3
orr x1, x25, #TT_S1_ATTR_TABLE // 组合 L3 基址和表描述符属性
ldr x2, =0xF2400000 // 虚拟地址范围起始
ubfx x3, x2, #21, #9 // 提取 L2 索引
str x1, [x22, x3, lsl #3] // 写入 L2 页表
// 设置 L2 页表项:0xf2600000 ~ 0xf27fffff 指向 L3 页表 (x26)
// level 2 table: 0xf2600000 ~ 0xf27fffff, 1 entry, point to level 3_4
orr x1, x26, #TT_S1_ATTR_TABLE // 组合 L3 基址和表描述符属性
ldr x2, =0xF2600000 // 虚拟地址范围起始
ubfx x3, x2, #21, #9 // 提取 L2 索引
str x1, [x22, x3, lsl #3] // 写入 L2 页表
// 设置 L2 页表项:F500 0000 - F500 7FFF 指向 L3 页表 (x27)
// level 2 table: F500 0000 - F500 7FFF, 1 entry, point to level 3_5
orr x1, x27, #TT_S1_ATTR_TABLE // 组合 L3 基址和表描述符属性
ldr x2, =0xF5000000 // 虚拟地址范围起始
ubfx x3, x2, #21, #9 // 提取 L2 索引
str x1, [x22, x3, lsl #3] // 写入 L2 页表
/*
0xf1000000 ~ 0xf11fffff peri -----2MB : l2 -> l3 3_1
0xf2000000 ~ 0xf21fffff usb -----2MB : l2 -> l3 3_2
0xf2400000 ~ 0xf25fffff peri -----2MB : l2 -> l3 3_3
0xf2600000 ~ 0xf27fffff ddr gic noc acodec -----0xf2600000 ~ 0xf26fffff 1MB : l2 -> l3 3_4
*/
// 填充 L3 页表 (x23):0xf1000000 ~ 0xf11fffff 区域 (512 个 4KB 页)
// level 3 table: 0xf1000000 ~ 0xf11fffff, 512 entry, x23
// valid addr 0xf1000000 ~ 0xf11fffff
ldr x1, =0x1000 // 页大小:4KB
ldr x2, =((0xF11FFFFF + 1 - 0xF1000000) >> 12) // 计算页数
ldr x3, =0xF1000000 // 物理地址起始
// 页描述符属性
// 设备内存属性
ldr x4, = (TT_S1_ATTR_PAGE | \
(2 << TT_S1_ATTR_MATTR_LSB) | \
TT_S1_ATTR_NS | \
TT_S1_ATTR_AP_RW_PL1 | \
TT_S1_ATTR_SH_INNER | \
TT_S1_ATTR_AF | \
TT_S1_ATTR_nG)
loop3_1:
ubfx x5, x3, #12, #9 // 提取页内索引 (位[20:12])
orr x6, x3, x4 // 组合物理地址和属性
str x6, [x23, x5, lsl #3] // 存储到 L3 页表 (x23)
add x3, x3, x1 // 移动到下一页
subs x2, x2, #1 // 递减页计数器
bne loop3_1 // 循环直到所有页映射完成
// 填充 L3 页表 (x24):0xf2000000 ~ 0xf21fffff 区域 (512 个 4KB 页)
// level 3 table: 0xf2000000 ~ 0xf21fffff,, 512 entry, x24
// valid addr 0xf2000000 ~ 0xf21fffff
ldr x1, =0x1000 // 页大小:4KB
ldr x2, =((0xF21FFFFF + 1 - 0xF2000000) >> 12) // 计算页数
ldr x3, =0xF2000000 // 物理地址起始
// 设备内存属性
ldr x4, = (TT_S1_ATTR_PAGE | \
(2 << TT_S1_ATTR_MATTR_LSB) | \
TT_S1_ATTR_NS | \
TT_S1_ATTR_AP_RW_PL1 | \
TT_S1_ATTR_SH_INNER | \
TT_S1_ATTR_AF | \
TT_S1_ATTR_nG)
loop3_2:
ubfx x5, x3, #12, #9 // 提取页内索引
orr x6, x3, x4 // 组合物理地址和属性
str x6, [x24, x5, lsl #3] // 存储到 L3 页表 (x24)
add x3, x3, x1 // 下一页
subs x2, x2, #1 // 递减页计数器
bne loop3_2 // 循环
// 填充 L3 页表 (x25):0xf2400000 ~ 0xf25fffff 区域 (512 个 4KB 页)
// level 3 table: 0xf2400000 ~ 0xf25fffff, 512 entry, x25
// valid addr 0xf2400000 ~ 0xf25fffff
ldr x1, =0x1000 // 页大小:4KB
ldr x2, =((0xF25FFFFF + 1 - 0xF2400000) >> 12) // 计算页数
ldr x3, =0xF2400000 // 物理地址起始
// 设备内存属性
ldr x4, = (TT_S1_ATTR_PAGE | \
(2 << TT_S1_ATTR_MATTR_LSB) | \
TT_S1_ATTR_NS | \
TT_S1_ATTR_AP_RW_PL1 | \
TT_S1_ATTR_SH_INNER | \
TT_S1_ATTR_AF | \
TT_S1_ATTR_nG)
loop3_3:
ubfx x5, x3, #12, #9 // 提取页内索引
orr x6, x3, x4 // 组合物理地址和属性
str x6, [x25, x5, lsl #3] // 存储到 L3 页表 (x25)
add x3, x3, x1 // 下一页
subs x2, x2, #1 // 递减页计数器
bne loop3_3 // 循环
// 填充 L3 页表 (x26):0xf2600000 ~ 0xf27fffff 区域 (512 个 4KB 页)
// level 3 table: 0xf2600000 ~ 0xf27fffff, 512 entry, x26
// valid addr 0xf2600000 ~ 0xf27fffff
ldr x1, =0x1000 // 页大小:4KB
ldr x2, =((0xF27FFFFF + 1 - 0xF2600000) >> 12) // 计算页数
ldr x3, =0xF2600000 // 物理地址起始
// 设备内存属性
ldr x4, = (TT_S1_ATTR_PAGE | \
(2 << TT_S1_ATTR_MATTR_LSB) | \
TT_S1_ATTR_NS | \
TT_S1_ATTR_AP_RW_PL1 | \
TT_S1_ATTR_SH_INNER | \
TT_S1_ATTR_AF | \
TT_S1_ATTR_nG)
loop3_4:
ubfx x5, x3, #12, #9 // 提取页内索引
orr x6, x3, x4 // 组合物理地址和属性
str x6, [x26, x5, lsl #3] // 存储到 L3 页表 (x26)
add x3, x3, x1 // 下一页
subs x2, x2, #1 // 递减页计数器
bne loop3_4 // 循环
// 填充 L3 页表 (x27):0xf5000000 ~ 0xf5007fff 区域 (512 个 4KB 页)
// level 3 table: 0xf5000000 ~ 0xf5007fff, 512 entry, x27
// valid addr 0xf5000000 ~ 0xf5007fff
ldr x1, =0x1000 // 页大小:4KB
ldr x2, =((0xF5007FFF + 1 - 0xF5000000) >> 12) // 计算页数
ldr x3, =0xF5000000 // 物理地址起始
// 设备内存属性
ldr x4, = (TT_S1_ATTR_PAGE | \
(2 << TT_S1_ATTR_MATTR_LSB) | \
TT_S1_ATTR_NS | \
TT_S1_ATTR_AP_RW_PL1 | \
TT_S1_ATTR_SH_INNER | \
TT_S1_ATTR_AF | \
TT_S1_ATTR_nG)
loop3_5:
ubfx x5, x3, #12, #9 // 提取页内索引
orr x6, x3, x4 // 组合物理地址和属性
str x6, [x27, x5, lsl #3] // 存储到 L3 页表 (x27)
add x3, x3, x1 // 下一页
subs x2, x2, #1 // 递减页计数器
bne loop3_5 // 循环
ret
.cfi_endproc