本文是对arm developer网站《Learn the architecture - AArch64 memory management Guide》的学习笔记(Documentation -- Arm Developer)
一、背景概述
本文介绍了AArch64中的内存转换,这是内存管理的关键,它解释了虚拟地址如何转换为物理地址、转换表的格式以及软件如何管理TLB。
1. 什么是内存管理
内存管理描述了如何控制对系统内存的访问。操作系统或应用程序访问内存时,硬件都会执行内存管理。内存管理是一种向应用程序动态分配内存区域的方法。
2. 为什么需要内存管理
AP旨在运行rich os(如Linux),需要支持虚拟内存系统,AP上运行的软件只能看到虚拟地址,AP将虚拟内存转换为物理地址,物理地址最终指向内存中的实际物理位置。
3. 虚拟地址和物理地址
使用虚拟地址的好处是它允许管理软件(例如OS)控制呈现给软件的内存视图,OS可以控制哪些内存是可见的、该内存可见的虚拟地址以及允许对该内存进行哪些访问。这允许操作系统对应用程序进行沙箱处理(对另一应用程序隐藏一个应用程序的资源)并提供对底层硬件的抽象。】
用虚拟地址的另一个好处是操作系统可以将多个碎片物理内存区域作为单个连续的虚拟地址空间呈现给应用程序。
虚拟地址也有利于软件开发人员,软件开发人员使用虚拟地址,无需关心物理内存。实际上,每个应用程序都可以使用自己的一组虚拟地址,这些地址将映射到物理系统中的不同位置。当操作系统在不同的应用程序之间切换时,需要重新组织物理地址和虚拟地址的映射关系。
虚拟地址通过映射转换为物理地址。虚拟地址和物理地址之间的映射存储在转换表(有时称为页表)中,如下图所示:
页表位于内存中,并由软件(通常是OS或hypervisor)管理, 页表不是静态的,并且随着软件需求的变化而刷新。
4. 地址空间
4.1 虚拟地址空间
AArch64有多个独立的虚拟地址空间,如下(Armv8-A中的地址空间):
NS.EL0:非安的EL0,在Linux中对应用户态虚拟地址空间,页表基址寄存器TTBR0_EL1
NS.EL1:非安的EL1,在Linux中对应内核态虚拟地址空间,页表基址寄存器TTBR1_EL1
NS.EL2:非安的EL2,EL2是可选的
EL3:EL3也是可选的
每个虚拟地址空间都是独立的,并且有自己的配置和页表。此外,还有Secure EL0、Secure EL1和Secure EL2的虚拟地址空间,图中没有显示。(Armv8.4-A 中添加了对 Secure EL2 的支持。)
由于存在多个虚拟地址空间,因此指定地址位于哪个地址空间非常重要。例如,NS.EL2:0x8000指的是非安全 EL2 虚拟地址空间中的地址0x8000
上图还显示了NS.EL0和NS.EL1中的虚拟地址经过了两步地址转换(2-stage),表示这些表支持虚拟化,虚拟机VM内看到的物理内存还需要经过stage-2的页表才能转换为实际的物理内存。;
Armv9-A支持上述 Armv8-A 的所有虚拟地址空间。 Armv9-A 引入了可选的领域管理扩展 (RME)。如果实现了RME ,还存在其他转换机制
- Realm EL1和EL0
- Realm EL2和EL0
- Realm EL2
在虚拟化中,我们将OS控制的一组转换称为stage-1。stage-1页表将虚拟地址转换为中间物理地址 (IPA)。在stage-1,操作系统认为 IPA 是物理地址空间。然而,虚拟机管理程序控制stage-2转换,第二组转换将 IPA 转换为物理地址。下图显示了两组翻译的工作原理:
4.2 物理地址空间
除了多个虚拟地址空间之外,AArch64 还具有多个物理地址空间(PAS):
- 非安全 PAS0
- 安全PAS
- Realm PAS(仅限 Armv9-A)
- root PAS(仅限 Armv9-A)
虚拟地址可以映射到什么物理地址空间取决于处理器当前的安全状态。下表显示了安全状态及其相应的虚拟地址映射
- 非安全状态:虚拟地址只能映射到非安全物理地址。
- 安全状态:虚拟地址可以映射到安全或非安全物理地址。
- Realm状态:虚拟地址可以映射到Realm或非安全物理地址
- root 状态:虚拟地址可以映射到任意物理地址空间。
note:这些访问权限由页表控制(//todo)
4.3 地址空间大小
AArch64是64位架构,但这并不意味着所有地址都是64位的。
4.3.1 虚拟地址空间的大小
虚拟地址以 64 位格式存储。因此,加载指令 (LDR) 和存储指令 (STR) 中的地址始终在通用寄存器中指定。然而,并非通用寄存器中的所有地址都是有效的。
下图显示了 AArch64 中虚拟地址空间的布局:
EL0/EL1虚拟地址空间有两个区域:内核空间和应用程序空间。这两个区域显示在图的左侧,内核空间位于顶部,应用程序空间(标记为"用户空间")位于地址空间的底部。内核空间和用户空间具有单独的页表,这意味着它们的映射可以保持分离。
在地址空间的底部有一个区域用于所有其他异常级别。该区域显示在图的右侧。
如果将 HCR_EL2.E2H 设置为 1,则会host os在 EL2 中运行,host os的应用程序在 EL0 中运行的配置。在这种情况下,EL2也有上部和下部区域。(即type2的虚拟化)
地址空间的每个区域的大小最多为 52 位。然而,每个区域都可以独立地缩小到更小的尺寸。 TCR_ELx 寄存器中的 TnSZ 字段控制虚拟地址空间的大小。例如,下图显示 TCR_EL1 控制 EL0/EL1 虚拟地址空间:
虚拟地址空间大小为:
virtual address size in bytes = 2^(64-TCR_ELx.TnSZ)
因此,如果TCR_ELx.T1SZ 设置为32,则EL0/EL1虚拟地址空间中内核区域的大小为2^32字节(0xFFFF_FFFF_0000_0000至0xFFFF_FFFF_FFFF_FFFF)。任何超出配置范围的地址在被访问时都会生成异常作为转换错误。这种配置的优点是我们只需要描述我们想要使用的地址空间,这节省了时间和空间。例如,假设操作系统内核需要 1GB 地址空间(30 位地址大小)作为其内核空间。如果操作系统将 T1SZ 设置为 34,则仅创建描述 1GB 的转换表条目,即 64 - 34 = 30。
所有 Armv8-A 实现都支持 48 位虚拟地址。对 52 位虚拟地址的支持是可选的,并由 ID_AA64MMFR2_EL1 报告。
4.3.2 物理地址空间的大小
物理地址的大小由实现定义,最大为 52 位。 ID_AA64MMFR0_EL1 寄存器报告处理器实现的大小。对于 Arm Cortex-A 处理器,通常为 40 位或 44 位。
在 Armv8.0-A 中,物理地址的最大大小为 48 位。在 Armv8.2-A 中这已扩展到 52 位
4.3.3 IPA地址空间的大小
如果在转换表条目中指定的输出地址大于实现的最大值,MMU)将产生地址大小错误异常。
IPA空间的大小可以与虚拟地址空间相同的方式配置。 VTCR_EL2.T0SZ 控制大小。可以配置的最大大小与处理器支持的物理地址大小相同。这意味着您无法配置比支持的物理地址空间更大的 IPA 空间。
4.4 ASID-地址空间标识符
许多现代操作系统的应用程序都在同一地址空间运行,但不同的应用程序有自己的虚拟地址空间。这意味着 VA 0x8000的地址转换取决于当前正在运行的应用程序。
理想情况下,我们希望不同应用程序的翻译能够在翻译后备缓冲区 (TLB) 中共存,以防止 TLB 在上下文切换时失效。但是处理器怎么知道要使用哪个版本的 VA 0x8000转换呢?在 AArch64 中,答案是地址空间标识符 (ASID)
对于 EL0/EL1 虚拟地址空间,可以使用转换表条目的属性字段中的 nG 位将转换标记为全局 (G) 或非全局 (nG)。例如,内核映射是全局翻译,应用程序映射是非全局翻译。全局翻译适用于当前正在运行的任意应用程序。非全局翻译仅适用于特定应用程序。
非全局映射在 TLB 中用 ASID 进行标记。在 TLB 查找中,TLB 条目中的 ASID 与当前选择的 ASID 进行比较。如果它们不匹配,则不使用 TLB 条目。下图显示了内核空间中没有 ASID 标记的全局映射和用户空间中带有 ASID 标记的非全局映射:
该图显示,多个应用程序的 TLB 条目可以在缓存中共存,ASID 决定使用哪个条目。
ASID 存储在两个 TTBRn_EL1 寄存器之一中。通常TTBR0_EL1用于用户空间。因此,更新TTBRn_EL1寄存器可以更改 ASID 及其指向的转换表。
HCR_EL2.E2H为1时(type2的虚拟化),ASID标记也可以在EL2中使用
4.5 VMID-虚拟机标识符
EL0/EL1 转换还可以使用虚拟机标识符 (VMID) 进行标记。 VMID 允许来自不同 VM 的转换在缓存中共存。这类似于 ASID 用于来自不同应用程序的翻译的方式。实际上,这意味着某些翻译将同时标记有 VMID 和 ASID,并且两者都必须与要使用的 TLB 条目匹配。
当安全状态支持虚拟化时,EL0/EL1 转换始终标记有 VMID,即使未启用stage-2转换。这意味着,如果您正在编写初始化代码并且不使用hypervisor,则在设置stage-1 MMU 之前设置已知的 VMID 值非常重要。
4.6 Common not Private
如果一个系统包括多个PE,在一个PE上使用的ASIDs和VMIDs在其他PE上是否有相同的意义?对于 Armv8.0-A,答案是它们不必表示相同的意思。不要求软件在多个PE上以相同的方式使用给定的 ASID。例如,ASID 5 可能由一个处理器上的计算器使用,并由另一处理器上的 Web 浏览器使用。这意味着由一个处理器创建的 TLB 条目不能被另一处理器使用。
实际上,软件不太可能在不同的PE上以不同的方式使用 ASID。对于软件来说,更常见的是在给定系统中的所有PE上以相同的方式使用 ASID 和 VMID。因此,Armv8.2-A 在转换表基址寄存器 (TTBR) 中引入了 Common not Private (CnP) 位。当 CnP 位被设置时,软件承诺在所有处理器上以相同的方式使用 ASID 和 VMID,这允许一个处理器创建的 TLB 条目可以由另一个处理器使用。
note:处理单元Processing Element (PE),PE是任何实现 Arm 架构的处理单元。这一点在这里很重要,因为微架构方面的原因导致处理器之间共享 TLB 会很困难。但在多线程处理器中,每个硬件线程都是一个 PE,因此更需要共享 TLB 条目。
5. MMU(内存管理单元)
内存管理单元 (MMU) 负责将软件使用的虚拟地址转换为内存系统中使用的物理地址。
MMU 包含以下内容:
- The table walk unit:从内存中读取转换表的逻辑
- Translation Lookaside Buffers (TLBs):缓存最近使用的翻译
软件分配的所有内存地址都是虚拟的。这些内存地址被传递到 MMU,MMU 检查 TLB 中是否有最近使用的缓存翻译。如果 MMU 没有找到最近缓存的翻译,表遍历单元会从内存中读取相应的表条目,如下所示:
在进行内存访问之前,必须将虚拟地址转换为物理地址(因为我们必须知道正在访问哪个物理内存位置)。这种转换需求也适用于缓存数据(cached data),因为在 Armv6 及更高版本的处理器上,数据缓存使用物理地址(物理标记的地址)存储数据。因此,在完成高速缓存查找之前必须转换地址。
5.1 Table entry(表项)
转换表的工作原理是将虚拟地址空间划分为大小相等的块,并为每个块提供表中的一个条目(表项)
表中的条目 0 提供块 0 的映射,条目 1 提供块 1 的映射,依此类推。每个条目包含相应物理内存块的地址以及访问物理地址时要使用的属性。
5.2 查表
翻译时会进行查表,当转换发生时,软件发出的虚拟地址被分成两部分,如下图所示:
上图是单级查找表,高位在图中标记为"which entry",低位被标记为"offset in block"。
5.3 多级页表
在单级查找中,虚拟地址空间被分割成大小相等的块。在实践中,使用多级页表。一级表将虚拟地址空间划分为大块,表中的每个表项指向大小相同的物理内存块,也可以指向将该块细分为更小的块的另一个表。下图是三个级别的多级页表示例。
在 Armv8-A 中,最大级别数为 4,级别编号为 0 到 3。这种多级方法允许描述较大的块和较小的块。大块和小块的特点如下:
- 大块比小块需要更少的读取级别来转换。另外,大块在 TLB 中缓存的效率更高
- 小块使软件可以对内存分配进行细粒度控制。然而,小块在 TLB 中缓存的效率较低。缓存效率较低,因为小块需要多次读取各个级别才能进行转换。
当处理器开始查表时,它不知道翻译的大小。处理器通过执行查表来计算出正在转换的块的大小。
5.4 控制地址转换表格式
在这里我们可以看到转换表条目允许的不同格式:
每个表项都是 64 位,底部两个bit位确定表项的类型。请注意,某些table entry仅在特定级别有效。table的最大级别数是四级,这就是为什么没有level3(或第四级)表的Table Descriptor的原因(即level 3不可能再指向下级表项)。同样地,第0级也没有块描述符或页描述符。因为第0级 entry覆盖了很大的虚拟地址空间区域,因此在level0允许块是没有意义的,即level0永远都会指向下级表。
5.5 转换粒度
转换粒度是可以描述的最小的内存块。AArch64 支持三种不同的粒度大小:4KB、16KB 和 64KB。处理器支持的粒度是自定义的,由 ID_AA64MMFR0_EL1 保存。所有Arm Cortex-A处理器都支持4KB和64KB。选择的粒度是最高level table中描述的大小。
一个处理器所支持的粒度大小是ID_AA64MMFR0_EL1定义的,所有Arm Cortex-A处理器都支持4KB和64KB。选择的颗粒是可以在最新级别表中描述的最小块。也可以描述更大的块。此表显示了基于所选粒度的每个级别表的不同块的尺寸:
在推出 Armv9.2-A 和 Armv8.7-A 之前,使用 52 位地址存在限制。当选择的颗粒为4KB或16KB时,最大虚拟地址区域大小为48位。同样,输出物理地址限制为 48 位。只有当使用64KB颗粒时才能使用完整的52位。
5.6 地址转换的起始级别
虚拟地址空间的粒度和大小共同控制地址转换的起始级别。上表总结了每个级别表中每个粒度的块大小(单个entry覆盖的虚拟地址范围的大小)。从块的大小,你可以算出虚拟地址的哪些位是用来索引每一级表的。
我们以4KB的粒度为例。这张图显示了用于索引4KB粒度的 不同级别表的索引。
由于是4K的页表,0:11的低12位是4K的块内偏移,4K的页表一共有512个表项(每个表项8个字节),所以需要9个bit来表示每一级页表的索引。
如图所示,将TCR_EL1.T0SZ设置为32,以地址为单位的虚拟地址空间的大小计算方式如下:64 - T0SZ = 32 bit
从之前的4KB配置的粒度图中可以看出,level0是47:39位索引的。这些在32位地址空间是不存在的。因此,地址翻译是从level1开始的。接着,假设设置T0SZ为34:64 - T0SZ = 30, 这一次,不存在level0 和level1的索引。因此,该配置的转译起始级别是level2。
当虚拟地址空间的大小减少时,您需要更少级别的表来描述它。这些示例基于使用 4KB 粒度。当使用 16KB 和 64KB 颗粒时,同样的原理也适用,但地址位会发生变化
5.7 控制地址转换的寄存器
- SCTLR_ELx
M:使能MMU
C:使能Dcache
- TTBR0_ELx and TTBR1_ELx
BADDR:转译表物理地址/中间物理地址的起点
ASID:非全局转译的标识符
- TCR_ELx
PS/IPS:PA 或 IPA 空间的大小
TnSZ:转译表能表示的地址空间的大小
TGn:粒度大小
SH/IRGN/ORGN:缓存能力共享能力的使能
TBIn:禁止搜索table中的某一行
- MAIR_ELx
Attr:statge1 的类型和缓存能力控制
5.8 禁用MMU
MMU被禁用时,所有地址都是平行映射,即输入和输出地址相同
5.9 维护TLB
Translation Lookaside Buffers (TLB) 缓存最近使用的转译项。此缓存允许后续查找重复使用转译,而无需重新读取table。
如果要更改转换表entry或控制entry的解释方式,则需要使 TLB 中受影响的entry无效。如果不使这些entry无效,则处理器可能会继续使用旧的转译。
处理器不允许导致以下任何故障的翻译缓存到 TLB:
- 翻译错误(未映射的地址)
- 地址大小错误(超出地址范围)
- 非法访问
因此,在第一次映射地址时,你不需要让TLB 无效。然而,如果要做以下操作,需要让TLB失效:
- Unmap an address:将有效的地址标记为无效
- 改变映射关系:将地址权限从只读改为读写。
- 改变table 的转译方式:修改粒度大小时,table的转译方式也会改变。
5.10 操作TLB的方式
TLBI 指令用于使 TLB 中的条目无效。该指令的语法为:
TLBI < type >< level >{IS|OS} {, < xt >}
< type >:指定无效的entries
All - 所有entries
VA - Entry matching VA and ASID in Xt
VAA - Entry matching VA in Xt, for any ASID
ASID - Any entry matching the ASID in Xt
< level >: 操作哪一个地址空间
E1 = EL0/1 virtual address space
E2 = EL2 virtual address space
E3 = EL3 virtual address space
< IS|OS >内部共享还是外部共享
< Xt >:操作那个地址或者ASID
例如,考虑一个正在更新其内核转换表中的条目的操作系统 (OS)。典型的 TLB 无效序列如下所示:
STR X1, [X5] // Write to translation table entry
DSB ISH // Barrier instructions - not covered in this guide
TLBI VAAE1IS , X0 // Invalidate VA specified by X0, in EL0/1
// virtual address space for all ASIDs
DSB ISH // Barrier instructions - not covered in this guide
ISB // Synchronize context on this processor
5.11 地址转换说明
地址转换指令(AT)可以查询特定地址的转换。地址翻译的结果,属性会写入物理寄存器PAR_EL1。
AT指令的语法具有优先级。例如,EL2指令可以查询EL0/EL1的翻译表,但是EL1指令不能查询EL2的。
6. check
Q:地址翻译中,statge和level有什么区别?
A:statge 指的是把输入地址转换为输出地址的过程。对于stage1,就是从VA到IPA的过程。stage2 从IPA到PA的过程。level指的是翻译给定阶段的table,将一个大块划分为小块的过程。
Q:物理地址最大是多少?
A:物理地址大小由IMPLEMENTATION DEFINED定义,在ARMV8.2-A后为52位。
Q:虚拟地址大小由哪些寄存器控制?
A:stage2的TCR_ELx.TnSZ, or VTCR_EL2.T0SZ
Q:翻译粒度是什么?支持的大小是多少?
A:翻译粒度指的是内存可以描述的最小的块。支持4KB, 16KB, and 64KB.
Q:TLBI ALLE3 作用是什么?
A:EL3虚拟地址中使所有的TLB entries无效。
Q:发生翻译错误时的翻译表项能缓存在TLB中吗?
A:不能
Q:当MMU关闭时,地址是如何映射的?
A:平行映射,即输入地址和输出地址相同。
Q:什么是ASID?什么时候TLB entry会包含ASID?
A:ASID是地址空间标识符,它标识了翻译与那个应用相关联。非全局映射(nG=1)在在TLB中被标记为一个ASID。