Linux 内存管理之早期页表的初始化过程

张开发
2026/5/17 9:27:12 15 分钟阅读
Linux 内存管理之早期页表的初始化过程
以 RK3588(ARMv8/AArch64、Linux 5.10+)为例,早期页表初始化是内核从 “物理地址裸跑” 切换到 “虚拟地址运行” 的关键步骤,核心代码集中在 arch/arm64/kernel/head.S 的 __create_page_tables,以及 C 语言阶段的 paging_init / map_kernel / __create_pgd_mapping。下面按 汇编阶段(MMU 开启前)→ C 语言阶段(MMU 开启后) 完整拆解,并结合 RK3588 硬件地址布局说明。一、前置背景:RK3588 地址布局 ARM64 页表级别RK3588 典型物理内存布局(Linux 视角)内核镜像加载:0x00200000 ~ 0x02000000(约 32MB)DRAM 起始:0x00000000(低 512MB/1GB/4GB 等)设备寄存器(GPIO/CRU/UART/PCIe):0xF8000000 ~ 0xFFFFFFFF 等内核虚拟地址:FFFF 0000 0000 0000 +(48 位 VA,高 16 位全 1)The Linux Kernel ArchivesARM64 4 级页表(4KB 页,48 位 VA)PGD → PUD → PMD → PTEPGD:页全局目录(每个表项 512GB)PUD:页上层目录(每个表项 1GB)PMD:页中间目录(每个表项 2MB)PTE:页表(每个表项 4KB)早期页表两个核心目标恒等映射(idmap):virt = phys,用于开启 MMU 瞬间不跑飞内核线性映射(swapper):virt = phys + PAGE_OFFSET(FFFF 0000 0000 0000)二、汇编阶段:__create_page_tables(head.S)位置:arch/arm64/kernel/head.S调用时机:_start → __primary_switch → __create_page_tables(MMU=OFF)代码逐行详解(核心片段)SYM_FUNC_START_LOCAL(__create_page_tables)mov x28,lr// 保存返回地址// 1. 清空 无效化初始页表内存(init_pg_dir)adrp x0,init_pg_dir// x0 = 物理地址:init_pg_dir(早期页表基址)adrp x1,init_pg_end sub x1,x1,x0// x1 = 页表总大小bl __inval_dcache_area// 清缓存,避免旧数据干扰bl __pi_memset// 全0初始化页表mov x7,SWAPPER_MM_MMUFLAGS// 页表属性:Inner/Outer Shareable、AF、WriteCombine等// ==============================================// 2. 建立【恒等映射 idmap】(TTBR0用,MMU开启瞬间)// ==============================================adrp x0,idmap_pg_dir// 恒等映射页表基址adrp x3,__idmap_text_start// 映射范围:内核idmap代码段adrp x4,__idmap_text_end create_sect_aligned x5,x3,x4// 对齐到section边界(2MB/1GB)ldr x6,=(PAGE_OFFSET-0x0)// 恒等:virt = physbl __create_block_map// 填充PGD/PUD/PMD/PTE// ==============================================// 3. 建立【内核线性映射】(TTBR1用,内核虚拟地址)// ==============================================adrp x0,init_pg_dir// 内核初始页表(swapper前身)adrp x3,_text// 映射:

更多文章