第一句子网 - 唯美句子、句子迷、好句子大全
第一句子网 > uboot 命令分析(一) — bootm

uboot 命令分析(一) — bootm

时间:2021-02-09 19:53:57

相关推荐

uboot 命令分析(一) — bootm

bootm用于将内核镜像加载到内存的指定地址处,如果有需要还要解压镜像,然后根据操作系统和体系结构的不同给内核传递不同的启动参数,最后启动内核。

一、arm 架构处理器对 linux 内核启动之前环境的五点需求

1、cpu 寄存器设置

* R0 = 0

* R1 = 板级 id

* R2 = 启动参数在内存中的起始地址

2、cpu 模式

* 禁止所有中断

* 必须为SVC(超级用户)模式

3、缓存、MMU

* 关闭 MMU

* 指令缓存可以开启或者关闭

* 数据缓存必须关闭并且不能包含任何脏数据

4、设备

* DMA 设备应当停止工作

5、boot loader 需要跳转到内核镜像的第一条指令处

这些需求都由 boot loader 实现,在常用的 uboot 中完成一系列的初始化后最后通过 bootm 命令加载 linux 内核。该命令用法介绍如下:

[cpp]view plaincopy#helpbootm bootm-bootapplicationimagefrommemory Usage: bootm[addr[arg...]] -bootapplicationimagestoredinmemory passingarguments'arg...';whenbootingaLinuxkernel, 'arg'canbetheaddressofaninitrdimage Sub-commandstodopartofthebootmsequence.Thesub-commandsmustbe issuedintheorderbelow(it'soktonotissueallsub-commands): start[addr[arg...]] loados-loadOSimage cmdline-OSspecificcommandlineprocessing/setup bdt-OSspecificbd_tprocessing prep-OSspecificprepbeforerelocationorgo go-startOS 二、基础数据结构

在 bootm 中常用的数据结构有image_info_tbootm_headers_t,定义如下:

