文章目录
- MMU Table 表分配原理及其代码实现
- 虚拟地址空间 Region的配置
- 系统物理地址位宽获取
MMU Table 表分配原理及其代码实现
假设当前系统中需要映射多个region,其中第一个要映射的region虚拟地址范围是0x0000_0000 -- 0x8000_0000
一共2G
的大小。MMU默认最大支持48bit的虚拟地址,由前面文章可知 如果按照颗粒度为4K大小进行映射,且按照4级映射的方式进行映射(9bit-9bit-9bit-9bit-12bit
),那么:
- Level0 table 一个entry对应的大小为
512G
; - Level1 table 一个entry对应的大小为
1G
; - Level2 table 一个entry对应的大小为
2M
; - Level3 table 一个entry对应的大小为
4K
。
由于每个Level 又由9bit
组成,所以每个Level 有 29 = 512 个entry。由于 Level0 的一个entry对应的地址范围是512G,所以通常用不到 Level0,虽然用不到Level0, 但是低级 table的地址都是由 Level0 table寻址而来的,所以仍然需要给Level0 的 table分配地址,其中Level0 table 也就是 root table,最终会被配置到 ARM 系统寄存器TTBR0
或者TTBR1
中。
每个 Entry 占 8bytes 空间
root table 通常会被分配4K大小的地址,root table(level0 table) 中的 每个entry 都会指向 level1 的 一个 table (当然 level0 table entry也可以是一个block,但是很少这样用),同样 level1 table 中的每个entry都会执行 level2 的 table …
在 table 分配期间 MMU 还没有开始运行,所以所使用的地址都是物理地址。
虚拟地址空间 Region的配置
通常虚拟地址空间的配置都是按照region进行分配的,也即将整个虚拟地址空间分为多个region,如下一共配置了5个region虚拟地址空间:
struct mmu_region {
uint64_t phy_base;
uint64_t vir_base;
uint64_t len;
uint64_t attr;
uint32_t id;
};
static struct mmu_region mem_region[] = {
{
.phy_base = 0x00000000,
.vir_base = 0x00000000,
.len = 0x80000000,
.attr = 0x00000008,
.id = 0,
},
{
.phy_base = 0x80000000,
.vir_base = 0x80000000,
.len = 0x06000000, // 96M
.attr = 0x00000002,
.id = 1,
},
{
.phy_base = 0x86000000,
.vir_base = 0x86000000,
.len = 0x02000000, // 32M
.attr = 0x00000000,
.id = 3,
},
{
.phy_base = 0x88000000,
.vir_base = 0x88000000,
.len = 0x18000000, // 384M
.attr = 0x00000002,
.id = 5,
},
{
.phy_base = 0xA0000000,
.vir_base = 0xA0000000,
.len = 0xF0000000,
.attr = 0x00000002,
.id = 6,
},
{
.phy_base = 0x00000000,
.vir_base = 0x00000000,
.len = 0x00000000,
.attr = 0x00000008,
.id = 8,
},
};
如上面代码所示,当给出一段虚拟地址空间后首先需要判断每个 region 需要多少个 level table,比如需要多少个level1 table,多少个 level2 table …,
那么如何计算出需要多少个 level table 及每个table中有多少个entry 呢? 很明显 是 需要知道要映射的起始地址及其长度。如前面说到 0x0000_0000 -- 0x8000_0000
假如一共要映射2G
的虚拟空间,我们知道 level1 table 中的一个entry 对应1G的大小,所以对于这段虚拟空间的映射只需在level1 table中填充两个entry就可以了, 其中 Level0 table 只使用到了entry0,如下图1-1所示:
图 1-1
Level0 Table 最大支持 256T 范围空间
我们知道,table 表中的一个entry对应一段地址,当给出一段地址后,我们需要知道这段地址在 table 表中的 entry 索引,那么 怎么计算出 entry 在 table 中的索引值呢? Entry 索引的计算由 level 等级的不同而不同,如下图 1-2 所示, level0 table的索引从bit39
开始计算,level1 的table索引从bit30
开始计算,level2的table的索引从bit21
开始计算。
图 1-2
所以需要将虚拟地址的 bit[47:39]
的值剥离出来,它就是该虚拟地址在 Level0 table 中的索引值,因为 Level0 table的一个entry指向 512G的地址范围,所以通常 level0 table 只会用到一个 Table Entry。比如虚拟地址0x0000_0000
它在 Level0 table中的索引值为0,同样0x8000_0000
在 Level0 table 中的索引值也为0,只要虚拟地址范围不超出512G
(0x80_0000_0000
)那么它们在level0 table 中的索引都一样,都是为0
.
下一步就是设定系统使用多少bit的虚拟地址空间了。在设置虚拟地址位宽前我们先看下如何获取当前系统支持的物理地址宽度?
系统物理地址位宽获取
在ARMv8架构中,ID_AA64MMFR0_EL1
是一个系统寄存器,用于提供有关内存管理单元(MMU)功能的信息。这个寄存器的全名是"A64 Memory Model Feature Register 0 at EL1",即在异常级别1(EL1)下的A64内存模型特征寄存器0。它包含的信息用于表明处理器支持的内存管理和地址转换的各种特征。
这里我们只先关注 bits [3:0]
,它定义了当前系统支持的物理地址位宽:
0b0000
32 bits, 4GB.0b0001
36 bits, 64GB.0b0010
40 bits, 1TB.0b0011
42 bits, 4TB.0b0100
44 bits, 16TB.0b0101
48 bits, 256TB.0b0110
52 bits, 4PB.