当前位置: 首页 > news >正文

U-Boot启动探秘:从汇编到命令行的奇幻之旅 - 指南

U-Boot启动探秘:从汇编到命令行的奇幻之旅 - 指南

引言

在嵌入式Linux系统中,U-Boot作为最主流的BootLoader,其启动过程犹如一场精心编排的舞台剧,分为上下两个半场。上半场(Stage1)是用汇编语言编写的硬件初始化“序章”,寂静而迅速;下半场(Stage2)则是用C语言构建的软件生态“主剧”,丰富而互动。理解这场“演出”的每一个环节,是进行U-Boot移植、定制和深度调试的基石。本文将带领大家深入U-Boot的源码世界,逐一解析其Stage1与Stage2的核心步骤。


一、 U-Boot的两种模式与两个阶段

在深入细节之前,我们首先回顾U-Boot的宏观架构:


二、 Stage1:汇编世界的奠基礼

Stage1是整个U-Boot的基石,其代码通常位于 cpu/<arch>/start.S 文件中(例如,对于ARM920T,路径为 cpu/arm920t/start.S)。它的目标是构建一个能够运行C代码的最小化环境。

2.1 入口点与链接脚本

一个可执行的映像文件必须有一个唯一的全局入口点。U-Boot通过链接脚本(如 board/smdk2410/u-boot.lds)来定义这个入口。

OUTPUT_ARCH(arm)
ENTRY(_start) // 定义入口点为 _start 符号
SECTIONS
{
. = 0x00000000; // 入口地址在Flash的0地址
.text :
{
cpu/arm920t/start.o (.text) // 确保start.S的代码在最前面
*(.text)
}
// ... 其他段(.rodata, .data, .bss等)
}

TEXT_BASE = 0x33F80000 定义了U-Boot在RAM中运行的基地址,Stage1后期会将自身重定位到此地址执行。

2.2 Stage1 核心步骤详解

1. 设置异常向量表
位于 _start 处,这是CPU上电后执行的第一条指令。

.globl _start
_start: b reset           // 复位异常,跳转到真正的启动代码ldr pc, _undefined_instructionldr pc, _software_interrupt// ... 其他异常向量

当发生任何异常时,CPU会自动跳转到对应的地址执行。

2. 进入SVC模式
复位后,首先将CPU设置为管理模式,此模式拥有最高特权,可以执行所有操作。

mrs r0, cpsr         // 读取当前程序状态寄存器CPSR到r0
bic r0, r0, #0x1f    // 清除模式位
orr r0, r0, #0xd3    // 设置为SVC模式,并禁止IRQ和FIQ中断
msr cpsr, r0         // 写回CPSR

3. 关闭看门狗
看门狗的作用是防止程序死机,但在初始化阶段,我们需要先关闭它,防止其误触发复位。

ldr r0, =pWTCON      // pWTCON为看门狗控制寄存器地址
mov r1, #0x0
str r1, [r0]         // 向控制寄存器写入0,关闭看门狗

4. 屏蔽所有中断
在初始化过程中,不允许被中断打扰。

mov r1, #0xffffffff
ldr r0, =INTMSK
str r1, [r0]         // 屏蔽所有主中断

5. 设置CPU时钟与分频
配置CPU核心时钟(FCLK)、AHB总线时钟(HCLK)和APB总线时钟(PCLK)的比例。

ldr r0, =CLKDIVN
mov r1, #3           // 设置分频比,例如 FCLK:HCLK:PCLK = 1:2:4
str r1, [r0]

6. 设置CP15协处理器
CP15用于控制MMU、Cache等系统核心功能。

cpu_init_crit:mov r0, #0mcr p15, 0, r0, c7, c7, 0  // 使ICache和DCache失效mcr p15, 0, r0, c8, c7, 0  // 使TLB失效// 关闭MMU和Cachemrc p15, 0, r0, c1, c0, 0bic r0, r0, #0x00002300     // 清除控制位bic r0, r0, #0x00000087orr r0, r0, #0x00000002     // 对齐检查使能orr r0, r0, #0x00001000     // 使能ICachemcr p15, 0, r0, c1, c0, 0

7. 初始化内存控制器
这是最关键也是最板级相关的一步。通过配置内存控制寄存器,让CPU能够正确地访问SDRAM。代码通常在 board/<board>/lowlevel_init.S 中。

mov ip, lr
bl lowlevel_init    // 调用低级初始化函数,配置内存时序
mov lr, ip
mov pc, lr

8. 设置栈指针并为BSS段清零
C语言的运行需要栈空间。同时,未初始化的全局变量位于BSS段,需要将其初始化为0。

// 栈设置(在RAM中分配空间)
stack_setup:
ldr r0, _TEXT_BASE  // TEXT_BASE = 0x33F80000
sub r0, r0, #CFG_MALLOC_LEN // 分配动态内存区
sub r0, r0, #CFG_GBL_DATA_SIZE // 分配全局数据结构体空间
sub sp, r0, #12     // 设置栈指针sp
// BSS段清零
clear_bss:
ldr r0, _bss_start
ldr r1, _bss_end
mov r2, #0
clbss_l:
str r2, [r0]
add r0, r0, #4
cmp r0, r1
ble clbss_l

9. 跳转到Stage2
至此,C语言运行环境已准备就绪,通过一条简单的指令,跳转到Stage2的C入口函数。

ldr pc, _start_armboot
_start_armboot: .word start_armboot // start_armboot是C函数地址

