所有集成了MMU功能的CPU,使能MMU之后,程序地址就是虚拟地址,不能直接访问存储器,必须经过MMU转换成物理地址。
MIPS32虚拟地址空间
MIPS32将虚拟地址空间划分为4个区域:
- kuseg 0x0000.0000 - 0x7FFF.FFFF(低2GB):
这些地址是用户态可用的地址。对于有MMU的机器,这些地址需要经过MMU(TLB)的转换后才可以使用;对于没有MMU的机器,这些地址的行为与具体机器实现相关。
- kseg0 0x8000.0000 - 0x9FFF.FFFF(512MB):
这段地址只需将最高位清零即转换为物理地址,映射到连续的低端512MB物理地址上,因此在描述其属性时一般称为unmapped。另外该段地址访问时总是需要通过cache,因此描述其属性时称为cached。在一般应用上,这段地址映射到的物理地址用于存放程序和数据或者操作系统核心。
- kseg1 0xA000.0000 - 0xBFFF.FFFF(512MB):
与kseg0类似,这段地址同样映射到了低端512MB物理地址(将高3个bit清零),但其访问不经过cache,因此其属性可以称为 unmapped/uncached。由于unmaped/uncached特性,这段地址无需配置TLB和cache即可使用,是唯一的在系统重启时能 正常工作的地址空间,因此复位入口点(0xBFC0.0000)被放在这个区域,其物理地址为0x1FC0.0000。
- kseg2 0xC000.0000 - 0xFFFF.FFFF(1GB):
该段地址只能在核心态下使用且需要经过MMU(TLB)转换。
综上,以BCM53003芯片为例,虚拟地址与物理地址的映射关系如下:
其中,kseg3段只是对kseg2段进一步细分,没有本质的区别。
kseg0和kseg1两个虚拟地址段为unmapped地址段,虚拟地址到物理地址的映射关系是固定的,这是因为它们不是通过TLB转换的,而是由MIPS CPU内部实现的Fix-Mapped MMU完成地址转换。这样设计的初衷,是因为CPU刚上电时,TLB未初始化,unmapped地址段可以将Flash Reset Vector(4MB)、DDR DMA(128MB)、CPU核心控制寄存器组以及关键外设寄存器组映射到CPU的虚拟地址空间。
kuseg和kseg2两个虚拟地址段为mapped地址段,由TLB负责处理地址映射。
MIPS CPU有四种工作模式——User/Supervisor/Kernel/Debug,每种工作模式下的访问权限不同,如下图所示:
MIPS32 TLB描述
TLB(Translation Lookside Buffer)介于CPU控制核心和Cache控制器之间,其作用是把程序地址(或称虚拟地址)转换成物理地址。
TLB被称为相联存储器或者内容寻址存储器,因为它不是按照索引来寻址,而是根据内容来选择TLB表项进行寻址。其逻辑非常复杂,每一个TLB表项都有内建的比较器,复杂度和性能扩展性很差,因此,TLB的表项一般不是很多。
典型的TLB包含16~64个TLB表项,每一个表项包含一个页的虚拟地址(VPN即虚拟页号)和一个物理页地址(PFN即物理页号),页大小是可以配置的。
当程序访问一个虚拟地址时,该地址和所有TLB表项中的VPN进行比较,TLB返回匹配的TLB表项的PFN,完成地址转换;如果找不到,则触发TLB miss异常(Linux系统中,因为各进程虚拟地址空间独立且地址范围相同,因此会触发TLB refill重填异常,会涉及TLB表项的更新;但是VxWorks系统中,所有任务共享同一虚拟地址空间,所有TLB表项都是启动时配置,运行时不动态更新)。有一组标志位和每个PFN一起返回,标志位用来标识地址页是否缓存、缓存模式、属于某个ASID还是全局有效地址等。
正所谓“知其然,然后知其所以然”,以BCM53003芯片(包含64个TLB表项)为例,将所有TLB表项导出如下:
BCM5300X>tlb 0 63
0 : 00 e0000000 64MB -> 0_40000000 C2DVG 0_44000000 C2DVG
1 : 00 c0000000 64MB -> 0_40000000 C0DVG 0_44000000 C0DVG
2 : 00 20000000 256MB -> 0_10000000 C0--- 0_20000000 C2DV-
3 : 00 00000000 256MB -> 0_10000000 C0--- 0_20000000 C0DV-
4 : 00 60000000 256MB -> 0_80000000 C2DVG 0_90000000 C2DVG
5 : 00 40000000 256MB -> 0_80000000 C0DVG 0_90000000 C0DVG
6 : 00 8000c000 4KB -> 0_00000000 C0--- 0_00000000 C0---
7 : 00 8000e000 4KB -> 0_00000000 C0--- 0_00000000 C0---
8 : 00 80010000 4KB -> 0_00000000 C0--- 0_00000000 C0---
9 : 00 80012000 4KB -> 0_00000000 C0--- 0_00000000 C0---
10 : 00 80014000 4KB -> 0_00000000 C0--- 0_00000000 C0---
11 : 00 80016000 4KB -> 0_00000000 C0--- 0_00000000 C0---
12 : 00 80018000 4KB -> 0_00000000 C0--- 0_00000000 C0---
13 : 00 8001a000 4KB -> 0_00000000 C0--- 0_00000000 C0---
14 : 00 8001c000 4KB -> 0_00000000 C0--- 0_00000000 C0---
15 : 00 8001e000 4KB -> 0_00000000 C0--- 0_00000000 C0---
16 : 00 80020000 4KB -> 0_00000000 C0--- 0_00000000 C0---
17 : 00 80022000 4KB -> 0_00000000 C0--- 0_00000000 C0---
18 : 00 80024000 4KB -> 0_00000000 C0--- 0_00000000 C0---
19 : 00 80026000 4KB -> 0_00000000 C0--- 0_00000000 C0---
20 : 00 80028000 4KB -> 0_00000000 C0--- 0_00000000 C0---
21 : 00 8002a000 4KB -> 0_00000000 C0--- 0_00000000 C0---
22 : 00 8002c000 4KB -> 0_00000000 C0--- 0_00000000 C0---
23 : 00 8002e000 4KB -> 0_00000000 C0--- 0_00000000 C0---
24 : 00 80030000 4KB -> 0_00000000 C0--- 0_00000000 C0---
25 : 00 80032000 4KB -> 0_00000000 C0--- 0_00000000 C0---
26 : 00 80034000 4KB -> 0_00000000 C0--- 0_00000000 C0---
27 : 00 80036000 4KB -> 0_00000000 C0--- 0_00000000 C0---
28 : 00 80038000 4KB -> 0_00000000 C0--- 0_00000000 C0---
29 : 00 8003a000 4KB -> 0_00000000 C0--- 0_00000000 C0---
30 : 00 8003c000 4KB -> 0_00000000 C0--- 0_00000000 C0---
31 : 00 8003e000 4KB -> 0_00000000 C0--- 0_00000000 C0---
32 : 00 80040000 4KB -> 0_00000000 C0--- 0_00000000 C0---
33 : 00 80042000 4KB -> 0_00000000 C0--- 0_00000000 C0---
34 : 00 80044000 4KB -> 0_00000000 C0--- 0_00000000 C0---
35 : 00 80046000 4KB -> 0_00000000 C0--- 0_00000000 C0---
36 : 00 80048000 4KB -> 0_00000000 C0--- 0_00000000 C0---
37 : 00 8004a000 4KB -> 0_00000000 C0--- 0_00000000 C0---
38 : 00 8004c000 4KB -> 0_00000000 C0--- 0_00000000 C0---
39 : 00 8004e000 4KB -> 0_00000000 C0--- 0_00000000 C0---
40 : 00 80050000 4KB -> 0_00000000 C0--- 0_00000000 C0---
41 : 00 80052000 4KB -> 0_00000000 C0--- 0_00000000 C0---
42 : 00 80054000 4KB -> 0_00000000 C0--- 0_00000000 C0---
43 : 00 80056000 4KB -> 0_00000000 C0--- 0_00000000 C0---
44 : 00 80058000 4KB -> 0_00000000 C0--- 0_00000000 C0---
45 : 00 8005a000 4KB -> 0_00000000 C0--- 0_00000000 C0---
46 : 00 8005c000 4KB -> 0_00000000 C0--- 0_00000000 C0---
47 : 00 8005e000 4KB -> 0_00000000 C0--- 0_00000000 C0---
48 : 00 80060000 4KB -> 0_00000000 C0--- 0_00000000 C0---
49 : 00 80062000 4KB -> 0_00000000 C0--- 0_00000000 C0---
50 : 00 80064000 4KB -> 0_00000000 C0--- 0_00000000 C0---
51 : 00 80066000 4KB -> 0_00000000 C0--- 0_00000000 C0---
52 : 00 80068000 4KB -> 0_00000000 C0--- 0_00000000 C0---
53 : 00 8006a000 4KB -> 0_00000000 C0--- 0_00000000 C0---
54 : 00 8006c000 4KB -> 0_00000000 C0--- 0_00000000 C0---
55 : 00 8006e000 4KB -> 0_00000000 C0--- 0_00000000 C0---
56 : 00 80070000 4KB -> 0_00000000 C0--- 0_00000000 C0---
57 : 00 80072000 4KB -> 0_00000000 C0--- 0_00000000 C0---
58 : 00 80074000 4KB -> 0_00000000 C0--- 0_00000000 C0---
59 : 00 80076000 4KB -> 0_00000000 C0--- 0_00000000 C0---
60 : 00 80078000 4KB -> 0_00000000 C0--- 0_00000000 C0---
61 : 00 8007a000 4KB -> 0_00000000 C0--- 0_00000000 C0---
62 : 00 8007c000 4KB -> 0_00000000 C0--- 0_00000000 C0---
63 : 00 8007e000 4KB -> 0_00000000 C0--- 0_00000000 C0---
以TLB Entry 0为例说明:
0 : 00 e0000000 64MB -> 0_40000000 C2DVG 0_44000000 C2DVG
该表项表示将虚拟地址0xe000 0000开始的128MB虚拟地址空间,映射到从0x4000 0000开始的64MB物理地址空间,以及从0x4400 0000开始的64MB物理地址空间,每个物理地址段后面的标志位(C2DVG)表示映射模式。
MIPS CPU一般采用双相相关联存储器实现TLB,即每个TLB表项包含一对相邻的虚拟地址页(虚拟地址空间上连续)对应两个单独的物理地址(物理地址空间上可以不连续)。
接下来,就是“知其所以然”的阶段了,MIPS CPU提供了几个CP0协处理器寄存器和几条TLB相关的指令,用来实现TLB的配置。以一个TLB表项为例,它由CP0的EntryHi、PageMask、EntryLo0、EntryLo1四个寄存器的不同字段组成。每次程序代码访问mapped虚拟地址时,TLB都通过这几个寄存器返回TLB表项给CPU,整个地址转换的过程是CPU逻辑电路实现的,软件不感知;软件可以做的事情是配置TLB表项,建立虚拟地址和物理地址的映射表。
、
VPN2表示虚拟页号,ASID用来唯一标识当前进程,如Linux系统中,每个进程的虚拟地址空间都是4GB大小,地址范围都是0x0000 0000 ~ 0xFFFF FFFF,相同的虚拟地址对不同的进程而言,是映射到不同的物理地址的,因此仅靠VPN2虚拟页号无法完成地址转换,还需要ASID。
PageMask用来标识每个TLB表项的虚拟地址段大小,可配置范围如下:
EntryLo0/1两个寄存器分别表示一个TLB表项的两个物理地址页,称为even page和odd page。
PFN表示物理页号;C表示Cache一致性属性,即是cached还是uncached,是write-through还是write-back;D表示是否为Dirty页,V表示是否有效;G表示是否为全局地址,如果置1,则不关心ASID(ignore ASID),如果置0,则进行地址转换时要根据ASID进行地址转换。另外,一般情况下,EntryLo0和EntryLo1的G位需要配置为相同值,因为TLB的表项中的G属性是两位相与的逻辑。
值得一提的是,kseg0和kseg1两个unmapped虚拟地址段的Cache一致性属性可以通过CP0相关寄存器配置:
综上,一个完成的TLB地址映射如下:
地址转换流程如下:
MIPS32 TLB配置
TLB配置还涉及其他CP0寄存器和TLB相关指令。
Index表示TLB表项序号,从0~63。P表示Probe Failure标志位(只读),当发生TLB miss时,则置位。
Random和Wired寄存器一起划定TLB refill重填的范围,即可以把64个TLB表项划分为静态表项和动态表项两个区域,当发生TLB refill异常时,则随机选取Wired~63之间的任意表项(Random随机数指向的表项)进行TLB表项更新。
Context寄存器是辅助TLB refill重填的,当发生TLB miss异常需要refill时,在PTE Base指向的区域取匹配的表项进行快速回填。
涉及TLB指令包括:
TLBWI - 写CP0 Index寄存器设置TLB Entry ID
TLBR - 读CP0 Index寄存器获取TLB Entry ID
TLBWR - 写CP0 Random寄存器设置TLB Entry ID
TLBP - 获取匹配的TLB Entry ID
一个完整的TLB配置过程如下:
Physical address 0x88000000 ~ 0x9FFFFFFF (384M) is mapped to virtual address 0xC8000000 ~ 0xDFFFFFFF.
– 1 Set PageMask register to configure 16M page size. The value should be 0x01ffe000. 384/(16*2) = 12 entries needed.
– 2 Set Index register to configure TLB entry index.
– 3 Set EntryHi register to configure virtual address. ASID fields can be ignored.
– 4 Set EntryLo0 and EntryLo1 registers to configure the even and the odd physical pages. The lower 6 bits, which are status bit, could be set to 0x1f. It means:
• Cacheable, write back
• Allow modify
• The entry is valid.
• Ignore ASID.
– 5 Use TLBWI instruction to write one TLB entry by Index register.
– 6 If more TLB entries needed, jump to 2