系统的启动流程.docx
- 文档编号:27469551
- 上传时间:2023-07-01
- 格式:DOCX
- 页数:16
- 大小:21.67KB
系统的启动流程.docx
《系统的启动流程.docx》由会员分享,可在线阅读,更多相关《系统的启动流程.docx(16页珍藏版)》请在冰豆网上搜索。
系统的启动流程
系统的启动流程
这里我们来讨论Uoot的启动流程,并将延伸至内核启动到start_kernel函数,我们主要讲start_kernel函数前系统都在做什么,系统是怎么样从开始运行,到解压内核,跳到内核运行,又怎么样从Uboot里将参数传到内核的。
Uboot的启动是从start.S文件开始的,系统一上电pc指针便指向这里开始程序的启动运行,在start.S文件中系统的工作主要有:
系统一上电便是reset异常,便跳到reset处进行处理,初始化Sdram,cache等,然后跳到board_init_f函数中运行。
Board_init_if(lib_mips/board.c):
在此函数中对定时器进行初始化,环境变量初始化,初始化串口,然后根据Sdram进行计算(具体的计算过程看代码),计算出一个具体的地址,即SDRAM的高端地址,然后调用relocate_code函数,将Uboot程序Copy到内存,copy完后即跳到in_ram(内存运行)中接着运行Uboot,最后调用board_init_r函数。
Relocate_code是一段汇编代码,位于start.S中,怎么实现copy就自已仔细看代码了啊,in_ram也在start.S中。
接下来我们来主要看board_init_r函数:
开始部分仍是对系统一些功能的初始化:
/*configureavailableFLASHbanks*/
size=flash_init();初始化flash
gpio_init();//初始gpio
/**leavethishere(aftermalloc(),environmentandPCIareworking)**/
/*Initializedevices*/
devices_init();//初始化外设
/*initializetheconsole(aftertherelocationanddevicesinit)*/
console_init_r();初始化console
cy_nvram_init();
mac_init();
进行一些系统初配置初始化后,Uboot就便等待三秒,等待用户输入字符,以进行不同的操作,即当按下4时会进入命令处理模式,按下CTRL+ESC时会进入tftpd,可以进行文件的上传。
我们这里只探讨正常启动不探讨tftpd下载和命令处理模式。
当我们启动时什么也不操作时,默认的启动类型为3,就会进入到这里:
if(BootType=='3'){
char*argv[3];
printf("/n3:
SystemBootsystemcodeviaFlash./n");
do_bootm(cmdtp,0,2,argv); //从这里开始对内核进行解压,并跳到内核处运行。
/*belowonlywhenbootfromflashfali*/
argv[2]=&file_name_space[0];
memset(file_name_space,0,ARGV_LEN);
eth_initialize(gd->bd);
do_tftpd(cmdtp,0,4,argv); //启动tftpd。
}
如果kernel确实被写入了flash中,则do_bootm是不会返回的,则之后的do_tftpd就不会执行。
当我们重新烧写Uboot后,必须重新烧写内核与文件系统,因此当重新烧写Uboot后,此时do_bootm就会出错,返回,则就开启了tftpd,进行下载模式。
接下来我们看看do_bootm函数,看它是怎么样解压内核,怎么样跳到内核执行的。
intdo_bootm(cmd_tbl_t*cmdtp,intflag,intargc,char*argv[])
{
………………………….
if(argc<2){
addr=load_addr;
}else{
addr=simple_strtoul(argv[1],NULL,16);//我们传过来的参数为2,所以就从地址就是argv[1],这个地址呢就是CFG_FLASH_BASE+0X40000,即0xbc440000,这里便是kernel的起始地址
}
memmove(&header,(char*)addr,sizeof(image_header_t)); //kernel启去始地址处我们在做code.bin时,在前面通过mkimage加了一个image_header的头,之后才加上code pattern的。
这里我们烧写的时侯去掉了codepattern,所以flash里开始处便是image_header头,这里就是将这个头读出来。
if(ntohl(hdr->ih_magic)!
=IH_MAGIC){ //这里判断这个头正确不正确,通过一个IH_MAGIC来判断,每次重新烧写Uboot后,flash内核开始处,image_header就会被破坏,这个条件就不成立,于时就会在这里直接返回,之后就像上面所讲的进入了tftpd.
{
printf("BadMagicNumber,%08X/n",ntohl(hdr->ih_magic));
SHOW_BOOT_PROGRESS(-1);
return1;
}
}
data=(ulong)&header;
len=sizeof(image_header_t);
checksum=ntohl(hdr->ih_hcrc);//计算校验和是否正确
hdr->ih_hcrc=0;
if(crc32(0,(char*)data,len)!
=checksum){
puts("BadHeaderChecksum/n");
SHOW_BOOT_PROGRESS(-2);
return1;
}
SHOW_BOOT_PROGRESS(3);
/*formulti-fileimagesweneedthedatapart,too*/
print_image_hdr((image_header_t*)addr); 将image_header头的内容打印出来
ImageType:
MIPSLinuxKernelImage(lzmacompressed)
DataSize:
3174400Bytes=3MB
LoadAddress:
8a00000
EntryPoint:
881ee040
这个是我们当前的image_header头。
data=addr+sizeof(image_header_t);//去掉image_header头,这里便是kernel开始的部分了。
len=ntohl(hdr->ih_size);
#ifdefCONFIG_HAS_DATAFLASH
if(addr_dataflash(addr)){
read_dataflash(data,len,(char*)CFG_LOAD_ADDR);
data=CFG_LOAD_ADDR;
}
#endif
if(verify){
puts("VerifyingChecksum...");
if(crc32(0,(char*)data,len)!
=ntohl(hdr->ih_dcrc)){
printf("BadDataCRC/n");
//kaikerSHOW_BOOT_PROGRESS(-3);
return1;
}
puts("OK/n");
}
SHOW_BOOT_PROGRESS(4);
len_ptr=(ulong*)data;
………………………………………….
switch(hdr->ih_type){ 查看这部分的类型,目前为KERNEL,
caseIH_TYPE_STANDALONE:
name="StandaloneApplication";
/*Asecondargumentoverwritestheloadaddress*/
if(argc>2){
hdr->ih_load=simple_strtoul(argv[2],NULL,16);
}
break;
caseIH_TYPE_KERNEL:
name="KernelImage"; //执行这里。
break;
caseIH_TYPE_MULTI:
name="Multi-FileImage";
len=ntohl(len_ptr[0]);
/*OSkernelisalwaysthefirstimage*/
data+=8;/*kernel_len+terminator*/
for(i=1;len_ptr[i];++i)
data+=4;
break;
default:
printf("WrongImageTypefor%scommand/n",cmdtp->name);
SHOW_BOOT_PROGRESS(-5);
return1;
}
SHOW_BOOT_PROGRESS(6);
/*
*Wehavereachedthepointofnoreturn:
wearegoingto
*overwriteallexceptionvectorcode,sowecannoteasily
*recoverfromanyfailuresanymore...
*/
iflag=disable_interrupts();
switch(hdr->ih_comp){ //判断压缩类型,刚才我们从image_header看出是lzma压缩的。
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:
#if1
printf("Uncompressing%s...",name);
if(gunzip((void*)ntohl(hdr->ih_load),unc_len,
(uchar*)data+sizeof(structtrx_header),&len)!
=0){
puts("GUNZIPERROR-mustRESETboardtorecover/n");
SHOW_BOOT_PROGRESS(-6);
do_reset(cmdtp,flag,argc,argv);
}
#endif
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*/
#ifdefCONFIG_LZMA //所以就跳到这里来了,
caseIH_COMP_LZMA:
printf("Uncompressing%s...",name);
#ifdefCONFIG_UNCOMPRESS_TIME
tBUncompress=get_ticks();
#endif
unsignedintdestLen=0;
接来来开始解压,解压目的地址hdr->ih_load,我们从打印的image_header看出,是0x8a000000.,所以解压到这里。
i=lzmaBuffToBuffDecompress((char*)ntohl(hdr->ih_load),
&destLen,(char*)data+sizeof(structtrx_header),len);
if(i!
=LZMA_RESULT_OK){
printf("LZMAERROR%d-mustRESETboardtorecover/n",i);
return1;
//SHOW_BOOT_PROGRESS(-6);
//udelay(100000);
//do_reset(cmdtp,flag,argc,argv);
}
#endif/*CONFIG_LZMA*/
default:
if(iflag)
enable_interrupts();
printf("Unimplementedcompressiontype%d/n",hdr->ih_comp);
SHOW_BOOT_PROGRESS(-7);
return1;
}
………………………………….
switch(hdr->ih_os){//这里我们是linux内核,所以要调用do_bootm_linux函数。
default:
/*handledby(original)Linuxcase*/
caseIH_OS_LINUX:
#ifdefCONFIG_SILENT_CONSOLE
fixup_silent_linux();
#endif
do_bootm_linux(cmdtp,flag,argc,argv,
addr,len_ptr,verify);
break;
…………………………………………………………………………………..
}
return1;
}
接下来我们看看do_bootm_linux函数,现在以经解压完了,所以这里我们只关注它是怎么跳转到内核开始运行的。
do_bootm_linux(cmd_tbl_t*cmdtp,intflag,
intargc,char*argv[],
ulongaddr,
ulong*len_ptr,
intverify)
{
…………………………………………………..
kernel=(void(*)(bd_t*,ulong,ulong,ulong,ulong))hdr->ih_ep;//image_header,ih_ep,对于当前内核,我们可以从刚才打印imager_header参数时看出,为0x881ee040,这个地址是什么呢,它其时就是head.S,kernel_entry的地址,
……………………………………………….
(*kernel)(kbd,initrd_start,initrd_end,cmd_start,cmd_end); //这里就是跳到kernel的地方,即跳到kernel_entry处开始内核的运行了。
之后就便是开始从head.S中的kernel_entry开始执行了。
从而完成从Uboot到内核的交接。
}
接下来我们进入内核看看:
*Kernelentrypoint
*/
NESTED(kernel_entry,16,sp) //kernel进入点
.setpush
/*
*Forthemomentdisableinterruptsandmarkthekernelmode.
*AfullinitializationoftheCPU'sstatusregisterisdone
*laterinper_cpu_trap_init().
*/
mfc0t0,CP0_STATUS
ort0,ST0_CU0|0x1f
xort0,0x1f
mtc0t0,CP0_STATUS
.setnoreorder
sllzero,3#ehb
.setreorder
/*
*Thefirmware/bootloaderpassesargc/argp/envp
*tousasarguments.Butclearbssfirstbecause
*theromvecandotherimportantinfoisstoredthere
*byprom_init().
*/
lat0,_edata
swzero,(t0)
lat1,(_end-4)
1:
addiut0,4
swzero,(t0)
bnet0,t1,1b
/*
*Stackforkernelandinit,currentvariable
*/
la$28,init_task_union
addiut0,$28,KERNEL_STACK_SIZE-32
subusp,t0,4*SZREG
swt0,kernelsp
jalinit_arch//跳到init_arch函数,从这里跳到C代码执行。
.setpop
END(kernel_entry)
Init_arch函数:
asmlinkagevoid__init
init_arch(intargc,char**argv,char**envp,int*prom_vec)
{
/*DeterminewhichMIPSvariantwearerunningon.*/
cpu_probe(); //CPU探测,读出CPU ID
prom_init(argc,argv,envp,prom_vec);
cpu_report();//打印CPU信息
/*
*Determinethemmu/cacheattachedtothismachine,
*thenflushthetlbandcaches.Onther4xx0
*variantsthisalsosetsCP0_WIREDtozero.
*/
load_mmu();//
start_kernel();
}
int__initprom_init()
{
/*changeparameterbybobtseng,2006.1.3.*/
mips_machgroup=MACH_GROUP_RT2880;
set_io_port_base(KSEG1);
prom_init_cmdline();//初始化启动参数,将启动参数分解并存入argc数组里
prom_init_sysclk(); //初始化CPU频率
prom_init_serial_port();/*NeededforSerialConsole*/ //初始化串口参数
//prom_init_mac();//removefortest,bobtseng2006.1.3.
prom_setup_printf(prom_get_ttysnum()); //可能有两个串口,选择哪一个
#ifdefined(CONFIG_RT2880_FPGA)
prom_printf("/nTHISISFPGA/n");
#elifdefined(CONFIG_RT2880_ASIC)
prom_printf("/nTHISISASIC/n");
#else
#errorPleaseChoiceChipType
#endif
prom_meminit(); //初始化SDRAM,取出base地址和size大小,为mmu做准备
return0;
}
以上便是从Uboot的启动到内核start_kernel的运行过程,start_kernel之后便是跟内核原理紧密相关的启动过程,在这里就不介绍了,有兴趣的同事可以慢慢研究。
这里仍有两个问题。
一个是我们正常的
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 系统 启动 流程