MMU-AArch64
限于篇幅大小,将AArch64架构上的介绍从MMU-AArch32拆分出来独立成篇。
MMU的作用
功能
- 地址转换
- 权限检测
- 异常中止(PageFault)
- 刷新TLB,锁定TLB中某些页表项
AArch64平台上使用三级页表/四级页表+4KB Page,或者二级页表/三级页表+64KB Page的模式
无论如何选择映射方式、页大小、内存模型、以及CPU位数,MMU均根据OS选择的页表实现来完成“虚拟地址”=>“物理地址”的转换
页表实现见下一节<PageTable的作用>
TODO...
TODO...
TODO...
TODO...
MMU初始化
TODO...
PageTable的作用
页表结构
在AArch64架构下,地址线宽度最大值是48bit,根据MMU支持能力、内存大小、SOC上存储实际挂接和映射的物理地址、内核实现修改方便等因素,Linux虚拟内存模型选择有所不同。因此64bit系统上PageTable是比较复杂的。
在Linux操作系统中:
- 高端地址区域部分TTBR1_EL1 Region分配给Kernelspace使用
- 低端地址区域部分TTBR0_EL1 Region分配给Userspace使用
① AArch64 Linux memory layout with 4KB pages + 3 levels:
Start | End | Size | Use |
---|---|---|---|
0000000000000000 | 0000007fffffffff | 512GB (39-bit) | user |
ffffff8000000000 | ffffffffffffffff | 512GB (39-bit) | kernel |
② AArch64 Linux memory layout with 4KB pages + 4 levels:
Start | End | Size | Use |
---|---|---|---|
0000000000000000 | 0000ffffffffffff | 256TB (48-bit) | user |
ffff000000000000 | ffffffffffffffff | 256TB (48-bit) | kernel |
③ AArch64 Linux memory layout with 64KB pages + 2 levels:
Start | End | Size | Use |
---|---|---|---|
0000000000000000 | 000003ffffffffff | 4TB (42-bit) | user |
fffffc0000000000 | ffffffffffffffff | 4TB (42-bit) | kernel |
④ AArch64 Linux memory layout with 64KB pages + 3 levels:
Start | End | Size | Use |
---|---|---|---|
0000000000000000 | 0000ffffffffffff | 256TB (48-bit) | user |
ffff000000000000 | ffffffffffffffff | 256TB (48-bit) | kernel |
① 64KB页 地址结构
页表级别 | Offset占据比特位 | 寻址空间 | 表项数量 | 表项寻址空间 | 页目录项是否支持Block |
---|---|---|---|---|---|
L1 | bit[47:42] | 256T = 2^48 | 64 = 2^6 | 4T | N |
L2 | bit[41:29] | 4T = 2^42 | 8192 = 2^13 | 512M | Y |
L3 | bit[28:16] | 512M = 2^29 | 8192 = 2^13 | 64K | N |
② 16K页 地址结构
页表级别 | Offset占据比特位 | 寻址空间 | 表项数量 | 表项寻址空间 | 页目录项是否支持Block |
---|---|---|---|---|---|
L0 | bit[47] | 256T = 2^48 | 2 | 128T | N |
L1 | bit[46:36] | 128T = 2^47 | 2048 = 2^11 | 64G | N |
L2 | bit[35:25] | 64G = 2^36 | 2048 = 2^11 | 32M | Y |
L3 | bit[24:14] | 32M = 2^25 | 2048 = 2^11 | 16K | N |
③ 4KB页 地址结构
页表级别 | Offset占据比特位 | 寻址空间 | 表项数量 | 表项寻址空间 | 页目录项是否支持Block |
---|---|---|---|---|---|
L0 | bit[47:39] | 256T = 2^48 | 512 = 2^9 | 512G | N |
L1 | bit[38:30] | 512G = 2^39 | 512 = 2^9 | 1G | Y |
L2 | bit[29:21] | 1G = 2^30 | 512 = 2^9 | 2M | Y |
L3 | bit[20:12] | 2M = 2^21 | 512 = 2^9 | 4K | N |
Tips:
① ARM VMSv8支持的16KB页的页表结构,这个在ARMv8 SPEC中有说明,但是在Linux Kernel文档中是没有给出的,不知是否因为Kernel不支持
② 实际表项寻址空间与下级页表寻址空间是一样大的
③ 最高位bit[63]标记是内核页表还是用户页表,1是内核页表占据高地址区域,0是用户页表占据低地址区域
CP15的C2寄存器(TTBR)保存PGD基址的bit位起止范围根据所选择页大小不同(4KB/16KB/64KB)而不同,如下图所示
Tips:
① 当选择小于48bit的物理地址时,那么相应的TTBR[m:n]需要缩小范围,规则是高于物理地址bit位数的bit位全补0。比如:选择40bit物理地址,那么TTBR[63:39]=0,即40bit以上全为0
② ASID用于标记不同的线程,一共16bit,当使用小于16bit的ASID实现时,高位补0
③ TTBR0用于保存“用户空间”页表基址,寻址位于虚拟地址空间“底部”的userspace;
④ TTBR1用于保存“内核空间”页表基址,寻址位于虚拟地址空间“顶部”的kernelspace;
在64位架构上,最多支持4级页表结构,各级页表名称有所变化,1~4级分别被称为L0级、L1级、L2级、L3级。页表的段页式管理比较复杂,它会以页式映射为主(区分64KB页、16KB页、4KB页三种),但在页式映射的L1/L2级页表项中会包含Block块(区分1G块、512M块、32M块、2M块)的直接映射,这样去管理一个特殊用途的大个内存区域。分类组合如下:
- 64KB页/512MB块 + 二级页表(L2+L3)/三级页表(L1+L2+L3)
- 16KB页/32MB块 + 三级页表(L1+L2+L3)/四级页表(L0+L1+L2+L3)
- 4KB页/1GB块/2MB块 + 三级页表(L1+L2+L3)/四级页表(L0+L1+L2+L3)
- 64KB页是没有L0级页表的,因此L0级页表只包含16KB页、4KB页索引!
- 对于stage1=>stage2页表转换阶段是否存在,需要去查看ARM SPEC规定,处理器处于不同的运行级别使用的有所不同
① 无效路径:(Demo)
比特位 | 取值 | 描述 |
---|---|---|
bit[0] | 0b0 | 页表项无效,输入地址是unmapped |
bit[63:1] | - | IGN,硬件忽略位,软件可以考虑根据情况使用 |
② 16KB页路径:(Demo)
比特位 | 取值 | 描述 |
---|---|---|
bit[0] | 0b1 | 页表项有效,输入地址已经map过了 |
bit[1] | 0b1 | 标识这是一个页表描述符 |
bit[13:2] | 0b0 | SBZ,未使用应该为0 |
bit[47:14] | 据实际 | L1页表基址 |
bit[51:48] | 0b0 | SBZ,未使用应该为0 |
bit[58:52] | - | IGN,硬件忽略位,软件可以考虑根据情况使用 |
bit[59] | 据实际 | PXN,在EL1内核态是否可执行 |
bit[60] | 据实际 | XN,在EL0用户态是否可执行 |
bit[62:61] | 据实际 | AP,数据访问权限控制 |
bit[63] | 据实际 | NS,用于安全模式 |
③ 4KB页路径:同上L0页目录项的16KB路径,区别bit[47:12]和bit[11:2];
- 4KB页L1级支持block块直接映射,被映射的块大小是1G
① 无效路径:同上L0页目录项的无效路径;
② 64KB页路径:同上L0页目录项的16KB路径,区别bit[47:16]和bit[15:2];
③ 16KB页路径:同上L0页目录项的16KB路径;
④ 4KB页路径:同上L0页目录项的4KB路径;
⑤ 4KB页Block路径:(Demo)
比特位 | 取值 | 描述 |
---|---|---|
bit[0] | 0b1 | 页表项有效,输入地址已经map过了 |
bit[1] | 0b0 | 标识这是一个block描述符 |
bit[11:2] | 据实际 | Lower Block Attributes |
bit[29:12] | 0b0 | SBZ,未使用应该为0 |
bit[47:30] | 据实际 | Block基址 |
bit[51:48] | 0b0 | SBZ,未使用应该为0 |
bit[63:52] | 据实际 | Upper Block Attributes |
- 64KB页L2级支持block块直接映射,被映射的块大小是512M
- 16KB页L2级支持block块直接映射,被映射的块大小是32M
- 4KB页L2级支持block块直接映射,被映射的块大小是2M
① 无效路径:同上L0~1页目录项的无效路径;
② 64KB页路径:同上L0~1页目录项的64KB路径;
③ 16KB页路径:同上L0~1页目录项的16KB路径;
④ 4KB页路径:同上L0~1页目录项的4KB路径;
⑤ 64KB页Block路径:同上L1 4KB页的BLock路径,区别bit[47:29]和bit[28:12];
⑥ 16KB页Block路径:同上L1 4KB页的BLock路径,区别bit[47:25]和bit[24:12];
⑦ 4KB页Block路径:同上L1 4KB页的BLock路径,区别bit[47:21]和bit[20:12];
Tips:
以上可见,对于64KB页、16KB页、4KB页L0、L1、L2页表项结构大体是类似的,只是“下一级页表基址”包含bit位数略有不同
- L3页表的页表项被称为page descriptor,指向对应page页
① 无效页:同上面L0~2页目录项
② 保留:bit[1:0]=0b01
③ 64KB大页:(Demo)
比特位 | 取值 | 描述 |
---|---|---|
bit[0] | 0b1 | 页表项有效,输入地址已经map过了 |
bit[1] | 0b1 | 标识这是一个page描述符 |
bit[11:2] | 据实际 | Lower Block Attributes |
bit[15:12] | 0b0 | SBZ,未使用应该为0 |
bit[47:16] | 据实际 | Page基址 |
bit[51:48] | 0b0 | SBZ,未使用应该为0 |
bit[63:52] | 据实际 | Upper Block Attributes |
④ 16KB页:同上L3 64KB大页,区别bit[47:14]和bit[13:12];
⑤ 4KB小页:同上L3 64KB大页,区别bit[47:12];
当Linux 64bit内核选择“页式映射 + 三级页表 + 4KB小页”转换模式,页表转换过程
以kernel空间为例使用基址寄存器TTBR1(user空间只是基址寄存器用TTBR0):
具体采用几级页表在MMU控制寄存器(TCR.TnSZ)和PageTable间对应关系如下图:
以上是以4KB的页为例,其它页见ARMv8 SPEC详细解释
初始化
1 | TODO...
|
存储
TODO...
TODO...
TLB的作用
结构
TODO...
更新
TODO...
CPU内存寻址过程
这里就描述一个64bit的用户态进程,从通过malloc分配堆内存到真正拿到物理内存的全过程,如下图表述:
TODO...
参考文档