Uboot源码分析
源码以u-boot-1.3.4为基准,主芯片采用at91sam9260,主要介绍uboot执行流程。
uboot官网:http://www.denx.de/wiki/U-Boot/WebHome。
一.工具
1. 主要采用vi查看源码,用到最多命令为grep。
grep –r –n ‘string’ ./*
搜索该目录及其以下包含string的文件并标出其所在位置。
2. objdump –D u-boot >>www
反汇编elf格式可执行文件u-boot,并输出到www中。
使用objdump命令可以查看它的分段信息:
objdump -x u-boot | more
3. 比较两文件或目录下文件不同用diff。
diff –r file file >>exp.diff
此外,在windows环境下,可用source insight工具搜索查看源码。
二.目录结构
- 有用目录
board:开发板相关目录,平台依赖。
cpu:cpu相关目录,平台依赖。
lib_arm:ARM体系结构通用文件,主要实现ARM平台通用的函数,如软件浮点。
include:头文件目录,开发板的配置文件也在其中,configs存放所有开发板的配置文件。
common:通用多功能函数实现,主要是命令函数相关文件。
drivers:通用设备驱动程序。主要为以太网接口,nandflash驱动。
Lib_generic:通用库函数的实现。
uboot目录可分为3类:
1. 与处理器体系结构或开发板硬件直接相关的。
2. 一些通用函数或驱动程序。
3. uboot应用程序、工具或文档。
- 重要文件
1)include/configs/at91sam9260ek.h
开发板配置文件。
CONFIG_ 用来选择处理器、设备接口、命令、属性等,主要用来决定是否编译某些文件或函数。
CFG_ 用来定义总线频率,串口波特率,flash地址等参数,主要用来支持通用目录中的代码,定义板子资源。
2)cpu/arm926ejs/at91sam9/u-boot.lds
程序衔接脚本。
定义程序入口ENTRY(_start),并安排各段衔接位置,程序首先运行start.S文件。
3) cpu/arm926ejs/start.S
程序首先运行文件,程序刚开始运行汇编代码,完成基本初始化。
4)cpu/arm926ejs/at91sam9/lowlevel_init.S
内存初始化,clock、SDRAM初始化代码(bootstrap中已完成,不执行)。
5)lib_arm/board.c
C语言入口start_armboot。全局数据结构gd,bd的初始化。
6) board/atmel/at91sam9260ek/config.mk
uboot在ram中的位置(TEXT_BASE=0x23f0 0000)定义。
7)common/main.c
main_loop()定义于此。
8)irq相关
lib_arm/interrupt..c(开关中断),cpu/arm926ejs/interrupt.c(中断初始化函数)。
注:at91sam9260中并没有采用中断方式。
9)I/O相关
gpio定义在include/asm-arm/arch-at91sam9/at91sam9260.h,include/asm-arm/io.h,include/asm-arm/arch-at91sam9/io.h
include/asm-arm/arch-at91sam9/gpio.h
led定义在lib_arm/board.c,board/atmel/at91sam9260ek/led.c中。
10)linux启动相关文件与函数
main_loop()在没有按键的情况下调用bootm
11)include/asm-arm/arch-at91sam9/at91sam9260.h中定义at91sam9260芯片的各个特殊功能寄存器(SFR)的地址。
uboot实际运行三个函数对应三个文件:_start => start_armboot => main_loop
start.S => lib_arm/board.c => common/main.c
三.生成文件简介
uboot生成文件:
System.map:uboot映像的符号表,它包含了uboot的全局变量和函数的地址信息。供用户或外部程序调试使用。
uboot.bin:uboot映像原始二进制文件
uboot:uboot映像ELF格式
uboot.srec:uboot映像的s_record格式
注:uboot和uboot.srec格式映像都自带定位信息。
tool/mkimge可把其他格式映像(linux内核映像和ramdisk文件系统映像转化为uboot格式)。
四.编译过程
1. Make过程
make distclean 清除原来编译,衔接结果
make at91sam9260ek_nandflash_config 配置开发板相关信息
make 编译
2. Make分析
uboot是通过gcc和Makefile组织编译的,顶层目录下的Makefile首先配置开发板,然后递归调用子目录下的makefile文件,最后把编译结果衔接成u-boot镜像。
顶层Makefile文件可分为独立的两部分:配置开发板和编译u-boot镜像。
配置开发板代码占大部分代码,起始于unconfig,结束于文件尾的clean目标。
编译u-boot镜像占据前面部分,根据config.mk是否存在(即是否配置开发板)分两种情况编译。
条件编译语句为:
ifeq($(obj)include/config.mk, $(wildcard $(obj)include/config.mk))
all:
sinclude $(obj)include/autoconf.mk.dep
sinclude $(obj)include/autoconf.mk
……
else #config.mk不存在
……
@echo “System not configured – see README” >&2
@exit 1
……
endif #config.mk
3. 配置开发板
顶层Makefile下定义如下:
at91sam9260ek_nandflash_config: unconfig
echo “#define CFG_USE_NANDFLASH 1” >>$(obj)include/config.h;
@$(MKCONFIG) –a at91sam9260ek arm arm926ejs at91sam9260ek atmel at91sam9
分析:
MKCONFIG :=$(SRCTREE)/mkconfig
即MKCONFIG就是执行顶层目录下mkconfig脚本。其主要目的就是生成文件include/config.mk,修改include/config.h,并软连接相应文件。
mkconfig的参数:Target Architecture CPU Board [VENDER] [SOC]
1)include/config.mk
ARCH = arm
CPU = arm926ejs
BOARD = at91sam9260ek
VENDOR = atmel
SOC = at91sam9
2)include/config.h
#define CFG_USE_NANDFLASH 1
/* Automatically generated – do not edit */
#include
3)创建到目标板的文件连接
首先进入include目录,
ln –s asm-arm asm
ln –s asm-arm/arch-926ejs asm-arm/arch
ln –s asm-arm/proc-armv asm-arm/proc
unconfig:
@rm –f $(obj)include/config.h $(obj)include/config.mk \
$(obj)board/*/config.tmp $(obj)board/*/*/comfig.tmp \
$(obj)include/autoconf.mk $(obj)include/autoconf.mk.dep
unconfig的主要目的是删除原来的配置文件。
make命令前加@,命令仅执行不显示。通常make会把要执行的命令行在命令执行前输出到屏幕上。
- make过程:
1)平台编译器配置:
ifeq($(ARCH), arm)
CROSS_COMPILE = arm-linux-
endif
2)导入其他配置文件
include $(TOPDIR)/config.mk
导入顶层config.mk
顶层config.mk中包含了其他配置规则,如顶层arm_config.mk,board/ateml/at91sam9260ek/config.mk等。
其中board/atmel/at91sam9260ek/config.mk中定义了TEXT_BASE如下:
TEXT_BASE=0x23f00000
uboot编译时将使用TEXT_BASE作为代码段连接的起始地址。
ifneq($(TEXT_BASE),)
LDFLAGS += -Ttext $(TEXT_BASE)
3)autoconf.mk和autoconf.mk.dep
Auto-generate the autoconf.mk file(which is included by all makefiles)
This target actually generates 2 files: autoconf.mk and autoconf.mk.dep.
the dep file is only include in this top level makefile to determine when to regerate the autoconf.mk file .
# -M的意思是说生成dependency文件.
# -MQ是用来指定生成的dependency文件中的target是什么,如不指定,模认为xxxx.o
# 即 gcc -M inlcude/common.h >[email protected] 生成的结果为:
# include/common.o : ... 即自动以.o为target,这不是我们想要的,我们想要
# autoconfig.mk作为target,因此要-MQ来手动指定,而且,include/common.h会自动
# 作为一个prequisite.
# 结果,common.h的依赖的所有的文件,包括common.h都被写入include/autoconf.mk.dep
# 文件。这样include/autoconf.mk依赖于common.h以及common.h中的一些依赖,而接下来
# 的一些要被编译的target有全都依赖于include/autoconf.mk,因此,一旦common.h有所
# 修改,全部都的重新编译.
# $(CPP) -dM file 会将file中使用的宏全部输出到标准输出.并且将预处理器的预定义
# 宏也输出了. 因此这里有了一个查看于处理器自定义的宏的方法:
# touch foo.h; cpp -dM foo.h,因为foo.h是个空文件,因此输出的都是预定义宏.
# sed -n是quiet模式,sed使用tools/define2mk.sed脚本来处理输出的宏.将宏处理成
# 了一种适合make处理的autoconf的格式.而且只提取出了common.h中的#define CONFIG_XXX模式的宏,即用户在common.h中自己定义的宏.
$(obj)include/autoconf.mk.dep:$(obj)include/config.h include/common.h
@$(XECHO) Generating $@; \
set –e ; \
:Generate the dependancies ; \
$(CC) –x c –DDO_DEPS_ONLY –M $(HOST_CFLAGS) $(CPPFLAGS) \
-MQ $(obj)include/autoconf.mk include/common.h >$@
$(obj)include/autoconf.mk:$(obj)include/config.h
@$(XECHO) Generating $@; \
set –e; \
: Extract the config macros; \
$(CPP) $(CFLAGS)-DDO_DEPS_ONLY –dM include/common.h | \
sed –n –f tools/scripts/define2mk.sed >$@
sinclude $(obj)include/atuoconf.mk.dep
include/autoconf.mk依赖于make
编译选项“-dM”的作用是输出include/common.h中定义的所有宏。根据上面的规则,编译器提取include/common.h中定义的宏,然后输出给tools/scripts/define2mk.sed脚本处理,处理的结果就是include/autoconf.mk文件。其中tools/scripts/define2mk.sed脚本的主要完成了在include/common.h中查找和处理以“CONFIG_”开头的宏定义的功能。
include/common.h文件包含了include/config.h文件,而include/config.h文件又包含了config_defaults.h,configs/at91sam9260ek.h文件。因此include/autoconf.mk实质上就是config_defaults.h,configs/at91sam9260ek.h两文件中“CONFIG_”开头的有效的宏定义的集合
4. uboot镜像编译
ALL += $(obj)u-boot.srec $(obj)u-boot.bin $(obj)System.map $(U_BOOT_NAND) $(U_BOOT_ONENAND)
all: $(ALL)
下面再来分析u-boot.bin文件生成的过程。ELF格式“u-boot”文件生成规则如下:
$(obj)u-boot: depend $(SUBDIRS) $(OBJS) $(LIBBOARD) $(LIBS) $(LDSCRIPT) $(obj)u-boot.lds
$(GEN_UBOOT)
ifeq ($(CONFIG_KALLSYMS),y)
smap=`$(call SYSTEM_MAP,u-boot) | \
awk '$$2 ~ /[tTwW]/ {printf $$1 $$3 "\\\\000"}'` ; \
$(CC) $(CFLAGS) -DSYSTEM_MAP="\"$${smap}\"" \
-c common/system_map.c -o $(obj)common/system_map.o
$(GEN_UBOOT) $(obj)common/system_map.o
endif
这里生成的$(obj)u-boot目标就是ELF格式的U-Boot文件了。由于CONFIG_KALLSYMS未定义,因此ifeq ($(CONFIG_KALLSYMS),y)与endif间的代码不起作用。
其中depend,$(SUBDIRS),$(OBJS),$(LIBBOARD),$(LIBS),$(LDSCRIPT), $(obj)u-boot.lds是$(obj)u-boot的依赖,而$(GEN_UBOOT)编译命令。
depend dep: $(VERSION_FILE)
for dir in $(SUBDIRS) ; do $(MAKE) –C $$dir _depend ; done
五.程序相关
1. 相关重要定义说明
CFG_CBSIZE 256:console I/O Buffer Size
2. 环境变量的处理:
The "environment" is stored as a list of '\0' terminated "name=value" strings. The end of the list is marked by a double
'\0'. New entries are always added at the end. Deleting an entry shifts the remaining entries to the front.. Replacing an entry is a
combination of deleting the old value and adding the new one.
The environment is proceeded by a 32 bit CRC over the data part.
环境变量相关文件:
1). common/cmd_nvedit.c getenv,setenv,do_printenv函数等。
2). common/env_common.c 环境变量存储相关结构default_environment等。
3). common/env_nand.c nand中存储env的相关函数readenv,saveenv,writeenv等。
3. main_loop分析
1) reset_cpu() 在cpu/arm926ejs/at91sam9/timer.c中,通过写RSTCR复位控制寄存器。
2) get_tbclk()在cpu/arm926ejs/at91sam9/timer.c中,tbclk=CFG_HZ。
3) abortboot()判断在延迟时间内是否有按键,有则终止,否则动态启动。
4) int run_command(const char *cmd, int flag) 定义于common/main.c
先分析命令行,后根据命令查表执行命令函数。
WARNING:
*
* We must create a temporary copy of the command since the command we get
* may be the result from getenv(), which returns a pointer directly to
* the environment data, which may change magicly when the command we run
* creates or modifies environment variables (like "bootp" does).
5) common/console.c定义与串口控制台相关的函数。 putc,dbg,puts等
clear_ctrlc() 清除ctrl+C 状态。( ctrl_was_pressed = 0)
had_ctrlc() 检测是否有ctrl+C。(return ctrl_was_pressed;)
drivers/serial/atmel_usart.c定义串口相关函数。
serial_getc(),serial_putc(),serial_puts(),serial_init()。
该开发板采用串口3 usart3。
6) main_loop流程:
run_command(preboot,0) ==》abortboot(bootdelay)==》run_command(bootcmd,0)或解析命令执行。
无论preboot还是bootcmd都调用了bootm命令,执行do_bootm()函数(common/cmd_bootm.c),其作用是从内存中引导应用程序或内核映像。do_bootm()调用do_bootm_linux()函数(lib_arm/bootm.c)。
do_bootm_linux最后调用theKernel()函数,其只是把已装入RAM的linux内核映像当做一个函数来调用。
4. nandflash处理
坏块处理
重要文件及函数
- lib_arm/board.c中调用了nand_init()函数。
- drivers/mtd/nand/nand.c中定义了nand_init_chip()函数。
- board/atmel/at91sam9260ek/nand.c定义了board_nand_init()函数。
4.scan_bbt(),nand_scan()函数定义在driver/mtd/nand/nand_base.c,搜索处理坏块。
5. 启动文件分析
Start.s流程:
硬件环境初始化:
进入svc模式;关闭MMU和CACHE;设置时钟频率;配置SDRAM。
重定位:
如果当前代码不在连接指定的地址上,则需要把u-boot从当前的位置拷贝到RAM指定位置中;
建立堆栈,堆栈是进入C函数前必须初始化的。
清.bss区。
跳到start_armboot函数中执行。(lib_arm/board.c)
六.重要数据结构
u-boot的大部分操作都是围绕它自身的数据结构,这些数据结构是通用的,但是不同的板子初始化这些数据不一样。
1.gd 全局数据变量指针,它保存了u-boot运行需要的全局数据。
include/asm-arm/global_data.h 与ARCH相关
typedef struct global_data {
bd_t *bd;
unsigned long flags;
unsigned long baudrate;
unsigned long have_console; /*serial_init( ) was called */
unsigned long reloc_off; /* 重定位偏移,就是实际定向的位置与编译连接时指定的位置之差,一般为0*/
unsigned long env_addr; /* 环境参数地址 */
unsigned long env_valid; /* 环境参数CRC校验有效标志*/
unsigned long fb_base; /* 帧缓冲基地址 */
#ifdef CONFIG_VFD
unsigned long vfd_type; /* display type */
#endif
void **jt; /* jump table */
} gd_t;
// Global Data Flags
#define GD_FLG_RELOC 0x00001 /* Code was relocated to RAM */
#define GD_FLG_DEVINIT 0x00002 /* Devices has been initialized */
#define GD_FLG_SILENT 0x00004 /* Silent mode */
#define GD_FLG_POSTFAIL 0x00008 /* Critical POST test failed */
#define GD_FLG_POSTSTOP 0x00010 /* POST sequence aborted */
#define GD_FLG_LOGINIT 0x00020 /* Log Buffer has been initialized */
#define DECLARE_GLOBAL_DATA_PTR register volatile gd_t *gd asm(“r8”)
- bd 板子数据指针。
include/asm-arm/u-boot.h 与ARCH相关
typedef struct bd_info {
int bi_baudrate; /*串口波特率*/
unsigned long bi_ip_addr; /*IP 地址*/
unsigned char bi_enetaddr[6]; /* MAC 地址;Ethernet address */
struct environment_s *bi_env;
ulong bi_arch_number; /*unique id for this board */
ulong bi_boot_params; /*启动参数*/
struct /* RAM 配置*/
{
ulong start;
ulong size;
} bi_dram[CONFIG_NR_DRAM_BANKS];
} bd_t;
#define bi_env_data bi_env->data
#define bi_env_crc bi_env->crc
- 环境变量指针 env_t
env_t定义于include/environment.h中
typedef struct environment_s {
uint32_t crc; /* CRC32 over data bytes */
#ifdef CFG_REDUNDAND_ENVIRONMENT
unsigned char flags; /* active/obsolete flags */
#endif
unsigned char data[ENV_SIZE];
} env_t;
env_t *env_ptr = (env_t *) (&environment[0]); // common/env_flash.c
env_ptr指向环境参数区,系统启动时默认的环境参数environment[],定义于common/environment.c中
env_t environment __PPCENV__ ={
ENV_CRC, /* CRC sum*/
#ifdef CFG_REDUNDAND_ENVIRONMENT
1, /* Flags:valid*/
#endif
{
#if defined(CONFIG_BOOTARGS)
“bootargs=” CONFIG_BOOTARGS “\0”
#endif
#if defined(CONFIG_BOOTCOMMAND)
“bootcmd=” COMFIG_BOOTCOMMAND “\0”
#endif
…… // ramboot nfsboot
#if defined(CONFIG_BOOTDELAY)&&(CONFIG_BOOTDELAY>=0)
“bootdelay=” MK_STR(CONFIG_BOOTDELAY) “\0”
…… //baudrate loads_echo ethaddr eth2addr eth3addr ethprime ipaddr serverip autoload
//rootpath gatewayip netmask hostname bootfile loadaddr preboot clocks_in_mhz
//pcidelay
#ifdef CONFIG_EXTRA_ENV_SETTINGS
CONFIG_EXTRA_ENV_SETTINGS
#endif
“\0” /* Term. env_t.data with 2 NULLs */
};
参数解释如下:
bootfile 定义缺省的下载文件
bootargs 定义传递给Linux内核的命令行参数
bootcmd 定义自动启动时执行的几条命令
serverip 定义tftp服务器端的IP地址
4.设备相关 device_t
u-boot把可以用为控制台输入输出的设备添加到设备列表devlist中,并把当前用作标准IO的设备指针假如stdio_devices数组中。在调用标准IO函数如printf()时将调用stdio_devices数组对应设备的IO函数如putc()。
device_t定义于include/device.h中
typedef struct {
int flags;
int ext; /*supported extensions */
char name[16]; /*Device name*/
/* GENERAL functions */
int (*start)(void);
int (*stop)(void);
/* OUTPUT functions */
void (*putc)(const char c);
void (*puts)(const char *s);
/* INPUT functions*/
int (*tstc)(void);
int (*getc)(void);
void *priv;
} device_t;
5. flash_info_t
定义于include/flash.h中
typedef struct {
ulong size; /* total bank size in bytes*/
ushort sector_count; /* number of erase units */
ulong flash_id; /* combined device & manufacturer code */
ulong start[CFG_MAX_FLASH_SECT]; /*physical sector start addresser */
uchar protect[CFG_MAX_FLASH_SECT]; /*sector protection status */
#ifdef CFG_FLASH_CFI
uchar portwidth; /* the width of the port */
uchar chipwidth ; /* the width of the chip */
ushort buffer_size; /* # of bytes in write buffer */
ulong erase_blk_tout; /* maximum block erase timeout */
ulong write_tout; /* maximum write timeout */
ulong buffer_write_tout; /*maximum buffer write timeout */
ushort vendor; /* the primary vendor id */
ushort cmd_reset; /* vendor specific reset command */
ushort interface; /* used for x8/x16 adjustments */
ushort legacy_unlock; /* support Intel legacy (unlocking */
uchar manufacturer_id; /* manufacturer id */
ushort device_id; /*device id*/
ushort device_id2; /* extended device id */
ushort ext_addr; /* extended query table address */
ushort cfi_version; /* cfi version */
ushort cfi_offset; /* offset for cfi query */
ulong addr_unlock1; /* unlock address 1 for AMD flash roms */
ulong addr_unlock2; /* unlock address 2 for AMD flash roms */
const char *name; /* human-readable name */
#endif
} flash_info_t;
七.uboot命令介绍及添加
八.配置选项
配置选项:
1. CONFIG_SILENT_CONSOLE: can be used to quiet messages on the console. If the option has been enabled, the output can be silenced by setting the environment variable “silent”. The variable is latched into the global data at an early stage in the boot process so deleting it with “setenv” will not take effect until the system is restarted. (README.silent)
2. CONFIG_SKIP_LOWLEVEL_INIT
CONFIG_SKIP_RELOCATE_UBOOT
If these variable defined, then certain low level initialization ( like setting up the memory controller) are omitted and/or U-Boot doesn’t relocate itself to ram.
Normally these variables must not be defined. The only exception is when u-Boot is loaded (to ram) by some other bootloader or by a debugger which performs these initializations itself.
九.移植
平台相关目标
cpu/$(CPU)/start.o
board/$(BOARDDIR)/lib$(BOARD).a
cpu/$(CPU)/lib$(CPU).a
cpu/$(CPU)/$(SOC)/lib$(SOC).a
lib_$(ARCH)/lib$(ARCH).a
移植时主要考虑这几个目标文件对应的目录。
参考文献:
-
- http://www.cnblogs.com/heaad/archive/2010/07/17/1779806.html
- http://blog.chinaunix.net/space.php?uid=12072359&do=blog&id=2960865