三、 Stage2:C语言的繁华世界

Stage2的入口函数是 lib_arm/board.c 中的 start_armboot()。从这里开始,世界变得“丰富多彩”。

3.1 初始化函数序列

start_armboot 函数会依次调用一个初始化函数表 init_sequence[],完成一系列基础设置:

init_fnc_t *init_sequence[] = {
cpu_init,        // 进一步的CPU设置
board_init,      // 开发板相关的GPIO、时钟初始化
interrupt_init,  // 初始化中断控制器
env_init,        // 初始化环境变量存储
init_baudrate,   // 初始化控制台波特率
serial_init,     // 初始化串口
console_init_f,  // 初始化控制台(第一阶段)
display_banner,  // 打印U-Boot启动信息
print_cpuinfo,   // 打印CPU信息
checkboard,      // 打印开发板信息
dram_init,       // 计算并保存SDRAM配置信息
NULL,
};

每个函数指针非NULL的函数都会被依次执行,如果任何函数执行失败,系统将挂起。

3.2 外围设备初始化全景

在基础初始化之后,U-Boot开始初始化更复杂的外设,为加载内核和用户交互做准备:

  1. Flash初始化flash_init() - 识别并初始化Nor Flash,建立Flash分区信息。
  2. 内存分配器初始化mem_malloc_init() - 初始化堆管理器,为后续的动态内存申请(如malloc)做准备。
  3. NAND Flash初始化nand_init() - 如果系统支持NAND,则初始化NAND控制器和驱动。
  4. 环境变量重定位env_relocate() - 将环境变量从Flash(如Nor的特定扇区或NAND的OOB区)加载到RAM中。
  5. 外围设备初始化devices_init() - 初始化其他平台设备。
  6. 中断使能enable_interrupts() - 此时才使能系统中断。
  7. 网络初始化:如 cs8900_get_enetaddr() - 初始化网卡,获取MAC地址,为网络下载功能做准备。
  8. 总线初始化i2c_init() - 初始化I2C总线,用于访问EEPROM、PMIC等I2C设备。
  9. 显示设备初始化drv_lcd_init() - 初始化LCD控制器和背光,为图形化启动界面或LOGO显示做准备。
3.3 终极舞台:主循环

当所有初始化工作完成后,U-Boot最终进入它的“舞台中心”——main_loop() 函数。

for (;;) {
main_loop(); // 位于 common/main.c
}

main_loop() 中,U-Boot会:

  • 执行启动延时倒计时。
  • 处理用户输入(在倒计时内按下任意键进入命令行模式)。
  • 根据环境变量(如 bootcmd)自动启动内核(启动加载模式)。
  • 如果进入命令行模式,则解析并执行用户输入的各种命令(下载模式)。

总结

U-Boot的启动过程是一个从底层硬件到上层应用的完美递进:

  • Stage1(汇编阶段):在“黑暗”中摸索,从设定异常向量开始,逐步构建光明(初始化SDRAM),最终搭建起C语言的舞台(设置栈、清零BSS段)。它的每一步都至关重要,任何错误都会导致系统“猝死”。
  • Stage2(C语言阶段):在Stage1搭建的舞台上,各种“角色”依次登场(CPU、串口、内存、Flash、网卡等),按照严格的顺序完成初始化。最终,舞台准备就绪,U-Boot作为“主持人”登场,要么按照剧本(bootcmd)自动执行,要么等待观众(用户)的指令。

理解这个过程,就如同掌握了嵌入式系统从“沉睡”到“苏醒”的全景图,无论是进行系统移植、性能优化还是疑难排查,都将得心应手。

http://www.hskmm.com/?act=detail&tid=31111

相关文章:

  • 双指针的初步了解
  • 倍增并查集学习笔记
  • 两数相加-leetcode
  • CF2147E
  • 线程共享区域
  • ZR 2025 NOIP 二十连测 #1
  • 运行时数据区
  • work1
  • 2025 年液压机厂家推荐榜:伺服/小型/大型/数控/液压机厂家口碑推荐,品质可靠 聚焦智能适配,助力企业高效生产
  • 快速上手!山海鲸 4 种高频数据接入方式
  • AI赋能,重塑未来招聘:深度解析易路AI人岗匹配解决方案
  • 2025高级语言程序设计第一次作业lcr
  • luogu 个人主页
  • D230809E. 勇敢的阿乐
  • 解码Linux文件IO之标准IO
  • 10.14 CSP-S模拟31 改题记录
  • 高级程序语言第一次作业
  • 安装devc++过程的分享以及问题的记录
  • Linux之线程池 - 指南
  • zlog1
  • LlamaIndex检索调优实战:分块、HyDE、压缩等8个提效方法快速改善答案质量
  • 动火作业风险早预警!AI + 热成像技术筑牢防火安全线
  • 解题报告-P5664 [CSP-S2019] Emiya 家今天的饭
  • object类
  • Day 10
  • 2025 年生态格宾网厂家推荐榜:格宾网石笼/格宾网护坡/格宾网挡墙/格宾网网箱厂家推荐,聚焦工程安全与生态保护,助力基建项目高效落地
  • 时序博弈算法荣获时间检验奖
  • 背叛 仇恨 消极 如刀子刺穿了铁心 嘲笑 嗤之以鼻 漠然后只剩下孤寂
  • STM32主控芯片硬件设计总结
  • 亚马逊因暗黑模式订阅设计支付25亿美元和解金