找回密码
立即注册
搜索
热搜: Java Python Linux Go
发回帖 发新帖

6052

积分

0

好友

774

主题
发表于 昨天 15:53 | 查看: 7| 回复: 0

今天继续沿着 一份靠谱的BSP开发学习路线 梳理 BSP 开发的关键知识点—— Linux 内核启动流程。以下分析基于爱芯 AX615 armv7 系列 4.19.125 版本的 内核源码,完整覆盖从 BootROM 到用户态 init 进程的各个阶段。

一、启动总览(AX615 架构)

AX615 采用 armv7 架构,内核启动遵循 BootROM -> U-Boot -> 内核自解压 -> 汇编初始化 -> C 语言核心初始化 -> 用户态切换 的标准流程,各阶段严格衔接,完成从硬件到系统的初始化。

核心流程如下:

  • 硬件上电 → BootROM(片上 ROM) → 加载 U-Boot
  • U-Boot → 加载内核镜像(zImage/Image)+ DTB → 跳转内核入口
  • 内核自解压(压缩镜像) → 执行汇编入口 stext
  • 汇编初始化 → 建立页表 / MMU → 跳转 start_kernel(C 入口)
  • start_kernel → 子系统初始化 → rest_init → 内核线程 kernel_init
  • 挂载根文件系统 → 启动用户态 init 进程 → 系统就绪

二、阶段 1:BootROM 与 U-Boot 引导(内核启动前序)

1. BootROM 阶段(AX615 片上 ROM,不可修改)

  • 功能:上电复位后 CPU 从固定地址执行,初始化时钟、DDR 控制器、存储接口(eMMC/SD/SPI-NOR),从指定介质加载 U-Boot 到 DDR。
  • 关键路径:AX615 专用 BootROM 代码(厂商提供,非内核源码)。
  • 参数传递:加载 U-Boot 后,将启动介质、DDR 配置等信息传递给 U-Boot。

2. U-Boot 阶段(AX615 定制 U-Boot,内核启动“桥梁”)

核心任务:

  • 完整初始化 DDR(AX615 专用 DDR 驱动),建立内存运行环境。
  • 从存储介质读取内核镜像(zImage/Image)与设备树(DTB,xxx.dtb)到 DDR 指定地址。
  • 解析启动参数(bootargs),设置寄存器:r0=0r1=机器IDr2=DTB物理地址
  • 执行 bootm 命令,跳转至内核镜像入口,将控制权完全交给内核。

三、阶段 2:内核自解压(压缩镜像必备)

1. 入口与核心文件

  • 入口地址:U-Boot 跳转的内核镜像起始地址(物理地址,MMU 关闭)。
  • 核心文件路径(内核源码):arch/arm/boot/compressed/head.S
  • 链接脚本:arch/arm/boot/compressed/vmlinux.lds.S(定义解压代码布局)

2. 自解压流程(head.S 执行)

  • 保存 U-Boot 传递的寄存器参数(r0/r1/r2
  • 初始化解压栈,关闭中断,配置 CPU 缓存(D-cache/I-cache)
  • 调用 decompress_kernel()arch/arm/boot/compressed/misc.c),解压内核镜像到 DDR 高端地址
  • 解压完成后打印:Uncompressing Linux... done, booting the kernel.
  • 清理解压环境,跳转至内核真正入口 stext(虚拟地址 0xffff000008000000,armv7 标准)

四、阶段 3:汇编初始化(stext,内核第一行执行代码)

1. 核心文件与入口

  • 入口符号:stext(内核链接脚本 ENTRY(stext) 指定)
  • 文件路径:arch/arm/kernel/head.S(armv7 架构核心汇编文件)
  • 链接脚本:arch/arm/kernel/vmlinux.lds.S(编译生成 vmlinux.lds,定义内核内存布局)

