
setup_arch是 Linux 内核启动时架构相关初始化的核心入口由start_kernel调用负责完成 CPU、内存、硬件拓扑、页表等底层平台初始化为后续通用内核代码运行搭建硬件环境。调用时机内核从汇编阶段head.S进入 C 语言阶段后start_kernel中第一个关键架构相关调用。代码位置每个架构独立实现路径为arch/arch/kernel/setup.c如x86_64、arm64、riscv。函数原型void __init setup_arch(char **cmdline_p);cmdline_p传递并保存内核命令行来自 Bootloader。通用核心职责跨架构共性硬件信息解析x86读取ACPI/BIOS/E820内存布局、CPU 拓扑、PCI 信息。ARM/ARM64/RISC‑V解析设备树DT获取 CPU、内存、中断控制器、外设等硬件描述。CPU 初始化识别 CPU 型号、特性缓存、MMU、指令集、虚拟化。初始化异常向量表、中断控制器GIC/APIC、SMP 多核启动逻辑。内存管理初始化关键路径初始化memblock登记物理内存、保留内核镜像 /initrd/IO 区域。建立内核页表、开启 MMU、完成虚拟‑物理地址映射paging_init。初始化早期内存分配器bootmem/memblock。命令行处理保存boot_command_line解析早期启动参数console、root、mem等。平台杂项初始化时钟、IO 映射、固件接口如 UEFI、安全特性KASLR、SMAP。主流架构实现差异1. x86_64arch/x86/kernel/setup.c依赖E820/ACPI获取内存与硬件信息。关键流程setup_arch ├── early_platform_quirks() ├── e820__memory_setup() // 解析内存布局 ├── memblock_x86_fill() // 填充 memblock ├── setup_real_mode() ├── init_mem_mapping() // 建立内核直接映射 ├── paging_init() // 初始化页表 └── reserve_initrd() // 保留 initrd2. ARM64arch/arm64/kernel/setup.c依赖设备树FDT描述硬件。关键流程setup_arch ├── early_init_fdt_scan() // 扫描设备树 ├── setup_initial_init_mm() // 初始化 init_mm ├── arm64_memblock_init() // 内存初始化 ├── paging_init() // 开启 MMU ├── cpu_init() // 启动 CPU 初始化 └── parse_early_param() // 解析命令行3. RISC‑Varch/riscv/kernel/setup.c纯设备树驱动无 BIOS/ACPI 层。关键流程setup_arch ├── setup_bootmem() ├── parse_dtb() // 解析设备树 ├── paging_init() ├── init_cpu_features() └── time_init()关键子函数与数据结构memblock早期物理内存管理器负责内存登记与保留。paging_init架构相关页表初始化开启 MMU建立内核虚拟地址空间。setup_processor识别并初始化 CPU 特性、缓存、异常模式。machine_descARM板级硬件描述结构体包含内存、中断、IO 映射等回调。启动流程中的位置Bootloader → 内核镜像head.S→ start_kernel() → setup_arch() → 后续通用初始化AArch64 (ARM64) setup_arch 完整详细流程函数定义void __init setup_arch(char **cmdline_p)调用者init/main.c→start_kernel()第一个关键函数核心使命把 ARM64 硬件环境全部准备好让后续通用内核代码可以运行依赖硬件DTB (设备树二进制)ARM64 唯一硬件描述来源1. 保存内核命令行最开始*cmdline_p boot_command_line;把 bootloader 传递的命令行交给内核全局使用后续所有参数解析console、root、mem 等都依赖它2. 早期设备树扫描硬件信息入口early_init_fdt_scan_reserved_mem(); early_init_dt_scan(__fdt_pointer);作用找到 DTB 地址bootloader 放在 x0 寄存器传入扫描设备树中的保留内存区域不能被内核占用这是 ARM64 认识硬件的第一步3. 初始化内存描述结构 init_mmsetup_initial_init_mm(_text, _etext, _edata);初始化内核第一个地址空间init_mm标记内核代码段、数据段的物理地址范围为后续页表、内存管理打基础4. 反冻结 UEFI 内存UEFI 平台必备efi_fake_mem_unfreeze();仅 UEFI 启动有效释放 UEFI 保留的内存让内核可以管理5. 核心ARM64 memblock 内存初始化最重要arm64_memblock_init();这是整个 setup_arch 最关键步骤内部流程从 DTB 读取所有物理内存布局把内存注册到memblock早期内存管理器自动保留内核镜像自身DTB 设备树initramfsUEFI 运行时服务硬件预留区域检查内存合法性修复内存重叠问题一句话告诉内核 “哪里有内存、哪里不能用”。6. 页表初始化 开启 MMU虚拟地址启动paging_init();ARM64 最核心动作创建内核TTBR0/TTBR1页表建立线性映射线性地址 物理地址 偏移开启MMU从此内核运行在虚拟地址空间配置内存属性缓存、设备内存、权限没有这一步内核无法正常运行 C 语言代码。7. 解析早期命令行参数parse_early_param();解析必须在内存初始化前生效的参数mem限制内存大小nommu关闭 MMUcpuidle、nohaltconsole早期控制台8. 初始化 CPU 相关结构cpu_init();初始化启动 CPUBoot CPU的运行环境设置异常级别、CPU 状态初始化 GIC 中断控制器依赖结构9. 初始化 FDT 区域设备树最终确认early_init_fdt_reserve_self(); early_init_fdt_scan_reserved_mem();把 DTB 所在物理内存永久保留不被覆盖二次扫描设备树预留内存10. DMA 预留区域初始化dma_contiguous_reserve();为设备驱动预留连续物理内存CMA网卡、GPU、显示等设备必须依赖11. 初始化 ACPIARM64 可选一般不用acpi_table_init(); acpiboot_map_mem();ARM64 服务器才用 ACPI嵌入式设备只走 DTB。12. 最后检查并修复内存漏洞memblock_allow_resize(); memblock_dump_all();允许 memblock 调整大小打印内存布局日志dmesg 可见确保所有硬件资源安全可用setup_arch 最终完成了什么当setup_arch返回start_kernel时内核已经具备✅ 识别全部 CPU✅ 识别全部物理内存✅ 建立完整页表✅ 开启 MMU虚拟内存✅ 保留所有硬件 / 固件 / 内核自身内存✅ 解析设备树知道所有外设✅ 准备好中断、异常、CPU 运行环境此时内核已经从 “裸机” 变成 “完整虚拟地址环境”。极简流程图start_kernel ↓ setup_arch ├─ 保存命令行 ├─ 扫描 DTB设备树 ├─ 初始化 init_mm ├─ memblock 初始化注册内存 ├─ paging_init建页表 开 MMU ├─ 解析早期启动参数 ├─ cpu_init初始化启动核 ├─ DMA 连续内存预留 └─ 内存检查 打印布局 ↓ 返回 start_kernel继续内核通用初始化最关键的 3 个函数early_init_dt_scan找到硬件arm64_memblock_init管理内存paging_init开启虚拟内存