ARMv8内存地址翻译

banner

一、Armv8-A地址翻译

Armv8-A使用一个虚拟内存系统,其中代码所使用的地址(虚拟地址)被转换为内存系统所使用的物理地址。此转换由被称为内存管理单元(MMU)的处理器的一部分执行。Arm体系结构中的mmu使用存储在内存中的转换表将虚拟地址转换为物理地址。MMU将在必要时自动读取翻译表,此过程被称为表行走。
MMU的一个重要功能是使系统能够运行多个任务,作为在自己的私有虚拟内存空间中的独立程序运行。它们不需要了解系统的物理内存映射,即硬件所使用的地址,或了解可能同时执行的其他程序。
您可以为每个程序使用相同的虚拟内存地址空间。您还可以使用连续的虚拟内存映射,即使物理内存是碎片化的。此虚拟地址空间与系统中内存的实际物理映射分开。您可以编写、编译和链接应用程序,以在虚拟内存空间中运行。单个系统中的不同处理器和设备可能具有不同的虚拟和物理地址映射。特权软件,如操作系统,通过编程使MMU在这两个内存视图之间进行转换,如下图所示。
1731557224408.png
要做到这一点,虚拟内存系统中的硬件必须提供地址转换,这是由处理器发出的虚拟地址到主内存中的物理地址的转换。

  • 虚拟地址是您在内存中放置代码时以及编译器和链接器所使用的地址。
  • 物理地址是指实际的硬件系统所使用的地址

MMU使用虚拟地址中最重要的位来索引转换表中的条目,并确定正在访问哪个块。MMU将代码和数据的虚拟地址转换为实际系统中的物理地址。该转换将在硬件中自动执行,并且对应用程序是透明的。除了地址转换之外,MMU还可以控制每个内存区域的内存访问权限、内存顺序和缓存策略。
使用转换表进行的地址转换如下图所示
1731557302264.png

二、内核空间与用户空间的虚拟地址分离

操作系统通常有多个应用程序或任务并发运行。每一个任务都有自己独特的转换表集,作为在一个任务和另一个任务之间进行上下文切换过程的一部分,内核从一个任务切换到另一个任务。然而,大部分内存系统只由内核使用,并且固定了虚拟到物理地址映射,其中转换表条目很少更改。Armv8-A体系结构提供了几个特性来有效地处理这一需求。
表基地址在转换表基寄存器(TTBR0_EL1)和(TTBR1_EL1)中指定。

  • 当虚拟地址(VA)的上位都设置为0时,将选择TTBR0指向的转换表。
  • 当VA的上位都设置为1时,选择TTBR1。您可以启用VA标记来排除检查中的前8位。
    下图显示了一个示例,说明如何将内核空间映射到内存中最重要的区域,而与每个应用程序关联的虚拟地址空间可以映射到内存中最不重要的区域。但是,这两个空间都可以映射到一个更小的物理地址空间,如下图所示:
    1731557437532.png

三、虚拟地址转换成物理地址

当处理器发出虚拟地址进行指令获取或数据访问时,MMU硬件将虚拟地址转换为相应的物理地址。对于n位地址空间中的虚拟地址,前64n位VA[63:n]必须全部为0或1,否则该地址会引发故障。
然后使用最小显著位来给出选定部分内的偏移量,以便MMU将来自块表条目的物理地址位与来自原始地址的最小显著位组合起来,以生成最终地址。
在一个只涉及一个级别的简单地址转换中进行查找,并假设我们使用了一个64KB的颗粒和一个42位的虚拟地址空间。MMU翻译一个虚拟地址如下:

  • 如果VA[63:42] = 1,则TTBR1用于第一个转换表的基本地址。当VA[63:42] = 0时,TTBR0用于第一个转换表的基本地址。
  • 翻译表包含8192个×64位翻译表条目,并使用VA[41:29]进行索引。
  • MMU将从表中读取相关的第2级翻译表条目。MMU会检查转换表条目的有效性,以及是否允许所请求的内存访问。假设它是有效的,则允许进行内存访问。
  • 在下图中,翻译表条目引用了一个512MB的页面(它是一个块描述符)。
    1731562530694.png
  • 因为我们有一个512MB的页面,所以VA的位被取[28:0]来形成PA。

