Linux内存管理(二):ARMv8 地址转换

本文基于kernel 5.8.0, 平台是arm64

物理地址和虚拟地址

关于内存管理的地址,有线性地址,逻辑地址,物理地址和虚拟地址这几个概念, 咋一看很容易混淆,让人云里雾里。

线性地址和逻辑地址都是x86的概念,主要用于内存分段机制,而在arm平台上,没有分段机制,线性地址/逻辑地址和虚拟地址都是同一个概念,都统称为虚拟地址。
物理地址:Physical addresses are those used by the actual hardware system。是硬件真实使用的地址。
虚拟地址: Virtual addresses are those used by you, and the compiler and linker, when placing code in
memory。 是我们程序使用的内存地址。

为什么要有一个虚拟地址的概念呢?
假设我们的CPU直接操作的是物理地址,那就无法同时运行两个程序了,程序A改写了某个地址,会影响到使用该地址的程序B,这是行不通的。
操作系统解决这个问题的办法就是有一个虚拟内存的概念,每一个进程都有一个独立的地址空间,但这就要求进程不能去访问物理地址。
Linux内存管理(二):ARMv8 地址转换_第1张图片

MMU

访问硬件内存需要的是物理地址,操作系统需要有一种机制,将进程持有的虚拟地址转换成物理地址。
这就是CPU中的MMU的作用。

MMU全称是memory management unit。 在ARM的体系结构中,MMU可以使用内存中保存的页表来进行虚拟地址到物理地址的转换,此外MMU还可以控制cache的策略,内存的属性以及访问权限的设置。

此外MMU中还有TLB和table walk unit的概念。
TLB(Translation Lookaside Buffer)是一块高速缓存,缓存最近查找过的VA对应的页表项。如果TLB中有需要查表的VA,就不用Translation Table Walk了,Translation Table Walk较慢,要从内存上读表。

Linux内存管理(二):ARMv8 地址转换_第2张图片
如上图所示,当一个虚拟地址来临的时候,MMU先是查找TLBs,看是否有cached translation,如果没有,那么table walk unit就去memory或cache中读取相应的translation tables.

物理地址到虚拟地址的转换

Linux内存管理(二):ARMv8 地址转换_第3张图片
上文说过,linux虚拟地址空间分为用户地址空间和内核地址空间。页表基地址(base translation address)也被分为2个部分: ttbr0 和 ttbr1.
Kernel Space的页表基地址存放在TTBR1_EL1寄存器中,User Space页表基地址存放在TTBR0_EL0寄存器中。
将虚拟地址(VA)的高位都设置为0时,选择TTBR0指向的转换表。当VA的高位都设置为1时,选择TTBR1。

64bit的虚拟地址并不是所有bit都被用上的。目前有效的VA_BITS的配置是:36, 39, 42, 47

假设我现在使用64K的页和42bit的虚拟地址空间, 使用三级页表。

  1. 如果VA[63:42] = 1, 那么就会使用ttbr1的地址作为一级页表的基地址;如果VA[63:42] = 0, 那么就会使用ttbr0的地址作为一级页表的基地址,那么就会使用ttbr0的地址作为一级页表的基地址;
  2. VA[41:29]放置Level 1页表中的索引,从而找到对应的描述符地址并获取描述符内容,根据描述符中的内容获取Level 2页表基地址
  3. VA[28:16]放置Level 2页表中的索引,从而找到对应的描述符地址并获取描述符内容,根据描述符中的内容获取物理地址的高36位,以4K地址对齐;
  4. VA[15: 0]放置的是物理地址的偏移,结合获取的物理地址高位,最终得到物理地址。

Linux内存管理(二):ARMv8 地址转换_第4张图片

页表描述符

描述完地址转换的过程, 再详细描述下Table Descriptor, 也就是页表中存放的内容
一个entry是64bit, 以一个4K page size的页来说, 那么就有512个entry。 每一个entry对应一个descriptor.

页表的格式

Linux内存管理(二):ARMv8 地址转换_第5张图片

类型有四种,bit[1:0]来决定,其中bit[0]指示该descriptor是否有效,如果为0,表示是一个invalid entry;
bit[1]表示该descriptor的类型:

0 Block entry defines the memory properties for the access. 定义了一个连续物理内存块的基地址和属性。
1 Table entry points to the next-level translation table,定义了下一级 translation table 的基地址

其中Level 0中的Table Descriptor只能输出Level 1页表的地址,Level 3中的Table Descriptor只能输出block addresses.
Linux内存管理(二):ARMv8 地址转换_第6张图片
如上图所示,level1/level2 table中有block和table entry, level3中的table entry指向具体的memory page.

grandule size

有三种不同的粒度: 4KB / 16KB / 64KB, 三种不同的粒度会影响所需translation tables的数量和大小.

以4KB grandule size为例, 硬件可以使用四级查表。每一级的地址转换有9bit(即512个entry), VA[47:39]是level0页表的索引,每一个entry对应一个512GB的range, 并指向L1 table; L1 table中也有512个entry, 每一个entry指向L1 table或1GB block; l2 table的entry指向L3的table 或2M的block; L3 table的entry指向4KB的block的基地址。
Linux内存管理(二):ARMv8 地址转换_第7张图片