2. 汇编初始化关键步骤(head.S

(1)CPU 与架构校验

  • 检查 CPU ID 是否匹配 AX615,不匹配则启动失败。
  • 验证机器类型与 DTB 兼容性,确保硬件与内核配置一致。

(2)内存与 MMU 初始化(最核心)

  • 建立内核页表(swapper_pg_dirarch/arm/mm/proc.S),完成物理地址→虚拟地址映射。
  • 使能 MMU 与缓存(D-cache/I-cache),切换到虚拟地址空间,为 C 代码运行奠定基础。
  • 初始化内核栈(init_thread_union),设置栈指针 sp

(3)BSS 段清零与参数处理

  • 清零 .bss 段(未初始化全局变量),避免随机值干扰。
  • 解析 U-Boot 传递的 DTB 地址(r2),保存至全局变量 __fdt_pointer
  • 关闭所有中断,设置异常向量表(vectorsarch/arm/kernel/entry.S)。

(4)跳转 C 语言入口

  • 执行 bl start_kernel,从汇编世界进入 C 语言世界,内核核心初始化开始。

五、阶段 4:C 语言核心初始化(start_kernel,内核“总控中心”)

1. 核心文件与入口

  • 函数定义:init/main.c(内核通用 C 入口,所有架构共享)
  • 函数原型:asmlinkage __visible void __init start_kernel(void)

2. start_kernel 关键初始化流程(按执行顺序)

(1)基础环境初始化

  • sched_clock_init():初始化调度时钟,为时间管理奠定基础。
  • setup_arch(&command_line):AX615 架构核心初始化arch/arm/kernel/setup.c
  • 解析 DTB,获取硬件信息(内存、中断、时钟、外设)
  • 初始化内存管理(memblock),标记可用 / 保留内存区域
  • 注册平台设备与驱动
  • parse_early_param():解析内核启动参数(bootargs
  • setup_log_buf():初始化日志缓冲区,启动内核打印

(2)子系统初始化(核心模块依次启动)

Linux内核启动关键初始化函数表

(3)后续初始化与线程启动

  • vfs_caches_init():虚拟文件系统缓存初始化
  • buffer_init()/pagecache_init():页缓存、缓冲区初始化
  • fork_init():进程管理初始化(最大 PID、进程表)
  • rest_init():启动内核线程,进入用户态准备(init/main.c

六、阶段 5:rest_init 与用户态切换(内核 → 用户空间)

1. rest_init 核心逻辑(init/main.c

  • 创建内核线程 1(PID=1):执行 kernel_init 函数(内核第一个线程)
  • 创建 kthreadd 线程(PID=2):管理所有内核线程,负责内核线程创建 / 销毁
  • 0 号进程(init_task)进入 cpu_idle() 循环,成为空闲进程(系统无任务时执行)

2. kernel_init 流程(init/main.c,PID=1)

(1)内核线程初始化

  • kernel_init_freeable():完成剩余内核初始化(驱动、网络、文件系统)
  • do_basic_setup()
    • driver_init():设备驱动模型初始化(总线、设备、驱动注册)
    • initcalls:执行所有 __init 标记的初始化函数(AX615 外设驱动:I2C、SPI、GPIO、以太网等)

(2)挂载根文件系统(关键)

  • prepare_namespace():解析 root= 参数,准备挂载根文件系统
  • mount_root():实际挂载根分区,完成从临时文件系统到真实根文件系统的切换

(3)启动用户态 init 进程

  • init_post()
    • 关闭内核控制台,释放启动资源
    • 执行 run_init_process(),尝试启动用户态 init 程序(顺序:/sbin/init/etc/init/bin/init/bin/sh
    • 若成功,内核态 PID=1 消失,用户态 init 进程(PID=1)接管系统,进入用户空间

七、专用关键文件汇总

架构通用核心文件:

  • 内核入口汇编:arch/arm/kernel/head.S
  • 自解压入口:arch/arm/boot/compressed/head.S
  • C 语言总入口:init/main.cstart_kernel/rest_init/kernel_init
  • 架构初始化:arch/arm/kernel/setup.c
  • 内存管理:arch/arm/mm/mmu.cmm/mm_init.c
  • 中断处理:arch/arm/kernel/irq.ckernel/irq/irqdesc.c

八、启动流程关键节点总结

  1. BootROM:硬件上电 -> 初始化基础外设 -> 加载 U-Boot
  2. U-Boot:DDR 初始化 -> 加载内核 + DTB -> 跳转内核入口
  3. 自解压arch/arm/boot/compressed/head.S -> 解压内核 -> 跳转 stext
  4. 汇编初始化arch/arm/kernel/head.S -> MMU/页表/栈初始化 -> bl start_kernel
  5. C 核心初始化init/main.c -> start_kernel -> 子系统初始化 -> rest_init
  6. 用户态切换kernel_init -> 挂载根文件系统 -> 启动 /sbin/init -> 系统就绪

九、调试与排查建议

  1. 启动卡死在解压阶段:检查 U-Boot 加载内核地址、DDR 初始化是否正常、内核镜像是否损坏。
  2. 卡死在 stext 汇编阶段:核对 DTB 与内核兼容性、MMU 页表配置、AX615 CPU ID 校验。
  3. 卡死在 start_kernel 之后:开启内核调试打印(bootargs 添加 debug),定位子系统初始化失败点。
  4. 根文件系统挂载失败:检查 root= 参数、存储驱动(eMMC/SD)是否初始化、文件系统格式是否正确。

以上为全文内容。




上一篇:Squashfs+initrd 根文件系统内存化改造:AX615 平台踩坑实录与避坑要点
下一篇:PacketSender开源网络发包工具实战:TCP私有协议调试与模拟请求
您需要登录后才可以回帖 登录 | 立即注册

手机版|小黑屋|网站地图|云栈社区 ( 苏ICP备2022046150号-2 )

GMT+8, 2026-9-10 15:14 , Processed in 1.175945 second(s), 40 queries , Gzip On.

Powered by Discuz! X3.5

© 2025-2026 云栈社区.

快速回复 返回顶部 返回列表