U-Boot 启动过程
(国嵌)
开发板上电后,执行 U-Boot 的第一条指令,然后顺序执行
U-Boot 启动函数。看一下 board/smdk2410/u-boot.lds 这个链
接脚本,可以知道目标程序的各部分链接顺序。第一个要链接的
是 cpu/arm920t/start.o,那么 U-Boot 的入口指令一定位于这
个程序中。下面分两阶段介绍启动流程:
1.cpu/arm920t/start.S
第一阶段
这个汇编程序是U-Boot 的入口程序,开头就是复位向量的代码。
_start: b
reset
//复位向量
ldr
ldr
ldr
ldr
ldr
ldr
ldr
pc, _undefined_instruction
pc, _software_interrupt
pc, _prefetch_abort
pc, _data_abort
pc, _not_used
pc, _irq
//中断向量
pc, _fiq
//中断向量
…
/* the actual reset code */
reset:
//复位启动子程序
/* 设置 CPU 为 SVC32 模式 */
mrs
r0,cpsr
bic
orr
r0,r0,#0x1f
r0,r0,#0xd3
msr
cpsr,r0
/* 关闭看门狗 */
……
relocate:
……
/* 把 U-Boot 重新定位到 RAM */
adr
ldr
址 */
cmp
r0, _start
/* r0 是代码的当前位置 */
r1, _TEXT_BASE
/*_TEXT_BASE 是 RAM 中的地
r0, r1
/* 比较 r0 和 r1,判断当前是从
Flash 启动,还是 RAM */
beq
stack_setup /* 如果 r0 等于 r1,跳过重定位代码 */
/* 准备重新定位代码 */
ldr
ldr
sub
add
r2, _armboot_start
r3, _bss_start
r2, r3, r2
/* r2 得到 armboot 的大小 */
r2, r0, r2
/* r2 得到要复制代码的末尾地址 */
copy_loop: /* 重新定位代码 */
ldmia r0!, {r3-r10}
/*从源地址[r0]复制 */
stmia r1!, {r3-r10}
/* 复制到目的地址[r1] */
cmp
r0, r2
/* 复制数据块直到源数据末尾地址[r2] */
ble
copy_loop
/* 初始化堆栈等
*/
stack_setup:
ldr
r0, _TEXT_BASE /* 上面是 128 KiB 重定位的 u-boot
*/
sub
*/
sub
r0, r0, #CFG_MALLOC_LEN /* 向下是内存分配空间
r0, r0, #CFG_GBL_DATA_SIZE /* 然后是 bdinfo 结构
体地址空间 */
#ifdef CONFIG_USE_IRQ
sub
r0,
r0,
#(CONFIG_STACKSIZE_IRQ+CONFIG_STACKSIZE_FIQ)
#endif
sub
sp, r0, #12
/* 为 abort-stack 预留 3 个字 */
clear_bss:
ldr
r0, _bss_start
/* 找到 bss 段起始地址 */
ldr
r1, _bss_end
/* bss 段末尾地址 */
mov
r2, #0x00000000
/* 清零 */
clbss_l:str r2, [r0]
/* bss 段地址空间清零循环...
*/
add
cmp
r0, r0, #4
r0, r1
bne
clbss_l
/* 跳转到 start_armboot 函数入口,_start_armboot 字保存函
数入口指针 */
ldr
pc, _start_armboot
_start_armboot: .word start_armboot
函数在 lib_arm/board.c 中实现
//start_armboot
2.lib_arm/board.c
第二阶段
start_armboot 是 U-Boot 执行的第一个 C 语言函数,完成系统
初始化工作,进入主循环,处理用户输入的命令。
3.init_sequence[]
init_sequence[]数组保存着基本的初始化函数指针。
init_fnc_t *init_sequence[] = {
cpu_init,
/* 基本的处理器相关配置 -- cpu/arm920t/cpu.c
*/
board_init,
/* 基 本 的 板 级 相 关 配 置
board/smdk2410/smdk2410.c */
interrupt_init,
/* 初 始 化 中 断 处 理
--
--
cpu/arm920t/s3c24x0/interrupt.c */
env_init,
/* 初始化环境变量 -- common/cmd_flash.c
*/
init_baudrate,
/* 初始化波特率设置 -- lib_arm/board.c */
serial_init,
/* 串 口 通 讯 设 置
--
cpu/arm920t/s3c24x0/serial.c */
console_init_f,
/* 控 制 台 初 始 化 阶 段 1 --
common/console.c */
display_banner,
lib_arm/board.c */
dram_init,
/* 打 印 u-boot 信 息 --
/* 配 置 可 用 的 RAM --
board/smdk2410/smdk2410.c */
display_dram_config,
/* 显 示 RAM 的 配 置 大 小 --
lib_arm/board.c */
NULL,
};
void start_armboot (void)
{
/* 顺序执行 init_sequence 数组中的初始化函数 */
for
(init_fnc_ptr = init_sequence;
*init_fnc_ptr;
++init_fnc_ptr) {
if ((*init_fnc_ptr)() != 0) {
hang ();
}
}
/*配置可用的 Flash */
size = flash_init ();
display_flash_config (size);
/* _armboot_start 在 u-boot.lds 链接脚本中定义 */
mem_malloc_init
(_armboot_start
-
CFG_MALLOC_LEN);
/* 配置环境变量*/
env_relocate ();
/* 从环境变量中获取 IP 地址 */
gd->bd->bi_ip_addr = getenv_IPaddr ("ipaddr");
/* 以太网接口 MAC 地址 */
……
devices_init ();
/* 获取列表中的设备 */
jumptable_init ();
console_init_r ();
/* 完整地初始化控制台设备 */
enable_interrupts (); /* 使能中断处理 */
/* 通过环境变量初始化 */
if ((s = getenv ("loadaddr")) != NULL) {
load_addr = simple_strtoul (s, NULL, 16);
}
/* main_loop()循环不断执行 */
for (;;)
{
main_loop ();
/* 主循环函数处理执行用户命令 --
common/main.c */
}
命令实现
U-Boot 作为 Bootloader,具备多种引导内核启动的方式。常用
的 go 和 bootm 命令可以直接引导内核映像启动。U-Boot 与内
核的关系主要是内核启动过程中参数的传递。
1.go 命令的实现
/* common/cmd_boot.c */
int do_go (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
{
ulong addr, rc;
int
rcode = 0;
if (argc < 2) {
printf ("Usage:\n%s\n", cmdtp->usage);
return 1;
}
addr = simple_strtoul(argv[1], NULL, 16);
printf ("## Starting application at 0x%08lX ...\n",
addr);
rc = ((ulong (*)(int, char []))addr) (--argc, &argv[1]);
/* 运行程序 */
if (rc != 0) rcode = 1;
printf ("## Application terminated, rc = 0x%lX\n",
rc);
/*如果是运行 linux,这条指令是否能运行?*/
return rcode;
}
go 命令调用 do_go()函数,跳转到某个地址执行的。如果在这
个地址准备好了自引导的内核映像,就可以启动了。尽管 go 命