将返回完整的PA[47:0],以及来自翻译表条目中的附加信息
一级表条目还可以指向二级转换表,而不是只使用这个一级转换表。通过这种方式,一个操作系统可以进一步将大量的虚拟内存划分为更小的页面。对于第二级表,第一级描述符包含第二级转换表的物理基本地址。与处理器请求的虚拟地址对应的物理地址可以在第二级描述符中找到。
下图显示了一个64KB颗粒的翻译例子,对于一个正常的64KB页面。它描述了一种有两个级别的查找的情况。同样,这假设一个64KB的颗粒和42位虚拟地址空间。
1731562683539.png
每个二级表都与一个或多个一级项相关联。您可以有多个指向同一二级表的一级描述符,这意味着您可以将多个虚拟位置别名到相同的物理地址。

  • 如果VA[63:42] = 1,则TTBR1用于第一个转换表的基本地址。当VA[63:42] = 0时,TTBR0用于第一个转换表的基本地址。
  • 翻译表包含8192个64位翻译表条目,并通过VA[41:29]进行索引。
  • MMU将从表中读取相关的第2级翻译表条目。MMU检查2级转换表条目的有效性以及是否允许请求的内存访问。假设它是有效的,则允许进行内存访问。
  • 在图中,2级翻译表条目表示第3级翻译表的地址(它是一个表描述符)。
  • 位[47:16]是从第2级翻译表条目中获取的,并构成了第3级翻译表的基本地址。
  • 位[28:16]位用于索引3级翻译表条目。MMU将从表中读取相关的第3级翻译表条目。
  • MMU将检查第3级转换表条目的有效性,以及是否允许所请求的内存访问。假设它有效,允许内存访问。
  • 在图中,第3级翻译表条目指的是一个64KB的页面(它是一个页面描述符)。
  • 位[47:16]取自第3级翻译表条目,用于形成PA[47:16]。
  • 因为有一个64KB的页面,所以我们用VA[15:0]来形成PA[15:0]。
  • 将返回完整的PA[47:0],以及从翻译表条目中获得的其他信息。

3.1 安全与非安全地址

Arm架构定义了两个物理地址空间。安全地址空间和非安全地址空间。在理论上,安全的和非安全的物理地址空间是相互独立的,并且是并行存在的。一个系统可以被设计成有两个完全独立的内存系统。然而,大多数实际系统将安全和不安全视为访问控制的属性。正常(非安全)世界只能访问非安全的物理地址空间。当启用MMU时,安全世界可以访问这两个物理地址空间。这是通过转换表来控制的。
这也会影响高速缓存的一致性。例如,因为安全0x8000和非安全0x8000是不同的物理地址,因此它们可以同时在缓存中。
在安全内存和非安全内存位于不同位置的系统中,不会有任何问题。它们更有可能是在同一个位置。理想情况下,内存系统将阻止对非安全内存的安全访问和对安全内存的非安全访问
实际上,大多数操作程序只阻止对安全内存的非安全访问。同样,这意味着您可能会在缓存中使用两次相同的物理内存,即安全和不安全。这总是一个编程错误。为了避免这种情况,安全世界必须始终使用对非安全内存的非安全访问
1731563070873.png

3.2 多个虚拟地址空间

在任何时候,只使用一个虚拟地址空间(当前安全状态异常级别)。但是,从概念上讲,由于有三个不同的ttbr,所以有三个并行的虚拟地址空间(EL0/1、EL2和EL3)。
1731563131994.png
您还可以有一个安全的和不安全的EL1/0。然而,只有一个物理集的TTBR0_EL1,TTBR1_EL1和TCR_EL1。因此,

当在不同的世界之间切换时,Secure Monitor必须保存和恢复这些寄存器。

3.3 禁用内存管理单元时的操作

当启用MMU时,转换表将控制内存的内存类型和属性。当MMU未启用时,例如在重置后立即启用,内存将采用默认类型。
用于指令获取的默认类型是具有由SCTLR_ELx.I控制的可缓存属性的正常内存。

  • 当I=0时,获取使用不可缓存和外部可共享属性。
  • 当I=1时,使用可访问的、内部通读分配无写分配、外部通读分配无写分配外部可共享属性

3.4 配置和启用MMU

