1. 源码地址

https://ftp.denx.de/pub/u-boot/


2. 找到想要的版本

此处我下载的是 u-boot-2012.10.tar.bz2。
在这里插入图片描述


3. 将源码拿到 linux 环境下解压缩

注意,使用 linux 虚拟机的话,不能在 linux-window 共享目录下编译,会报错。

必须拷贝到纯 linux 环境的目录下解压缩。

cp /mnt/hgfs/linux_win_shared/u-boot-2012.10.tar.bz2  .  # 将 uboot 源码从共享目录拷贝到 Linux 目录
tar -xvf u-boot-2012.10.tar.bz2 # 解压缩

root@backvm-virtual-machine:u-boot-2012.10# pwd
/home/backvm/work0/u-boot-2012.10
root@backvm-virtual-machine:u-boot-2012.10# ls -lhtr
总用量 2.2M
drwxrwxr-x   2 root root 4.0K 1015  2012 spl
-rw-rw-r--   1 root root   74 1015  2012 snapshot.commit
-rw-rw-r--   1 root root 2.5K 1015  2012 rules.mk
-rw-rw-r--   1 root root 176K 1015  2012 README
drwxrwxr-x   3 root root 4.0K 1015  2012 nand_spl
-rwxrwxr-x   1 root root 4.5K 1015  2012 mkconfig
-rw-rw-r--   1 root root  28K 1015  2012 Makefile
-rwxrwxr-x   1 root root  21K 1015  2012 MAKEALL
-rw-rw-r--   1 root root  25K 1015  2012 MAINTAINERS
drwxrwxr-x  11 root root 4.0K 1015  2012 fs
drwxrwxr-x   4 root root 4.0K 1015  2012 examples
drwxrwxr-x   2 root root 4.0K 1015  2012 dts
drwxrwxr-x  28 root root 4.0K 1015  2012 drivers
drwxrwxr-x   6 root root 4.0K 1015  2012 doc
-rw-rw-r--   1 root root  12K 1015  2012 CREDITS
-rw-rw-r--   1 root root  17K 1015  2012 COPYING
-rw-rw-r--   1 root root  11K 1015  2012 config.mk
-rw-rw-r--   1 root root 113K 1015  2012 boards.cfg
drwxrwxr-x 290 root root  12K 1015  2012 board
drwxrwxr-x  16 root root 4.0K 1015  2012 arch
drwxrwxr-x  19 root root  12K 1111 23:58 include
drwxrwxr-x  12 root root 4.0K 1111 23:58 tools
drwxrwxr-x   2 root root 4.0K 1111 23:58 api
drwxrwxr-x   3 root root  12K 1111 23:58 common
......

4. 编译 uboot

root@backvm-virtual-machine:work0# cd   u-boot-2012.10
root@backvm-virtual-machine:work0#
root@backvm-virtual-machine:u-boot-2012.10# make s5p_goni_config
Configuring for s5p_goni board...
root@backvm-virtual-machine:u-boot-2012.10#
root@backvm-virtual-machine:u-boot-2012.10# make
Generating include/autoconf.mk
Generating include/autoconf.mk.dep
arm-linux-gcc -DDO_DEPS_ONLY \
	-g  -Os   -fno-common -ffixed-r8 -msoft-float  -D__KERNEL__ -DCONFIG_SYS_TEXT_BASE=0x34800000 -I/home/backvm/work0/u-boot-2012.10/include -fno-builtin -ffreestanding -nostdinc -isystem /home/backvm/work0/linux_ker/gcc-4.6.4/bin/../lib/gcc/arm-arm1176jzfssf-linux-gnueabi/4.6.4/include -pipe  -DCONFIG_ARM -D__ARM__ -marm -mno-thumb-interwork -mabi=aapcs-linux -march=armv7-a  -Wall -Wstrict-prototypes -fno-stack-protector -Wno-format-nonliteral -Wno-format-security -fstack-usage   \
	-o lib/asm-offsets.s lib/asm-offsets.c -c -S

......
root@backvm-virtual-machine:u-boot-2012.10# ls -lh u*  # uboot 编译成功
-rwxr-xr-x 1 root root 847K 1111 23:58 u-boot
-rw-r--r-- 1 root root 182K 1111 23:58 u-boot.bin
-rw-r--r-- 1 root root  923 1111 23:58 u-boot.lds
-rw-r--r-- 1 root root  77K 1111 23:58 u-boot.map
-rw-r--r-- 1 root root 545K 1111 23:58 u-boot.srec
root@backvm-virtual-machine:u-boot-2012.10#

5. 理解 uboot 中的汇编指令

arm-linux-objdump -S 命令

在这里插入图片描述

在这里插入图片描述

arm-linux-objdump -S 命令,可以让我们查看汇编代码对应的指令,以及根据汇编指令翻译出 “直观的汇编程序”。

如上图,汇编代码 _start: b reset 所在的内存地址为 34800000, 汇编代码 _start: b reset 所对应的汇编指令为 ea000014,这是一个四个字节的汇编指令。
在后面,arm-linux-objdump -S 命令根据汇编指令 ea000014,翻译出直观的汇编代码为:b 34800058 <reset>

