Uboot启动流程与嵌入式系统引导原理详解

Uboot启动流程与嵌入式系统引导原理详解 Uboot启动流程深度解析1. 嵌入式系统引导基础1.1 Bootloader核心概念在嵌入式系统开发中当我们从裸机程序转向使用操作系统时必须引入一段特殊的引导程序——这就是Uboot(Universal Boot Loader)。作为Bootloader的一种实现Uboot的主要职责是在加载操作系统内核之前完成硬件初始化、内存映射等关键任务为内核运行创造良好的环境。Uboot与普通Bootloader的主要区别在于其通用性和可移植性。它支持多种处理器架构(ARM、MIPS、PowerPC等)和丰富的硬件平台提供了统一的开发接口和命令集。这种设计使得Uboot成为嵌入式Linux系统开发的事实标准引导程序。1.2 嵌入式存储介质分析理解Uboot启动流程前必须掌握嵌入式系统中常见的存储介质特性存储器类型特性用途访问方式NOR Flash非易失性可直接执行代码(XIP)但写入需先擦除存储引导程序随机读取需特殊操作写入NAND Flash非易失性容量大价格低但不能直接执行代码存储内核和文件系统页访问需ECC校验SRAM静态随机访问速度快但容量小价格高关键代码执行环境直接读写SDRAM动态随机访问需定期刷新容量大主运行内存需初始化控制器在典型ARM系统中这些存储器的分工如下NOR Flash存储Uboot等引导程序上电后CPU直接从NOR Flash取指执行SRAM提供初始运行环境(如堆栈空间)速度快但容量有限SDRAM主运行内存需初始化后才能使用NAND Flash存储操作系统内核和文件系统等大容量数据2. Uboot链接与内存布局2.1 链接脚本分析Uboot的内存布局由链接脚本(如u-boot.lds)定义该文件位于u-boot/board/平台/目录下。以下是一个典型ARM平台的链接脚本片段OUTPUT_FORMAT(elf32-littlearm, elf32-littlearm, elf32-littlearm) OUTPUT_ARCH(arm) ENTRY(_start) /* 指定程序入口点为_start符号 */ SECTIONS { . 0x00000000; /* 起始地址 */ . ALIGN(4); .text : { cpu/arm920t/start.o (.text) /* 首先放置start.S的代码 */ *(.text) /* 其他代码段 */ } .rodata : { *(SORT_BY_ALIGNMENT(SORT_BY_NAME(.rodata*))) } .data : { *(.data) } /* 可读写数据段 */ .u_boot_cmd : { *(.u_boot_cmd) } /* Uboot命令表 */ .bss (NOLOAD) : { *(.bss) } /* 未初始化数据段 */ }链接脚本的关键作用包括指定程序入口点(_start)定义各段(.text, .data, .bss等)的布局顺序控制代码和数据的对齐方式为特殊段(如Uboot命令表)分配空间2.2 地址空间映射在实际硬件中Uboot的运行涉及多个地址空间加载地址Uboot镜像在Flash中的存储位置(如0x00000000)运行地址Uboot在RAM中的执行地址(如0x33F80000)重定位将Uboot从Flash复制到RAM的过程这种设计源于ARM处理器的启动特性。以S3C2440为例上电后NAND Flash前4KB内容自动加载到内部SRAM(Steppingstone)并执行这4KB代码负责初始化SDRAM控制器然后将完整Uboot从NAND Flash加载到SDRAM(如0x33F80000)最后跳转到SDRAM中执行3. Uboot启动第一阶段分析3.1 启动入口_startUboot的第一阶段代码通常用汇编编写位于cpu/架构/start.S。启动流程如下_start: b reset /* 复位向量 */ ldr pc, _undefined_instruction /* 未定义指令异常 */ ldr pc, _software_interrupt /* 软中断异常 */ /* 其他异常向量... */ reset: /* 设置CPU为SVC模式并禁用中断 */ mrs r0, cpsr bic r0, r0, #0x1f orr r0, r0, #0xd3 msr cpsr, r0 /* 关闭看门狗 */ ldr r0, pWTCON mov r1, #0x0 str r1, [r0] /* 屏蔽所有中断 */ mov r1, #0xffffffff ldr r0, INTMSK str r1, [r0]3.2 关键硬件初始化在进入C语言环境前Uboot需完成以下硬件初始化CPU模式设置切换到超级管理员(SVC)模式确保对系统资源的完全访问权限关闭看门狗防止在初始化过程中触发复位中断屏蔽避免初始化过程中的意外中断时钟配置设置CPU和总线时钟频率内存控制器初始化配置SDRAM时序参数内存初始化通常由平台特定的lowlevel_init.S完成主要任务包括设置内存控制器的时序参数配置存储器的bank和总线宽度验证内存访问是否正常3.3 代码重定位初始化基本硬件后Uboot需要将自己从Flash复制到RAM中relocate: ldr r0, _start /* 当前代码位置 */ ldr r1, _TEXT_BASE /* 目标位置(如0x33F80000) */ ldr r2, _bss_start sub r2, r2, r0 /* 计算需要复制的代码大小 */ copy_loop: ldmia r0!, {r3-r10} /* 从源地址加载8个字 */ stmia r1!, {r3-r10} /* 存储到目标地址 */ cmp r0, r2 ble copy_loop重定位完成后需要清除.bss段并设置堆栈指针为C语言环境做准备clear_bss: ldr r0, _bss_start ldr r1, _bss_end mov r2, #0x00000000 clbss_l: str r2, [r0] add r0, r0, #4 cmp r0, r1 ble clbss_l stack_setup: ldr r0, _TEXT_BASE sub sp, r0, #12 /* 预留abort栈空间 */4. Uboot启动第二阶段分析4.1 全局数据结构初始化进入第二阶段后Uboot首先初始化两个关键数据结构gd_t(global data)存储全局变量和系统状态typedef struct global_data { bd_t *bd; /* 板级信息 */ unsigned long flags; /* 状态标志 */ unsigned long baudrate; /* 串口波特率 */ /* 其他成员... */ } gd_t;bd_t(board info)存储板级特定信息typedef struct bd_info { int bi_baudrate; /* 控制台波特率 */ unsigned char bi_enetaddr[6]; /* MAC地址 */ ulong bi_arch_number; /* 板子唯一ID */ /* 其他成员... */ } bd_t;这些结构通过特殊方式分配在内存中确保在重定位前后都能正确访问。4.2 硬件设备初始化第二阶段通过初始化函数表完成各类硬件初始化init_fnc_t *init_sequence[] { cpu_init, /* CPU相关初始化 */ board_init, /* 板级初始化 */ interrupt_init, /* 中断控制器 */ env_init, /* 环境变量 */ init_baudrate, /* 串口波特率 */ serial_init, /* 串口设备 */ console_init_f, /* 控制台第一阶段 */ dram_init, /* SDRAM配置 */ NULL, }; for (init_fnc_ptr init_sequence; *init_fnc_ptr; init_fnc_ptr) { if ((*init_fnc_ptr)() ! 0) { hang(); /* 初始化失败则挂起 */ } }4.3 环境变量与命令系统Uboot提供了灵活的环境变量机制env_relocate(); /* 从存储介质加载环境变量 */ /* 设置IP地址 */ gd-bd-bi_ip_addr getenv_IPaddr(ipaddr); /* 设置MAC地址 */ char *s getenv(ethaddr); for (int i 0; i 6; i) { gd-bd-bi_enetaddr[i] s ? simple_strtoul(s, e, 16) : 0; if (s) s (*e) ? e 1 : e; }命令系统通过.u_boot_cmd段实现每个命令对应一个cmd_tbl_t结构struct cmd_tbl_s { char *name; /* 命令名 */ int maxargs; /* 最大参数 */ int repeatable; /* 是否可重复 */ int (*cmd)(struct cmd_tbl_s *, int, int, char *[]); /* 处理函数 */ /* 其他成员... */ };5. Uboot到内核的过渡5.1 主循环与自动引导Uboot最终进入主循环处理用户输入或自动引导void main_loop(void) { char *s; int bootdelay getenv_ulong(bootdelay, 10, CONFIG_BOOTDELAY); /* 检查是否中断自动引导 */ if (bootdelay 0 !abortboot(bootdelay)) { s getenv(bootcmd); if (s) run_command(s, 0); /* 执行引导命令 */ } /* 命令行交互模式 */ for (;;) { len readline(CFG_PROMPT); if (len 0) run_command(console_buffer, 0); } }5.2 内核加载机制典型的引导命令流程包括从存储介质(NAND/NOR Flash、MMC等)加载内核镜像必要时解压内核(zImage或uImage)设置启动参数(ATAGs或设备树)跳转到内核入口点/* 示例NAND Flash引导Linux */ nand read 0x30008000 0x60000 0x200000 bootm 0x30008000跳转到内核前Uboot需确保内存控制器已正确初始化内核镜像已加载到正确位置启动参数已按内核要求格式设置CPU处于适当的模式(通常关闭MMU和缓存)6. 关键问题与优化6.1 NOR与NAND启动选择选择启动介质时的工程考量NOR Flash启动优势代码可直接执行(XIP)简化初始引导可靠性高适合工业环境调试方便支持在线编程NAND Flash启动优势成本低适合消费类产品容量大可存储完整系统主流方案社区支持完善实际选择需权衡产品定位(工业/消费)成本敏感度生产便利性系统复杂度6.2 重定位技术细节Uboot重定位涉及的关键技术位置无关代码(PIC)确保代码在任意地址都能正确执行全局偏移表(GOT)解决跨模块数据访问问题重定位表记录所有需要修正的地址引用/* GOT表示例 */ extern uint32_t _GLOBAL_OFFSET_TABLE_[]; /* 通过GOT访问变量 */ uint32_t *var_ptr _GLOBAL_OFFSET_TABLE_[index];6.3 启动时间优化优化Uboot启动时间的常用方法精简功能移除不必要的驱动和命令并行初始化合理调度硬件初始化顺序使用更快的存储介质(如SPI NOR替代NAND)优化重定位过程减少内存拷贝延迟初始化非关键外设