Uboot启动流程.docx
《Uboot启动流程.docx》由会员分享,可在线阅读,更多相关《Uboot启动流程.docx(11页珍藏版)》请在冰豆网上搜索。
Uboot启动流程
U-Boot启动过程
问题:
u-boot 入口点?
C语言是main. U-boot是lds链接器脚本
开发板上电后,执行U-Boot的第一条指令,然后顺序执行U-Boot启动函数。
看一下board/smdk2410/u-boot.lds这个链接脚本,可以知道目标程序的各部分链接顺序。
第一个要链接的是cpu/arm920t/start.o,那么U-Boot的入口指令一定位于这个程序中。
下面分两阶段介绍启动流程:
第一阶段
1.cpu/arm920t/start.S
这个汇编程序是U-Boot的入口程序,开头就是复位向量的代码。
_start:
breset//复位向量
ldrpc,_undefined_instruction
ldrpc,_software_interrupt
ldrpc,_prefetch_abort
ldrpc,_data_abort
ldrpc,_not_used
ldrpc,_irq//中断向量,在程序执行中发生中断了,CPU会跳转到这里来
ldrpc,_fiq//中断向量
…
/*theactualresetcode*/
reset:
//复位启动子程序
/*设置CPU为SVC32模式*/
mrsr0,cpsr
bicr0,r0,#0x1f
orrr0,r0,#0xd3
msrcpsr,r0
/*关闭看门狗*/
…………
relocate:
/*把U-Boot重新定位到RAM*/
adrr0,_start/*r0是代码的当前位置*/
ldrr1,_TEXT_BASE/*_TEXT_BASE是RAM中的地址*/
cmpr0,r1/*比较r0和r1,判断当前是从Flash启动,还是RAM*/
beqstack_setup/*如果r0等于r1,跳过重定位代码*/
/*准备重新定位代码*/
ldrr2,_armboot_start
ldrr3,_bss_start
subr2,r3,r2/*r2得到armboot的大小*/
addr2,r0,r2/*r2得到要复制代码的末尾地址*/
copy_loop:
/*重新定位代码*/
ldmiar0!
{r3-r10}/*从源地址[r0]复制*/
stmiar1!
{r3-r10}/*复制到目的地址[r1]*/
cmpr0,r2/*复制数据块直到源数据末尾地址[r2]*/
blecopy_loop
/*初始化堆栈等*/
stack_setup:
ldrr0,_TEXT_BASE/*上面是128KiB重定位的u-boot*/
subr0,r0,#CFG_MALLOC_LEN/*向下是内存分配空间*/
subr0,r0,#CFG_GBL_DATA_SIZE/*然后是bdinfo结构体地址空间*/
#ifdefCONFIG_USE_IRQ
subr0,r0,#(CONFIG_STACKSIZE_IRQ+CONFIG_STACKSIZE_FIQ)
#endif
subsp,r0,#12/*为abort-stack预留3个字*/
clear_bss:
ldrr0,_bss_start/*找到bss段起始地址*/
ldrr1,_bss_end/*bss段末尾地址*/
movr2,#0x00000000/*清零*/
clbss_l:
strr2,[r0]
/*bss段地址空间清零循环...*/
addr0,r0,#4
cmpr0,r1
bneclbss_l
/*跳转到start_armboot函数入口,_start_armboot字保存函数入口指针*/
ldrpc,_start_armboot
_start_armboot:
.wordstart_armboot//start_armboot函数在lib_arm/board.c中实现
//不要求背住,只要求了解
第二阶段
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,/*打印u-boot信息--lib_arm/board.c*/
dram_init,/*配置可用的RAM--board/smdk2410/smdk2410.c*/
display_dram_config,/*显示RAM的配置大小--lib_arm/board.c*/
NULL,
};
voidstart_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超级终端演示,获取用户命令,然后去解析执行
命令实现
U-Boot作为Bootloader,具备多种引导内核启动的方式。
常用的go和bootm命令可以直接引导内核映像启动。
U-Boot与内核的关系主要是内核启动过程中参数的传递。
1.go命令的实现
/*common/cmd_boot.c*/
intdo_go(cmd_tbl_t*cmdtp,intflag,intargc,char*argv[])
{
ulongaddr,rc;
intrcode=0;
if(argc<2){
printf("Usage:
\n%s\n",cmdtp->usage);
return1;
}
addr=simple_strtoul(argv[1],NULL,16);
printf("##Startingapplicationat0x%08lX...\n",addr);
rc=((ulong(*)(int,char[]))addr)(--argc,&argv[1]);/*运行程序*/
if(rc!
=0)rcode=1;
printf("##Applicationterminated,rc=0x%lX\n",rc);/*如果是运行linux,这条指令是否能运行?
*/
returnrcode;
}
go命令调用do_go()函数,跳转到某个地址执行的。
如果在这个地址准备好了自引导的内核映像,就可以启动了。
尽管go命令可以带变参,实际使用时不用来传递参数。
2.bootm命令的实现
/*common/cmd_bootm.c*/
intdo_bootm(cmd_tbl_t*cmdtp,intflag,intargc,char*argv[])
{
…………
/*检查头部*/
if(crc32(0,(uchar*)data,len)!
=checksum){
puts("BadHeaderChecksum\n");
SHOW_BOOT_PROGRESS(-2);
return1;
}
…………
/*解压缩*/
switch(hdr->ih_comp){
caseIH_COMP_NONE:
if(ntohl(hdr->ih_load)==addr){
printf("XIP%s...",name);
}else{
#ifdefined(CONFIG_HW_WATCHDOG)||defined(CONFIG_WATCHDOG)
size_tl=len;
void*to=(void*)ntohl(hdr->ih_load);
void*from=(void*)data;
printf("Loading%s...",name);
while(l>0){
size_ttail=(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;
caseIH_COMP_GZIP:
printf("Uncompressing%s...",name);
if(gunzip((void*)ntohl(hdr->ih_load),unc_len,
(uchar*)data,&len)!
=0){
puts("GUNZIPERROR-mustRESETboardtorecover\n");
SHOW_BOOT_PROGRESS(-6);
do_reset(cmdtp,flag,argc,argv);
}
break;
#ifdefCONFIG_BZIP2
caseIH_COMP_BZIP2:
printf("Uncompressing%s...",name);
/*
*Ifwe'vegotlessthan4MBofmalloc()space,
*useslowerdecompressionalgorithmwhichrequires
*atmost2300KBofmemory.
*/
i=BZ2_bzBuffToBuffDecompress((char*)ntohl(hdr->ih_load),
&unc_len,(char*)data,len,
CFG_MALLOC_LEN<(4096*1024),0);
if(i!
=BZ_OK){
printf("BUNZIP2ERROR%d-mustRESETboardtorecover\n",i);
SHOW_BOOT_PROGRESS(-6);
udelay(100000);
do_reset(cmdtp,flag,argc,argv);
}
break;
#endif/*CONFIG_BZIP2*/
default:
if(iflag)
enable_interrupts();
printf("Unimplementedcompressiontype%d\n",hdr->ih_comp);
SHOW_BOOT_PROGRESS(-7);
return1;
}
}
………………
switch(hdr->ih_os){
default:
/*handledby(original)Linuxcase*/
caseIH_OS_LINUX:
do_bootm_linux(cmdtp,flag,argc,argv,
addr,len_ptr,verify);
break;
caseIH_OS_NETBSD:
do_bootm_netbsd(cmdtp,flag,argc,argv,
addr,len_ptr,verify);
break;
caseIH_OS_RTEMS:
do_bootm_rtems(cmdtp,flag,argc,argv,
addr,len_ptr,verify);
break;
caseIH_OS_VXWORKS:
do_bootm_vxworks(cmdtp,flag,argc,argv,
addr,len_ptr,verify);
break;
caseIH_OS_QNX:
do_bootm_qnxelf(cmdtp,flag,argc,argv,
addr,len_ptr,verify);
break;
}
bootm命令调用do_bootm函数。
这个函数专门用来引导各种操作系统映像,可以支持引导Linux、vxWorks、QNX等操作系统。
引导Linux的时候,调用do_bootm_linux()函数。
3.do_bootm_linux函数的实现
/*lib_arm/armlinux.c*/
voiddo_bootm_linux(cmd_tbl_t*cmdtp,intflag,intargc,char*argv[],
ulongaddr,ulong*len_ptr,intverify)
{
theKernel=(void(*)(int,int,uint))ntohl(hdr->ih_ep);
…………
/*weassumethatthekernelisinplace*/
printf("\nStartingkernel...\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可以识别的符号。