从零到负一

ARMv8 MMU的基础知识

2022/12/13

这篇笔记主要记录ARMv8 MMU的学习,学习资料主要是下面两个文档:

  1. Armv8-A Address Translation
  2. ARM Cortex-A Series Programmer's Guide for ARMv8-A

Armv8-A Address Translation中的内容都来自于ARM Cortex-A Series Programmer's Guide for ARMv8-A,但它把后者不同章节的相关内容放在了一起,因此免去了查找不同章节内容的痛苦。相比于后者,我会更多地使用前者中的内容。注意,ARMv8有多个Exception/Excution/Security Level,但这篇笔记只关注NS + EL1 + AArch64的组合。

MMU的基本功能

MMU的功能就是实现VA->PA的地址转换,我们会创建一系列的映射表来实现这个功能。至于为什么要用虚拟地址我就不解释了,网上随便搜搜就能找到一大堆的资料。 下图显示了从CPU开始,虚拟地址是如何进行转换并被使用的,这里除了涉及MMU外,还有TLB以及各级的Cache。TLB也是一种特殊的Cache,我就不在这里多介绍了,以后专门开个系列介绍Cache吧。

MMU AT

上图显示的是VA->PA在架构层面如何实现的,下图显示的是地址空间如何进行VA->PA的转换。我们可以看出,不同区域的虚拟地址可以映射到同一区域的物理空间,同一区域的虚拟地址也可以映射到不同区域的物理空间。

MMU VA_2_PA

除了地址的映射,MMU还有一个重要的作用就是修改各个内存空间的权限、Cache策略等等。

MMU相关的两个重要寄存器

TTBRx

虽然ARMv8支持64bit的地址空间,但实际上最多只可以使用48bit的地址空间。[63 : 48]这16位是不会作为地址被使用的。在绝大多数情况下,这16位要么是0xFFFF要么是0x0000,因此64bit的地址空间被分成了3部分 - user space, kernel spacereserved。如果[63 : 48] = 0xFFFF,那么它对应的虚拟地址将使用TTBR1_ELx来寻找最低一级页表;如果[63 : 48] = 0x0000,那么它对应的虚拟地址将使用TTBR0_ELx来寻找最低一级的页表。

TTBRx存的就是最低一级页表的物理地址。除了EL1,这个寄存器也有EL2EL3的版本。TTBR1只有EL0EL1,其它两种ELx只能使用TTBR0

TCR_ELx

这个寄存器是设置页表的一个重要的寄存器(它将控制所有级别的页表),它可以设置有效虚拟地址的位数(除了48bit外,还有42bit,39bit等),颗粒度(granule)的大小(4/16/64KB)等等。

TCR_EL1

上图中,TxSZ决定64 - 有效虚拟地址的位数, TGx决定转换颗粒度的大小, IRGN/ORGN设置页表的cacheability, SH设置页表的shareability等。总之,这个寄存器主要是对页表进行设置。

VA->PA转换的实例以及页表的相关属性

下图是一个2级页表、granule = 64KB、有效虚拟地址位 = 42的例子,我们来看看有哪些需要注意的地方。

VA->PA

  1. TTBRx和页表中保存的页表地址都是物理地址;
  2. 虚拟地址的最高位决定是去TTBR0还是TTBR1寻找页表;
  3. 页表entry的数量 = granule size / 8B = 64KB / 8B = 8K(在这个例子中,页的大小是64KB,页的大小也可能是其它值);
  4. 物理地址的位数可以和虚拟地址的位数不一样 - VA = 42bit,PA = 48bit,PA的大小由TCR_ELx中的IPS决定。

关于地址转换的例子和 Linux 中页表的定义可以参考【原创】(一)ARMv8 MMU及Linux页表映射

页表描述符

这篇笔记讨论的描述符都是基于这种格式 - Armv8-A AArch64 Long Descriptor format,一共有3类有效的描述符,请看下图:

描述符

  1. 指向页表的描述符;
  2. block entry的描述符 - block简单来说就是人为的合并/简化高级的页表,在Linux开启MMU前创建的临时页表用了block;
  3. table entry的描述符 - 可以理解成PTE。

注意,页表中存储的地址都是物理地址,不管是页表的地址还是最后映射的地址,都是物理地址。MMU的硬件会处理好这些物理地址。
在上图中,描述符都有attributes的位,下图显示了这些位的具体定义。要看完整的描述符的定义,还需要看Arm Architecture Reference Manual,具体内容请参考 - D8.3 Translation table descriptor formats

描述符位

其中的IndexMAIR_ELn的索引,用于确定Cache policies,其它主要用于设置access permission, shareability等等。

Granule对页表的影响

页表的结构主要受两个因素的影响 -

  1. 虚拟地址的位数
  2. granule的值

我们下面看看VA = 48bit, granule = 4KB的一个例子:

页表结构

下面我简单介绍下这个结构是如何构成的,

  1. granule = 4KB,这个决定了最后一部分是VA[11:0] = 12bit
  2. 因为granule = 4KB,所以每个页表的大小是4KB。每个页表描述符是64bit,因此有512个entry,需要9bit
  3. 如果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内核上去。

参考资料

  1. Armv8-A Address Translation
  2. ARM Cortex-A Series Programmer's Guide for ARMv8-A
  3. 【原创】(一)ARMv8 MMU及Linux页表映射
CATALOG
  1. 1. MMU的基本功能
  2. 2. MMU相关的两个重要寄存器
    1. 2.1. TTBRx
    2. 2.2. TCR_ELx
  3. 3. VA->PA转换的实例以及页表的相关属性
    1. 3.1. 页表描述符
    2. 3.2. Granule对页表的影响
    3. 3.3. 页表的属性
  4. 4. 参考资料