写入控制MMU的系统寄存器是发生上下文变化的事件,它们之间没有排序要求。在发生上下文同步事件之前,不能保证会看到这些事件的结果。
下面放出linux-5.15关于enable_mmu的汇编代码部分

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
/*
* Enable the MMU.
*
* x0 = SCTLR_EL1 value for turning on the MMU.
* x1 = TTBR1_EL1 value
*
* Returns to the caller via x30/lr. This requires the caller to be covered
* by the .idmap.text section.
*
* Checks if the selected granule size is supported by the CPU.
* If it isn't, park the CPU
*/
SYM_FUNC_START(__enable_mmu)
mrs x2, ID_AA64MMFR0_EL1
ubfx x2, x2, #ID_AA64MMFR0_TGRAN_SHIFT, 4
cmp x2, #ID_AA64MMFR0_TGRAN_SUPPORTED_MIN
b.lt __no_granule_support
cmp x2, #ID_AA64MMFR0_TGRAN_SUPPORTED_MAX
b.gt __no_granule_support
update_early_cpu_boot_status 0, x2, x3
adrp x2, idmap_pg_dir
phys_to_ttbr x1, x1
phys_to_ttbr x2, x2
msr ttbr0_el1, x2 // load TTBR0
offset_ttbr1 x1, x3
msr ttbr1_el1, x1 // load TTBR1 //填充两个页表基地址到TTBR0,TTBR1
isb

set_sctlr_el1 x0 //填充M域,使能MMU

ret
SYM_FUNC_END(__enable_mmu)

四、ARMv8-A架构上的地址翻译流程

Armv8-A支持三种不同的翻译表格式:

  • Armv8-AAArch64长描述符格式。
  • Armv7-一种长描述符格式,如Armv7-A架构的大型物理地址扩展(LPAE),例如,Arm Cortex-A15处理器。
  • Armv7-一个简短的描述符格式

在AArch32状态下,现有的Armv7-一个长的和短的描述符格式可以用于运行现有的来宾操作系统和现有的应用程序代码,而无需修改。Armv7-A简短的描述符只能在EL0和EL1阶段1的翻译中使用。它们不能被系统管理程序或安全监控器代码使用。

Armv8-一个长描述符格式总是在AArch64执行状态中使用。这类似于Armv7——一种带有大型物理地址扩展的长描述符格式。它使用了相同的64位长描述符格式,但也做了一些更改。它引入了第0级表索引,它使用与第1级表相同的描述符格式。此外,还增加了对多达48位的输入和输出地址的支持。输入的虚拟地址现在是64位的。
但是,由于体系结构不支持完全的64位寻址,地址的位[63:48]必须都是相同的,即所有0或全部1,或者前8位可以用于VA标记

AArch64支持三种不同的翻译颗粒。它们在翻译表的最低级别定义块大小,并控制正在使用的翻译表的大小。更大的颗粒大小可以减少所需的翻译表级别的数量,这可能成为使用虚拟管理程序提供虚拟化的系统中的一个重要考虑因素。

支持的颗粒大小分别为4KB、16KB和64KB,并通过实现定义了支持这三个颗粒中的哪一种。创建转换表的代码能够读取内存模型特性寄存器0系统寄存器(ID_AA64MMFR0_EL1),以找出哪些是支持的大小。可以为翻译控制寄存器(TCR_EL1)中的每个翻译表配置大小。

4.1 AArch64描述符格式

AArch64描述符格式用于表的所有级别,从级别0到级别3。第0级描述符只能输出第1级表的地址。第3级描述符不能指向另一个表,并且只能输出块地址。因此,级别3的表格的格式略有不同。
下图显示,表描述符类型由条目的位1:0标识,可以引用:

  • 下一级别表的地址,在这种情况下,内存可以进一步细分为更小的块。
  • 一个可变大小的内存块的地址。
  • 表条目,可以标记为故障或无效。

1731564051927.png

4.2 颗粒尺寸对翻译表的影响

4KB

在4kB颗粒的情况下,硬件可以使用4级查找过程。48位地址有9个地址位(即每个512个条目),最后的12位选择了4kB内直接来自原始地址的一个字节。
位[47:39]将虚拟地址索引的位输入到512条目L0表中。每个表条目都跨越了512GB的范围,并指向一个L1表。在那个512个条目L1表中,位[38:30]被用作索引来选择一个条目,每个条目都指向一个1GB块或一个L2表
位[29:21]索引到一个512个入口L2表,每个入口指向一个2MB的块或下一个表级别。在最后一层,位[20:12]索引到一个512条目L2表中,每个条目都指向一个4kB的块。
1731564217560.png

