AArch64 Linux 上的内存布局

作者:Catalin Marinas <catalin.marinas@arm.com>

本文档描述了 AArch64 Linux 内核使用的虚拟内存布局。该架构在 4KB 页大小下允许最多 4 级转换表,在 64KB 页大小下允许最多 3 级转换表。

AArch64 Linux 在 4KB 页配置下使用 3 级或 4 级转换表,分别为用户空间和内核空间提供 39 位 (512GB) 或 48 位 (256TB) 的虚拟地址。对于 64KB 页,仅使用 2 级转换表,提供 42 位 (4TB) 的虚拟地址,但内存布局是相同的。

ARMv8.2 增加了对大虚拟地址空间(Large Virtual Address space)的可选支持。这仅在使用 64KB 页大小运行时可用,并且扩展了第一级转换中的描述符数量。

TTBRx 的选择由虚拟地址的第 55 位决定。swapper_pg_dir 仅包含内核(全局)映射,而用户 pgd 仅包含用户(非全局)映射。swapper_pg_dir 的地址被写入 TTBR1,绝不写入 TTBR0。

在使用没有虚拟化主机扩展(Virtualization Host Extensions)的 KVM 时,虚拟机监控程序(hypervisor)将 EL2 中的内核页映射到距离线性映射固定(且可能随机)偏移量的地方。有关更多详细信息,请参阅 kern_hyp_va 宏和 kvm_update_va_mask 函数。诸如 GICv2 之类的 MMIO 设备被映射到 HYP idmap 页附近,当针对特定 CPU 启用 ARM64_SPECTRE_V3A 时,向量(vectors)也是如此。

在使用带有虚拟化主机扩展的 KVM 时,不会创建额外的映射,因为宿主机内核直接在 EL2 中运行。

内核中的 52 位 VA 支持

如果存在 ARMv8.2-LVA 可选特性,并且我们以 64KB 页大小运行,则可以为用户空间和内核地址使用 52 位的地址空间。然而,如果硬件特性不存在,任何支持 52 位的内核二进制文件也必须能够在早期启动时回退到 48 位。

这种回退机制要求内核的 .text 段位于较高的地址,以便它们对于 48/52 位 VA 是不变的。由于 kasan shadow 只是整个内核 VA 空间的一小部分,因此对于 48/52 位,kasan shadow 的末尾也必须位于内核 VA 空间的高半部分。(从 48 位切换到 52 位时,kasan shadow 的末尾是不变的且取决于 ~0UL,而起始地址会向较低地址“增长”)。

为了优化 phys_to_virt 和 virt_to_phys,PAGE_OFFSET 保持恒定在 0xFFF0000000000000(对应于 52 位),这免去了额外读取变量的需求。physvirt 偏移量和 vmemmap 偏移量在启动早期计算以启用此逻辑。

由于单个二进制文件需要同时支持 48 位和 52 位 VA 空间,因此 VMEMMAP 的大小必须足够大以容纳 52 位 VA,并且还必须足够大以适应固定的 PAGE_OFFSET。

内核中的大多数代码不需要考虑 VA_BITS,对于确实需要知道 VA 大小的代码,变量定义如下:

VA_BITS 常量,表示 最大 VA 空间大小

VA_BITS_MIN 常量,表示 最小 VA 空间大小

vabits_actual 变量,表示 实际 VA 空间大小

最大和最小尺寸可用于确保缓冲区足够大,或者地址在“最坏”情况下靠得足够近。

52 位用户空间 VA

为了保持与依赖 ARMv8.0 VA 空间最大大小为 48 位的软件的兼容性,内核默认将从 48 位范围内向用户空间返回虚拟地址。

软件可以通过指定大于 48 位的 mmap 提示(hint)参数来“选择”接收来自 52 位空间的 VA。

例如

maybe_high_address = mmap(~0UL, size, prot, flags,...);

也可以通过启用以下内核配置选项来构建一个从 52 位空间返回地址的调试内核:

CONFIG_EXPERT=y && CONFIG_ARM64_FORCE_52BIT=y

请注意,此选项仅用于调试应用程序,不应在生产环境中使用。