即:_start: b reset 等价于 b 34800058 <reset>


指令:ldr pc, _undefined_instruction

在这里插入图片描述

接下来是第二条汇编程序:ldr pc, _undefined_instruction,它的意思是,将 标号_undefined_instruction 地址处的内容(即位于下方的34800020地址处的内容34800200)放入 pc 指针。

此处有个问题:我们知道, 一条 ARM 指令的长度是 4 个字节,而标号_undefined_instruction代表的内容34800200也是 4 个字节;34800200(4 字节)+ ldr(n 字节)+ pc (n 字节) > 4 字节,明显不对劲。为什么呢?

实际上,我们看到的汇编程序 ldr pc, _undefined_instruction 是一条伪指令,汇编器会把它翻译为 ldr pc, [pc, #20]。即当前指令 ldr pc, _undefined_instruction 的内存地址为 34800004,pc 指针指向该内存地址 34800004 [pc, #20] 将 pc 地址加上 20的值,即 0x34800004 + 20 = 0x34800018;ldr pc, [pc, #20] 将地址 34800018 放入 pc 指针。我们看到 地址为 34800018 的汇编程序是:ldr pc, _irq,明显不是我们的标号_undefined_instruction 的地址。这又是为什么呢?


这就涉及到 ARM 架构的“流水线”的概念了。
一条汇编程序(汇编指令),需要经历三个步骤才能执行完成:取指令——译码——执行。

在这里插入图片描述
即:
1) pc 指向第一条指令,此时 CPU 从内存中取出 第一条指令(取指);接着 pc 指针向前偏移一条指令。
2) pc 指向第二条指令,此时 CPU 对上面取出的第一条指令进行解析(译码);接着 pc 指针向前偏移一条指令。
3) pc 指向第三条指令,此时 CPU 执行译码后的指令(执行);

所以, pc 的值,等于当前 CPU 执行的指令所在地址值 + 8(ARM 一条指令是 4 个字节,即 pc 的值领先 2 条 ARM 指令)。

即,

  • 当前指令 ldr pc, _undefined_instruction 的内存地址为 34800004,pc 当前的值已经向前偏移了 2 条指令(8个字节),为 0x34800004 + 8 = 0x3480000C

  • [pc, #20]:将该内存地址加上 20的值,为 0x3480000C+ 20 = 0x34800020;即,标号 <_undefined_instruction> 所在的内存地址。

在这里插入图片描述

  • ldr pc, [pc, #20] :因此这条指令的作用为, 将标号 <_undefined_instruction> 的内容 34800200 ,放入 pc 指针;因此下一条取指的汇编指令为如下图所示的异常处理程序:

在这里插入图片描述


指令 .balignl 16,0xdeadbeef

该指令的作用是,指导汇编器,该条指令 .balignl 16,0xdeadbeef的下一条指令 _TEXT_BASE 的内存地址,需要 16 字节对齐。如果不能16字节对齐,则从 .balignl 16,0xdeadbeef 的上一条指令 _pad: .word 0x12345678 开始,不断填充内容: 0xdeadbeef 。直到指令 _TEXT_BASE 的内存地址是 16 字节对齐为止。

在这里插入图片描述

可以看到,指令 34800040 <_TEXT_BASE>: 的内存地址确实是 16 字节对齐了。

在这里插入图片描述


指令:_TEXT_BASE: .word CONFIG_SYS_TEXT_BASE

CONFIG_SYS_TEXT_BASE 是在配置文件中定义的变量,即,使用命令make s5p_goni_config的配置文件。

这个变量地址,就是将 uboot 拷贝到 DDR 中的起始内存地址。

从下图可以看到,这个内存地址就是:0x34800000

在这里插入图片描述


中断地址:0x0badc0de

这里主要是用来说明堆栈地址开始的地方。

因为我们的程序刚开始运行,还没有做初始化,还不知道堆栈指针应该指向什么地方。

所以此处用特殊内容0x0badc0de填充,以便后续找到该地方,修改原内容0x0badc0de,填充其他特定的内容。

在这里插入图片描述


指令:bl save_boot_params

start.S 文件中,一开始就使用指令:_start: b reset 跳转到 reset 标号处;下面 bl save_boot_params 就是 reset 的内容。

汇编指令 bbl 的区别:

1) b 是普通的跳转指令,不带 return(回复)的跳转。相当于 C 语言的 goto 指令,跳转到指定标号处之后,就从标号处顺序往下执行。

2)bl是带 return (回复)的跳转。就相当于 C 语言中,在此处调用一个函数,函数执行完后,又 return (回到)到 函数被调用的地方,继续往下执行。

start.S 文件中就是, bl 执行完 save_boot_params后,又回到此处,继续执行下面的 mrs r0, cpsr

bl 的本质是,在跳转之前,会先记录指令 mrs r0, cpsr 的内存地址,将其存放到 lr 寄存器。在 save_boot_params 的最后,使用汇编指令 mov pc, lr,将保存的 lr 的内存地址(即指令 mrs r0, cpsr 的内存地址)放入 pc 指针,于是程序的下一条指令将执行 mrs r0, cpsr

在这里插入图片描述

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