16KB

在16kB颗粒的情况下,硬件可以使用4级查找过程。
48位的地址每一级有11个地址位被翻译,即每个地址位有2048个条目,最后的14位选择了4kB内直接来自原始地址的一个字节。
级别为0的表只包含两个条目。虚拟地址的位[47]从两个条目L0表中选择一个描述符。每个表条目都跨越128TB范围,并指向一个L1表。在该2048个条目L1表中,位[46:36]被用作一个索引,以选择一个条目,并且每个条目都指向一个L2表。位[35:25]索引到一个2048条目L2表中,每个条目指向一个32MB的块或下一个表级别。
在最后的翻译阶段,位[24:14]索引到一个2048条目L2表中,每个条目都指向一个16kB的块。
1731564308154.png

64KB

在64kB颗粒的情况下,硬件可以使用3级查找过程。级别1的表只包含64个条目。从64个条目L1表中选择一个描述符。每个表条目都跨越4TB范围,并指向一个L2表。在那个8192个条目L2表中,位[41:29]被用作索引来选择一个条目,每个条目都指向一个512MB的块或一个L2表。在最后的翻译阶段,位[28:16]索引到一个8192条目L3表中,每个条目都指向一个64kB的块。
1731564358481.png

4.3 Cache配置

MMU使用转换表和转换寄存器来控制哪些内存位置是可缓存的。MMU控制缓存策略、内存属性和访问权限,并提供虚拟到物理地址的转换。
1731564408685.png

4.4 缓存策略

内存类型没有直接编码在翻译表条目中。相反,每个块条目在一个内存类型表中指定一个3位索引。此表存储在内存属性间接寄存器MAIR_ELn中。该表有8个条目,每个条目都有8个位,如下图所示:
1731564457212.png

尽管转换表块条目本身并不直接包含内存类型编码,但处理器内部的TLB条目通常会为特定的条目存储这些信息。因此,在ISB指令障碍和TLB无效操作之后,可能才会观察到MAIR_ELn的变化。
MMU转换表还为内存系统中的每个块定义了高速缓存策略。定义为“正常”的内存区域可以标记为可缓存或不可缓存。
翻译表条目中的位[4:2]指的是MAIR_ELn中八个内存属性编码中的一个。内存属性编码指定访问该内存时使用的缓存策略。这些都是对处理器的提示,它定义了在一个特定的实现中是否支持所有的缓存策略,以及哪些缓存数据被视为是一致的。内存区域可以根据其可共享性属性来定义

4.5 内存属性

下图显示了如何在阶段1的块条目中指定内存属性。转换表中的块条目定义了每个内存区域的属性。阶段2的条目有一个不同的布局。
1731564635457.png

五、转换表配置

除了在TLB中存储单个转换外,MMU还可以配置为在可缓存内存中存储转换表。这通常比总是从外部内存读取表更快地访问表。TCR_EL1有可以控制这个问题的字段。这些字段指定了TTBR0和TTBR1的转换表的可缓存性和可共享性。相关字段称为SH0/1可共享性、IRGN0/1内部可存性和ORGN0/1外部可存性。
1731564732045.png
1731564764796.png

5.1 虚拟地址标记

转换控制寄存器(TCR_ELn)有一个额外的字段,称为顶部字节忽略(TBI),该字段提供了带标记的寻址支持。64位通用寄存器中最重要的16位地址必须是0xFFFF或0x0000。任何试图使用不同的位值的尝试都会触发故障。
启用标记寻址支持后,处理器将忽略虚拟地址的前8位[63:56]。它在内部设置位[55],将地址扩展为64位格式。前8位可以用来传递数据。这些位被忽略为寻址和转换故障。TCR_EL1对EL0和EL1都有单独的启用位。

六、Multiple地址空间

6.1 两阶段翻译

