作者: 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 添加了对大虚拟地址空间的可选支持。此功能仅在运行 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 时,向量表也会映射在此处。
当在使用虚拟化主机扩展的情况下使用 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 位),这消除了额外变量读取的需要。物理-虚拟偏移量和 vmemmap 偏移量在早期启动时计算,以实现此逻辑。
由于单个二进制文件需要同时支持 48 位和 52 位 VA 空间,VMEMMAP 的大小必须足够容纳 52 位 VA,并且也必须足够大以容纳固定的PAGE_OFFSET。
内核中的大多数代码不需要考虑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
请注意,此选项仅用于调试应用程序,不应在生产环境中使用。
Src
https://www.kernel.org/doc/html/latest/arch/arm64/memory.html