这篇笔记主要记录ARMv8 MMU的学习,学习资料主要是下面两个文档:
Armv8-A Address Translation
ARM Cortex-A Series Programmer's Guide for ARMv8-A
第1个其实是将2中不同章节和地址转换相关的内容放在一起,因此免去了查找不同章节内容的痛苦。相比于2,我会更多地使用1中内容。
ARMv8有多个Exception/Excution/Security Level,这篇笔记主要关注NS + EL1 + AArch64的组合。
MMU的基本功能
MMU地功能就是实现VA->PA
地转换,我们会创建一系列的映射表来实现这个功能。至于为什么要用虚拟地址我就不解释了,网上随便搜搜就能找到一大堆的资料。
下图显示了从CPU开始,虚拟地址是如何进行转换并被使用的,这里除了牵涉MMU外,还有TLB以及各级的Cache。TLB也是一种特殊的Cache,我就不在这里介绍了,以后专门开个系列笔记介绍Cache吧。
上图显示的是VA->PA
在架构方面是如何实现的,下图显示的是地址空间如何进行VA->PA
的转换。我们可以看出,不同区域的虚拟地址可以映射到同一区域的物理空间。同一区域的虚拟地址也可以映射到不同区域的物理空间。
MMU相关的两个重要寄存器
TTBRx
虽然ARMv8支持64bit的地址空间,但实际上最多只可以使用48bit的地址空间。[63 : 48]
这16位是不会作为地址被使用的。在绝大多数情况下,这16位要么是0xFFFF
要么是0x0000
,因此64bit的地址空间被分成了3部分(如上图所示)。
如果[63 : 48] = 0xFFFF
,那么它对应的虚拟地址将使用TTBR1_ELx
寻找最低一级页表;如果[63 : 48] = 0x0000
,那么它对应的虚拟地址将使用TTBR0_ELx
寻找最低一级的页表。
TTBRx
存的就是最低一级页表的物理地址。注意,除了EL1
,这个寄存器也有EL2
和EL3
的版本,但只有TTBR0
的版本。
TCR_ELx
这个寄存器是设置页表的一个重要的寄存器(它将控制所有级别的页表),它可以设置有效虚拟地址的位数(除了48bit外,还有42bit,39bit等),granule的大小(4/16/64KB)等等。
上图中,TxSZ
决定64 - 有效虚拟地址的位数;TGx
决定转换颗粒的大小;IRGN/ORGN
设置页表的cacheability;SH
设置页表的shareability等。总之,这个寄存器主要是对页表进行设置。
VA->PA转换的实例
下图一个2级页表、granule = 64KB、有效虚拟地址 = 42bit的例子,我们来看看有哪些需要注意的地方。
TTBRx
和页表中保存的页表地址都是物理地址;- 虚拟地址的最高位决定是去
TTBR0
还是TTBR1
寻找页表; - 页表entry的数量 = granule size / 8B = 64KB / 8B = 8K;
- 物理地址的位数可以和虚拟地址的位数不一样 - VA = 42bit,PA = 48bit,PA的大小由
TCR_ELx
中的IPS
决定。
页表描述符
这篇笔记讨论的描述符都是基于这种格式 - Armv8-A AArch64 Long Descriptor format,一共有3类有效的描述符,请看下图:
- 指向页表的描述符;
- block entry的描述符 - block简单来说就是人为的合并/简化高级的页表,在Linux开启MMU前创建的临时页表用了2MB的block;
- table entry的描述符 - 可以理解成PTE。
注意,页表中存储的地址都是物理地址,不管是页表的地址还是最后映射的地址,都是物理地址。MMU的硬件会处理好这些物理地址。
在上图中,描述符都有attributes的位,下图显示了这些位的具体定义。要看完整的描述符的定义,还需要看Arm Architecture Reference Manual
,具体内容请参考 - D8.3 Translation table descriptor formats
。
其中的Index
是MAIR_ELn
的索引,用于确定Cache policies,其它主要用于设置access permission, shareability等等。
Granule对页表的影响
页表的结构主要受两个因素影响 - 1.虚拟地址的位数;2. granule的值。我们下面看看VA = 48bit, granule = 4KB
的一个例子:
下面我简单介绍下这个结构是如何构成的,
granule = 4KB
,这个决定了最后一部分是VA[11:0] = 12bit
;- 因为
granule = 4KB
,所以每个页表的大小是4KB
。每个页表描述符是64bit
,因此有512个entry,需要9bit
; - 如果
VA
只有39bit,那么我们就直接忽略第0级页表,直接从第1级页表开始;
页表的属性
在页表描述符中,我们可以对不同区域的内存进行cache, memory, access等属性的设置。这些设置都具有继承性,因此,在低级页表描述符中设置的参数会覆盖之后几级设置的参数。因为Lower attributes
只在最后一级页表出现,因此它设置的属性其实是不会被覆盖的。
除了页表映射的内存空间,我们也可以给页表本身的属性进行设置。比如,页表是否cacheable,是否shareable等等,这些都是通过TCR_EL1
进行设置 - IRGN/ORGN
, SH0/1
。
注意,我们设置页表的cache/memory等属性,必须和其所在的内存空间的属性一样。
到这里,MMU的基础知识基本就介绍完了。还有一些知识点,比如TLB, Cache以及不同EL的映射我就不介绍了,前两部分我会专门开几篇文章介绍,Cache本来就是一个大的知识点。至于不同EL的映射,这个我目前还没遇到实例,如果Linux内核遇到相关内容,我再做总结。