Armv8-A虚拟化引入了翻译的第二个阶段。当系统中存在一个虚拟机管理程序时,可能会存在一个或多个来宾操作系统。这些操作使用了前面所述的TTBRn_EL1,MMU操作似乎没有改变。
在一个两阶段的过程中,管理程序必须执行一些额外的转换步骤,以在不同的来宾操作系统之间共享物理内存系统。在第一阶段,VA被转换为一个中间物理地址(IPA)。这通常是在操作系统的控制下进行的。第二阶段由管理程序控制,将IPA转换为最终物理地址(PA)。
下图总结了这两个阶段的翻译过程。
1731564967651.png
阶段2的转换,将中间物理地址转换为物理地址,使用了在管理程序控制下的额外一组表。对于非安全的EL1/0访问,必须通过写入系统管理程序配置寄存器HCR_EL2来显式地启用这些访问。
阶段2转换表的基地址在虚拟化转换表基寄存器(VTTBR0_EL2)中指定。它在内存的底部指定一个单个连续的地址空间。所支持的地址空间的大小将在虚拟化转换控制寄存器,VTCR_EL2的T0SZ[5:0]字段中指定。

6.2 EL2和EL3地址空间

1731565048686.png
Hypervisor EL2模式和安全监控EL3模式都有自己的1级表,它们直接从虚拟地址空间映射到物理地址空间。表基地址分别在TTBR0_EL2和TTBR0_EL3中指定,在内存底部启用一个可变大小的连续地址空间。TG字段指定颗粒大小,而SL0字段控制表查找的第一级。任何超出定义地址范围的访问都会导致转换故障。
EL3也有自己的专用转换表。表的基地址在TTBR0_EL3中指定,并通过TCR_EL3进行配置。转换表能够同时访问安全的和非安全的物理地址。TTBR0_EL3仅在安全监视器EL3模式下使用,而不是由受信任的内核本身使用。
当向安全世界的转换完成后,受信任的内核将使用EL1转换,即TTBR0_EL1和TTBR1_EL1指向的转换表。由于这些寄存器不存储在AArch64中,因此安全监视器代码必须为安全世界配置新的表,并保存和恢复TTBR0_EL1或TTBR1_EL1的副本。
EL1转换机制在安全状态下的行为与其在非安全状态下的正常操作不同。翻译的第二阶段被禁用,EL1翻译机制现在同时指向安全和不安全的物理地址。在安全的世界中没有虚拟化,所以IPA总是与最终的PA相同。
TLB中的条目被标记为安全或不安全,因此当在安全和正常世界之间移动时,不需要进行TLB维护。

七、访问权限

Access permissions可通过转换表条目进行控制。访问权限控制区域是可读还是可写,或者两者都有,可以单独设置为EL0,也可以分别访问EL1、EL2和EL3,如下表所示。
1731565247590.png
操作系统内核通常在EL1中运行。操作系统定义了转换表映射,它由内核本身和运行在EL0上运行的应用程序使用。由于内核为自己的代码和应用程序指定不同的权限,因此需要区分非特权和特权访问权限之间的差异。

这里补充说明一下ELx的各层级划分:

  • EL0(用户态)
    用户态代码运行在EL0,这是应用程序的执行层级,权限最低,不能直接访问硬件或敏感系统资源。可以通过中断或者异常进入到EL1从而访问硬件。
  • EL1(内核态)
    Linux内核运行在EL1,这是操作系统的执行层级,拥有较高权限,可以访问硬件和控制系统资源。
  • EL2(虚拟化层)
    EL2主要用于支持虚拟化(由Hypervisor使用)。在一些手机SoC中,Android设备的TrustZone会涉及EL3,但Linux内核通常不会运行在EL2,除非设备的设计需要虚拟化层直接管理。
  • EL3(安全监控模式)
    EL3一般是ARM TrustZone的Secure Monitor运行的地方,用于实现安全相关功能,与Linux无直接关系。

八、翻译表描述符的操作系统使用

操作系统使用访问标志位来跟踪正在使用的是哪些页面。软件管理标志。当首次创建页面时,其条目的AF设置为0。第一次通过代码访问页面时,如果AF为0,这将触发MMU故障。页面错误处理程序记录现在正在使用此页面,并在表条目中手动设置AF位。例如,Linux内核对ARM64上的PTE_AF使用[AF]位(AArch64上的Linux内核名称),它用于检查页面是否曾经被访问过。这将影响到一些内核内存管理的选择。例如,当一个页面必须耗尽内存时,它不太可能替换正在主动使用的页面。
1731565644547.png
下图显示了如何在一个阶段的块条目中指定内存属性
1731565689961.png
描述符中的内存属性位,即访问标志(AF),指示首次使用块条目的时间。

  • AF = 0:此块条目尚未被使用。
  • AF = 1:已使用此块条目。

