AArch64 Linux 上的内存布局

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

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

AArch64 Linux 使用 3 级或 4 级转换表,配置为 4KB 页面,允许用户和内核分别使用 39 位 (512GB) 或 48 位 (256TB) 虚拟地址。使用 64KB 页面时,仅使用 2 级转换表,允许 42 位 (4TB) 虚拟地址,但内存布局相同。

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

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

当使用没有虚拟化主机扩展的 KVM 时,hypervisor 会在 EL2 中以相对于线性映射的固定(且可能随机)偏移量映射内核页面。 有关更多详细信息,请参阅 kern_hyp_va 宏和 kvm_update_va_mask 函数。 当为特定 CPU 启用 ARM64_SPECTRE_V3A 时,GICv2 等 MMIO 设备会映射到 HYP idmap 页面旁边,向量也会映射到旁边。

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

内核中的 52 位 VA 支持

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

这种回退机制需要内核 .text 位于较高的地址中,以便它们对 48/52 位 VA 不变。 由于 kasan 阴影是整个内核 VA 空间的一小部分,因此 kasan 阴影的末尾也必须位于内核 VA 空间的较高一半中,对于 48/52 位。(从 48 位切换到 52 位,kasan 阴影的末尾是不变的,并且依赖于 ~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 提示参数来“选择加入”从 52 位空间接收 VA。

例如

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

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

CONFIG_EXPERT=y && CONFIG_ARM64_FORCE_52BIT=y

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