4.Bootloader:u-boot.2009.08分析与移植
4.1:分析u-boot根目录下的Makefile,可以看到uboot编译的顺序如下,由此可知编译执行的第一个文件是cpu/$(CPU)/start.o,又由于是基于
arm920t架构的,所以去分析cpu/arm920t/start.S源文件。
# U-Boot objects....order is important (i.e. start must be first)
OBJS = cpu/$(CPU)/start.o
OBJS := $(addprefix $(obj),$(OBJS))
LIBS = lib_generic/libgeneric.a
LIBS += lib_generic/lzma/liblzma.a
LIBS += lib_generic/lzo/liblzo.a
LIBS += $(shell if [ -f board/$(VENDOR)/common/Makefile ]; then echo \
"board/$(VENDOR)/common/lib$(VENDOR).a"; fi)
LIBS += cpu/$(CPU)/lib$(CPU).a
LIBS += lib_$(ARCH)/lib$(ARCH).a
LIBS += fs/...(.a)
LIBS += net/libnet.a
LIBS += disk/libdisk.a
LIBS += drivers/...(.a)
LIBS += common/libcommon.a
LIBS += libfdt/libfdt.a
LIBS += api/libapi.a
LIBS += post/libpost.a
LIBS := $(addprefix $(obj),$(LIBS))
4.2:分析cpu/arm920t/start.S源文件:由ARM架构可知程序的执行顺序是开发板一上电即从零地址开始执行,在零地址存放的是一条复位异常中断处
理,依次分析可知程序从上电开始的执行依次如下:设置处理器模式、关闭看门狗、关闭中断、设置分频系数比、系统初始化(flush I/D cache、disable MMU
、内存sdram相关初始化)、重定位代码(从flash复制uboot代码到SDRAM中)、初始化堆栈、清除bss段、跳转到第二阶段的C语言代码入口函数start_armboot处
执行。
(1).globl _start
_start:
b start_code
(2)start_code:
/* set the cpu to SVC32 mode*/
/* turn off the watchdog */
/* mask all IRQs by setting all bits in the INTMR - default */
/* setup FCLK:HCLK:PCLK */
bl cpu_init_crit /*do sys-critical inits only at reboot*/
#ifndef CONFIG_SKIP_RELOCATE_UBOOT
/* relocate U-Boot from nor flash to RAM */
/* Set up the stack */
/* Clear bss */
/* jump to second stage */
ldr pc, _start_armboot
_start_armboot:
.word start_armboot
4.3:分析/lib_arm/board.c里的start_armboot函数:
gd = (gd_t*)(_armboot_start - CONFIG_SYS_MALLOC_LEN - sizeof(gd_t));
memset ((void*)gd, 0, sizeof (gd_t));等,初始化gd_t结构体指针gd,并初始化。
typedef int (init_fnc_t) (void);
init_fnc_t *init_sequence[] = {
#if defined(CONFIG_ARCH_CPU_INIT)
arch_cpu_init,
/* basic arch cpu dependent setup */
#endif
board_init,
/* basic board dependent setup */
#if defined(CONFIG_USE_IRQ)
interrupt_init,
/* set up exceptions */
#endif
timer_init,
/* initialize timer */
env_init,
/* initialize environment */
init_baudrate,
/* initialze baudrate settings */
serial_init,
/* serial communications setup */
console_init_f,
/* stage 1 init of console */
display_banner,
/* say that we are here */
#if defined(CONFIG_DISPLAY_CPUINFO)
print_cpuinfo,
/* display cpu info (and speed) */
#endif
#if defined(CONFIG_DISPLAY_BOARDINFO)
checkboard,
/* display board info */
#endif
#if defined(CONFIG_HARD_I2C) || defined(CONFIG_SOFT_I2C)
init_func_i2c,
#endif
dram_init,
/* configure available RAM banks */
#if defined(CONFIG_CMD_PCI) || defined (CONFIG_PCI)
arm_pci_init,
#endif
display_dram_config,
NULL,
};
for (init_fnc_ptr = init_sequence; *init_fnc_ptr; ++init_fnc_ptr) {
if ((*init_fnc_ptr)() != 0) {
hang ();
}
},通过一个for循环来依次访问函数指针数组init_sequence中的成员函数,进一步完成相关初始化和相关设置。
nand_init();
/* go init the NAND */,初始化nand flash。
serial_initialize(); ,初始化串口。
/* main_loop() can return to retry autoboot, if so just run it again. */
for (;;) {
main_loop ();
}
/* NOTREACHED - no way out of command loop except booting */,在无限for循环内,执行main_loop函数。
4.4:分析/common/main.c里的main_loop函数:处理uboot命令。
/*
* Main Loop for Monitor Command Processing
*/
for(;;){
#ifdef CONFIG_BOOT_RETRY_TIME
if (rc >= 0) {
/* Saw enough of a valid command to
* restart the timeout.
*/
reset_cmd_timeout();
}
#endif
len = readline (CONFIG_SYS_PROMPT);
flag = 0;
/* assume no special flags for now */
if (len > 0)
strcpy (lastcommand, console_buffer);
else if (len == 0)
flag |= CMD_FLAG_REPEAT;
#ifdef CONFIG_BOOT_RETRY_TIME
else if (len == -2) {
/* -2 means timed out, retry autoboot
*/
puts ("\nTimed out waiting for command\n");
# ifdef CONFIG_RESET_TO_RETRY
/* Reinit board to run initialization code again */
do_reset (NULL, 0, 0, NULL);
# else
return;
/* retry autoboot */
# endif
}
#endif
if (len == -1)
puts ("<INTERRUPT>\n");
else
rc = run_command (lastcommand, flag);
if (rc <= 0) {
/* invalid command or not repeatable, forget it */
lastcommand[0] = 0;
}
}
根据输入的命令格式,解析命令参数,执行命令(执行run_command函数),/commmon/main.c里的run_command函数,根据命令表结构体cmd_tbl_s来找
到输入命令所对应的实现函数。
/*
* Monitor Command Table
*/
struct cmd_tbl_s {
char *name;
/* Command Name */
int maxargs;
/* maximum number of arguments */
int repeatable;
/* autorepeat allowed? */
/* Implementation function
*/
int (*cmd)(struct cmd_tbl_s *, int, int, char *[]);
char *usage;
/* Usage message (short)
*/
#ifdef
CONFIG_SYS_LONGHELP
char *help;
/* Help message (long)
*/
#endif
#ifdef CONFIG_AUTO_COMPLETE
/* do auto completion on the arguments */
int (*complete)(int argc, char *argv[], char last_char, int maxv, char *cmdv[]);
#endif
};
typedef struct cmd_tbl_s
cmd_tbl_t;
cmd_tbl_t *cmdtp;
/* OK - call function to do the command */
if ((cmdtp->cmd) (cmdtp, flag, argc, argv) != 0) {
rc = -1;
}//至此,根据不同的uboot命令,去执行不同的实现函数。
4.5:uboot启动linux操作系统的命令是bootm,uboot的bootm命令的实现文件是cmd_bootm.c,所以接着来分析/common/cmd_bootm.c文件:命令实
现的格式如下:其中命令是bootm,实现函数是do_bootm函数。
#define Struct_Section __attribute__ ((unused,section (".u_boot_cmd")))
#ifdef CONFIG_SYS_LONGHELP
#define U_BOOT_CMD(name,maxargs,rep,cmd,usage,help) \
cmd_tbl_t __u_boot_cmd_##name Struct_Section = {#name, maxargs, rep, cmd, usage, help}
#else /* no long help info */
#define U_BOOT_CMD(name,maxargs,rep,cmd,usage,help) \
cmd_tbl_t __u_boot_cmd_##name Struct_Section = {#name, maxargs, rep, cmd, usage}
#endif
/* CONFIG_SYS_LONGHELP */
U_BOOT_CMD(
bootm,
CONFIG_SYS_MAXARGS, 1,
do_bootm,
"boot application image from memory",
"[addr [arg ...]]\n - boot application image stored in memory\n"
"\tpassing arguments ‘arg ...‘; when booting a Linux kernel,\n"
"\t‘arg‘ can be the address of an initrd image\n"
"\tbdt - OS specific bd_t processing\n"
"\tcmdline - OS specific command line processing/setup\n"
"\tprep - OS specific prep before relocation or go\n"
"\tgo - start OS"
);
4.6:分析/common/cmd_bootm.c中的do_bootm函数:
static bootm_headers_t images;
/* pointers to os/initrd/fdt images */
int do_bootm (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]);
disable_interrupts();
usb_stop();
icache_disable();
dcache_disable();
ret = bootm_load_os(images.os, &load_end, 1); //加载具体的操作系统
images.os.os == IH_OS_LINUX //如果具体加载的是linux,则有以下
boot_os_fn
*boot_fn;
boot_fn = boot_os[images.os.os];//boot_fn指针指向boot_os数组中的特定类型函数
boot_fn(0, argc, argv, &images);//调用do_bootm_linux函数
boot_os_fn * boot_os[] = {
#ifdef CONFIG_BOOTM_LINUX
[IH_OS_LINUX] = do_bootm_linux,
#endif
... ...
}; //由此可知,若加载的是linux系统,则调用do_bootm_linux函数,do_bootm_linuxm在/lib_arm/bootm.c文件里
4.7:分析/lib_arm/bootm.c中的do_bootm_linux函数:
void (*theKernel)(int zero, int arch, uint params);
theKernel = (void (*)(int, int, uint))images->ep;//images->ep(entry point)
setup_start_tag (bd);
... ...
setup_end_tag (bd);
theKernel (0, machid, bd->bi_boot_params); //至此,跳转到linux内核开始执行,系统启动起来
/* does not return */
return 1;
u-boot分析