收藏大全读书名言大全收藏大全

U-Boot启动过程 (国嵌2440培训)

收藏大全
永远不要看不起任何人
计算机考试指南

U-Boot启动过程

(国嵌2440培训)

开发板上电后,执行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//中断向量

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作为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可以识别的符号。

责任编辑:收藏大全