0、回顾
前面知道,MMU 用作虚拟地址和物理地址的相互转换,是为了能够给 OS 提供统一视角的虚拟地址空间;
TLB 的作用是作为 MMU 的 Cache,以提高 MMU 的性能,他们之间的关系如下:
1、ARM 处理器发出地址访问(虚拟地址),首先过 MMU 地址翻译单元的 TLB,如果 TLB 命中,那么直接返回真实的物理地址;
2、如果 TLB Miss,那么就要靠 Table Walk 单元去主存中查找表,以获取物理地址,然后通过 Cache,去访问;
3、Cache 如果命中,那么直接返回实际物理地址的数据,否则,也就是最糟糕的情况,会去访问主存的数据;
上面的过程呢,软件要做的,只有配置并放好这个 Transliation Tables,其他的过程,全部是硬件行为;下面马上仔细的过这部分的细节;
使能 MMU 的参考代码(因为是 CP15 的系统控制寄存器,所以使用 MRC/MCR 指令):
MRC p15, 0, R1, c1, C0, 0 ;Read control register
ORR R1, #0x1 ;Set M bit
MCR p15, 0,R1,C1, C0,0 ;Write control register and enable MMU
这里要注意的一点是,可能要用到内存屏障指令,因为这里就开启了 MMU,即将进入虚拟内存的世界,要确保在这之前,流水线干净,所以执行已经完毕;
1、TLB
TLB 的全称是:Translation Lookaside Buffer;从第一节的那个图可以看出来,MMU 做 Table Walk 的这个 Transliation Tables 是放到主存中,主存访问速度很慢(加 Cache 的根本原因),所以,这里每次都去再主存中做 Table Walk,显然效率非常低,所以,这里就为这个 Table Walk 定制了一个属于他的 “Cache”,称之为 TLB;
但是与 真是的 Cache 不一样(详见《ARMv7-A 处理器窥探(4) —— Cache》),这个 TLB 是专门缓存 Transliation Tables 的,典型的情况,他的组成如下:
由 VA、ASID、PA、Attributes 组成,即:
VA:虚拟地址;
PA:物理地址;
ASID:Address Space ID;
Attributes:属性;
1.1、TLB coherency
TLB 既然扮演的 Transliation Tables Cache 的角色,那么也会有一致性问题,最典型的就是再 OS 中,上下文切换的时候,上一个进程的虚拟地址对应的物理地址表,肯定是和另一个不一样,导致 TLB 一致性问题;此刻,OS 必须处理这种情况,使得上一个进程的 TLB 对下一个失效,也可以直接通过 CP15 控制寄存器,来 flush 掉 TLB(代价太大);
2、MMU
这里,抛开大物理地址扩展和 section 和 supersection 的分析,暂时就看最最常用的两段查找;两段页表查找,我们称第一级页表为 L1,第二级为 L2;
2.1、TTBR0、TTBR1、TTBCR
前面知道,软件需要负责构建这个虚拟地址到物理地址的转换表:Transliation Tables,当软件构件完毕这个表后,只需要告诉硬件,这个 Transliation Tables 放到了那个首地址即可,这个配置通过写 ARM 的 TTBR 寄存器实现(Translation Table Base Address );这里其实有两个 TTRB 寄存器,分别叫 TTBR0 和 TTBR1,为啥两个,后面解释;
2.1.1、TTBCR
和这个 TTBR0、TTBR1 勾肩搭背的,还有一个 TTBCR 寄存器,他们直接什么关系呢,看寄存器说明:
TTBCR:
PD0 和 PD1 是和 Security Extensions 相关的,不管他;
EAE 是和 Large Physical Address Extension 相关的,不管他;
主要关注这里的 N[2:0],指示TTBR0页表基址寄存器基址位宽,同时指示使用 TTBR0 还是 TTBR1 作为页表基址寄存器,以及 TTBR0 页表尺寸:
如果 N = 0 的话,则在做 Table Walk 的时候使用 TTBR0 指定的基地址作为 Transliation Tables 入口的地址;
如果 N > 0 的话:指示TTBR0页表基址寄存器基址位宽,同时指示使用 TTBR0 还是 TTBR1 作为页表基址寄存器,以及 TTBR0 页表尺寸;
N==0,使用 TTBR0。
N>0,如果虚拟地址[31:32-N]为0,则使用 TTBR0;其他情况使用TTRB1。这种情况下,N 指示了TTBR1的页表地址,也指示了 TTBR0 的页表大小。
TTRB0的页表大小由TTBCR.N控制,TTRB1的页表大小为16KB。
我换句话来说,当 N>0 的时候,比如 N=1,那么按照这种说法,VA [31:31] 也就是 VA 的 bit[31] 为 0 的时候,使用 TTBR0 否则使用 TTRB1,按照地址空间来划分,即,32bits 地址,当最高位为 0,即虚拟地址为 0x0000_0000 ~ 0x7FFF_FFFF 这个区间的时候,使用 TTBR0 作为 Transliation Tables 入口的地址,从 0x8000_000 ~ 0xFFFF_FFFF 的虚拟地址空间,使用 TTBR1;
ARM 官方举了个例子,当 TTBCR.N=3‘b111 的时候,VA [31:25] 全部为 0 的时候,使用 TTBR0,按照地址空间来划分就是,虚拟地址为:0x0000_0000 ~ 0x01FF_FFFF 这段区间使用 TTBR0 作为 Transliation Tables 入口的地址;
0x0200_0000 ~ 0xFFFF_FFFF 的虚拟地址空间,使用 TTBR1;
OK,现在可以理解为,配置 TTBCR.N 这个值,可以实现将虚拟地址切割成为两部分,一部分使用 TTBR0 指定的 Transliation Tables 进行 Table Walk,另一部分使用 TTBR1 指定的 Transliation Tables 进行 Table Walk,这个有什么好处呢?比如,内核的页表,是不会改变的,而进程上下文的页表是会改变的,有了这个的话,就可以考虑用一个 TTBR 来专门为内核服务,另一个 TTBR 为进程服务,这样避免进程和内核使用同一个页表,每次都要进行内核页表的拷贝;
由于 TTBCR 是 CP15 的寄存器,访问 TTBCR 的指令为:
MRC p15, 0,