uboot 启动流程分析.docx
《uboot 启动流程分析.docx》由会员分享,可在线阅读,更多相关《uboot 启动流程分析.docx(12页珍藏版)》请在冰豆网上搜索。
uboot启动流程分析
U-Boot启动流程
大多数bootloader都分为stage1和stage2两大部分,u-boot也不例外。
依赖于CPU体系结构的代码(如设备初始化代码等)通常都放在stage1,且可以用汇编语言来实现,而stage2则通常用C语言来实现,这样可以实现复杂的功能,而且有更好的可读性和移植性。
u-boot启动大致流程如图1所示:
图1
>Stage1
在flash中执行的引导代码,也就是bootloader中的stage1,负责初始化硬件环境,把u-boot从flash加载到RAM中去,然后跳到lib_arm/board.c中的start_armboot中去执行。
u-boot的stage1代码通常放在文件中,它用汇编语言写成,其主要代码部分如下:
1) 定义入口由于一个可执行的Image必须有一个入口点,并且只能有一个全局入口,通常这个入口放在ROM(Flash)的0x0地址,因此,必须通知编译器以使其知道这个入口,该工作可通过修改连接器脚本来完成。
2) 设置异常向量(ExceptionVector)。
3) 设置CPU的速度、时钟频率及中断控制寄存器。
4) 初始化内存控制器
5) 将ROM中的程序复制到RAM中。
6) 初始化堆栈
7) 转到RAM中执行,该工作可使用指令ldrpc,_start_armboot来完成。
>Stage2
lib_arm/board.c中的start_armboot是C语言开始的函数,也是整个启动代码中C语言的主函数,同时还是整个u-boot(armboot)的主函数,该函数主要流程分析如下:
voidstart_armboot(void)
{
init_fnc_t**init_fnc_ptr;
char*s;
#if!
defined(CFG_NO_FLASH)||defined(CONFIG_VFD)||defined(CONFIG_LCD)
ulongsize;
#endif
#ifdefined(CONFIG_VFD)||defined(CONFIG_LCD)
unsignedlongaddr;
#endif
/*Pointeriswritablesinceweallocatedaregisterforit*/
/*给全局数据变量gd安排空间*/
gd=(gd_t*)(_armboot_start-CFG_MALLOC_LEN-sizeof(gd_t));
/*compileroptimizationbarrierneededforGCC>=3.4*/
__asm____volatile__("":
:
:
"memory");
/*给板子数据变量gd->bd安排空间*/
memset((void*)gd,0,sizeof(gd_t));
gd->bd=(bd_t*)((char*)gd-sizeof(bd_t));
memset(gd->bd,0,sizeof(bd_t));
monitor_flash_len=_bss_start-_armboot_start;
/*顺序执行init_sequence数组中的初始化函数*/
for(init_fnc_ptr=init_sequence;*init_fnc_ptr;++init_fnc_ptr){
if((*init_fnc_ptr)()!
=0){
hang();
}
}
/*初始化函数列表:
init_fnc_t*init_sequence[]={
cpu_init, /*basiccpudependentsetup*/
#ifdefined(CONFIG_SKIP_RELOCATE_UBOOT)
reloc_init, /*Settherelocationdoneflag,must
dothisAFTERcpu_init(),butassoon
aspossible*/
#endif
board_init, /*basicboarddependentsetup*/
interrupt_init, /*setupexceptions*/
env_init, /*initializeenvironment*/
init_baudrate, /*initialzebaudratesettings*/
serial_init, /*serialcommunicationssetup*/
console_init_f, /*stage1initofconsole*/
display_banner, /*saythatwearehere*/
#ifdefined(CONFIG_HW_WATCHDOG)
hw_watchdog_init, /*watchdogsetup*/
#endif
#ifdefined(CONFIG_DISPLAY_CPUINFO)
print_cpuinfo, /*displaycpuinfo(andspeed)*/
#endif
#ifdefined(CONFIG_DISPLAY_BOARDINFO)
checkboard, /*displayboardinfo*/
#endif
#ifdefined(CONFIG_HARD_I2C)||defined(CONFIG_SOFT_I2C)
init_func_i2c,
#endif
dram_init, /*configureavailableRAMbanks*/
display_dram_config,
NULL,
};
*/
/*armboot_startisdefinedintheboard-specificlinkerscript*/
mem_malloc_init(_armboot_start-CFG_MALLOC_LEN);
#ifdefined(CONFIG_CMD_NAND)
puts("NAND:
");
/*NANDFLASH初始化*/
nand_init(); /*goinittheNAND*/
#endif
/*重新定位环境变量*/
env_relocate();
#ifdefCONFIG_VFD
/*mustdothisaftertheframebufferisallocated*/
drv_vfd_init();
#endif/*CONFIG_VFD*/
#ifdefCONFIG_SERIAL_MULTI
/*串口初始化*/
serial_initialize();
#endif
/*从环境变量中获取IP地址和MAC地址*/
gd->bd->bi_ip_addr=getenv_IPaddr("ipaddr");
/*MACAddress*/
{
inti;
ulongreg;
char*s,*e;
chartmp[64];
i=getenv_r("ethaddr",tmp,sizeof(tmp));
s=(i>0)?
tmp:
NULL;
for(reg=0;reg<6;++reg){
gd->bd->bi_enetaddr[reg]=s?
simple_strtoul(s,&e,16):
0;
if(s)
s=(*e)?
e+1:
e;
}
#ifdefCONFIG_HAS_ETH1
i=getenv_r("eth1addr",tmp,sizeof(tmp));
s=(i>0)?
tmp:
NULL;
for(reg=0;reg<6;++reg){
gd->bd->bi_enet1addr[reg]=s?
simple_strtoul(s,&e,16):
0;
if(s)
s=(*e)?
e+1:
e;
}
#endif
}
devices_init(); /*getthedeviceslistgoing.*/
#ifdefCONFIG_CMC_PU2
load_sernum_ethaddr();
#endif/*CONFIG_CMC_PU2*/
/*跳转表的初始化*/
jumptable_init();
/*控制台的初始化*/
console_init_r(); /*fullyinitconsoleasadevice*/
/*IRQ中断使能*/
enable_interrupts();
/*各种型号网络设备的初始化*/
#ifdefCONFIG_DRIVER_TI_EMAC
externvoiddm644x_eth_set_mac_addr(constu_int8_t*addr);
if(getenv("ethaddr")){
dm644x_eth_set_mac_addr(gd->bd->bi_enetaddr);
}
#endif
#ifdefCONFIG_DRIVER_CS8900
cs8900_get_enetaddr(gd->bd->bi_enetaddr);
#endif
/*通过环境变量初始化load_addr
默认定义ulongload_addr=CFG_LOAD_ADDR;*/
if((s=getenv("loadaddr"))!
=NULL){
load_addr=simple_strtoul(s,NULL,16);
}
/**/
#ifdefined(CONFIG_CMD_NET)
if((s=getenv("bootfile"))!
=NULL){
copy_filename(BootFile,s,sizeof(BootFile));
}
#endif
#ifdefBOARD_LATE_INIT
board_late_init();
#endif
#ifdefined(CONFIG_CMD_NET)
#ifdefined(CONFIG_NET_MULTI)
puts("Net:
");
#endif
eth_initialize(gd->bd);
#ifdefined(CONFIG_RESET_PHY_R)
debug("ResetEthernetPHY\n");
reset_phy();
#endif
#endif
/*循环不断地执行main_loop()函数
main_loop()主要处理用户命令*/
for(;;){
main_loop();
}
}
整个u-boot的执行就进入等待用户输入命令,解析并执行命令的死循环中。
也许细心的你会问:
我在用UBoot的时候并没有直接进入用户命令界面呀,而是在倒计时结束后自动引导kernel。
这是怎么回事呢?
在main_loop()函数当中有如下一段代码:
#ifdefined(CONFIG_BOOTDELAY)&&(CONFIG_BOOTDELAY>=0)
•
•
•
s=getenv("bootcmd");
/*获取bootcmd的内容*/
/*bootcmd=nandread0x220000000xB00000x200000;bootm*/
•
•
#ifndefCFG_HUSH_PARSER
run_command(s,0);
/*运行s包含的命令*/
/*运行nandread0x220000000xB00000x200000表示将NANDFLASH
0xB0000处数据读取放于0x22000000处,读取长度为0x200000
*/
/*运行bootm命令,引导内核启动*/
#else
parse_string_outer(s,FLAG_PARSE_SEMICOLON|
FLAG_EXIT_FROM_LOOP);
#endif
•
•
•
#endif /*CONFIG_BOOTDELAY*/
bootm命令是什么?
它是怎样引导内核的?
要知道想解决这个问题,就要分析中的函数do_bootm,因为引导kernel就是bootm这条命令的工作,do_bootm是命令bootm的执行函数。
现在我们来分析一下中的函数do_bootm,这是bootm命令的处理函数。
intdo_bootm(cmd_tbl_t*cmdtp,intflag,intargc,char*argv[])
{
ulong iflag;
constchar *type_name;
uint unc_len=CFG_BOOTM_LEN;
uint8_t comp,type,os;
void *os_hdr;
ulong os_data,os_len;
ulong image_start,image_end;
ulong load_start,load_end;
ulong mem_start;
phys_size_t mem_size;
structlmblmb;
memset((void*)&images,0,sizeof(images));
=getenv_yesno("verify");
=&lmb;
lmb_init(&lmb);
mem_start=getenv_bootm_low();
mem_size=getenv_bootm_size();
lmb_add(&lmb,(phys_addr_t)mem_start,mem_size);
board_lmb_reserve(&lmb);
/*getkernelimageheader,startaddressandlength*/
/*获取内核镜像头信息*/
/*打印“##BootingkernelfromLegacyImageat22000000...
ImageName:
Linux-
ImageType:
ARMLinuxKernelImage(uncompressed)
DataSize:
1507760Bytes=1.4MB
LoadAddress:
20008000
EntryPoint:
20008000
VerifyingChecksum...OK”*/
os_hdr=boot_get_kernel(cmdtp,flag,argc,argv,
&images,&os_data,&os_len);
if(os_len==0){
puts("ERROR:
can'tgetkernelimage!
\n");
return1;
}
/*getimageparameters*/
/*获取内核镜像格式*/
switch(genimg_get_format(os_hdr)){
caseIMAGE_FORMAT_LEGACY:
/*获取内核镜像参数*/
type=image_get_type(os_hdr);
comp=image_get_comp(os_hdr);
os=image_get_os(os_hdr);
image_end=image_get_image_end(os_hdr);
load_start=image_get_load(os_hdr);
break;
}
image_start=(ulong)os_hdr;
load_end=0;
type_name=genimg_get_type_name(type);
/*禁止所有中断*/
iflag=disable_interrupts();
#ifdefCONFIG_AMIGAONEG3SE
/*
*We'vepossibleleftthecachesenabledduring
*biosemulation,soturnthemoffagain
*/
icache_disable();
invalidate_l1_instruction_cache();
flush_data_cache();
dcache_disable();
#endif
switch(comp){
caseIH_COMP_NONE:
/*加载内核镜像*/
/*打印“LoadingKernelImage...OK”*/
if(load_start==(ulong)os_hdr){
printf(" XIP%s...",type_name);
}else{
printf(" Loading%s...",type_name);
memmove_wd((void*)load_start,
(void*)os_data,os_len,CHUNKSZ);
}
load_end=load_start+os_len;
puts("OK\n");
break;
}
puts("OK\n");
debug(" kernelloadedat0x%08lx,end=0x%08lx\n",load_start,load_end);
show_boot_progress(7);
/*加载错误*/
if((load_startimage_start)){
debug("image_start=0x%lX,image_end=0x%lx\n",image_start,image_end);
debug("load_start=0x%lx,load_end=0x%lx\n",load_start,load_end);
if(){
if(image_get_type()==IH_TYPE_MULTI)
puts("WARNING:
legacyformatmulticomponent"
"imageoverwritten\n");
}else{
puts("ERROR:
newformatimageoverwritten-"
"mustRESETtheboardtorecover\n");
show_boot_progress(-113);
do_reset(cmdtp,flag,argc,argv);
}
}