[cpp]view plaincopy/*镜像信息*/ typedefstructimage_info{ ulongstart,end;/*start/endofblob*/ ulongimage_start,image_len;/*startofimagewithinblob,lenofimage*/ ulongload;/*loadaddrfortheimage*/ uint8_tcomp,type,os;/*compression,typeofimage,ostype*/ }image_info_t; /*bootm头*/ typedefstructbootm_headers{ image_header_t*legacy_hdr_os;/*指向镜像头的指针*/ image_header_tlegacy_hdr_os_copy;/*镜像头的备份*/ ulonglegacy_hdr_valid;/*镜像头存在标记*/ image_info_tos;/*系统镜像信息*/ ulongep;/*系统入口地址*/ ulongrd_start,rd_end;/*虚拟磁盘起始地址*/ ulongft_len;/*平坦设备树的长度*/ ulonginitrd_start; ulonginitrd_end; ulongcmdline_start; ulongcmdline_end; bd_t*kbd; intverify;/*getenv("verify")[0]!='n'*/ #defineBOOTM_STATE_START(0x00000001) #defineBOOTM_STATE_LOADOS(0x00000002) #defineBOOTM_STATE_RAMDISK(0x00000004) #defineBOOTM_STATE_FDT(0x00000008) #defineBOOTM_STATE_OS_CMDLINE(0x00000010) #defineBOOTM_STATE_OS_BD_T(0x00000020) #defineBOOTM_STATE_OS_PREP(0x00000040) #defineBOOTM_STATE_OS_GO(0x00000080) intstate;/*状态标记*/ structlmblmb;/*逻辑内存块*/ }bootm_headers_t; 三、代码解析

bootm 的主函数为do_bootm,代码如下:

[cpp]view plaincopyintdo_bootm(cmd_tbl_t*cmdtp,intflag,intargc,char*argv[]) { ulongiflag; ulongload_end=0; intret; boot_os_fn*boot_fn; if(bootm_start(cmdtp,flag,argc,argv))/*获取镜像信息*/ return1; iflag=disable_interrupts();/*关闭中断*/ usb_stop();/*关闭usb设备*/ ret=bootm_load_os(images.os,&load_end,1);/*加载内核*/ lmb_reserve(&images.lmb,images.os.load,(load_end-images.os.load)); if(images.os.os==IH_OS_LINUX)/*如果有需要关闭内核的串口*/ fixup_silent_linux(); boot_fn=boot_os[images.os.os];/*获取启动函数*/ arch_preboot_os();/*启动前准备*/ boot_fn(0,argc,argv,&images);/*启动,不再返回*/ puts("\n##Controlreturnedtomonitor-resetting...\n"); do_reset(cmdtp,flag,argc,argv); return1; } 该函数的实现分为 3 个部分:首先通过 bootm_start 函数分析镜像的信息,如果满足判定条件则进入 bootm_load_os 函数进行加载,加载完成后就可以调用 boot_fn 开始启动。

1、bootm_start[cpp]view plaincopystaticintbootm_start(cmd_tbl_t*cmdtp,intflag,intargc,char*argv[]) { ulongmem_start; phys_size_tmem_size; void*os_hdr; intret; memset((void*)&images,0,sizeof(images)); images.verify=getenv_yesno("verify");/*获取环境变量*/ lmb_init(&images.lmb); mem_start=getenv_bootm_low(); mem_size=getenv_bootm_size(); lmb_add(&images.lmb,(phys_addr_t)mem_start,mem_size); arch_lmb_reserve(&images.lmb); board_lmb_reserve(&images.lmb); /*获取镜像头,加载地址,长度*/ os_hdr=boot_get_kernel(cmdtp,flag,argc,argv, &images,&images.os.image_start,&images.os.image_len); if(images.os.image_len==0){ puts("ERROR:can'tgetkernelimage!\n"); return1; } /*获取镜像参数*/ switch(genimg_get_format(os_hdr)){ caseIMAGE_FORMAT_LEGACY: images.os.type=image_get_type(os_hdr);/*镜像类型*/ p=image_get_comp(os_hdr);/*压缩类型*/ images.os.os=image_get_os(os_hdr);/*系统类型*/ images.os.end=image_get_image_end(os_hdr);/*镜像结束地址*/ images.os.load=image_get_load(os_hdr);/*加载地址*/ break; default: puts("ERROR:unknownimageformattype!\n"); return1; } /*查询内核入口地址*/ if(images.legacy_hdr_valid){ images.ep=image_get_ep(&images.legacy_hdr_os_copy); }else{ puts("Couldnotfindkernelentrypoint!\n"); return1; } if(images.os.os==IH_OS_LINUX){ /*查询是否存在虚拟磁盘*/ ret=boot_get_ramdisk(argc,argv,&images,IH_INITRD_ARCH, &images.rd_start,&images.rd_end); if(ret){ puts("Ramdiskimageiscorruptorinvalid\n"); return1; } } images.os.start=(ulong)os_hdr;/*赋值加载地址*/ images.state=BOOTM_STATE_START;/*更新状态*/ return0; } 该函数主要进行镜像的有效性判定、校验、计算入口地址等操作,大部分工作通过boot_get_kernel ->image_get_kernel完成,代码如下:[cpp]view plaincopystaticimage_header_t*image_get_kernel(ulongimg_addr,intverify) { image_header_t*hdr=(image_header_t*)img_addr; if(!image_check_magic(hdr)){/*魔数比较*/ puts("BadMagicNumber\n"); show_boot_progress(-1); returnNULL; } show_boot_progress(2); if(!image_check_hcrc(hdr)){/*镜像头校验和*/ puts("BadHeaderChecksum\n"); show_boot_progress(-2); returnNULL; } show_boot_progress(3); image_print_contents(hdr);/*打印镜像头信息*/ if(verify){/*校验镜像*/ puts("VerifyingChecksum..."); if(!image_check_dcrc(hdr)){ printf("BadDataCRC\n"); show_boot_progress(-3); returnNULL; } puts("OK\n"); } show_boot_progress(4); if(!image_check_target_arch(hdr)){/*处理器类型比较*/ printf("UnsupportedArchitecture0x%x\n",image_get_arch(hdr)); show_boot_progress(-4); returnNULL; } returnhdr; } staticvoid*boot_get_kernel(cmd_tbl_t*cmdtp,intflag,intargc,char*argv[], bootm_headers_t*images,ulong*os_data,ulong*os_len) { ulongimg_addr; image_header_t*hdr; if(argc<2){/*如果没有参数则用默认加载地址*/ img_addr=load_addr; debug("*kernel:defaultimageloadaddress=0x%08lx\n",load_addr); }else{ img_addr=simple_strtoul(argv[1],NULL,16); debug("*kernel:cmdlineimageaddress=0x%08lx\n",img_addr); } *os_data=*os_len=0; switch(genimg_get_format((void*)img_addr)){ caseIMAGE_FORMAT_LEGACY: printf("##BootingkernelfromLegacyImageat%08lx...\n",img_addr); hdr=image_get_kernel(img_addr,images->verify);/*获取内核*/ if(!hdr) returnNULL; show_boot_progress(5); /*getos_dataandos_len*/ switch(image_get_type(hdr)){ caseIH_TYPE_KERNEL: *os_data=image_get_data(hdr);/*内核入口地址*/ *os_len=image_get_data_size(hdr);/*内核长度(不包括头部)*/ break; default: printf("WrongImageTypefor%scommand\n",cmdtp->name); show_boot_progress(-5); returnNULL; } /*备份镜像头以防止在内核解压时被覆盖*/ memmove(&images->legacy_hdr_os_copy,hdr,sizeof(image_header_t)); images->legacy_hdr_os=hdr; images->legacy_hdr_valid=1;/*标记存在入口点*/ break; default: printf("WrongImageFormatfor%scommand\n",cmdtp->name); show_boot_progress(-108); returnNULL; } debug("kerneldataat0x%08lx,len=0x%08lx(%ld)\n",*os_data,*os_len,*os_len); return(void*)img_addr;/*返回加载地址*/ }

在有效性判定完成时会打印出镜像的一些信息,代码如下:

[cpp]view plaincopyvoidimage_print_contents(constvoid*ptr) { constimage_header_t*hdr=(constimage_header_t*)ptr; constchar*p; p=""; printf("%sImageName:%.*s\n",p,IH_NMLEN,image_get_name(hdr));/*镜像名称*/ #ifdefined(CONFIG_TIMESTAMP)||defined(CONFIG_CMD_DATE)||defined(USE_HOSTCC) printf("%sCreated:",p);/*创建时间*/ genimg_print_time((time_t)image_get_time(hdr)); #endif printf("%sImageType:",p);/*镜像类型*/ image_print_type(hdr); printf("%sDataSize:",p);/*镜像大小*/ genimg_print_size(image_get_data_size(hdr)); printf("%sLoadAddress:%08x\n",p,image_get_load(hdr));/*加载地址*/ printf("%sEntryPoint:%08x\n",p,image_get_ep(hdr));/*入口地址*/ }

2、bootm_load_os

这个函数主要判段镜像是否需要解压,并且将镜像移动到加载地址:

[cpp]view plaincopystaticintbootm_load_os(image_info_tos,ulong*load_end,intboot_progress) { uint8_tcomp=p;/*压缩格式*/ ulongload=os.load;/*加载地址*/ ulongblob_start=os.start;/*镜像起始地址*/ ulongblob_end=os.end;/*镜像结束地址*/ ulongimage_start=os.image_start;/*镜像起始地址*/ ulongimage_len=os.image_len;/*镜像长度*/ uintunc_len=CONFIG_SYS_BOOTM_LEN;/*镜像最大长度*/ constchar*type_name=genimg_get_type_name(os.type);/*镜像类型*/ switch(comp){/*选择解压格式*/ caseIH_COMP_NONE:/*镜像没有压缩过*/ if(load==blob_start){/*判断是否需要移动镜像*/ printf("XIP%s...",type_name); }else{ printf("Loading%s...",type_name); if(load!=image_start){ memmove_wd((void*)load,(void*)image_start,image_len,CHUNKSZ); } } *load_end=load+image_len; puts("OK\n"); break; caseIH_COMP_GZIP:/*镜像采用gzip解压*/ printf("Uncompressing%s...",type_name); if(gunzip((void*)load,unc_len,(uchar*)image_start,&image_len)!=0){/*解压*/ puts("GUNZIP:uncompress,out-of-memoroverwriteerror" "-mustRESETboardtorecover\n"); returnBOOTM_ERR_RESET; } *load_end=load+image_len; break; ... default: printf("Unimplementedcompressiontype%d\n",comp); returnBOOTM_ERR_UNIMPLEMENTED; } puts("OK\n"); debug("kernelloadedat0x%08lx,end=0x%08lx\n",load,*load_end); if((load<blob_end)&&(*load_end>blob_start)){ debug("images.os.start=0x%lX,images.os.end=0x%lx\n",blob_start,blob_end); debug("images.os.load=0x%lx,load_end=0x%lx\n",load,*load_end); returnBOOTM_ERR_OVERLAP; } return0; } 3、do_bootm_linux

在 arm linux 平台中 boot_fn 函数指针指向的函数是位于 lib_arm/bootm.c 的 do_bootm_linux,这是内核启动前最后的一个函数,该函数主要完成启动参数的初始化,并将板子设定为满足内核启动的环境,代码如下:[cpp]view plaincopyintdo_bootm_linux(intflag,intargc,char*argv[],bootm_headers_t*images) { bd_t*bd=gd->bd; char*s; intmachid=bd->bi_arch_number; void(*theKernel)(intzero,intarch,uintparams); char*commandline=getenv("bootargs");/*从环境变量中获取命令参数*/ if((flag!=0)&&(flag!=BOOTM_STATE_OS_GO))/*状态判定*/ return1; theKernel=(void(*)(int,int,uint))images->ep;/*内核入口函数*/ s=getenv("machid");/*从环境变量中获取机器id*/ if(s){ machid=simple_strtoul(s,NULL,16); printf("Usingmachid0x%xfromenvironment\n",machid); } debug("##TransferringcontroltoLinux(ataddress%08lx)...\n",(ulong)theKernel); /*初始化启动参数*/ setup_start_tag(bd);/*初始化参数列表起始符*/ setup_serial_tag(&params);/*初始化串口参数*/ setup_revision_tag(&params);/*初始化版本参数*/ setup_memory_tags(bd);/*初始化内存参数*/ setup_commandline_tag(bd,commandline);/*初始化命令参数*/ if(images->rd_start&&images->rd_end) setup_initrd_tag(bd,images->rd_start,images->rd_end);/*初始化虚拟磁盘参数*/ setup_videolfb_tag((gd_t*)gd);/*初始化fb参数*/ setup_end_tag(bd);/*初始化参数列表结束符*/ printf("\nStartingkernel...\n\n"); cleanup_before_linux();/*启动前清空缓存*/ /*启动内核,满足arm架构linux内核启动时的寄存器设置条件:第一个参数为0 第二个参数为板子id需与内核中的id匹配,第三个参数为启动参数地址*/ theKernel(0,machid,bd->bi_boot_params);/*doesnotreturn*/ return1; }

这里还列举部分启动参数的初始化函数:

[cpp]view plaincopystaticvoidsetup_start_tag(bd_t*bd) { params=(structtag*)bd->bi_boot_params;/*启动参数保存在板子指定的内存空间CONFIG_ATAG_ADDR*/ params->hdr.tag=ATAG_CORE;/*起始标签*/ params->hdr.size=tag_size(tag_core); params->u.core.flags=0; params->u.core.pagesize=0; params->u.core.rootdev=0; params=tag_next(params);/*params指向下一个结构*/ } staticvoidsetup_memory_tags(bd_t*bd) { inti; for(i=0;i<CONFIG_NR_DRAM_BANKS;i++){ params->hdr.tag=ATAG_MEM;/*内存参数的标签*/ params->hdr.size=tag_size(tag_mem32); params->u.mem.start=bd->bi_dram[i].start;/*物理内存起始地址*/ params->u.mem.size=bd->bi_dram[i].size;/*物理内存结束地址*/ params=tag_next(params); } } staticvoidsetup_commandline_tag(bd_t*bd,char*commandline) { char*p; if(!commandline) return; for(p=commandline;*p=='';p++);/*定位到第一个有效字符*/ if(*p=='\0')/*没有有效字符则返回*/ return; params->hdr.tag=ATAG_CMDLINE;/*命令参数的标签*/ params->hdr.size=(sizeof(structtag_header)+strlen(p)+1+4)>>2;/*整个标签的长度*/ strcpy(params->u.cmdline.cmdline,p);/*将命令参数拷贝到标签,结束符为'\0'*/ params=tag_next(params); } staticvoidsetup_end_tag(bd_t*bd) { params->hdr.tag=ATAG_NONE;/*结束标签*/ params->hdr.size=0; }

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。