memory attributes

Table descriptor和table entry, block entry中都有attribute的概念

table descriptor(only for stage1)
Linux内存管理(二):ARMv8 地址转换_第8张图片

NSTable NS表示Non-Secure,该table描述符指向的下一级的转换表是否存储在secure memory之中。为0表示存储在Secutre PA中,否则表示存储在Non-secure PA中。当在non-secure状态下进行地址映射时,该标志别忽略
APTable 下一级level lookup的access permission
UXNTable Unprivileged Execute Never, 下一级level lookup的是否有内存的执行权限
PXNTable 下一级level lookup的是否有内存的特权执行权限(EL1)

table/block entry:
Linux内存管理(二):ARMv8 地址转换_第9张图片

UXN Excute-never, 决定了descriptor指向的region是否excuteable
PXN privileged excute-never, 决定了descriptor指向的region在EL1是否excuteable
Contiguous 该表项是否为连续表项中的一项。即转换表在该表项前后是连续的,没有空洞。这样,这些连续的表项便有可能一次性加载到cache中(比如由一个TLB entry缓存)
DBM dirty bit modifier, dirty bit指示内存页有没有被修改
nG not global, 指明当前的entry是global(nG=0,所有process都可以访问)还是non-global(nG=1,only本process允许访问)。如果是global类型,则TLB中不会tag ASID;如果是non-global类型,则TLB会tag上ASID,且MMU在TLB中查询时需要判断这个ASID和当前进程的ASID是否一致,只有一致才证明这条entry当前process有权限访问。
AF access flag,当该标志为0,标明对应的内存区域(一个block或者一个page)是第一次访问。
SH shareable attribute
AP access permission, 设置数据访问的权限,可以设置为4中, 只读,读写,非EL0的只读, 非EL0的读写
NS security bit, 当从Secure状态访问内存时,该标志指示转换后的地址在Secure区域还是在Non-Secure区域
Indx index into the MAIR_ELn, 指向内存区域的类型以及可缓存性(见下文)

memory region attributes

arm系统中,memory都被分为各个region,每个region都有自己的privilege level,memory type,cache policy;
这部分的管理是由MMU来实现的,各个region都对应其中的一个或几个block、page。

MAIR_ELx定义了memory region的属性:
有2种memory types: Normal memory和Device memory

  • Normal memory,对该种类型的内存可以进行常见的读写操作或只读操作,系统中大部分内存都是这种类型;

  • Device memory,通常都是内存映射外设会采用这种内存类型。
    device类型的内存还有三种属性:
    (1) G, Gather, Determines whether multiple accesses can be merged into a single bus transaction。多个memory access merge为 合并成一个bus-transaction
    (2) R, reorder, Determines whether accesses to the same device can be reordered。device memory的连续的transation可以乱序
    (3) E, Early Write Ack,Indicates to the memory system whether a buffer can send acknowledgements。 write不写入device,通过中间buffer之后就 return ack。
    对应四种类型:
    (1)Device-nGnRnE,不允许gather, reorder, ealry
    (2)Device-nGnRE,允许early
    (3)Device-nGRE,允许reorder,early
    (4)Device-GRE, 允许gather,reorder,early

除了类型外,ARM还定义了其他内存特性

Shareability(可共享性) 指当前内存页表项的数据是否可以同步到其它CPU上,多核CPU调用带有该属性页表项的数据,一旦某个CPU修改了数据,那么系统将自动更新到其它CPU的数据拷贝,实现内存数据一致性.

对于Normal类型的内存,有3种情况:
• Inner Shareable, 该内存位置可以被InnerShareability domain 中的所有处理器访问,并且硬件保证该位置在这些处理器间的数据一致性,InnerShareability domain中的处理器一般被同一个虚拟机监视器或操作系统控制

• Outer Shareable, 该内存位置可以被OuterShareability domain中的所有处理器访问,并且硬件保证该位置在这些处理器间的数据一致性,InnerShareability domain 是OuterShareability domain的一个子集

• Non-shareable, 该内存位置一般只能被唯一处理器访问,如果还有其他处理器能访问该位置,可能需要软件用缓存一致性指令来保证缓存一致性
Cacheability(可缓存性) 指当前内存页表项对于的数据是否可以加载到Cache当中.

对于Normal类型的内存,有3种情况:
• Write-Through Cacheable, 同时更新缓存和内存.

• Write-Back Cacheable, 先更新缓存,替换时将修改过的块写回内存.

• Non-cacheable, 不使用缓存,直接更新内存

Normal类型和Device类型的内存有不同的内存属性
Linux内存管理(二):ARMv8 地址转换_第10张图片

参考资料

ARMv8 address translation
DDI0487E ARMv8 Spec
armv8 memory system
【华为云技术分享】ARMv8-A存储模型概述(2)

你可能感兴趣的:(内存管理,地址转换,TTBR,页表,arm64,MMU)