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

news/2025/10/14 20:15:47/文章来源:https://www.cnblogs.com/ljbguanli/p/19141929

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.mzph.cn/news/937055.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

实用指南:【Lsky-Pro开源图床】Lsky-Pro+cpolar:云端素材库的远程协作方案

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …

双指针的初步了解

双引用的初步了解 10.14今天在力扣上刷题,第一次了解到了双引用的概念,如图对于这个题,我一开始的思路是从0到size-1一步步遍历,如果找到值为val的,就删去,然后让计数器加1,最后输出计数器。但是不知道为什么总…

倍增并查集学习笔记

学完板子即可开始水紫题倍增并查集,可以在 \(O(m log^2 n)\) 的时间复杂度内求解 \(m\) 个诸如此类的合并问题: \[\forall \,\,\,\,\, 0 \leq i \leq k \, , \, merge(x+i,y+i) \]就真的是倍增和并查集的结合体,而不…

两数相加-leetcode

题目描述 给你两个 非空 的链表,表示两个非负的整数。它们每位数字都是按照 逆序 的方式存储的,并且每个节点只能存储 一位 数字。 请你将两个数相加,并以相同形式返回一个表示和的链表。 你可以假设除了数字 0 之外…

CF2147E

给定 \(n\) 个数和 \(q\) 次查询,每次查询给定 \(k\),问最多进行 \(k\) 次以下操作后 \(n\) 个数按位或的 popcount 最大值。 令 \(ans_i\) 表示使得 \(popcount \ge i\) 至少需要几次操作,显然 \(ans_i\) 单调不降…

线程共享区域

线程共享区域🔴 线程共享区域 (Thread-Shared Areas) #JVM/线程共享区域 🔴 特点:所有线程共享同一个内存区域,需要考虑线程安全问题,是垃圾回收的主要工作区域 🔴 1. Java堆 (Java Heap) #JVM/Java堆 🔴 定…

ZR 2025 NOIP 二十连测 #1

100 + 0 + 30 + 0 = 130, Rank 72/133.大模拟!/tuu25noip二十连测day1 链接:link 题解:题目内 时间:4.5h (2025.10.14 07:40~12:10) 题目数:4 难度:A B C D\(\color{#F39C11} 橙\)*1200估分:100 + 0 + 30 + 0 =…

work1

这个作业属于哪个课程:https://edu.cnblogs.com/campus/fzu/gjyycx 这个作业要求在哪里:https://edu.cnblogs.com/campus/fzu/gjyycx/homework/13558 学号:102500331 姓名:余武 安装过程:安装成功:代码结果:

2025 年液压机厂家推荐榜:伺服/小型/大型/数控/液压机厂家口碑推荐,品质可靠 聚焦智能适配,助力企业高效生产

随着制造业智能化升级、产品精度要求提升及生产效率优化需求增加,液压机作为关键成型设备,已从传统重工业领域逐步延伸至汽车零部件、五金制品、粉末冶金、电子元件等多个细分行业,2025 年市场规模预计持续扩大。但…

快速上手!山海鲸 4 种高频数据接入方式

在数据可视化实践中,“数据能顺畅接入” 是大屏发挥价值的前提。山海鲸数据可视化大屏针对不同业务场景与数据形态,推出了 4 种高频使用的数据接入方式,既降低了技术门槛,又保障了数据对接的稳定性与时效性,以下为…

AI4S Cup学习赛 - 超导体临界温度预测

AI4S Cup学习赛 - 超导体临界温度预测https://www.bohrium.com/competitions/3521345283?tab=mine

2025高级语言程序设计第一次作业lcr

班级链接:https://edu.cnblogs.com/campus/fzu/gjyycx 作业要求链接:https://edu.cnblogs.com/campus/fzu/gjyycx/homework/13558 我的学号:102500417 我的名字:刘朝榕 1.安装dev-c++2.成功安装好dev-c++3.编写示例…

D230809E. 勇敢的阿乐

题意: 一个 包含 \(n\) 个点 \(m\) 条边的简单无向连通图。现在,删掉其中的一些边让度数为奇数的点尽可能多。 输出要删掉哪些边, 用一个长为 \(m\) 的 01串 表示, 第 \(i\) 位为 \(1\) 表示不删第 \(i\) 条边, 为 \…

完整教程:面向.NET开发者:Prosys OPC UA .NET SDK 全面解析

完整教程:面向.NET开发者:Prosys OPC UA .NET SDK 全面解析pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Cons…

分布式秒杀系统设计方案 - 实践

分布式秒杀系统设计方案 - 实践2025-10-14 19:39 tlnshuju 阅读(0) 评论(0) 收藏 举报pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !importa…

高级程序语言第一次作业

这个作业属于哪个课程:高级语言程序设计 这个作业要求在哪里:https://edu.cnblogs.com/campus/fzu/gjyycx/homework/13558 学号:102500434 姓名:王志勇 一:dev-c++的安装:注意设置相关要求,安装正常。二:代码的…

安装devc++过程的分享以及问题的记录

课程:https://edu.cnblogs.com/campus/fzu/gjyycx 作业要求 https://edu.cnblogs.com/campus/fzu/gjyycx/homework/13558 学号:102500330 姓名:黄明浩1.解压压缩文件,进行的还算顺利,将程序存放到了一个全英文目录…

Linux之线程池 - 指南

Linux之线程池 - 指南pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "…

LlamaIndex检索调优实战:分块、HyDE、压缩等8个提效方法快速改善答案质量

分块策略、混合检索、重排序、HyDE、上下文压缩、元数据过滤、自适应k值——八个实用技巧快速改善检索质量 RAG系统答不准问题,本质上是检索精度的问题。LlamaIndex本身提供的调优空间还挺大,不用推翻重写就能把准确…

zlog1

一、python的基本语法 1.常见的数据类型:整数(int)【1,2,3】、浮点数(float)【1.1,1.2,1.3】、字符串(str)【abc, hello】、布尔值(bool)【0,1 (True=1,False=0)】、列表(list)【[1.2,3]】、元组(tuple)、字典(dic…