尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

AArch64 Linux 虚拟内存布局深度解析:从页表级数、TTBR 切换到 52-bit VA 支持

AArch64 Linux 虚拟内存布局深度解析:从页表级数、TTBR 切换到 52-bit VA 支持 AArch64 Linux 虚拟内存布局深度解析从页表级数、TTBR 切换到 52-bit VA 支持【免费下载链接】linuxLinux kernel source tree项目地址: https://gitcode.com/GitHub_Trending/li/linux本指南基于 Linux 内核仓库中的 Documentation/arch/arm64/memory.rst系统讲解 AArch64 Linux 内核的虚拟内存布局设计包括页大小4KB/16KB/64KB与翻译表级数如何决定虚拟地址空间大小、用户态与内核态如何通过 TTBR0/TTBR1 区分、固定内核映射区域的排布规律以及 ARMv8.2-LVA 带来的 52-bit 虚拟地址支持及其对内核二进制、KASAN shadow 和用户态地址分配的影响。读完本文你将掌握 VA_BITS/VA_BITS_MIN/vabits_actual 三组变量的区别与用途能够理解 mmap 高位 hint 触发 52-bit 用户地址的机制并能在阅读内核源码时快速定位内存布局相关的宏与实现。AArch64 虚拟地址空间的宏观设计AArch64 架构本身支持最多 4 级翻译表translation tables配合 4KB 页大小使用或最多 3 级翻译表配合 64KB 页大小使用。Linux 内核在此基础上通过页大小 VA 位数的组合来确定实际使用的翻译表级数页大小翻译表级数虚拟地址位数虚拟地址空间大小4KB3 级39-bit512GB4KB4 级48-bit256TB64KB2 级42-bit4TB其中 64KB 页配置下42-bit 虚拟地址空间的内存布局与其他配置保持一致。ARMv8.2 还引入了可选的 Large Virtual Address spaceLVA特性只有在 64KB 页大小下才可用它通过扩展第一级翻译表中的描述符数量来扩大地址空间。这些组合关系在 arch/arm64/Kconfig 中有完整的体现ARM64_4K_PAGES、ARM64_16K_PAGES、ARM64_64K_PAGES三个选项决定页大小ARM64_VA_BITS_36仅 16KB 页 EXPERT、ARM64_VA_BITS_39仅 4KB 页、ARM64_VA_BITS_42仅 64KB 页、ARM64_VA_BITS_47仅 16KB 页、ARM64_VA_BITS_48、ARM64_VA_BITS_52则决定虚拟地址位数最终汇总为编译期常量CONFIG_ARM64_VA_BITS。TTBRx 选择与内核/用户空间分离AArch64 Linux 通过虚拟地址的第 55 位bit 55来决定使用哪个翻译表基址寄存器TTBRxswapper_pg_dir仅包含内核全局global映射其地址写入TTBR1且永远不会写入 TTBR0用户 pgd 仅包含用户非全局non-global映射。也就是说内核虚拟地址空间位于高半区bit 55 置 1走 TTBR1用户空间位于低半区bit 55 为 0走 TTBR0。这也是 arch/arm64/include/asm/memory.h 中PAGE_OFFSET被定义为-(UL(1) VA_BITS)即_PAGE_OFFSET(VA_BITS)的原因线性映射linear map起始于 TTBR1 地址空间的开头PAGE_END则是线性映射的结束、其他内核映射的开始。#define _PAGE_OFFSET(va) (-(UL(1) (va))) #define PAGE_OFFSET (_PAGE_OFFSET(VA_BITS)) #define _PAGE_END(va) (-(UL(1) ((va) - 1)))内核源码中普遍使用vabits_actual来判断当前实际生效的 VA 位数。在 arch/arm64/include/asm/memory.h 中可以看到它的两种形态#if VA_BITS 48 #define vabits_actual (64 - ((read_tcr() 16) 63)) #else #define vabits_actual ((u64)VA_BITS) #endif当编译配置的VA_BITS 48时vabits_actual在运行时从TCR_EL1.T1SZ字段位 [21:16]实时读取从而反映硬件是否真的支持大地址空间否则直接退化为编译期常量VA_BITS。内核虚拟地址空间的固定区域排布虽然 memory.rst 正文未逐一列出地址值但从 arch/arm64/include/asm/memory.h 的宏定义可以完整还原内核 TTBR1 高半区各区域的排布由高地址向低地址FIXADDR_TOP-UL(SZ_8M)固定映射区fixmap顶部PCI_IO_START/PCI_IO_ENDVMEMMAP_END SZ_8M起大小为PCI_IO_SIZESZ_16M即 PCI I/O 空间VMEMMAP_END-UL(SZ_1G)vmemmap 区域的结束VMEMMAP_STARTVMEMMAP_END - VMEMMAP_SIZEvmemmapstruct page 数组区域的开始MODULES_VADDR_PAGE_END(VA_BITS_MIN)内核模块区域开始MODULES_END MODULES_VADDR MODULES_VSIZE其中MODULES_VSIZE为 SZ_2GKIMAGE_VADDR即内核镜像起始地址等于MODULES_ENDPAGE_OFFSET线性映射起始也是 TTBR1 空间的最低位边界。vmemmap 区域是线性区域对应的 struct page 数组其大小必须足以覆盖整个线性区。在 52-bit 支持场景下arch/arm64/include/asm/memory.h 给出了关键设计#define VMEMMAP_RANGE (_PAGE_END(VA_BITS_MIN) - PAGE_OFFSET) #define VMEMMAP_SIZE ((VMEMMAP_RANGE PAGE_SHIFT) * sizeof(struct page))这里的VMEMMAP_RANGE覆盖了从 52-bit 的PAGE_OFFSET一直到 48-bit 的PAGE_END_PAGE_END(VA_BITS_MIN)的整个区间。其注释明确指出之所以这样设计是为了保持PAGE_OFFSET恒定同时在硬件不支持 52-bit 时回退使用 vmemmap 的高端部分。这正是一个二进制同时支持 48-bit 与 52-bit的核心机制。当启用 KASANGeneric 或 Software Tag-Based 模式时内核 VA 空间还会被占用 1/8 或 1/16 作为 shadow 内存。arch/arm64/include/asm/memory.h 中的映射公式为shadow_addr (addr KASAN_SHADOW_SCALE_SHIFT) KASAN_SHADOW_OFFSET其中KASAN_SHADOW_START依赖vabits_actual动态计算而PAGE_END会被重定义为KASAN_SHADOW_START即线性映射的结束点由 KASAN shadow 的起点决定。由于 shadow 只占内核 VA 空间的一小部分其结束地址在 48-bit 与 52-bit 下保持恒定只依赖~0UL而起始地址在切换到 52-bit 时会向更低地址方向生长这一点在 memory.rst 中也有明确说明。在 arch/arm64/mm/mmu.c 的map_mem()函数中可以验证线性映射的结束点直接使用了_PAGE_END(VA_BITS_MIN)static const u64 direct_map_end _PAGE_END(VA_BITS_MIN);并对线性区与 vmalloc 区是否共享 PGD 表项做了BUILD_BUG_ON编译期断言保证线性区可以安全地设置分层的 PXNTable 属性。此外arch/arm64/mm/mmu.c 中的linear_map_split_to_ptes()展示了线性映射的运行时调整在 KPTI 场景下由 boot CPU 以_PAGE_OFFSET(vabits_actual)为起点把线性映射拆分为 PTE 粒度同时借助 BBML3 机制与 idmap 配合保证其他 CPU 的安全切换。KVM 与 Hypervisor 的地址映射当使用 KVM 但未启用Virtualization Host ExtensionsVHE时hypervisor 在 EL2 以固定的且可能是随机的偏移映射内核页该偏移相对线性映射计算。相关细节可查看kern_hyp_va宏与kvm_update_va_mask函数。arch/arm64/kvm/va_layout.c 中的kvm_compute_layout()展示了 HYP VA 的生成格式V 为 hypervisor VA 位数63 ... V | V-1 | V-2 .. tag_lsb | tag_lsb - 1 .. 0 --------------------------------------------------------- | 0000000 | hyp_va_msb | random tag | kern linear VA | |--------- tag_val -----------|----- va_mask ---|即HYP VA 的低位保留内核线性 VA中间插入一个在CONFIG_RANDOMIZE_BASE开启时随机化的 tag高位确保与 idmap 区域不冲突。随后kvm_update_va_mask()arch/arm64/kvm/va_layout.c通过 alternatives 机制把这套掩码/移位/加法逻辑现场修补进kern_hyp_va指令序列中若系统支持 VHE 或 tag 为零则相关指令被替换为 NOP。在启用 VHE 时宿主内核直接运行在 EL2因此不需要创建任何额外的映射。memory.rst 还提到MMIO 设备如 GICv2会被映射到 HYP idmap 页旁边当 CPU 需要ARM64_SPECTRE_V3A加固时异常向量vectors也会映射到该区域——对应kvm_patch_vector_branch()arch/arm64/kvm/va_layout.c中为 Spectre-v3a 场景修补向量跳转指令的逻辑。52-bit VA内核侧的支持机制ARMv8.2-LVA 特性存在且使用 64KB 页时内核和用户空间都可以使用 52-bit 地址空间。但任何支持 52-bit 的内核二进制必须能在早期启动阶段回退到 48-bit以兼容不支持该特性的硬件。这种回退机制带来三个约束内核 .text 必须位于较高地址使得其虚拟地址在 48-bit 与 52-bit 下保持不变invariant否则切换位宽会改变内核代码自身的寻址KASAN shadow 的结束地址必须位于内核 VA 空间的高半区因为它是~0UL的函数与具体位宽无关切换从 48-bit 到 52-bit 时只有 shadow 的起始地址向低地址方向增长PAGE_OFFSET必须保持恒定为0xFFF0000000000000对应 52-bit从而免去一次额外的变量读取优化phys_to_virt与virt_to_phys的开销——物理/虚拟偏移physvirt offset和 vmemmap 偏移都在启动早期计算好以支撑该逻辑。单一二进制同时支持 48-bit 与 52-bit 还要求VMEMMAP 的尺寸必须按 52-bit 的 VA 空间来规划同时还要能容纳固定的PAGE_OFFSET——这正是上一节VMEMMAP_RANGE覆盖到_PAGE_END(VA_BITS_MIN)的原因。VA_BITS / VA_BITS_MIN / vabits_actual内核中绝大多数代码无需关心具体 VA 位数只有少数代码需要知道 VA 空间大小此时使用三组不同的量memory.rst 的原文定义名称类型含义VA_BITS常量编译期配置的最大VA 空间大小VA_BITS_MIN常量编译期配置的最小VA 空间大小vabits_actual变量运行时实际生效的 VA 空间大小最大与最小值的用途在于确保缓冲区按最坏情况最大 VA足够大或确保地址按最坏情况最小 VA放置得足够近。从 arch/arm64/include/asm/memory.h 可以看到VA_BITS_MIN的取值规则当VA_BITS 48时16KB 页配置取 47其余取 48否则VA_BITS_MIN就等于VA_BITS。52-bit 用户空间地址mmap hint 与强制模式为了兼容依赖 ARMv8.0 最大 48-bit VA 空间的旧软件内核默认仍然向用户空间返回 48-bit 范围内的虚拟地址。软件可以主动选择接收 52-bit 空间的地址传入一个大于 48-bit 的 mmap hint 参数即可例如 memory.rst 给出的示例maybe_high_address mmap(~0UL, size, prot, flags,...);~0UL作为 hint 意味着尽量往高地址分配从而触发 52-bit 地址空间的分配路径。这一行为在 arch/arm64/Kconfig 的ARM64_VA_BITS_52帮助文本中也有印证开启 52-bit 虚拟寻址后用户空间仅在通过 hint 显式请求时才使用 52-bit 地址内核自身也会在硬件支持时使用 52-bit 虚拟地址否则回退到 48-bit。此外还可以构建一个专门用于调试的内核强制所有用户空间地址都来自 52-bit 空间只需启用两个配置项CONFIG_EXPERTy CONFIG_ARM64_FORCE_52BITy从 arch/arm64/Kconfig 的说明来看CONFIG_ARM64_FORCE_52BIT会禁用 48-bit 兼容逻辑在硬件支持时把所有用户空间地址强制为 52-bit。该选项仅用于压力测试用户空间内存管理代码绝不应用于生产环境。另外需要注意一个安全权衡52-bit 虚拟寻址与 ARMv8.3 Pointer AuthenticationPAC组合使用时PAC 位数会从 7 位缩减到 3 位显著降低其抗暴力破解能力——Kconfig 对此有明确警告这也是官方建议不确定时选择 48-bit的原因之一。总结AArch64 Linux 的虚拟内存布局是一个由页大小、翻译表级数、VA 位数和 KASAN/KVM 等特性共同塑造的复杂系统TTBR0/TTBR1 按 bit 55 划分用户与内核空间PAGE_OFFSET、MODULES_VADDR、VMEMMAP、PCI_IO、FIXADDR_TOP依次排布于高半区52-bit 支持则以保持 PAGE_OFFSET 恒定 按 52-bit 规划 vmemmap 启动期回退为设计核心配合VA_BITS/VA_BITS_MIN/vabits_actual三组量实现单二进制双位宽兼容。理解这些机制是深入阅读 arch/arm64/mm/mmu.c、arch/arm64/include/asm/memory.h 以及 arch/arm64/kvm/va_layout.c 等核心代码的前提也是排查地址空间类内核问题的基础。【免费下载链接】linuxLinux kernel source tree项目地址: https://gitcode.com/GitHub_Trending/li/linux创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表