23. 基于Cortex-A9 uboot代码启动分析
本篇文章是彭老师第一次在B站直播间,边直播边记录笔记,视频已经上传到B站。
现在完善整理成该篇文章,有想学习uboot启动的代码详细流程的老铁可以进入我B站空间配合视频一起学习。
【视频地址】
B站用户名:一口Linux
@
前言
我们在前面的arm系列课程,已经讲解了arm的架构、汇编指令、异常、常用外设的控制器驱动,那么我们已经具备开发arm系列产品的基本技能。
本篇给大家介绍一款比较常用的bootloader:uboot,通过uboot的介绍以及源代码的详细分析,让大家把之前所有ARM相关的知识点融会贯通起来。
一、uboot
1. 概念
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可支持的主要功能列表:
- 系统引导支持NFS挂载、RAMDISK(压缩或非压缩)形式的根文件系统;支持NFS挂载、从FLASH中引导压缩或非压缩系统内核;
- 基本辅助功能强大的操作系统接口功能;可灵活设置、传递多个关键参数给操作系统,适合系统在不同开发阶段的调试要求与产品发布,尤以Linux支持最为强劲;支持目标板环境参数多种存储方式,如FLASH、NVRAM、EEPROM;
- CRC32校验可校验FLASH中内核、RAMDISK镜像文件是否完好;
- 设备驱动串口、SDRAM、FLASH、以太网、LCD、NVRAM、EEPROM、键盘、USB、PCMCIA、PCI、RTC等驱动支持;
- 上电自检功能SDRAM、FLASH大小自动检测;SDRAM故障检测;CPU型号。
3. 常用命令
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
配置信息如下:
2)开发板设置
开发板ip:192.168.6.187
配置命令:
setenv ipaddr 192.168.6.187 ;板子的ip
setenv serverip 192.168.6.186 ;虚拟机的ip
setenv gatewayip 192.168.1.1 ;网关
saveenv ;保存配置
- 加载内核和设备树
setenv bootcmd tftp 41000000 uImage\;tftp 42000000 exynos4412-fs4412.dtb\;bootm 41000000 - 42000000
bootcmd:uboot2启动之后,首先先执行找到这个参数,执行后面的命令。
从tftp服务器下载内核镜像uImage到地址41000000,设备树文件exynos4412-fs4412.dtb到42000000,并通过命令bootm加载启动内核。
- 挂载nfs
setenv bootargs root=/dev/nfs nfsroot=192.168.6.186:/rootfs rw console=ttySAC2,115200 init=/linuxrc ip=192.168.6.187
挂载nfs文件系统,
- root=/dev/nfs
- nfsroot=192.168.6.186:/rootfs nfs服务器地址192.168.6.186,目录为/rootfs,
- rw 文件系统操作权限为可续写
- console=ttySAC2,115200 串口名称和波特率
- init=/linuxrc 内核启动后运行的进程为linuxrc
- ip=192.168.6.187 开发板地址
二、exynos-4412 Soc 启动顺序
要想了解exynos-4412的启动顺序,我们首先需要了解该soc的内存布局。
1. exynos-4412内存布局
通常一款soc的内存在厂家设计的时候就已经规定死了,对于使用者来说,我们无法改变。
我们只关心和启动相关的一个地址,
- iROM 在soc内部,出厂时厂家固化了特定的程序,iROM中程序对应用户来说不可改变
- iRAM 在soc内部,速度较快,但空间不大
- DMC RAM控制器,位于SOC内部,用于驱动RAM,大容量的RAM都需要连接到该控制器
2. Booting Sequence
不同的厂家的启动顺序是不太一样的,本篇主要以三星的exynos-4412 soc为基础,讲解该基于该板子的uboot启动顺序。
根据上图,系统启动的大概顺序:
- iROM在SOC内部,是一个64KB的ROM,他树池化一些系统启动必须的功能。比如:时钟、栈。
- iROM负责从特殊的启动外设加载BL1的image到soc内部的256KB的SRAM中。启动的外设由操作按钮来决定的。根据不同按键的值,iROM将会对bl1 的image做不同的校验。
- BL1初始化系统时钟和DRAM控制器,然后从启动外设加载OS image到DRAM中。根据启动按钮的值的不同,BL1会对OS做不同的校验。
- 启动完成之后,BL1跳转到操作系统(kernel)。
iROM会根据OM 引脚的不同选择不同的启动设备,对应的OM寄存器需要提供对应的启动信息。
三、内核启动流程概述
1. 内核启动流程 概述
如上图所示:
- 设备上电之后,先执行iROM中的出厂代码,先进行必要硬件的初始化
去执行uboot, - 通常把kernel、设备树文件放到flash中
- 程序启动之后,往往先从flash启动,运行uboot
- 第一步:先进行硬件的初始化(svc模式栈、clock、内存、串口)
第二步:自搬移:把uboot从flash中拷贝到RAM中,跳转到RAM中执行剩下的uboot代码
第三步:把内核拷贝到RAM中,执行内核,把控制权交给内核。
2. 内核启动详细流程
四、uboot启动流程代码详解
在三星的SoC中, 启动流程可以分为三个阶段BL0, BL1, BL2, BL3, 三星自己的手册对BL1的解释也不尽相同, 一种是将在iRAM中运行的程序都归结为BL1; 一种是将iRAM中三星加密的代码bl1.bin作为BL1, iRAM中剩余的部分作为BL2, 本文采用后者, 他们的主要分工如下:
- BL0: ARM的起始地址都是0地址, 三星的芯片一般将0地址映射到iROM中, BL0就是指iROM中固化的启动代码, 主要负责加载BL1
- BL1: 三星对于bootloader的加密代码bl1.bin, 要放在外设中uboot.bin的头上, 和一部分uboot.bin一起加载到iRAM中运行.
- BL2: 从(nand/sd/usb)中拷贝的uboot.bin头最大14K到iRAM中代码中除去bl1.bin后剩余的部分, 负责设置CPU为SVC模式, 关闭MMU, 关闭中断, 关闭iCache, 关闭看门狗, 初始化DRAM,初始化时钟, 初始化串口, 设置栈, 校验BL2并将其搬移到DRAM高位地址, 重定位到DRAM中执行BL3
- BL3:是指在代码重定向后在内存中执行的uboot的完整代码, 负责初始化外设,更新向量表, 清BSS, 准备内核启动参数, 加载并运行OS内核
可以借助下图理解这个流程
我们常说的uboot是一个两阶段bootloader,就是指上述的BL2和BL3. BL2主要做硬件直接相关的初始化,使用汇编编写;BL3主要为操作系统的运行准备环境,主要用C编写,这里以ARM平台为例分析其启动流程。下面是启动过程中主要涉及的文件
arch/arm/cpu/armv7/start.S
board/samsung/myboard/lowlevel_init.S
arch/arm/lib/crt0.S
arch/arm/lib/board.c
arch/samsung/myboard/myboard.c
1. BL2
BL2的主要文件和任务流程如下
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
文件内容:
26 OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm")
27 OUTPUT_ARCH(arm)
28 ENTRY(_start)
29 SECTIONS
30 {
31 . = 0x00000000;
32
33 . = ALIGN(4);
34 .text :
35 {
36 __image_copy_start = .;
37 CPUDIR/start.o (.text*)
38 *(.text*)
39 }
40
41 . = ALIGN(4);
42 .rodata : { *(SORT_BY_ALIGNMENT(SORT_BY_NAME(.rodata*))) }
43
44 . = ALIGN(4);
45 .data : {
46 *(.data*)
47 }
48
49 . = ALIGN(4);
50
51 . = .;
52
53 . = ALIGN(4);
54 .u_boot_list : {
55 #include <u-boot.lst>
56 }
57
58 . = ALIGN(4);
59
60 __image_copy_end = .;
61
62 .rel.dyn : {
63 __rel_dyn_start = .;
64 *(.rel*)
65 __rel_dyn_end = .;
66 }
67
68 .dynsym : {
69 __dynsym_start = .;
70 *(.dynsym)
71 }
72
73 _end = .;
74
75 /*
76 * Deprecated: this MMU section is used by pxa at present but
77 * should not be used by new boards/CPUs.
78 */
79 . = ALIGN(4096);
80 .mmutable : {
81 *(.mmutable)
82 }
83
84 .bss __rel_dyn_start (OVERLAY) : {
85 __bss_start = .;
86 *(.bss*)
87 . = ALIGN(4);
88 __bss_end__ = .;
89 }
90
91 /DISCARD/ : { *(.dynstr*) }
92 /DISCARD/ : { *(.dynamic*) }
93 /DISCARD/ : { *(.plt*) }
94 /DISCARD/ : { *(.interp*) }
95 /DISCARD/ : { *(.gnu*) }
96 }
97
核心内容解释:
27 OUTPUT_ARCH(arm) : 该镜像运行在arm架构的硬件上
28 ENTRY(_start) : 程序的入口是 _start
29 SECTIONS
30 {
31 . = 0x00000000; : 程序的链接地址,不是运行地址【uboot一定是位置无关码】
34 .text :
35 {
36 __image_copy_start = .; : 宏对应整个程序编译好后首地址,自搬移代码的初始位置
37 CPUDIR/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 }
3. uboot启动代码流程概要
代码只分析到uboot命令行,函数main_loop()位置。
4. 启动代码详细分析
_start入口位于以下文件:
u-boot-2013.01/arch/arm/cpu/armv7/start.S
第一阶段:
第二阶段
第二阶段代码从_main开始:
以上代码详细解释,请结合B站视频同步学习。
五、uboot启动的几个关键知识点
- 如何判断第一条机器指令的位置?
链接脚本决定了内存的布局。
uboot链接脚本如下:
u-boot-2013.01/arch/arm/cpu/u-boot.lds
文件内容:
28 ENTRY(_start)
29 SECTIONS
30 {
31 . = 0x00000000;
32
uboot的入口是_start
链接地址是0x00000000
- uboot如何搬运代码?
代码位于:
u-boot-2013.01/arch/arm/cpu/armv7/start.S
搬移代码如下:
ENTRY(relocate_code)
mov r4, r0 /* save addr_sp */
mov r5, r1 /* save addr of gd */
mov r6, r2 /* save addr of destination */
adr r0, _start
cmp r0, r6
moveq r9, #0 /* no relocation. relocation offset(r9) = 0 */
beq relocate_done /* skip relocation */
mov r1, r6 /* r1 <- scratch for copy_loop */
ldr r3, _image_copy_end_ofs
add r2, r0, r3 /* r2 <- source end address */
copy_loop:
ldmia r0!, {r9-r10} /* copy from source address [r0] */
stmia r1!, {r9-r10} /* copy to target address [r1] */
cmp r0, r2 /* until source end address [r2] */
blo copy_loop
详情参考第四章,第3节。
- uboot中,如何判断此次开机是从断电状态开机还是从休眠状态启动的?
board/samsung/fs4412/lowlevel_init.S
代码如下:
41 lowlevel_init:
54 /* AFTR wakeup reset */
55 ldr r2, =S5P_CHECK_DIDLE
56 cmp r1, r2
57 beq exit_wakeup
58
59 /* LPA wakeup reset */
60 ldr r2, =S5P_CHECK_LPA
61 cmp r1, r2
62 beq exit_wakeup
63
64 /* Sleep wakeup reset */
65 ldr r2, =S5P_CHECK_SLEEP
66 cmp r1, r2
67 beq wakeup_reset
112 wakeup_reset:
113 bl system_clock_init
114 bl mem_ctrl_asm_init
115 bl tzpc_init
116
117 exit_wakeup:
118 /* Load return address and jump to kernel */
119 ldr r0, =(EXYNOS4_POWER_BASE + INFORM0_OFFSET)
120
121 /* r1 = physical address of exynos4210_cpu_resume function */
122 ldr r1, [r0]
123
124 /* Jump to kernel*/
125 mov pc, r1
由上可知,当手机因为各种原因进入休眠时,会将当前程序执行的上下文保护起来,并向一些pmic的寄存器中写入指定的数据,以表明此次是因为何种原因进入休眠。
而手机并没有完全断电,而是处于一个低功耗模式下,此时启动RAM仍然有数据,所以在此启动后,只需要从特殊的寄存器中读取相应的值,就可以知道之前是因为什么原因休眠,进而回复休眠之前的上下文即可。
- uboot代码搬到ram之后,代码的运行地址发生了变化,如何保证程序跳转不会出错?
除了要保证uboot代码是基于地址无关的,此外.rel.dyn帮我们解决了,其实主要还是编译器帮我们做了很多工作。
位置无关码参考《15. 从0开始学ARM-位置无关码》
- 设备启动的时候,有可能直接从ram启动, 如何知道当前是从flah启动还是ram启动的?
文件:
board/samsung/fs4412/lowlevel_init.S
代码:
lowlevel_init:
85 /*
86 * If U-boot is already running in ram, no need to relocate U-Boot.
87 * Memory controller must be configured before relocating U-Boot
88 * in ram.
89 */
90 ldr r0, =0x0ffffff /* r0 <- Mask Bits*/
91 bic r1, pc, r0 /* pc <- current addr of code */
92 /* r1 <- unmasked bits of pc */
93 ldr r2, _TEXT_BASE /* r2 <- original base addr in ram */
94 bic r2, r2, r0 /* r2 <- unmasked bits of r2*/
95 cmp r1, r2 /* compare r1, r2 */
96 beq 1f /* r0 == r1 then skip sdram init */
原理:
RAM地址空间是:0x40000000-0xA0000000 0xA0000000-0x00000000
而iROM/iRAM地址的bit:28-31均是0,所以只需要读取出执行到lowlevel_init时pc的值,判断其bit:28-31是否是0即可知道现在代码是否运行在RAM中。
文中用到的源码、datasheet、交叉编译工具可以关注GH,后台回复 【uboot2013】即可获得。
更多嵌入式 ARM知识,请关注 一口Linux。
23. 基于Cortex-A9 uboot代码启动分析的更多相关文章
- 基于335X的UBOOT网口驱动分析
基于335X的UBOOT网口驱动分析 一.软硬件平台资料 1. 开发板:创龙AM3359核心板,网口采用RMII形式 2. UBOOT版本:U-Boot-2016.05,采用FDT和DM. 参考链 ...
- 十、uboot 代码流程分析---run_main_loop
调用 board_init_r,传入全局 GD 和 SDRAM 中的目的地址 gd->rellocaddr void board_init_r(gd_t *new_gd, ulong dest_ ...
- 六、uboot 代码流程分析---start.S
6.1 _start 入口函数 6.1.1 vectors.S (arch\arm\lib) 从上一节可以知道,uboot 的入口函数为 _start .此 函数定义在 vectors.S (arch ...
- 基于Office 365 无代码工作流分析-需求基本分析!
客户需求分析: 嘉昊信息是一家IT创业型公司,因为公司初创,有较多的招聘员工的需求,公司近期购买了Office 365,因为招聘工作繁琐,HR人员须要做非常多反复繁琐工作,HR主管提议开发一个招 ...
- 七、uboot 代码流程分析---C环境建立
7.1 start.S 修改 在上一节中的流程中,发现初始化的过程并没由设置看门狗,也未进行中断屏蔽 如果看门狗不禁用,会导致系统反复重启,因此需要在初始化的时候禁用看门狗:中断屏蔽保证启动过程中不出 ...
- 八、uboot 代码流程分析---board_init_f
接着上一节,板子开始做前期初始化工作. 8.1 board_init_f Board_f.c (common) /* 板子初次初始化.boot_flags = 0 */ void board_init ...
- 九、uboot 代码流程分析---relloc_code
执行完 board_init_f 后,重新跳转回 _main 中执行. 9.1 relloc_code 前 9.1.1 gd 设置 在调用board_init_f()完成板卡与全局结构体变量 gd 的 ...
- 基于Office 365 无代码工作流分析-表单基本需求分析!
3.2表单的制作 基于下图的需求,我们须要定义例如以下的表单列表:
- 基于Office 365 无代码工作流分析-数据源的建立!
标准操作步骤 下面整个步骤我们是以嘉昊信息的招聘过程的整个流程为一个场景,整个的流程场景的步骤例如以下: 整个的过程,我们通过Infopath 进行对应的表单流转,然后利用Sharepoint ...
- ARM上电启动及Uboot代码分析
版权声明:本文为博主原创文章,未经博主同意不得转载. https://blog.csdn.net/qianlong4526888/article/details/27698707 注意:由于文档是去年 ...
随机推荐
- ClickHouse介绍(一)初次使用
ClickHouse使用 ClickHouse是一个面向列存储的OLAP分析数据库,以其强大的分析速度而闻名.有关ClickHouse的介绍可以参考其官网说明[1].本文主要介绍它的基本使用. 1. ...
- OpenBMB × Hugging Face × THUNLP,联袂献上经典大模型课
这个夏天,THUNLP 携手 Hugging Face 和 OpenBMB,推出 大模型公开课第二季.在大模型公开课第二季中,将有全球知名开源社区 OpenBMB X Hugging Face 梦幻联 ...
- [oeasy]python0120_英语的崛起_英文字符_小写字母的由来_不列颠帝国
各语言字符编码 回忆上次内容 罗马 承袭了 希腊的文化 学习了 希腊的字符 拥有 罗马帝国的战力 基督教文化的影响 添加图片注释,不超过 140 字(可选) 这个时候 不列颠 ...
- 记一次 Redisson 线上问题 → 你怎么能释放别人的锁
开心一刻 今天,我的又一个好哥们脱单了,只剩下我自己单身了 我向一个我喜欢的女生吐苦水 我:我这辈子是找不到女朋友了 她:怎么可能,你很优秀的,会有很多女孩子愿意当你女朋友的 我内心窃喜,问道:那你愿 ...
- Windows/Linux 安装NVM及npm配置
nvm安装及npm配置 nvm nvm下载 Windows: Releases · coreybutler/nvm-windows (github.com) Windows直接下载相应版本的nvm-s ...
- 从菜鸟到大牛!嵌入式完整学习路线:STM32单片机-RTOS-Linux(文末领取开发板全套资料)
嵌入式系统是许多现代电子设备和智能系统的核心,掌握嵌入式系统,意味着能够设计和开发更加智能化的产品.本文为所有想进入嵌入式领域的初学者提供一个完整系统学习的路线图,按照 "STM32单片 ...
- 关于异步编程中的bind(this)
异步编程中的.bind(this)方法解决了异步执行后this指针指向全局函数的问题,主要可以通过以下两个场景加以说明:(本文所用例子基于React场景:为简便起见,仅在第一个例子中展示完整HTML代 ...
- 如何实现对ELK各组件的监控?试试Metricbeat
一.前言 开发排查系统问题用得最多的手段就是查看系统日志,ELK 是 Elastic 公司开源的实时日志处理与分析解决方案,已经为日志处理方案的主流选择. 而在生产环境中,如何针对 ELK 进行监控, ...
- 【Web】 抓包工具Charles
官方软件包下载 https://www.charlesproxy.com/download/ 在线生成激活码: https://www.zzzmode.com/mytools/charles/ 代理设 ...
- pytorch 第三方模块 GraphNAS 安装成功记录
实验室的小师妹要安装pytorch的第三方模块,经过多方努力没有安装上,后来我接手后也是感觉头疼. 该模块地址: https://github.com/GraphNAS/GraphNAS 该模块主 ...