从事嵌入式音视频AI开发中,需要多次使用uboot这个bootleader,
本文用来记述我工作学习中所学到的知识点和心得
本文主要是为了能够运行起uboot,裁减后面有时间再学习。
首先第一步,删除uboot目录下没有用的到的配置文件,太多了,不需要,架构和板子支持的没法动,删除就会出错。

链接脚本详解

• 拿到半导体厂商给的uboot,进行第一次编译,编译后得到一个链接文件。从链接文件开始分析,链接的后缀为.lds
在使用imx6ull进行编译时,要把程序的起点链接到0x878000000

1
arm-linux-gnueabihf-ld  -Ttext 0X87800000  -o  ledc.elf   $^

这样所有文件都会链接到这个地址区。

分析得到uboot程序启动入口为_start。

1
2
3
OUTPUT_FORMAT( ("elf32-littlearm", , "elf32-littlearm", , "elf32-littlearm") )
OUTPUT_ARCH(arm)
ENTRY(_start)

•上面代码为链接文件的开头。
而在架构文件中–arch/arm/lib/vectors.S中有这个_start的定义。
这里的_start里有大量的汇编命令,回顾之前的汇编编写从启动跳入c执行程序

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

.global _start /* 全局标号 */

.global __bss_start
_bss_start:
.word __bss_start

.global __bss_end
_bss_end:
.word __bss_end

/*
* 描述: _start函数,程序从此函数开始执行,此函数主要功能是设置C
* 运行环境。
*/
_start:

/* 进入SVC模式 */
mrs r0, cpsr
bic r0, r0, #0x1f /* 将r0寄存器中的低5位清零,也就是cpsr的M0~M4 */
orr r0, r0, #0x13 /* r0或上0x13,表示使用SVC模式 */
msr cpsr, r0 /* 将r0 的数据写入到cpsr_c中 */

/* 清BSS段 */
ldr r0, _bss_start
ldr r1, _bss_end
mov r2, #0
bss_loop:
stmia r0!, {r2} /* 向r0的地址写入0,然后r0寄存器保存的地址值加1 */
cmp r0, r1 /* 比较r0和r1,也就是__bss_start和__bss_end的值*/
ble bss_loop /* 如果小于等于的话就跳转到bss_loop继续清bss段*/
//cmp和ble一起用,相当于c语言的if
/* 设置sp指针 */
ldr sp,=0X80200000 /* 设置栈指针 */
b main /* 跳转到main函数 */

BSS 段通常是指用来存放程序中未初始化的或者初始化为0的全局变量和静态变量的一块内存区域

BSS 段使用前需要清0,通过在这里提供 BSS 段的地址,方便链接时清0

•回归vectors.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

.globl _start

/*
*************************************************************************
*
* Vectors have their own section so linker script can map them easily
*
*************************************************************************
*/

.section ".vectors", "ax" // ax这个代码放到了.vectors这个代码段中
//“ax”表示该段--可执行并且可分配
/*
*************************************************************************
*
* Exception vectors as described in ARM reference manuals
*
* Uses indirect branch to allow reaching handlers anywhere in memory.
*
*************************************************************************
*/

_start:

#ifdef CONFIG_SYS_DV_NOR_BOOT_CFG
.word CONFIG_SYS_DV_NOR_BOOT_CFG
#endif

b reset
ldr pc, _undefined_instruction
ldr pc, _software_interrupt
ldr pc, _prefetch_abort
ldr pc, _data_abort
ldr pc, _not_used
ldr pc, _irq
ldr pc, _fiq

中断单独做一篇分析

•对以上的分析。
根据vectors存放的信息知道,他需要放在程序的最开始位置。
查看u-boot.map知道,他被放到了0x878000000的位置上。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
uboot编译后的链接文件
OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm")
OUTPUT_ARCH(arm)
ENTRY(_start)
SECTIONS
{
. = 0x00000000;
. = ALIGN(4);
.text :
{
*(.__image_copy_start)
*(.vectors)
arch/arm/cpu/armv7/start.o (.text*)
*(.text*)
}

上面部分解析在uboot中链接文件分析中有解释。

目前先到这,后面吃透有点吃力,我在研究下https://zhuanlan.zhihu.com/p/195706497和读下uboot源码

1、uboot初次编译

1-1、前提

安装ncurses库

1
sudo apt-get install libncurses5-dev

1-2、uboot命令

help //bootz命令查看支持的命令

1-3、移植

简单来说,修改configs配置文件,修改include/configs配置文件,对板级支持包进行修改(主要添加include/configs配置文件位置),对架构内容修改(架构文件添加板级支持包位置)
这里的编译器无法支持//注释功能,必须使用/**/

使用grep -nR 去找我们include/configs中添加的.h文件又没有引用

1
2
3
4
5
CONFIG_SYS_EXTRA_OPTIONS="IMX_CONFIG=board/freescale/mx6ull_wesker_evk/imximage.cfg,MX6ULL_EVK_EMMC_REWORK"
CONFIG_ARM=y
CONFIG_ARCH_MX6=y
CONFIG_TARGET_MX6ULL_WESKER_EVK=y //涉及能否正常编译 是一个编译的条件
CONFIG_CMD_GPIO=y

一般 uboot 中修改驱动基本都是在 xxx.h 和 xxx.c 这两个文件中进行的,xxx 为板子名称,
例如imx6ull的就是在include/configs中和board/freescale

留一问

怎么通过源码修改默认环境变量

编译uboot生成的文件就在根目录下,和linux内核有点区别
linux编译的在arch/arm/boot下,设备树在arch/arm/boot/dts下