一、ARM架构的地址映射
1.1、流程
- (1)CPU发出虚拟地址VA
- (2)经过预处理VA转变成MAV(硬件自动完成)
如果 VA 小于 32MB,则 MVA = (PID << 25) | VA
否则,MVA = VA - (3)MVA作为MMU的输入,经过地址映射后,MMU输出物理地址PA
1.2、预习知识
- (1)TTB:Translation table base,一级页表基址,创建好页表后需要写入CP15的寄存器C2(称为页表基址寄存器)。在一级页表中,存放着4096个描述符,这些描述符按类型可分为粗页表描述符、细页表描述符、段描述符、无效描述符。
- (2)粗页表:Coarse page table,属于二级页表,该表存放了256个描述符,这256个描述符总共可寻址1MB范围,也即每个描述符寻找范围4KB,按类型可分为大页描述符、小页描述符、极小页描述符。
- (3)细页表:Fine page table,属于二级页表,该表存放了1024个描述符,也即每个描述符寻找范围1KB。这些描述符按类型可分为大页描述符、小页描述符、极小页描述符。
- (4)段:Section,该段内存放了1MB的数据,不是页表。
- (5)描述符:占32位,各个位代表不同含义
- (6)粗页表描述符
[31:10]称为粗页表基址。粗页表描述符低10位填充0,则每个描述符可寻址1KB范围的物理地址,这1KB范围内,存放的是二级页表的256个描述符。 - (7)细页表描述符
[31:12]称为细页表基址。细页表描述符低12位填充0,则每个描述符可寻址4KB范围,这4KB范围内,存放的是二级页表的1024个描述符。 - (8)段描述符
[31:20]称为段基址。段描述符低20位填充0,则每个描述符可寻址1MB范围的物理地址的物理地址,这1MB范围内,存放的是数据,不是页表。 - (9)大页描述符
[31:16]称为大页基址。大页描述符低16位填充0,则每个描述符可寻址64KB范围的物理地址,这64KB范围内,存放的是数据。若在粗页表中出现了大页描述符,则连续16个描述符都指向同一个大页。其他情况同理。 - (10)小页描述符
[31:12]称为小页基址。小页描述符低12位填充0,则每个描述符可寻址4KB范围的物理地址,这4KB范围内,存放的是数据。 - (11)极小页描述符
[31:10]称为极小页基址。极小页描述符低12位填充0,则每个描述符可寻址1KB范围的物理地址,这1KB范围内,存放的是数据。
二、ARM架构上的Linux系统的地址映射
Linux 从 2.6.11 版本开始引入四级页表:
- 页全局目录PGD
- 页上级目录PUD
- 页中间目录PMD
- 页表PT
arm架构实际只有两级的页表结构。两级页表已经足够了,这时通过将页上级目录和页中间目录位全部置0,从而保持了两级页表的格式。
Linux中的页大小一般是4KB,那就对应了ARM架构中的小页。
| ARM架构 | Linux |
|---|---|
| 粗页表描述符 | 页全局目录PGD |
| 小页表描述符 | 页表PT |
但是,在linux内核启动的初始化阶段,临时建立页表(initial page tables)以供linux内核初始化提供执行环境,这时使用的只有一级页表,即RAM中,以TTB起始的16KB空间中,存放了4096个段描述符,每个段描述符直接映射了1MB的空间,不存在二级页表。具体的可以参考arch/arm/kernel/head.S中的__create_page_tables函数。
在内核启动的后期,paging_init—>map_lowmem函数中会重新建立页表,覆盖掉临时页表(initial page tables)。
该函数为物理内存从0地址到低端内存(lowmem_limit)建立一个一一映射的映射表。所谓的一一映射就是物理地址和虚拟地址就差一个固定的偏移量,该偏移量一般就是0xc0000000(3G)
说到这里引入一个重要的概念,就是与低端内存相对的高端内存,什么是高端内存?为什么需要高端内存?
为了解析这个问题,我们假设我们使用的物理内存有2GB大小,另外由于我们内核空间的地址范围是从3G-4G的空间,并且前面也说到了,linux内核的低端内存空间都是一一映射的,如果不引入高端内存这个概念,全部都使用一一映射的方式,那内核只能访问到1GB的物理内存,但实际上,我们是需要内核在内核空间能够访问所有的4GB的内存大小的,那怎么做到呢?
方法就是我们不让3G-4G的空间都使用一一映射,而是将物理地址的[0x00,fix_addr](fix_addr<1GB)映射到内核空间虚拟地址[0x00+3G,fix_addr+3G],然后将[fix_addr+3G,4G]这段空间保留下来用于动态映射,这样我们可以通过这段虚拟地址来访问从fix_addr到4GB的物理内存空间。怎么做到的呢?
譬如我们想要访问物理地址[fix_addr,4GB]这段区间中的任何一段,我就用宝贵的内核虚拟地址[fix_addr+3G,4G]的一段去映射他,建立好mmu硬件使用的页表,访问完后,将映射清除,将内核的这段虚拟地址释放,以供下次访问其他的物理内存使用。这样就可以达到访问所有4GB的物理内存的目的。