从上一章的链接脚本分析中,我们知道了uboot的程序是从*ENTRY(_start)*开始的

在整个uboot工程下搜索**_start,可以找到一个start.S**文件包含了这个label,因此uboot启动的分析从这个文件开始

头文件分析

1
2
3
4
5
6
#include <config.h>
#include <version.h>
#if defined(CONFIG_ENABLE_MMU)
#include <asm/proc/domain.h>
#endif
#include <regs.h>

start.S一开始include的config.h这个头文件是由$(TOPDIR)/mkconfig自动生成的,具体生成的脚本如下

1
2
3
4
5
6
7
8
9
10
11
#
# Create board specific header file
#
if [ "$APPEND" = "yes" ] # Append to existing config file
then
echo >> config.h
else
> config.h # Create new config file
fi
echo "/* Automatically generated - do not edit */" >>config.h
echo "#include <configs/$1.h>" >>config.h

config.h这个头文件中的内容如下,configs/x210_sd.h中包含的是与x210特性相关的用于适配的宏

1
2
/* Automatically generated - do not edit */
#include <configs/x210_sd.h>

通过搜索这个头文件,我们可以发现CONFIG_ENABLE_MMU被定义,所以会包含asm/proc/domain.h这个头文件

1
2
3
4
greedyhao@greedyhao-PC:.../qt_x210v3s_160307/uboot$ cat include/configs/x210_sd.h | grep CONFIG_ENABLE_MMU
#define CONFIG_ENABLE_MMU
#ifdef CONFIG_ENABLE_MMU
#ifdef CONFIG_ENABLE_MMU

通过ls -l可以发现这个头文件是proc-armv文件夹的一个软连接

1
2
greedyhao@greedyhao-PC:.../qt_x210v3s_160307/uboot$ ls -il include/asm/ | grep proc
6430221 lrwxrwxrwx 1 greedyhao greedyhao 9 3月 15 09:31 proc -> proc-armv

之所以使用proc而不是proc-armv,是为了可移植性。

试想一下,在移植时需要去把proc-xxx一个个修改是件多么痛苦的事,而且这种启动代码应该可重用的部分很多,使用软连接的方式可以减少很多不必要的工作

启动文件的校验头

uboot 选择 SD/NAND 启动方式,需要启动文件提供一个16字节的校验头,start.S 就在开头填充了16字节的空间

1
2
3
4
5
6
#if defined(CONFIG_EVT1) && !defined(CONFIG_FUSED)
.word 0x2000
.word 0x0
.word 0x0
.word 0x0
#endif

在 uboot 编译完,得到可执行文件后,还需要补充这16字节的校验和,uboot才能启动

三星有提供程序用来计算这个校验和,下面简单看看

计算校验和

程序的用法如下

Usage: mkbl1 <source file> <destination file> <size>

在使用这个程序的时候,会向程序的 main 函数中传递一个参数数组 *argv[] ,而且 argv[0] = mkbl1,argv[1] = source file,argv[2] = destination file,argv[3] = size,这些都是基本常识,就不多说了

mkbl1 在得到文件的大小后,先会分配一个缓冲区,准备用来存放需要处理的文件

1
2
3
BufLen = atoi(argv[3]);
Buf = (char *)malloc(BufLen);
memset(Buf, 0x00, BufLen);

准备好缓冲区后,就把需要处理的文件读入,使用 fseek 计算文件长度,再使用 fread 将文件读入

1
2
3
4
5
6
7
8
9
// 已删除错误处理
fp = fopen(argv[1], "rb");

fseek(fp, 0L, SEEK_END);
fileLen = ftell(fp);
fseek(fp, 0L, SEEK_SET);

nbytes = fread(Buf, 1, BufLen, fp);
fclose(fp);

计算校验和的时候需要跳过前16个字节,然后从第9个字节开始写入计算出来的校验和

1
2
3
4
5
6
a = Buf + 16;
for(i = 0, checksum = 0; i < BufLen - 16; i++)
checksum += (0x000000FF) & *a++;

a = Buf + 8;
*( (unsigned int *)a ) = checksum;

写入也和读取类似,先 fopen 再使用 fwrite 写入,最后释放掉缓存区的空间

异常向量表

1
2
3
4
5
6
7
8
9
.globl _start
_start: b reset
ldr pc, _undefined_instruction
ldr pc, _software_interrupt
ldr pc, _prefetch_abort
...
.global _end_vect
_end_vect:
.balignl 16,0xdeadbeef

异常向量表是当程序发生异常时的处理方式,在向量表制作完后,使用 balignl 伪指令,让内存16字节对齐,加速硬件访问;至于 0xdeadbeef 代表用来补位垃圾数据

复位向量设置

1
2
_TEXT_BASE:
.word TEXT_BASE

定义了一个 _TEXT_BASE 标签用来存放复位向量,主要是用在处理器初始化和 stack 的设置

至于 TEXT_BASE 之前介绍过,由 makefile 文件导入

1
2
3
x210_sd_config :	unconfig
@$(MKCONFIG) $(@:_config=) arm s5pc11x x210 samsung s5pc110
@echo "TEXT_BASE = 0xc3e00000" > $(obj)board/samsung/x210/config.mk