当前位置:首页>科技>arm 启动方式ARM上电后都干了哪些事
发布时间:2026-07-25阅读(1)
关于本文配套的视频《uboot启动详解-1》《uboot启动详解-2》

我们在前面的arm系列课程,已经讲解了arm的架构、汇编指令、异常、常用外设的控制器驱动,那么我们已经具备开发arm系列产品的基本技能。
本篇给大家介绍一款比较常用的bootloader:uboot,通过uboot的介绍以及源代码的详细分析,让大家把之前所有ARM相关的知识点融会贯通起来。
一、uboot1. 概念U-Boot 是一个主要用于嵌入式系统的引导加载程序,可以支持多种不同的计算机系统结构,包括PPC、ARM、AVR32、MIPS、x86、68k、Nios与MicroBlaze。这也是一套在GNU通用公共许可证之下发布的自由软件。
U-Boot不仅仅支持嵌入式Linux系统的引导,它还支持NetBSD, VxWorks, QNX, RTEMS, ARTOS, LynxOS, android嵌入式操作系统。其目前要支持的目标操作系统是OpenBSD, NetBSD, FreeBSD,4.4BSD, Linux, SVR4, Esix, Solaris, Irix, SCO, Dell, NCR, VxWorks, LynxOS, pSOS, QNX, RTEMS, ARTOS, android。
2. uboot基本功能U-Boot可支持的主要功能列表:
uboot命令比较多,下面只列举网络启动要用到的命令:
命令 含义 bootdelay 执行自动启动(bootcmd中的命令)的等候秒数 baudrate 串口控制台的波特率 netmask 以太网的网络掩码 ethaddr 以太网的MAC地址 bootfile 默认的下载文件名 printenv 打印Uboot环境变量 setenv 设置Uboot环境变量 ipaddr 本地的IP地址 serverip TFTP服务器端的IP地址 gateway 以太网的网关 bootcmd 自动启动时执行命令 bootargs 传递给Linux内核的启动参数 bootm 引导启动存储在内存中的程序映像。这些内存包括RAM和可以永久保存的Flash。
4. 配置参数举例以下以网络下载内核、网络挂载nfs为例。
1)ubuntu环境ubuntu ip:192.168.6.186
nfs配置:
配置文件如下:
/etc/exports
配置信息如下:

nfs
2)开发板设置开发板ip:192.168.6.187
配置命令:
setenvipaddr192.168.6.187;板子的ipsetenvserverip192.168.6.186;虚拟机的ipsetenvgatewayip192.168.1.1;网关saveenv;保存配置
setenvbootcmdtftp41000000uImage\;tftp42000000exynos4412-fs4412.dtb\;bootm41000000-42000000
bootcmd:uboot2启动之后,首先先执行找到这个参数,执行后面的命令。 从tftp服务器下载内核镜像uImage到地址41000000,设备树文件exynos4412-fs4412.dtb到42000000,并通过命令bootm加载启动内核。
setenvbootargsroot=/dev/nfsnfsroot=192.168.6.186:/rootfsrwconsole=ttySAC2,115200init=/linuxrcip=192.168.6.187
挂载nfs文件系统,
要想了解exynos-4412的启动顺序,我们首先需要了解该soc的内存布局。
1. exynos-4412内存布局通常一款soc的内存在厂家设计的时候就已经规定死了,对于使用者来说,我们无法改变。

我们只关心和启动相关的一个地址,
不同的厂家的启动顺序是不太一样的,本篇主要以三星的exynos-4412 soc为基础,讲解该基于该板子的uboot启动顺序。

根据上图,系统启动的大概顺序:
iROM会根据OM 引脚的不同选择不同的启动设备,对应的OM寄存器需要提供对应的启动信息。
三、内核启动流程概述1. 内核启动流程 概述
如上图所示:

开发板从上电到启动内核的过程
四、uboot启动流程代码详解在三星的SoC中, 启动流程可以分为三个阶段BL0, BL1, BL2, BL3, 三星自己的手册对BL1的解释也不尽相同, 一种是将在iRAM中运行的程序都归结为BL1; 一种是将iRAM中三星加密的代码bl1.bin作为BL1, iRAM中剩余的部分作为BL2, 本文采用后者, 他们的主要分工如下:
可以借助下图理解这个流程

