概述
本指南介绍AArch64中的内存转换,这是内存管理的关键。本文介绍了如何将虚拟地址转换为物理地址、转换表格式以及软件如何管理页表缓存 (TLB)。
这些对于底层代码(例如启动代码或驱动程序)开发人员都很有用。对于编写软件来设置或管理内存管理单元 (MMU) 的人来说尤其重要。
在本指南的最后,您可以检查您学到的知识。了解如何将虚拟地址转换为物理地址,能够命名不同的地址空间,并描述地址空间如何映射到转换阶段。您还将了解软件何时必须执行 TLB 维护,以及 TLB 维护命令的语法。
什么是内存管理
内存管理描述了如何控制对系统内存的访问。每次操作系统或应用程序访问内存时,硬件都会执行内存管理。内存管理是一种向应用程序动态分配内存区域的方法。
为什么需要内存管理
应用处理器旨在运行rich OS(例如Linux),并支持虚拟内存系统。在处理器上执行的软件只能看到虚拟地址,处理器将其转换为物理地址。这些物理地址呈现给内存系统并指向内存中的实际物理位置。
虚拟地址和物理地址
使用虚拟地址的优势在于它允许管理软件(例如操作系统(OS))控制呈现给软件的内存视图。操作系统可以控制哪些内存是可见的,内存中哪些虚拟地址是可见的,以及对内存的访问权限。这允许操作系统对应用程序进行沙箱处理(对一个应用程序隐藏另外一个应用程序的资源),并提供对底层硬件的抽象。 (优点:内存隔离、减少内存碎片化、比物理内存更大的虚拟内存空间)
使用虚拟地址的好处之一是操作系统可以将多个碎片化的物理内存区域作为单个连续的虚拟地址空间呈现给应用程序。
虚拟地址也有利于软件开发人员,他们在编写应用程序时不需要知道实际物理内存地址。使用虚拟地址,软件开发人员无需关心物理内存。应用程序只需知道地址转换是由操作系统和硬件共同协作来完成的。
实际上,每个应用程序都可以使用自己的一组虚拟地址,这些地址将映射到物理系统中的不同位置。当操作系统在不同的应用程序之间切换时,它会对映射进行重新配置。这意味着当前应用程序的虚拟地址可以映射到正确的物理位置。
虚拟地址通过映射转换为物理地址。虚拟地址和物理地址之间的映射存储在转换表(有时称为页表)中,如下图所示:
转换表位于内存中并由软件(通常是操作系统或虚拟机管理程序)管理。转换表不是静态的,可以随着软件需求的变化而更新,这会改变虚拟地址和物理地址之间的映射关系。
地址空间
AArch64中有多个独立的虚拟地址空间。下图展示了这些虚拟地址空间:
上图显示了三个虚拟地址空间:
- Non-secure EL0和Non-secure EL1
- Non-secure EL2
- EL3
每个虚拟地址空间都是独立的,并且有自己的配置和页表。我们通常将这些配置和表格称为“转换机制”。还有Secure EL0、Secure EL1和Secure EL2的虚拟地址空间,但图中没有显示。
由于存在多个虚拟地址空间,因此指明地址位于哪个地址空间非常重要。例如,NS.EL2:0x8000 指的是非安全 EL2 虚拟地址空间中的地址 0x8000。
上图还展示了来自Non-secure EL0和No-secure EL1的虚拟地址会经过两组页表。这些页表支持虚拟化,允许hypervisor虚拟化VM虚拟机看到的物理内存视图。
Armv9‑A 支持上述 Armv8‑A 的所有虚拟地址空间。 Armv9‑A 引入Realm Management Extension (RME)。当实施 RME 时,还存在其他转换机制:
- Realm EL1和EL0
- Realm EL2和EL0
- Realm EL2
在虚拟化中,我们将操作系统控制的转换设置称为第 1 阶段——Stage1。第 1 阶段将虚拟地址转换为中间物理地址 (IPAs)。在第 1 阶段,操作系统认为 IPA 是物理地址空间。然而,虚拟机管理程序控制第2组转换设置,我们称之为第 2 阶段——Stage2。第 2 组转换将 IPAs 转换为物理地址。下图显示了两组转换的工作原理:
尽管页表格式存在不同,Stage1和Stage2的转换过程通常是一样的。
物理地址
除了多个虚拟地址空间之外,AArch64 还具有多个物理地址空间(PAS):
- Non-secure PAS0
- Secure PAS
- Realm PAS(仅限Armv9-A)
- Root PAS(仅限Armv9-A)
虚拟地址可以映射到哪些物理地址空间取决于处理器当前的安全状态。下面展示了不同安全状态下,虚拟地址可以映射的目标:
- 非安全状态:虚拟地址只能映射到非安全物理地址
- 安全状态:虚拟地址可以映射到安全或者非安全物理地址
- Realm状态:虚拟地址可以映射到Realm或者非安全物理地址
- Root状态:虚拟地址可以映射到任何物理地址空间
当处于可以看到多个物理地址空间的安全状态时,转换表会控制使用哪个物理地址空间。下图展示了多个物理地址空间的映射关系。
地址大小
AArch64是64位架构,但这并不意味着所有地址都是64位的。
虚拟地址的大小
虚拟地址以 64 位格式存储。因此,加载指令 (LDR) 和存储指令 (STR) 中的地址始终在 X 寄存器中指定。然而,并非 X 寄存器中的所有地址位都是有效的。
下图显示了 AArch64 中虚拟地址空间的布局:
EL0/EL1虚拟地址空间有两个区域布局:内核空间和应用空间,如上图左侧所示,内核空间位于顶部,用户空间位于底部(标记为“用户空间”)。内核空间和用户空间具有单独的转换表,意味着它们的映射是独立开的。
对于其他异常等级,只有一个区域位于地址空间底部,如上图右侧所示。
地址空间的每个区域的大小最多为 52 位。然而,每个区域都可以单独减小。TCR_ELx 寄存器中的 TnSZ 字段控制虚拟地址空间的大小。例如,下图展示了 TCR_EL1 控制 EL0/EL1 虚拟地址空间:
虚拟地址大小表达式为:
v
i
r
t
u
a
l
a
d
d
r
e
s
s
s
i
z
e
i
n
b
y
t
e
s
=
2
64
−
T
C
R
E
L
x
.
T
n
S
Z
virtual\ address\ size\ in\ bytes = 2^{64-TCR_ELx.TnSZ}
virtual address size in bytes=264−TCRELx.TnSZ
虚拟地址也可以表达为地址位数:
N
u
m
b
e
r
o
f
a
d
d
r
e
s
s
b
i
t
s
=
64
−
T
n
S
Z
Number\ of\ address\ bits=64-TnSZ
Number of address bits=64−TnSZ
因此,如果TCR_EL1.T1SZ
设置为32,则EL0/EL1虚拟地址空间大小为2^32
字节(0xFFFF_FFFF_0000_0000
到0xFFFF_FFFF_FFFF_FFFF
),不在上述地址配置范围的访问均会生成异常即转换错误。这样我们只需要描述打算使用多大的地址空间。例如,假设操作系统内核需要1GB地址空间(30位地址大小),那么只需设置T1SZ为34(64-34=30),就创建了1GB的转换表。
物理地址的大小
物理地址的大小由实现定义,最大为 52 位。 ID_AA64MMFR0_EL1 寄存器报告处理器实现的大小。对于Arm Cortex‑A 处理器,通常为 40 位或 44 位。
中间物理地址的大小
如果转换页表项中指定的输出地址大于实现的最大值,则内存管理单元MMU将生成异常,即地址大小错误。
IPA空间的大小配置方式与虚拟地址空间相同。 VTCR_EL2.T0SZ 控制大小,可以配置的最大大小与处理器支持的物理地址大小相同。这意味着IPA 空间大小不能超过支持的物理地址空间。
地址空间标识——用所属进程标记转换
许多现代操作系统的应用程序似乎都在同一地址区域运行,这就是我们所描述的用户空间。实际上,不同的应用程序需要不同的映射(因为每个应用程序可以看到全部的地址空间,都有自己的独立的页表)。例如,VA 0x8000 的地址转换取决于当前正在运行的应用程序。
理想情况下,我们希望不同应用程序的转换映射操作能够在页表缓存 (TLB) 中共存,以防止 TLB 在上下文切换时失效。但是处理器如何知道要使用哪个版本的 VA 0x8000 转换呢?在 Armv8‑A 中,答案是地址空间标识符(ASIDs)。
对于 EL0/EL1 虚拟地址空间,可以使用页表项属性字段中的 nG 位将转换标记为全局 (G) 或非全局 (nG)。例如,内核映射是全局转换,应用程映射是非全局转换。全局转换适用于当前正在运行的应用程序。非全局转换仅适用于特定应用程序。
非全局转换在 TLB 中用 ASID 进行标记。在查找 一个 TLB 时,将页表项中的 ASID 与当前选择的 ASID 进行比较。如果它们不匹配,则不使用该 TLB 项。下图显示了内核空间中没有 ASID 标记的全局转换和用户空间中带有 ASID 标记的非全局映射:
上图说明多个应用程序的 TLB 项可以在缓存中共存,ASID 决定使用哪个页表项。
虚拟机标识符——使用所属VM标记转换
EL0/EL1 转换还可以使用虚拟机标识符 (VMID) 进行标记。 VMID 允许来自不同 VM 的转换在缓存中共存。这类似于 ASID 用于来自不同应用程序的转换。实际上,这意味着某些转换将同时标记有 VMID 和 ASID,并且两者都必须与要使用的 TLB 条目匹配。
Common not Private
如果系统包含多个处理器,一个处理器上使用的 ASID 和 VMID 在其他处理器上是否具有相同的含义?
对于 Armv8.0‑A 来说,答案是它们不必表示相同的意思。不要求软件在多个处理器上以相同的方式使用给定的 ASID。例如,ASID 5 可能由一个处理器上的计算器使用,并由另一处理器上的 Web 浏览器使用。这意味着由一个处理器创建的 TLB 条目不能被另一处理器使用。
实际上,软件不太可能在不同的处理器上以不同的方式使用 ASID。对于软件来说,更常见的是在给定系统中的所有处理器上以相同的方式使用 ASID 和 VMID。因此,Armv8.2‑A在转换表基址寄存器(TTBR)中引入了Common not Private (CnP)位。当 CnP 位被设置时,软件承诺在所有处理器上以相同的方式使用 ASID 和 VMID,这允许一个处理器创建的 TLB 条目可以由另一个处理器使用。
内存管理单元(MMU)
内存管理单元 (MMU) 负责将软件使用的虚拟地址转换为内存系统中使用的物理地址。
MMU包括如下内容:
- table walk unit:从内存中读取转换表(查表,完成虚拟地址到物理地址的转换)
- Translation Lookaside Buffers(TLBs):缓存最近使用的转换(类似cache,将转换映射放入缓存,提高映射效率)
软件分配的所有内存地址都是虚拟的。这些内存地址被传递到 MMU,MMU 检查 TLB 中是否有最近使用的缓存转换。如果 MMU 没有找到最近缓存的转换,table walk unit会从内存中读取相应的表条目,如下所示:
在访问内存之前,虚拟地址必须转换为物理地址(因为我们必须知道正在访问哪个物理内存)。这种转换需求也适用于缓存数据,因为Armv6及之后处理器,数据缓存了物理地址上存储的数据。因此,在完成缓存查找之前必须转换该虚拟地址。
表项
转换表的工作原理是将虚拟地址空间划分为大小相等的块,并在表中为每个块提供一个条目。
表中的条目 0 提供块 0 的映射,条目 1 提供块 1 的映射,依此类推。每个条目包含相应物理内存块的地址以及访问物理地址时需要使用的属性。
查表
当进行地址转换时,会进行查表,软件发出的虚拟地址分为两部分,如下图所示。
上图展示了单个页表查找过程。
图中标记为“Which entry”的高位,告诉你需要查看哪个页表项,用于索引转换表。这个页表项存放虚拟地址对应的物理地址的块号。
图中标记为“Offset in block”位,是物理块内的偏移量,直接寻址使用,不会因转换而改变。
多级转换
在单个页表查找中,虚拟地址划分为大小相等的块。但在实际中,经常使用多级页表(因为每个进程都需要一个页表,多级页表主要是用来减少页表占用的内存空间)。
一级页表将虚拟地址划分为比较大的块。该表中的每一项可以指向相同大小的物理内存块,也可以指向另外一个表,其将该内存块划分为更小的块,我们将这种类型的表称为“多级表”。下面我们可以看到一个具有三级页表的示例:
在 Armv8‑A 中,最大支持到4级页表,编号为从 0 到 3。这种多级页表允许同时描述较大的块和较小的块,这些块的特点如下:
- 较大的块比较小的块需要的读取转换更少。另外大块通常缓存在TLBs中,这样比较高效。
- 小块使软件可以对内存分配进行精细控制。然而,小块在 TLB 中缓存的效率较低,因为小块需要多次读取各个页表才能进行转换。
为了管理这种权衡,操作系统必须在大块映射的效率与小块映射的灵活性之间做出平衡,从而获得最佳性能。
转换表格式
下图我们可以看到转换表允许的不同格式。
每个表项都是64位,最低2两位决定了表项的类型。
请注意,某些表项仅在特定等级下有效。表的最大级别数为四,这就是为什么没有第三级(或第四级)表描述符的原因。类似地,没有0 级块描述符或页描述符。因为 0 级表项覆盖了很大的虚拟地址空间区域,所以标记为块是没有意义的。
转换粒度
转换粒度是可描述的最小内存块。仅可描述比转换粒度大的块,是粒度的倍数。
AArch64 支持三种转换粒度大小:4KB、16KB 和 64KB。
处理器支持的粒度大小是由实现定义的,并由ID_AA64MMFR0_EL1指明。所有 Arm Cortex‑A 处理器都支持 4KB 和 64KB。粒度大小是最新页表等级中可以描述的最小块,更大的块也可以描述。下表是根据不同粒度大小,每一级页表可以描述的块大小。
Level | 4KB | 4KB | 16KB | 16KB | 64KB | 64KB |
---|---|---|---|---|---|---|
Size | Bits | Size | Bits | Size | Bits | |
0 | 512GB | 47:39 | 128TB | 47 | - | - |
1 | 4GB | 38:30 | 64GB | 46:36 | 4TB | 51:42 |
2 | 2MB | 29:21 | 32MB | 35:25 | 512MB | 41:29 |
3 | 4KB | 20:12 | 16KB | 24:14 | 64KB | 28:16 |
在推出 Armv9.2‑A 和 Armv8.7‑A 之前,使用 52 位有限制。当选择的粒度为4KB或16KB时,最大虚拟地址区域大小为48位。同样地,输出物理地址也限制为48位。仅当使用64KB粒度大小时,可以使用完整的 52 位大小地址空间。
地址转换起始等级
虚拟地址空间大小和粒度共同控制地址转换的起始等级(即哪个等级开始进行虚拟地址到物理地址的转换操作)。
上表总结了每级页表中每种粒度大小对应的块大小,通过块大小,你可以计算出虚拟地址的哪些位用于索引哪个页表。
以4KB粒度大小为例,下图展示了4KB粒度下哪些位用于索引哪个表。
想象一下,你将虚拟地址空间即TCR_ELx.T0SZ设置为32位,则虚拟地址空间的大小计算如下:
64 - T0SZ = 32位地址空间(bits 31:0)
如果我们再次查看前面的 4KB 粒度图,等级 0 由位 47:39 索引。对于 32 位地址空间,您没有这些位。因此,您的配置的起始转换等级为级别 1。
再想象一下,假设你将T0SZ设置为34:
64 - T0SZ = 30位地址空间(bits 29:0)
这次,您没有任何位用于索引 0 级表或 1 级表,因此您的配置的起始转换等级为 2 级。
综上,当虚拟地址空间大小减小时,你需要描述的页表也越少。
这些示例基于使用 4KB 粒度。同样适用于 16KB 和 64KB 粒度,但地址位发生变化。
控制地址转换的寄存器
地址转换由一些系统寄存器控制:
- SCTLR_ELx
- M:启用内存管理单元(MMU)
- C:启用数据缓存和统一缓存
- EE:转换表遍历的大小端
- TTBR0_ELx和TTBR1_ELx
- BADDR:转换表起始的物理地址PA(或者对于EL0/EL1,中间物理地址IPA)
- ASID:非全局转换的地址空间标识
- TCR_ELx
- PS/IPS:PA或IPA空间大小,最大输出地址大小
- TnSZ:表所覆盖的地址空间大小
- TGn:粒度大小
- SH/IRGN/ORGN:MMU遍历使用可缓存性和共享性
- TBIn:禁用某个表的遍历
- MRIR_ELx
- Attr:控制第 1 阶段表的类型和可缓存性
禁用MMU
当 MMU 在转换阶段被禁用时,所有地址都是直接映射的,即意味着输入和输出地址相同。
页表缓存维护
TLBs缓存了最近使用的转换,其允许后续查表复用转换,而无需重新读取表。
如果更改一个转换页表项,或者控制转换,你需要将这个页表项变为无效状态。如果你不这样做,处理器可能还是使用的旧的转换。
处理器不允许缓存会导致错误的转换:
- 转换错误(未映射的地址)
- 地址大小错误(地址超出范围)
- 访问标志错误
因此,首次地址映射时,你不需要发出TLB无效命令。但是,如果您想要执行以下任一操作,则需要发出 TLB 无效命令:
- 取消地址映射:获取先前有效或映射的地址,并将其标记为错误。
- 更改地址映射:更改输出地址或者属性。例如,将地址从只读权限更改为读写权限。
- 更改表转换方式:这种情况不太常见。但是,例如如果粒度大小发生变化,页表转换也需要变化。因此需要发出TLB无效命令。
TLB操作格式
TLBI指令用于将TLB中的表项变为无效。该指令的语法如下:
TLBI < type >< level >{IS|OS} {, < xt >}
其中:
-
< type >:哪些项无效
- All:所有项
- VA:与Xt中的VA和ASID相匹配的项
- VAA:与Xt中的VA匹配的项,适用于任何ASID
- ASID:与Xt中的ASID匹配的任何项
- ……
-
< level >:要操作的地址空间
- E1 = EL0/1虚拟地址空间
- E2 = EL2虚拟地址空间
- E3 = EL3虚拟地址空间
-
< IS|OS >:操作是内部共享(IS)还是外部共享(OS)
- 当 IS 被添加到操作中时,它会广播到内部共享域中的其他core
- 当 OS 被添加到操作中时,它会广播到外部共享域中的其他core(在 Armv8.4‑A 中添加)。
-
< Xt >:要操作的地址或ASID
- 仅用于地址或ASID进行的操作
例如,当操作系统考虑更新内核转换表,典型的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
地址转换指令
地址转换(AT)指令允许软件查询某一地址的转换。转换结果(包括属性)将写入物理地址寄存器PAR_EL1中。
AT 指令的语法允许您指定要使用的转换机制。例如,EL2可以查询EL0/EL1转换机制。但是,EL1 不能使用 AT 指令来查询 EL2 转换机制,因为这违反了特权原则。
如果请求的转换会导致错误,但是这不会产生异常。相反,会将生成的故障类型记录在 PAR_EL1 中。
检查你学到的知识
-
地址转换中的阶段和等级有什么区别?
阶段是将输入地址转换为输出地址的过程。对于第 1 阶段,这是从 VA 到 IPA 的过程,对于第 2 阶段,这是从 IPA 到 PA 的过程。
等级与给定转换阶段的表相关,这也决定如何将较大的块细分为较小的块。
-
物理地址的最大大小是多少?
物理地址空间的最大大小是由实现定义的,最多52位(从Armv8.2-A以后)。
-
哪个寄存器字段控制虚拟地址空间的大小?
TCR_ELx.TnSZ,或者阶段2的VTCR_EL2.T0SZ。
-
什么是转换粒度,支持的大小是多少?
它是可以描述的最小内存块,支持的大小有4KB,16KB,64KB。
-
指令TLBI ALLE3 有什么作用?
它会使 EL3 虚拟地址空间的所有 TLB 条目无效。
-
导致转换错误的表项是否可以缓存在 TLB 中?
不可以,它不能存储在 TLB 中。
-
当 MMU 禁用时,地址如何映射?
地址是恒等映射,即输入和输出地址是相同的。
-
什么是 ASID?TLB 条目何时包含 ASID?
ASID 是地址空间标识符,它标识转换与哪个应用程序(进程)关联。非全局映射 (nG=1) 在 TLB 中用 ASID 进行标记。
相关信息
以下是与本文相关的一些资源:
- 虚拟化:对于Armv8‑A 和Armv9‑A,此主题在虚拟化中介绍。
- 有用的培训链接:
下一步
本指南介绍了内存管理的概念,解释了虚拟地址到物理地址的映射。了解此信息您将能创建自己的裸机页表,并了解操作系统在分配内存时执行的过程。
Memory Model guide中介绍了内存类型和属性,例如访问权限。
除了处理器中的内存管理单元 (MMU) 之外,用于非处理器主设备的 MMU 也越来越普遍,例如直接内存访问(DMA) 引擎。这些在 Arm 系统中称为 SMMU(系统 MMU),在其他地方称为 IOMMU。
要继续了解 Armv8‑A 架构,请参阅Arm系列文章series of guides。