ARM上电后都干了哪些事?-uboot启动代码详解
nanshan 2024-12-05 14:57 10 浏览 0 评论
关于本文配套的视频《uboot启动详解-1》《uboot启动详解-2》
前言
我们在前面的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
配置信息如下:
nfs
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内核
可以借助下图理解这个流程
img
我们常说的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开始:
以上代码详细解释,请结合视频同步学习。
五、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。
- 上一篇:NFS网络文件系统
- 下一篇:VMWare经典配置和超级配置
相关推荐
- 使用nginx配置域名及禁止直接通过IP访问网站
-
前段时间刚搭建好这个网站,一直没有关注一个问题,那就是IP地址也可以访问我的网站,今天就专门研究了一下nginx配置问题,争取把这个问题研究透彻。1.nginx配置域名及禁止直接通过IP访问先来看n...
- 如何在 Linux 中使用 PID 号查找进程名称?
-
在Linux的复杂世界中,进程是系统运行的核心,每个进程都由一个唯一的「进程ID」(PID)标识。无论是系统管理员在排查失控进程,还是开发者在调试应用程序,知道如何将PID映射到对应的进程名称都是一项...
- Linux服务器硬件信息查询与日常运维命令总结
-
1.服务器硬件信息查询1.1CPU信息查询命令功能描述示例lscpu显示CPU架构、核心数、线程数等lscpucat/proc/cpuinfo详细CPU信息(型号、缓存、频率)cat/proc/c...
- Ubuntu 操作系统常用命令详解(ubuntu常用的50个命令)
-
UbuntuLinux是一款流行的开源操作系统,广泛应用于服务器、开发、学习等场景。命令行是Ubuntu的灵魂,也是高效、稳定管理系统的利器。本文按照各大常用领域,详细总结Ubuntu必学...
- 从 0 到 1:打造基于 Linux 的私有 API 网关平台
-
在当今微服务架构盛行的时代,API网关作为服务入口和安全屏障,其重要性日益凸显。你是否想过,不依赖商业方案,完全基于开源组件,在Linux上构建一个属于自己的私有API网关平台?今天就带你...
- Nginx搭建简单直播服务器(nginx 直播服务器搭建)
-
前言使用Nginx+Nginx-rtmp-module在Ubuntu中搭建简单的rtmp推流直播服务器。服务器环境Ubuntu16.04相关概念RTMP:RTMP协议是RealTi...
- Linux连不上网?远程卡?这篇网络管理指南你不能错过!
-
大家好!今天咱们聊个所有Linux用户都躲不开的“老大难”——网络管理。我猜你肯定遇到过这些崩溃时刻:新装的Linux系统连不上Wi-Fi,急得直拍桌子;远程服务器SSH连不上,提示“Connecti...
- 7天从0到上线!手把手教你用Python Flask打造爆款Web服务
-
一、为什么全网开发者都在疯学Flask?在当今Web开发的战场,Flask就像一把“瑞士军刀”——轻量级架构让新手3天速成,灵活扩展能力又能支撑百万级用户项目!对比Django的“重型装甲”,Flas...
- nginx配置文件详解(nginx反向代理配置详解)
-
Nginx是一个强大的免费开源的HTTP服务器和反向代理服务器。在Web开发项目中,nginx常用作为静态文件服务器处理静态文件,并负责将动态请求转发至应用服务器(如Django,Flask,et...
- 30 分钟搞定 Docker 安装与 Nginx 部署,轻松搭建高效 Web 服务
-
在云计算时代,利用容器技术快速部署应用已成为开发者必备技能。本文将手把手教你在阿里云轻量应用服务器上,通过Docker高效部署Nginx并发布静态网站,全程可视化操作,新手也能轻松上手!一、准...
- Nginx 配置实战:从摸鱼到部署,手把手教你搞定生产级配置
-
各位摸鱼搭子们!今天咱不聊代码里的NullPointerException,改聊点「摸鱼必备生存技能」——Nginx配置!先灵魂拷问一下:写了一堆接口却不会部署?服务器被恶意请求打崩过?静态资源加载...
- 如何使用 Daphne + Nginx + supervisor部署 Django
-
前言:从Django3.0开始支持ASGI应用程序运行,使Django完全具有异步功能。Django目前已经更新到5.0,对异步支持也越来越好。但是,异步功能将仅对在ASGI下运行的应用程序可用...
- Docker命令最全详解(39个最常用命令)
-
Docker是云原生的核心,也是大厂的必备技能,下面我就全面来详解Docker核心命令@mikechen本文作者:陈睿|mikechen文章来源:mikechen.cc一、Docker基本命令doc...
- ubuntu中如何查看是否已经安装了nginx
-
在Ubuntu系统中,可以通过以下几种方法检查是否已安装Nginx:方法1:使用dpkg命令(适用于Debian/Ubuntu)bashdpkg-l|grepnginx输出...
- OVN 概念与实践(德育概念的泛化在理论和实践中有什么弊端?)
-
今天我们来讲解OVN的概念和基础实践,要理解本篇博客的内容,需要前置学习:Linux网络设备-Bridge&VethPairLinux网络设备-Bridge详解OVS+Fa...
你 发表评论:
欢迎- 一周热门
-
-
UOS服务器操作系统防火墙设置(uos20关闭防火墙)
-
极空间如何无损移机,新Z4 Pro又有哪些升级?极空间Z4 Pro深度体验
-
手机如何设置与显示准确时间的详细指南
-
NAS:DS video/DS file/DS photo等群晖移动端APP远程访问的教程
-
如何在安装前及安装后修改黑群晖的Mac地址和Sn系列号
-
如何修复用户配置文件服务在 WINDOWS 上登录失败的问题
-
一加手机与电脑互传文件的便捷方法FileDash
-
日本海上自卫队的军衔制度(日本海上自卫队的军衔制度是什么)
-
10个免费文件中转服务站,分享文件简单方便,你知道几个?
-
爱折腾的特斯拉车主必看!手把手教你TESLAMATE的备份和恢复
-
- 最近发表
-
- 使用nginx配置域名及禁止直接通过IP访问网站
- 如何在 Linux 中使用 PID 号查找进程名称?
- Linux服务器硬件信息查询与日常运维命令总结
- Ubuntu 操作系统常用命令详解(ubuntu常用的50个命令)
- 从 0 到 1:打造基于 Linux 的私有 API 网关平台
- Nginx搭建简单直播服务器(nginx 直播服务器搭建)
- Linux连不上网?远程卡?这篇网络管理指南你不能错过!
- 7天从0到上线!手把手教你用Python Flask打造爆款Web服务
- nginx配置文件详解(nginx反向代理配置详解)
- 30 分钟搞定 Docker 安装与 Nginx 部署,轻松搭建高效 Web 服务
- 标签列表
-
- linux 查询端口号 (58)
- docker映射容器目录到宿主机 (66)
- 杀端口 (60)
- yum更换阿里源 (62)
- internet explorer 增强的安全配置已启用 (65)
- linux自动挂载 (56)
- 禁用selinux (55)
- sysv-rc-conf (69)
- ubuntu防火墙状态查看 (64)
- windows server 2022激活密钥 (56)
- 无法与服务器建立安全连接是什么意思 (74)
- 443/80端口被占用怎么解决 (56)
- ping无法访问目标主机怎么解决 (58)
- fdatasync (59)
- 405 not allowed (56)
- 免备案虚拟主机zxhost (55)
- linux根据pid查看进程 (60)
- dhcp工具 (62)
- mysql 1045 (57)
- 宝塔远程工具 (56)
- ssh服务器拒绝了密码 请再试一次 (56)
- ubuntu卸载docker (56)
- linux查看nginx状态 (63)
- tomcat 乱码 (76)
- 2008r2激活序列号 (65)