Uboot启动代码解析

发布时间:2010-10-21


U-Boot启动过程

开发板上电后,执行U-Boot的第一条指令,然后顺序执行U-Boot启动函数。看一下board/smdk2410/u-boot.lds这个链接脚本,可以知道目标程序的各部分链接顺序。第一个要链接的是cpu/arm920t/start.o那么U-Boot的入口指令一定位于这个程序中。下面分两阶段介绍启动流程:

第一阶段
1cpu/arm920t/start.S 这个汇编程序是U-Boot的入口程序,开头就是复位向量的代码。 _start: b reset //复位向量 ldr pc, _undefined_instruction ldr pc, _software_interrupt ldr pc, _prefetch_abort ldr pc, _data_abort ldr pc, _not_used ldr pc, _irq //中断向量 ldr pc, _fiq //中断向量
/* the actual reset code */ reset: //复位启动子程序



/* 设置CPUSVC32模式 */ mrs r0,cpsr bic r0,r0,#0x1f orr r0,r0,#0xd3 msr cpsr,r0
/* 关闭看门狗 */ …… …… relocate: /* U-Boot重新定位到RAM */ adr r0, _start /* r0是代码的当前位置 */ ldr r1, _TEXT_BASE /*_TEXT_BASERAM中的地址 */ cmp r0, r1 /* 比较r0r1,判断当前是从Flash启动,还是RAM */ beq stack_setup /* 如果r0等于r1跳过重定位代码 */
/* 准备重新定位代码 */ ldr r2, _armboot_start ldr r3, _bss_start sub r2, r3, r2 /* r2 得到armboot的大小 */ add 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 r0, r0, #CFG_MALLOC_LEN /* 向下是内存分配空间 */ sub 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 r0, r0, #4 cmp r0, r1


bne clbss_l
/* 跳转到start_armboot函数入口,_start_armboot字保存函数入口指针 */ ldr pc, _start_armboot _start_armboot: .word start_armboot //start_armboot函数在lib_arm/board.c中实现

第二阶段
2lib_arm/board.c start_armbootU-Boot执行的第一个C语言函数,完成系统初始化工作,进入主循环,处理用户输入的命令。

3init_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, /* u-boot -- lib_arm/board.c */ dram_init, /* 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 {
RAM --

if ((*init_fnc_ptr( != 0 { hang (; } } /*配置可用的Flash */ size = flash_init (; display_flash_config (size; /* _armboot_start u-boot.lds链接脚本中定义 */ mem_malloc_init CFG_MALLOC_LEN;
/* 配置环境变量*/ env_relocate (;
/* 从环境变量中获取IP地址 */ gd->bd->bi_ip_addr = getenv_IPaddr ("ipaddr"; /* 以太网接口MAC 地址 */ ……
devices_init (; /* 获取列表中的设备 */ jumptable_init (; console_init_r (; /* 完整地初始化控制台设备
*/

(_armboot_start -

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,具备多种引导内核启动的方式。常用gobootm命令可以直接引导内核映像启动。U-Boot与内核的关系主要是内核启动过程中参数的传递。

1go命令的实现
/* 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命令可以带变参,实际使用时不用来传递参数。




2bootm命令的实现
/* common/cmd_bootm.c */ int do_bootm (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[] { …… …… /* 检查头部 */ if (crc32 (0, (uchar *data, len != checksum { puts ("Bad Header Checksum\n"; SHOW_BOOT_PROGRESS (-2; return 1;
} …… …… /*解压缩*/ switch (hdr->ih_comp { case IH_COMP_NONE: if(ntohl(hdr->ih_load == addr { printf (" XIP %s ... ", name; } else { #if defined(CONFIG_HW_WATCHDOG || defined(CONFIG_WATCHDOG
size_t l = len;



void *to = (void *ntohl(hdr->ih_load; void *from = (void *data;
printf (" Loading %s ... ", name;

while (l > 0 {
size_t tail = (l > CHUNKSZ ? CHUNKSZ :
l;
WATCHDOG_RESET(; memmove (to, from, tail; to += tail; from += tail; l -= tail;
} #else /* !(CONFIG_HW_WATCHDOG || CONFIG_WATCHDOG */ memmove ((void * ntohl(hdr->ih_load, (uchar *data, len; #endif /* CONFIG_HW_WATCHDOG || CONFIG_WATCHDOG */ }
break; case IH_COMP_GZIP:


printf (" Uncompressing %s ... ", name; if (gunzip ((void *ntohl(hdr->ih_load, unc_len,
(uchar *data, &len != 0 {
puts ("GUNZIP ERROR - must RESET board to
recover\n";
SHOW_BOOT_PROGRESS (-6; do_reset (cmdtp, flag, argc, argv; }
break; #ifdef CONFIG_BZIP2
case IH_COMP_BZIP2: printf (" Uncompressing %s ... ", name;
/*
* If we've got less than 4 MB of malloc(
space,
* use slower decompression algorithm which
requires * at most 2300 KB of memory. */
i
=
BZ2_bzBuffToBuffDecompress
((char*ntohl(hdr->ih_load,



&unc_len, (char *data, len, CFG_MALLOC_LEN < (4096 * 1024, 0;
if (i != BZ_OK { printf ("BUNZIP2 ERROR %d - must RESET board to recover\n", i; SHOW_BOOT_PROGRESS (-6;
udelay(100000; do_reset (cmdtp, flag, argc, argv; }
break; #endif /* CONFIG_BZIP2 */ default:
if (iflag
enable_interrupts(; printf ("Unimplemented compression type %d\n", hdr->ih_comp; SHOW_BOOT_PROGRESS (-7; return 1;
}
} …… …… ……




switch (hdr->ih_os { default: /* handled by (original Linux case */ case IH_OS_LINUX:
do_bootm_linux (cmdtp, flag, argc, argv, addr, len_ptr, verify;
break; case IH_OS_NETBSD: do_bootm_netbsd (cmdtp, flag, argc, argv, addr, len_ptr, verify; break; case IH_OS_RTEMS: do_bootm_rtems (cmdtp, flag, argc, argv, addr, len_ptr, verify;
break; case IH_OS_VXWORKS: do_bootm_vxworks (cmdtp, flag, argc, argv, addr, len_ptr, verify; break; case IH_OS_QNX: do_bootm_qnxelf (cmdtp, flag, argc, argv, addr, len_ptr, verify;
break;




} bootm命令调用do_bootm函数。这个函数专门用来引导各种操作系统映像,可以支持引导LinuxvxWorksQNX等操作系统。引导Linux的时候,调用do_bootm_linux(函数。

3do_bootm_linux函数的实现 /* lib_arm/armlinux.c */ void do_bootm_linux (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[], ulong addr, ulong *len_ptr, int verify { theKernel = (void (*(int, int, uintntohl(hdr->ih_ep;
/* we assume that the kernel is in place */ printf ("\nStarting kernel ...\n\n";
theKernel
(0,
bd->bi_arch_number, bd->bi_boot_params; /*启动内核,传递启动参数*/ }


do_bootm_linux(函数是专门引导Linux映像的函数,它还可以处理ramdisk文件系统的映像。这里引导的内核映像和ramdisk映像,必须是U-Boot格式的。U-Boot格式的映像可以通过mkimage工具来转换,其中包含了U-Boot可以识别的符号。


Uboot启动代码解析

相关推荐