描述符的位[58:55]被标记为为软件使用而保留,可用于在翻译表中记录特定于操作系统的信息。例如,Linux内核使用其中一个位将条目标记为干净或脏。脏状态记录页面是否已被写入。如果页面后来被内存替换,一个干净的页面可以简单地丢弃,但一个肮脏的页面必须先保存其内容。

九、安全性和MMU

在非安全状态下,将忽略转换表中的NS位和NSTable位。只能访问不安全的内存。在安全状态下,NS位和NSTable位控制虚拟地址是否转换为安全物理地址或非安全物理地址。您可以使用SCR_EL3.CIF,以防止安全世界从任何转换为非安全物理地址的虚拟地址中执行。此外,在安全的世界中,您可以使用SCR.CIF位控制是否安全指令获取到非安全物理内存。

9.1 具有用户权限的内核访问权限

LDTR或STTR指令允许在EL1(例如,一个操作系统)上执行的代码使用EL0或应用程序权限执行内存访问。例如,这可以用于引用系统调用提供的指针,并使操作系统检查是否只访问应用程序可访问的数据。当在EL1执行时,这些指令执行加载或存储,就好像在EL0上执行一样。在所有其他异常级别上,LDTR和STTR的行为都类似于常规的LDR或STR指令。它们是通常的大小,并且具有与正常加载和存储指令相同的有符号和无符号变量,但具有更小的偏移量和受限制的索引选项。

十、翻译旁观缓冲区(TLB)

翻译查找缓冲区(TLB)是MMU中最近访问的页面翻译的缓存。对于处理器执行的每个内存访问,MMU将检查转换是否缓存在TLB中。如果所请求的地址转换在TLB中,则该地址的翻译立即可用。
每个TLB条目通常不仅包含物理地址和虚拟地址,而且还包含诸如内存类型、缓存策略、访问权限、地址空间ID(ASID)和虚拟机ID(VMID)等属性。如果TLB不包含由处理器发出的虚拟地址的有效转换,这称为TLB丢失,则执行外部转换表遍历或查找。MMU内的专用硬件使它能够读取内存中的转换表。然后,如果翻译表行走没有导致页面故障,则可以将新加载的翻译缓存在TLB中,以便进行可能的重用。TLB的精确结构在不同的Arm处理器的实现之间有所不同。
如果操作系统修改了已缓存在TLB中的翻译条目,则操作系统有责任使这些陈旧的TLB条目无效。

TLBI <type><level>{IS} {, <Xt>}

1731566029463.png
TLB可以保存固定数量的条目。您可以通过最小化由转换表遍历引起的外部内存访问次数和获得高TLB命中率来获得最佳性能。Armv8-A体系结构提供了一个被称为连续块条目的特性,以有效地利用TLB空间。转换表块条目每个都包含一个连续的位。当设置时,这个位向TLB发出信号,表明它可以缓存一个覆盖多个块的翻译的单个条目。查找可以索引到连续块所覆盖的地址范围内的任何位置。因此,TLB可以为已定义的地址范围缓存一个条目,从而可以在TLB中存储更大范围的虚拟地址。

十一、系统MMU(SMMU)

系统中像DMA或GPU这样的设备可以看到物理地址空间,所以当它们被编程时,您必须使用PA来指定DMA的源地址和目标地址,或者GPU的帧缓冲位置。这通常由内核级代码处理,它调用内核以获得VA到PA的映射。
当添加第二阶段翻译时,内核不再看到“真正的”物理地址。相反,它看到了ipa。这意味着,如果一个指针从内核模块传递到GPU或DMA,它可能是错误的地址。
一种解决方案是让管理程序拦截操作系统和设备之间的所有通信,通过管理程序将传递的地址从IPA转换到PA。这种方法可能代价昂贵,因为这意味着每次写入设备的内存映射寄存器时都必须异常(进入Hypervisor模式)。
另一种方法是让设备看到与内核相同的IPA空间,这就是System MMU(SMMU)的作用所在。
SMMU实际上是处理器内部MMU的外部拷贝。它可以放置在您的系统中的设备(如DMA或GPU)和互连之间。然后,通过SMMU的任何事务都可以被翻译,这意味着DMA或GPU可以看到一个翻译后的地址空间。
SMMU体系结构使用与Armv7-A和Armv8-A相同的转换表格式。因此,SMMU通常指向处理器正在使用的内存中的同一组表。这意味着DMA或GPU可以拥有与客户操作系统相同的内存视图,从而消除了软件中昂贵的捕获需求。SMMU架构允许进行第一阶段转换(VA转换到IPA)、第二阶段转换(IPA转换到PA)或两者都进行转换(VA转换到IPA转换到PA)的设计。并非所有的实现都支持所有这些选项。