img
我们常说的uboot是一个两阶段bootloader,就是指上述的BL2和BL3. BL2主要做硬件直接相关的初始化,使用汇编编写;BL3主要为操作系统的运行准备环境,主要用C编写,这里以ARM平台为例分析其启动流程。下面是启动过程中主要涉及的文件
arch/arm/cpu/armv7/start.Sboard/samsung/myboard/lowlevel_init.Sarch/arm/lib/crt0.Sarch/arm/lib/board.carch/samsung/myboard/myboard.c
1. BL2BL2的主要文件和任务流程如下
arch/arm/cpu/armv7/start.S \1. 设置CPU为SVC模式 \2. 关闭MMU \3. 关闭Cache \4. 跳转到lowlevel_init.S low_level_init board/samsung/origen/lowlevel_init.S \5. 初始化时钟 \6. 初始化内存 \7. 初始化串口 \8. 关闭看门狗 \9. 跳转到crt0.S _main arch/arm/lib/crt0.S \10. 设置栈 \11. 初始化C运行环境 \12. 调用board_init_f() arch/arm/lib/board.c \13. board_init_f对全局信息GD结构体进行填充 arch/arm/lib/crt0.S \14. 代码重定位------------BL2的最后的工作, 执行完就进入DRAM执行BL2
2. lds文件要想了解uboot整个项目的代码流程,必须首先了解链接脚本【链接脚本参考《7. 从0开始学ARM-GNU伪指令,lds使用》】。
该文件决定了uboot最终生成的镜像文件,各个段的布局。
uboot链接脚本如下:
u-boot-2013.01/arch/arm/cpu/u-boot.lds
文件内容:
26OUTPUT_FORMAT("elf32-littlearm","elf32-littlearm","elf32-littlearm")27OUTPUT_ARCH(arm)28ENTRY(_start)29SECTIONS30{31.=0x00000000;3233.=ALIGN(4);34.text:35{36__image_copy_start=.;37CPUDIR/start.o(.text*)38*(.text*)39}4041.=ALIGN(4);42.rodata:{*(SORT_BY_ALIGNMENT(SORT_BY_NAME(.rodata*)))}4344.=ALIGN(4);45.data:{46*(.data*)47}4849.=ALIGN(4);5051.=.;5253.=ALIGN(4);54.u_boot_list:{55#include<u-boot.lst>56}5758.=ALIGN(4);5960__image_copy_end=.;6162.rel.dyn:{63__rel_dyn_start=.;64*(.rel*)65__rel_dyn_end=.;66}6768.dynsym:{69__dynsym_start=.;70*(.dynsym)71}7273_end=.;7475/*76*Deprecated:thisMMUsectionisusedbypxaatpresentbut77*shouldnotbeusedbynewboards/CPUs.78*/79.=ALIGN(4096);80.mmutable:{81*(.mmutable)82}8384.bss__rel_dyn_start(OVERLAY):{85__bss_start=.;86*(.bss*)87.=ALIGN(4);88__bss_end__=.;89}9091/DISCARD/:{*(.dynstr*)}92/DISCARD/:{*(.dynamic*)}93/DISCARD/:{*(.plt*)}94/DISCARD/:{*(.interp*)}95/DISCARD/:{*(.gnu*)}96}97
核心内容解释:
27OUTPUT_ARCH(arm):该镜像运行在arm架构的硬件上28ENTRY(_start):程序的入口是_start29SECTIONS30{31.=0x00000000;:程序的链接地址,不是运行地址【uboot一定是位置无关码】34.text:35{36__image_copy_start=.;:宏对应整个程序编译好后首地址,自搬移代码的初始位置37CPUDIR/start.o(.text*):第一个目标文件CPUDIR/start.o中的代码段38*(.text*):剩下的目标文件的代码段39}60__image_copy_end=.;:自搬移代码的结束为止
BSS全局未初始化变量、全局初始化为0的变量所在的段:
84.bss__rel_dyn_start(OVERLAY):{85__bss_start=.;88__bss_end__=.;89}
代码只分析到uboot命令行,函数main_loop()位置。

_start入口位于以下文件:
u-boot-2013.01/arch/arm/cpu/armv7/start.S

第二阶段代码从_main开始:

以上代码详细解释,请结合视频同步学习。
五、uboot启动的几个关键知识点链接脚本决定了内存的布局。
uboot链接脚本如下:
u-boot-2013.01/arch/arm/cpu/u-boot.lds
文件内容:
28ENTRY(_start)29SECTIONS30{31.=0x00000000;32
uboot的入口是_start 链接地址是0x00000000
u-boot-2013.01/arch/arm/cpu/armv7/start.S
搬移代码如下:
ENTRY(relocate_code)movr4,r0/*saveaddr_sp*/movr5,r1/*saveaddrofgd*/movr6,r2/*saveaddrofdestination*/adrr0,_startcmpr0,r6moveqr9,#0/*norelocation.relocationoffset(r9)=0*/beqrelocate_done/*skiprelocation*/movr1,r6/*r1<-scratchforcopy_loop*/ldrr3,_image_copy_end_ofsaddr2,r0,r3/*r2<-sourceendaddress*/copy_loop:ldmiar0!,{r9-r10}/*copyfromsourceaddress[r0]*/stmiar1!,{r9-r10}/*copytotargetaddress[r1]*/cmpr0,r2/*untilsourceendaddress[r2]*/blocopy_loop
详情参考第四章,第3节。
board/samsung/fs4412/lowlevel_init.S
代码如下:
41lowlevel_init:54/*AFTRwakeupreset*/55ldrr2,=S5P_CHECK_DIDLE56cmpr1,r257beqexit_wakeup5859/*LPAwakeupreset*/60ldrr2,=S5P_CHECK_LPA61cmpr1,r262beqexit_wakeup6364/*Sleepwakeupreset*/65ldrr2,=S5P_CHECK_SLEEP66cmpr1,r267beqwakeup_reset112wakeup_reset:113blsystem_clock_init114blmem_ctrl_asm_init115bltzpc_init116117exit_wakeup:118/*Loadreturnaddressandjumptokernel*/119ldrr0,=(EXYNOS4_POWER_BASE INFORM0_OFFSET)120121/*r1=physicaladdressofexynos4210_cpu_resumefunction*/122ldrr1,[r0]123124/*Jumptokernel*/125movpc,r1
由上可知,当手机因为各种原因进入休眠时,会将当前程序执行的上下文保护起来,并向一些pmic的寄存器中写入指定的数据,以表明此次是因为何种原因进入休眠。
而手机并没有完全断电,而是处于一个低功耗模式下,此时启动RAM仍然有数据,所以在此启动后,只需要从特殊的寄存器中读取相应的值,就可以知道之前是因为什么原因休眠,进而回复休眠之前的上下文即可。
除了要保证uboot代码是基于地址无关的,此外.rel.dyn帮我们解决了,其实主要还是编译器帮我们做了很多工作。
位置无关码参考《15. 从0开始学ARM-位置无关码》
文件:
board/samsung/fs4412/lowlevel_init.S
代码:
lowlevel_init:
85/*86*IfU-bootisalreadyrunninginram,noneedtorelocateU-Boot.87*MemorycontrollermustbeconfiguredbeforerelocatingU-Boot88*inram.89*/90ldrr0,=0x0ffffff/*r0<-MaskBits*/91bicr1,pc,r0/*pc<-currentaddrofcode*/92/*r1<-unmaskedbitsofpc*/93ldrr2,_TEXT_BASE/*r2<-originalbaseaddrinram*/94bicr2,r2,r0/*r2<-unmaskedbitsofr2*/95cmpr1,r2/*comparer1,r2*/96beq1f/*r0==r1thenskipsdraminit*/
原理: RAM地址空间是:0x40000000-0xA0000000 0xA0000000-0x00000000 而iROM/iRAM地址的bit:28-31均是0,所以只需要读取出执行到lowlevel_init时pc的值,判断其bit:28-31是否是0即可知道现在代码是否运行在RAM中。
文中用到的源码、datasheet、交叉编译工具可以关注GH,后台回复 【uboot2013】即可获得。
更多嵌入式 ARM知识,请关注 一口Linux。
Copyright © 2024 有趣生活 All Rights Reserve吉ICP备19000289号-5 TXT地图HTML地图XML地图