目录
1. u-boot命令学习
- 输入help或者?查看u-boot所支持的命令
-
信息命令查询:
- bdinfo:查看板子信息
- print或者printenv:查看环境变量
- version:查看版本
- bdinfo:查看板子信息
-
环境变量操作命令:
-
setenv:设置环境变量、新建环境变量
- saveenv:保存环境变量设置
保存成功后会显示保存到了什么位置,即eMMC(u-boot烧再来eMMC中) - 修改多组值时,需要用单引号括起来,每组之间用空格隔开
- 删除环境变量:使用setenv对该变量赋空值
-
-
内存操作命令
-
显示内存值:md[.b, .w, .l] address [# of objects]
.b.w.l分别对应byte、word、long即1字节、2字节、4字节查看
address为内存的起始地址,[# of objects]表示要查看的数据长度
例如查看:起始地址为20000000的20个字节内存值(20应该换算为16进制0x14,u-boot中使用的是16进制)
注意字节顺序.... - 修改内存值(不会进行地址递增):nm [.b, .w, .l] address
将20000000的第一个字节修改为0x11
- 修改内存值(地址递增):mm [.b, .w, .l] address
- 使用指定的数填充一段内存:mw [.b, .w, .l] address value [count]
- 内存拷贝:cp [.b, .w, .l] source target count
source 为源地址
target 为目标地址
count 为拷贝的数量 - 内存比较:cmp [.b, .w, .l] addr1 addr2 count
-
-
网络命令
-
ping命令
-
dhcp命令:从路由器获取 IP 地址并通过 TFTP 来启动 linux 内核
-
nfs命令:nfs [loadAddress] [[hostIPaddr:]bootfilename]
将nfs服务器上的文件加载到loadAddress地址上
-
tftp命令:tftp [loadAddress] [[hostIPaddr:]bootfilename]
与nfs不同,tftp不需要输入完整的文件路径
-
-
boot命令
-
bootz 命令用于启动 zImage 镜像文件
-
bootm 用于启动 uImage 镜像文件
-
不启动设备树时:bootm addr
addr为uImage在DRAM中的地址
-
使用设备树的话同bootz
-
- boot命令: boot 会读取环境变量 bootcmd 来启动 Linux 系统
-
-
其他命令
-
reset命令:重启u-boot
-
2. u-boot编译
- 设置环境变量ARCH、CROSS_COMPILE,make distclean
- 配置defconfig文件生成.config文件,defconfig文件在源码目录configs文件夹
- make编译,编译后目录如下
3. u-boot.lds文件分析
- 依次存放.text、.rodata 、.data、.u_boot_list、.bss
- u-boot的入口为_start
_start定义在 arch/arm/lib/vectors.S 中,这部分定义在了.vectors段中,vectors段中保存了中断向量表,通过对.map文件分析,可以看出.vectors的开始位置为0x20800000,因此u-boot的起始位置也是0x20800000
- arch/arm/cpu/armv7/start.s 编译出来的代码放到中断向量表后面。
4. u-boot.map文件分析
- .map文件中镜像的开始和结束位置:0x20800000 ~ 0x20881bac
- 几个重要的地址:.text段起始位置0x20800000
5. 启动流程分析
5.1 reset函数分析
- 从 reset 函数跳转到了 save_boot_params 函数,save_boot_params 函数也是只有一句跳转语句,跳转到 save_boot_params_ret 函数
- 读取寄存器 cpsr 中的值,并保存到 r0 寄存器中。
- 将寄存器 r0 中的值与 0X1F 进行与运算,结果保存到 r1 寄存器中,目的就是提取 cpsr 的 bit0~bit4 这 5 位,这 5 位为 M4 M3 M2 M1 M0,M[4:0]这五位用来设置处理器的工作模式
- 判断 r1 寄存器的值是否等于 0X1A(0b11010),判断是否为HYP模式
- 如果 r1 和 0X1A 不相等,将0到5位清除
- 如果处理器不处于 Hyp 模式的话就将 r0 的寄存器的值与 0x13 进行或运算, 0x13=0b10011,也就是设置处理器进入 SVC 模式。
- r0 寄存器的值再与 0xC0 进行或运算,那么 r0 寄存器此时的值就是 0xD3,cpsr 的 I 位和 F 位分别控制 IRQ 和 FIQ 这两个中断的开关,设置为 1 就关闭了 FIQ 和 IRQ
- 将 r0 寄存器写回到 cpsr 寄存器中。完成设置 CPU 处于 SVC32 模式,并且关闭 FIQ 和 IRQ 这两个中断。
-
接下来执行:
#if !(defined(CONFIG_OMAP44XX) && defined(CONFIG_SPL_BUILD))
/* Set V=0
in
CP15 SCTLR register -
for
VBAR to point to vector */
mrc p15, 0, r0, c1, c0, 0 @ Read CP15 SCTLR Register
bic r0,
#CR_V @ V = 0
mcr p15, 0, r0, c1, c0, 0 @ Write CP15 SCTLR Register
/* Set vector address
in
CP15 VBAR register */
ldr r0, =_start
mcr p15, 0, r0, c12, c0, 0 @Set VBAR
#endif
- 为了接下来的向量表重定位做准备
- 行设置r0寄存器的值为_start,_start就是整个uboot的入口地址,相当于 uboot 的起始地址
-
接下来执行:分别调用函数 cpu_init_cp15、cpu_init_crit 和 _main
#ifndef CONFIG_SKIP_LOWLEVEL_INIT
bl cpu_init_cp15
#ifndef CONFIG_SKIP_LOWLEVEL_INIT_ONLY
bl cpu_init_crit
#endif
#endif
bl _main
主要是关闭MMU和cache
函数 cpu_init_crit 内部仅仅是调用了函数 lowlevel_initENTRY(cpu_init_crit)
/*
* Jump to board specific initialization...
* The Mask ROM will have already initialized
* basic memory. Go here to bump up clock rate and handle
* wake up conditions.
*/
b lowlevel_init @ go setup pll,mux,memory
ENDPROC(cpu_init_crit)
5.2 lowlevel_init函数
-
函数 lowlevel_init 在文件 arch/arm/cpu/armv7/lowlevel_init.S 中定义
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
ENTRY(lowlevel_init)
/*
* Setup a temporary stack. Global data is not available yet.
*/
#if defined(CONFIG_SPL_BUILD) && defined(CONFIG_SPL_STACK)
ldr sp, =CONFIG_SPL_STACK
#else
ldr sp, =CONFIG_SYS_INIT_SP_ADDR
#endif
bic sp, sp,
#7 /* 8-byte alignment for ABI compliance */
#ifdef CONFIG_SPL_DM
mov r9,
#0
#else
/*
* Set up global data
for
boards that still need it. This will be
* removed soon.
*/
#ifdef CONFIG_SPL_BUILD
ldr r9, =gdata
#else
sub sp, sp,
#GD_SIZE
bic sp, sp,
#7
mov r9, sp
#endif
#endif
/*
* Save the old lr(passed
in
ip) and the current lr to stack
*/
push {ip, lr}
/*
* Call the very early init
function
. This should
do
only the
* absolute bare minimum to get started. It should not:
*
* -
set
up DRAM
* - use global_data
* -
clear
BSS
* - try to start a console
*
* For boards with SPL this should be empty since SPL can
do
all of
* this init
in
the SPL board_init_f()
function
which
is called
* immediately after this.
*/
bl s_init
pop {ip, pc}
ENDPROC(lowlevel_init)
-
第8行将SP指针设置为 CONFIG_SYS_INIT_SP_ADDR,在include/configs/hi3519av100.h中地址如下
- 进行8字节对齐
- 调用 s_init 函数
-
5.3 _main函数
-
_main 函数定义在文件 arch/arm/lib/crt0.S 中
展开原码
-
76行设置 sp 指针为 CONFIG_SYS_INIT_SP_ADDR
- 进行8字节对齐,将sp指针存放在r0中
-
调用用函数 board_init_f_alloc_reserve,此函数有一个参数,参数为 r0 中的值,此函数定义在文件 common/init/board_init.c 中
展开原码
函数 board_init_f_alloc_reserve 主要是留出早期的 malloc 内存区域和 gd 内存区域,其中 CONFIG_SYS_MALLOC_F_LEN=0X2000( 在文件 include/generated/autoconf.h 中定义 )
-
函数 board_init_f_alloc_reserve 是有返回值的,返回值为新的 top 值,并且会进行16字节对齐
- 85行,将 r0 写入到 sp 里面,r0 保存着函数 board_init_f_alloc_reserve 的返回值
- 89行中,将 r0 寄存器的值写到寄存器 r9 里面,因为 r9 寄存器存放着全局变量 gd 的地址, 在文件 arch/arm/include/asm/global_data.h 中,此时设置了是设置 gd 所指向的位置
- 第 90 行调用函数 board_init_f_init_reserve,此函数在文件 common/init/board_init.c 中有定义,此函数用于初始化 gd,其实就是清零处理。
- 92行将r0设置为0
- 93行调用 board_init_f 函数,此函数定义在文件 common/board_f.c 中,主要用来初始 化 DDR,定时器,完成代码拷贝等
- 103行重新设置环境(sp 和 gd)、获取 gd->start_addr_sp 的值赋给 sp,在函数 board_init_f 中会初始化 gd 的所有成员变量,新的 sp 和 gd 将会存 放到 DDR 中,而不是内部的 RAM 了。
- 第 116行,lr 寄存器的值加上 r0 寄存器的值,重新赋值给 lr 寄存器。因为接下来要重定位 代码,也就是把代码拷贝到新的地方去,下面要 将 uboot 拷贝到 DDR 最后面的地址空间
- 第 120 行,读取 gd->relocaddr 的值赋给 r0 寄存器,此时 r0 寄存器就保存着 uboot 要拷贝 的目的地址
- 第 121 行,调用函数 relocate_code,也就是代码重定位函数,此函数负责将 uboot 拷贝到新 的地方去,此函数定义在文件 arch/arm/lib/relocate.S 中
- 第 127 行,调用函数 relocate_vectors,对中断向量表做重定位, 此函数定义在文件 arch/arm/lib/relocate.S 中
- 第 131 行,调用函数 c_runtime_cpu_setup,此函数定义在文件 arch/arm/cpu/armv7/start.S 中
- 清除bss段
-
5.4 board_init_f函数
- board_init_f函数的两个主要功能:
- 初始化一系列的外设,比如串口、定时器,或者打印一些消息等。
- 初始化 gd 的各个成员变量,uboot 会将自己重定位到 DRAM 最后面的地址区域,也就是将自己拷贝到 DRAM 最后面的内存区域中。这么做的目的是给 Linux 腾出空间,防止 Linux kernel 覆盖掉 uboot,将 DRAM 前面的区域完整的空出来。在拷贝之前给 uboot 各部分分配好内存位置和大小。
- board_init_f 通过函数 initcall_run_list 来运行初始化序列 init_sequence_f 里面的一些 列函数,init_sequence_f 里面包含了一系列的初始化函数,init_sequence_f 也是定义在文件 common/board_f.c 中
5.5 board_init_r函数
- board_init_f 函数里面会调用一系列的函数来初始化一些外设和 gd 的成员变量。但是 board_init_f 并没有初始化所有的外设,还需要做一些后续工作,这些后续工作就是由函数 board_init_r 来完成的,board_init_r 函数定义在文件 common/board_r.c 中
5.6 relocate_code函数
- relocate_code 函数是用于代码拷贝的,此函数定义在文件 arch/arm/lib/relocate.S 中
- uboot 对于重定位后链接地址和运行地址不一致的解决方法就是采用位置无关码
- uboot 就是靠.rel.dyn 来解决重定位问题的
5.7 relocate_vectors函数
- 函数 relocate_vectors 用于重定位向量表,此函数定义在文件 relocate.S 中
5.8 run_main_loop函数
- uboot 启动以后会进入 3 秒倒计时,如果在 3 秒倒计时结束之前按下按下回车键,那么就 会进入 uboot 的命令模式,如果倒计时结束以后都没有按下回车键,那么就会自动启动 Linux 内 核 , 这 个 功 能 就 是 由 run_main_loop 函 数 来 完 成 的 。
5.9 总体流程
6. 相关问题
- 在u-boot初期为什么要关闭MMU和cache?
(6)uboot详解——关闭缓存和mmu_奔跑的路的博客-CSDN博客