十二、内存屏障(barriers)

ARM体系结构包括在特定点强制完成访问指令和访问指令的屏障指令。障碍用于防止发生不安全的优化,并强制执行特定的内存顺序。因此,使用不必要的障碍指令可能会降低软件的性能。仔细考虑在特定情况下是否需要一个屏障,如果是,这是正确的屏障使用。障碍指令有三种类型。

现代处理器可能会对内存操作进行乱序优化(Out-of-Order Execution)以提高性能。然而,这种优化可能导致程序实际的执行顺序与代码中的顺序不一致,从而引发并发错误。内存屏障通过强制特定的顺序执行内存操作,保证程序的正确性。

12.1 指令同步屏障(ISB)

这用于保证获取任何后续指令,以便使用当前的MMU配置检查特权和访问权限。它用于确保任何以前执行的上下文更改操作,如对系统控制寄存器的写入操作,在ISB完成时就已经完成。
例如,在硬件术语中,这可能意味着指令管道将被刷新。它的典型用途是在内存管理、缓存控制和上下文切换代码中,或者在内存中移动的代码。
下面的示例展示了如何启用浮点单元和SIMD,您可以在AArch64中通过写入CPACR_EL1寄存器的第[20]位来实现这一点。ISB是一个上下文同步事件,它保证在执行任何后续的FPU或NEON指令之前完成启用。

1
2
3
4
MRS X1, CPACR_EL1 // Copy contents of CPACR to X1
ORR X1, X1, #(0x3 << 20) // Write to bit 20 of X1. (Enable FPU and SIMD)
MSR CPACR_EL1, X1 // Write contents of X1 to CPACR
ISB //
  • ISB刷新管道,并确保ISB之前任何完成的上下文更改操作的效果对ISB之后的任何指令都可见。将引用来自缓存或内存的指令。
  • 它还确保在ISB指令之后的任何上下文更改操作只有在ISB完成后才生效,而在ISB之前的指令不会看到。
  • 但这并不意味着在每个修改处理器寄存器的指令之后都需要一个ISB。例如,读取或写入PSTATE字段、ELR、SP和SPSR总是相对于其他指令按程序顺序出现。

一句话概括:刷新指令流水线,确保后续指令执行时使用最新的指令和状态

12.2 数据内存屏障(DMB)

这可以防止跨DMB指令重新排序数据访问指令。该处理器在DMB之前执行的所有数据访问,即加载或存储,而不是指令获取,在DMB之后的任何数据访问之前,对指定共享域内的所有其他主服务器都是可见的。
它还确保在执行任何后续数据访问之前,任何显式数据或统一的缓存维护操作已经完成。

一句话概括:确保所有的内存访问(加载和存储)在屏障指令之前完成后,才能开始屏障之后的内存访问

12.3 数据同步屏障(DSB)

DSB强制执行与数据内存屏障相同的顺序,但它还会阻止任何进一步指令的执行,而不仅仅是加载或存储,直到同步完成。这可以用来防止SEV指令的执行,例如,它会向其他核心发出事件发生的信号。它等待该处理器为指定的共享域发布的所有缓存、TLB和分支预测器维护操作都完成。

一句话概括:确保所有前面的内存操作和指令都完成后,才继续执行后面的指令

指令

硬件同步

描述

应用场景

dmb 确保内存访问顺序 普通共享内存同步
dsb 确保所有指令完成后再执行后续指令 外设寄存器编程
isb 确保新的指令流有效 修改控制寄存器后
| | | |