本篇文章是彭老师第一次在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的内存在厂家设计的时候就已经规定死了,对于使用者来说,我们无法改变。

我们只关心和启动相关的一个地址,

    1. iROM 在soc内部,出厂时厂家固化了特定的程序,iROM中程序对应用户来说不可改变
    1. iRAM 在soc内部,速度较快,但空间不大
    1. 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. 内核启动流程 概述



如上图所示:

  1. 设备上电之后,先执行iROM中的出厂代码,先进行必要硬件的初始化

    去执行uboot,
  2. 通常把kernel、设备树文件放到flash中
  3. 程序启动之后,往往先从flash启动,运行uboot
  4. 第一步:先进行硬件的初始化(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启动的几个关键知识点

  1. 如何判断第一条机器指令的位置?

链接脚本决定了内存的布局。

uboot链接脚本如下:

u-boot-2013.01/arch/arm/cpu/u-boot.lds

文件内容:

 28 ENTRY(_start)
29 SECTIONS
30 {
31 . = 0x00000000;
32

uboot的入口是_start

链接地址是0x00000000

  1. 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节。

  1. 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仍然有数据,所以在此启动后,只需要从特殊的寄存器中读取相应的值,就可以知道之前是因为什么原因休眠,进而回复休眠之前的上下文即可。

  1. uboot代码搬到ram之后,代码的运行地址发生了变化,如何保证程序跳转不会出错?

除了要保证uboot代码是基于地址无关的,此外.rel.dyn帮我们解决了,其实主要还是编译器帮我们做了很多工作。

位置无关码参考《15. 从0开始学ARM-位置无关码》

  1. 设备启动的时候,有可能直接从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代码启动分析的更多相关文章

  1. 基于335X的UBOOT网口驱动分析

    基于335X的UBOOT网口驱动分析 一.软硬件平台资料 1.  开发板:创龙AM3359核心板,网口采用RMII形式 2.  UBOOT版本:U-Boot-2016.05,采用FDT和DM. 参考链 ...

  2. 十、uboot 代码流程分析---run_main_loop

    调用 board_init_r,传入全局 GD 和 SDRAM 中的目的地址 gd->rellocaddr void board_init_r(gd_t *new_gd, ulong dest_ ...

  3. 六、uboot 代码流程分析---start.S

    6.1 _start 入口函数 6.1.1 vectors.S (arch\arm\lib) 从上一节可以知道,uboot 的入口函数为 _start .此 函数定义在 vectors.S (arch ...

  4. 基于Office 365 无代码工作流分析-需求基本分析!

     客户需求分析: 嘉昊信息是一家IT创业型公司,因为公司初创,有较多的招聘员工的需求,公司近期购买了Office 365,因为招聘工作繁琐,HR人员须要做非常多反复繁琐工作,HR主管提议开发一个招 ...

  5. 七、uboot 代码流程分析---C环境建立

    7.1 start.S 修改 在上一节中的流程中,发现初始化的过程并没由设置看门狗,也未进行中断屏蔽 如果看门狗不禁用,会导致系统反复重启,因此需要在初始化的时候禁用看门狗:中断屏蔽保证启动过程中不出 ...

  6. 八、uboot 代码流程分析---board_init_f

    接着上一节,板子开始做前期初始化工作. 8.1 board_init_f Board_f.c (common) /* 板子初次初始化.boot_flags = 0 */ void board_init ...

  7. 九、uboot 代码流程分析---relloc_code

    执行完 board_init_f 后,重新跳转回 _main 中执行. 9.1 relloc_code 前 9.1.1 gd 设置 在调用board_init_f()完成板卡与全局结构体变量 gd 的 ...

  8. 基于Office 365 无代码工作流分析-表单基本需求分析!

     3.2表单的制作 基于下图的需求,我们须要定义例如以下的表单列表:

  9. 基于Office 365 无代码工作流分析-数据源的建立!

     标准操作步骤 下面整个步骤我们是以嘉昊信息的招聘过程的整个流程为一个场景,整个的流程场景的步骤例如以下: 整个的过程,我们通过Infopath 进行对应的表单流转,然后利用Sharepoint ...

  10. ARM上电启动及Uboot代码分析

    版权声明:本文为博主原创文章,未经博主同意不得转载. https://blog.csdn.net/qianlong4526888/article/details/27698707 注意:由于文档是去年 ...

随机推荐

  1. ClickHouse的物化视图及MySQL表引擎

    MySQL表引擎可以与MySQL数据库中的数据表简历映射,并通过SQL向其发起远程查询. MySQL表引擎可以与物化视图结合,来同步更新MySQL数据库中的数据. 语法: CREATE TABLE [ ...

  2. [AGC030C] Coloring Torus

    非常巧妙的一道构造题,发现对于所构造的 \(n\) 有上限,那么对于 \(K<=500\) 的情况,很好构造,每行全是一个数就行了,对于 \(K>500\) 的情况,显然每行都是 \(1, ...

  3. 鸿蒙生态伙伴SDK市场正式发布,驱动千行百业鸿蒙原生应用开发

    6月21-23日,华为开发者大会(HDC 2024)在东莞举办.在22日举办的[鸿蒙生态伙伴SDK]论坛中,正式发布了[鸿蒙生态伙伴SDK市场](以下简称:伙伴SDK市场),伙伴SDK市场是为开发者提 ...

  4. 集成学习与随机森林(四)Boosting与Stacking

    Boosting Boosting(原先称为hypothesis boosting),指的是能够将多个弱学习器结合在一起的任何集成方法.对于大部分boosting方法来说,它们常规的做法是:按顺序训练 ...

  5. Freertos学习:08-信号量

    --- title: rtos-freertos-08-ipc-semaphore date: 2020-06-23 11:01:12 categories: tags: - freertos - i ...

  6. 计算订单签收率的sql查询思路与过程(涉及百分比和四舍五入)

    领导提出一个签收率需求,想要通过数据库达到excel中表现的形式,提高计算速度和工作效率, 如下形式: 数据库中表数据结构: 部分数据如下: sql语句思路如下: -- 1.已签收:以物流反馈管道,状 ...

  7. 【Grafana】Grafana模板自定义-1-创建选择框

    如何创建选择框 第一步:编辑模板 第二步:配置变量 配置说明: General: [Name]变量名,后面模板中如果要按条件筛选,会用到这个变量名. [Type]类型,目前没仔细研究,使用默认的Que ...

  8. Spark3学习【基于Java】5. Spark-Sql联表查询JOIN

    大数据场景下,联表远比微小型关系型数据库中使用的频繁.网上有句话: 传统数据库单机模式做Join的场景毕竟有限,也建议尽量减少使用Join. 然而大数据领域就完全不同,Join是标配,OLAP业务根本 ...

  9. UWP WinUI 制作一个路径矢量图标按钮样式入门

    本文将告诉大家如何在 UWP 或 WinUI3 或 UNO 里,如何制作一个路径按钮.路径按钮就是使用几何路径轮廓表示内容的按钮,常见于各种图标按钮,或 svg 系贴图矢量图按钮 在网上有非常多矢量图 ...

  10. yb课堂之用户登陆校验拦截器开发 《十一》

    开发对应的登陆拦截器 开发loginInterceptor 登陆校验成功放行 登陆不成功返回json数据 LoginInterceptor.java package net.ybclass.onlin ...