首页 Uboot启动过程详解

Uboot启动过程详解

举报
开通vip

Uboot启动过程详解U-BootU-BootU-BootU-Boot工作过程U-Boot启动内核的过程可以分为两个阶段,两个阶段的功能如下:(1)第一阶段的功能�硬件设备初始化�加载U-Boot第二阶段代码到RAM空间�设置好栈�跳转到第二阶段代码入口(2)第二阶段的功能�初始化本阶段使用的硬件设备�检测系统内存映射�将内核从Flash读取到RAM中�为内核设置启动参数�调用内核U-BootU-BootU-BootU-Boot启动第一阶段代码分析启动第一阶段代码分析启动第一阶段代码分析启动第一阶段代码分析第一阶段对应的文件是cpu/a...

Uboot启动过程详解
U-BootU-BootU-BootU-Boot工作过程U-Boot启动内核的过程可以分为两个阶段,两个阶段的功能如下:(1)第一阶段的功能�硬件设备初始化�加载U-Boot第二阶段代码到RAM空间�设置好栈�跳转到第二阶段代码入口(2)第二阶段的功能�初始化本阶段使用的硬件设备�检测系统内存映射�将内核从Flash读取到RAM中�为内核设置启动参数�调用内核U-BootU-BootU-BootU-Boot启动第一阶段代码 分析 定性数据统计分析pdf销售业绩分析模板建筑结构震害分析销售进度分析表京东商城竞争战略分析 启动第一阶段代码分析启动第一阶段代码分析启动第一阶段代码分析第一阶段对应的文件是cpu/arm920t/start.S和board/samsung/mini2440/lowlevel_init.S。U-Boot启动第一阶段 流程 快递问题件怎么处理流程河南自建厂房流程下载关于规范招聘需求审批流程制作流程表下载邮件下载流程设计 如下:图2.1U-Boot启动第一阶段流程根据cpu/arm920t/u-boot.lds中指定的连接方式:ENTRY(_start)SECTIONS{.=0x00000000;.=ALIGN(4);.text:{cpu/arm920t/start.o(.text)board/samsung/mini2440/lowlevel_init.o(.text)board/samsung/mini2440/nand_read.o(.text)*(.text)}……}第一个链接的是cpu/arm920t/start.o,因此u-boot.bin的入口代码在cpu/arm920t/start.o中,其源代码在cpu/arm920t/start.S中。下面我们来分析cpu/arm920t/start.S的执行。1.1.1.1.硬件设备初始化硬件设备初始化硬件设备初始化硬件设备初始化(1111)设置异常向量cpu/arm920t/start.S开头有如下的代码:.globl_start_start:bstart_code/*复位*/ldrpc,_undefined_instruction/*未定义指令向量*/ldrpc,_software_interrupt/*软件中断向量*/ldrpc,_prefetch_abort/*预取指令异常向量*/ldrpc,_data_abort/*数据操作异常向量*/ldrpc,_not_used/*未使用*/ldrpc,_irq/*irq中断向量*/ldrpc,_fiq/*fiq中断向量*//*中断向量表入口地址*/_undefined_instruction:.wordundefined_instruction_software_interrupt:.wordsoftware_interrupt_prefetch_abort:.wordprefetch_abort_data_abort:.worddata_abort_not_used:.wordnot_used_irq:.wordirq_fiq:.wordfiq.balignl16,0xdeadbeef以上代码设置了ARM异常向量表,各个异常向量介绍如下:表2.1ARM异常向量表地址异常进入模式描述0x00000000复位管理模式复位电平有效时,产生复位异常,程序跳转到复位处理程序处执行0x00000004未定义指令未定义模式遇到不能处理的指令时,产生未定义指令异常0x00000008软件中断管理模式执行SWI指令产生,用于用户模式下的程序调用特权操作指令0x0000000c预存指令中止模式处理器预取指令的地址不存在,或该地址不允许当前指令访问,产生指令预取中止异常0x00000010数据操作中止模式处理器数据访问指令的地址不存在,或该地址不允许当前指令访问时,产生数据中止异常0x00000014未使用未使用未使用0x00000018IRQIRQ外部中断请求有效,且CPSR中的I位为0时,产生IRQ异常0x0000001cFIQFIQ快速中断请求引脚有效,且CPSR中的F位为0时,产生FIQ异常在cpu/arm920t/start.S中还有这些异常对应的异常处理程序。当一个异常产生时,CPU根据异常号在异常向量表中找到对应的异常向量,然后执行异常向量处的跳转指令,CPU就跳转到对应的异常处理程序执行。其中复位异常向量的指令“bstart_code”决定了U-Boot启动后将自动跳转到标号“start_code”处执行。(2222)CPUCPUCPUCPU进入SVCSVCSVCSVC模式start_code:/**setthecputoSVC32mode*/mrsr0,cpsrbicr0,r0,#0x1f/*工作模式位清零*/orrr0,r0,#0xd3/*工作模式位设置为“10011”(管理模式),并将中断禁止位和快中断禁止位置1*/msrcpsr,r0以上代码将CPU的工作模式位设置为管理模式,并将中断禁止位和快中断禁止位置一,从而屏蔽了IRQ和FIQ中断。(3333)设置控制寄存器地址#ifdefined(CONFIG_S3C2400)#definepWTCON0x15300000#defineINTMSK0x14400008#defineCLKDIVN0x14800014#else/*s3c2410与s3c2440下面4个寄存器地址相同*/#definepWTCON0x53000000/*WATCHDOG控制寄存器地址*/#defineINTMSK0x4A000008/*INTMSK寄存器地址*/#defineINTSUBMSK0x4A00001C/*INTSUBMSK寄存器地址*/#defineCLKDIVN0x4C000014/*CLKDIVN寄存器地址*/#endif对与s3c2440开发板,以上代码完成了WATCHDOG,INTMSK,INTSUBMSK,CLKDIVN四个寄存器的地址的设置。各个寄存器地址参见参考文献[4]。(4444)关闭看门狗ldrr0,=pWTCONmovr1,#0x0strr1,[r0]/*看门狗控制器的最低位为0时,看门狗不输出复位信号*/以上代码向看门狗控制寄存器写入0,关闭看门狗。否则在U-Boot启动过程中,CPU将不断重启。(5555)屏蔽中断/**maskallIRQsbysettingallbitsintheINTMR-default*/movr1,#0xffffffff/*某位被置1则对应的中断被屏蔽*/ldrr0,=INTMSKstrr1,[r0]INTMSK是主中断屏蔽寄存器,每一位对应SRCPND(中断源引脚寄存器)中的一位,表明SRCPND相应位代表的中断请求是否被CPU所处理。根据参考文献4,INTMSK寄存器是一个32位的寄存器,每位对应一个中断,向其中写入0xffffffff就将INTMSK寄存器全部位置一,从而屏蔽对应的中断。#ifdefined(CONFIG_S3C2440)ldrr1,=0x7fffldrr0,=INTSUBMSKstrr1,[r0]#endifINTSUBMSK每一位对应SUBSRCPND中的一位,表明SUBSRCPND相应位代表的中断请求是否被CPU所处理。根据参考文献4,INTSUBMSK寄存器是一个32位的寄存器,但是只使用了低15位。向其中写入0x7fff就是将INTSUBMSK寄存器全部有效位(低15位)置一,从而屏蔽对应的中断。(6666)设置MPLLCON,UPLLCON,MPLLCON,UPLLCON,MPLLCON,UPLLCON,MPLLCON,UPLLCON,CLKDIVNCLKDIVNCLKDIVNCLKDIVN#ifdefined(CONFIG_S3C2440)#defineMPLLCON0x4C000004#defineUPLLCON0x4C000008ldrr0,=CLKDIVNmovr1,#5strr1,[r0]ldrr0,=MPLLCONldrr1,=0x7F021strr1,[r0]ldrr0,=UPLLCONldrr1,=0x38022strr1,[r0]#else/*FCLK:HCLK:PCLK=1:2:4*//*defaultFCLKis120MHz!*/ldrr0,=CLKDIVNmovr1,#3strr1,[r0]#endifCPU上电几毫秒后,晶振输出稳定,FCLK=Fin(晶振频率),CPU开始执行指令。但实际上,FCLK可以高于Fin,为了提高系统时钟,需要用软件来启用PLL。这就需要设置CLKDIVN,MPLLCON,UPLLCON这3个寄存器。CLKDIVN寄存器用于设置FCLK,HCLK,PCLK三者间的比例,可以根据表2.2来设置。表2.2S3C2440的CLKDIVN寄存器格式CLKDIVN位说明初始值HDIVN[2:1]00:HCLK=FCLK/1.01:HCLK=FCLK/2.10:HCLK=FCLK/4(当CAMDIVN[9]=0时)HCLK=FCLK/8(当CAMDIVN[9]=1时)11:HCLK=FCLK/3(当CAMDIVN[8]=0时)HCLK=FCLK/6(当CAMDIVN[8]=1时)00PDIVN[0]0:PCLK=HCLK/11:PCLK=HCLK/20设置CLKDIVN为5,就将HDIVN设置为二进制的10,由于CAMDIVN[9]没有被改变过,取默认值0,因此HCLK=FCLK/4。PDIVN被设置为1,因此PCLK=HCLK/2。因此分频比FCLK:HCLK:PCLK=1:4:8。MPLLCON寄存器用于设置FCLK与Fin的倍数。MPLLCON的位[19:12]称为MDIV,位[9:4]称为PDIV,位[1:0]称为SDIV。对于S3C2440,FCLK与Fin的关系如下面公式:MPLL(FCLK)=(2×m×Fin)/(p×)其中:m=MDIC+8,p=PDIV+2,s=SDIVMPLLCON与UPLLCON的值可以根据参考文献4中“PLLVALUESELECTIONTABLE”设置。该表部分摘录如下:表2.3推荐PLL值输入频率输出频率MDIVPDIVSDIV12.0000MHz48.00MHz56(0x38)2212.0000MHz405.00MHz127(0x7f)21当mini2440系统主频设置为405MHZ,USB时钟频率设置为48MHZ时,系统可以稳定运行,因此设置MPLLCON与UPLLCON为:MPLLCON=(0x7f<<12)|(0x02<<4)|(0x01)=0x7f021UPLLCON=(0x38<<12)|(0x02<<4)|(0x02)=0x38022(7777)关闭MMUMMUMMUMMU,cachecachecachecache接着往下看:#ifndefCONFIG_SKIP_LOWLEVEL_INITblcpu_init_crit#endifcpu_init_crit这段代码在U-Boot正常启动时才需要执行,若将U-Boot从RAM中启动则应该注释掉这段代码。下面分析一下cpu_init_crit到底做了什么:320#ifndefCONFIG_SKIP_LOWLEVEL_INIT321cpu_init_crit:322/*323*使数据cache与指令cache无效*/324*/325movr0,#0326mcrp15,0,r0,c7,c7,0/*向c7写入0将使ICache与DCache无效*/327mcrp15,0,r0,c8,c7,0/*向c8写入0将使TLB失效*/328329/*330*disableMMUstuffandcaches331*/332mrcp15,0,r0,c1,c0,0/*读出控制寄存器到r0中*/333bicr0,r0,#0x00002300@clearbits13,9:8(--V---RS)334bicr0,r0,#0x00000087@clearbits7,2:0(B----CAM)335orrr0,r0,#0x00000002@setbit2(A)Align336orrr0,r0,#0x00001000@setbit12(I)I-Cache337mcrp15,0,r0,c1,c0,0/*保存r0到控制寄存器*/338339/*340*beforerelocating,wehavetosetupRAMtiming341*becausememorytimingisboard-dependend,youwill342*findalowlevel_init.Sinyourboarddirectory.343*/344movip,lr345346bllowlevel_init347348movlr,ip349movpc,lr350#endif/*CONFIG_SKIP_LOWLEVEL_INIT*/代码中的c0,c1,c7,c8都是ARM920T的协处理器CP15的寄存器。其中c7是cache控制寄存器,c8是TLB控制寄存器。325~327行代码将0写入c7、c8,使Cache,TLB内容无效。第332~337行代码关闭了MMU。这是通过修改CP15的c1寄存器来实现的,先看CP15的c1寄存器的格式(仅列出代码中用到的位):表2.3CP15的c1寄存器格式(部分)1514131211109876543210..VI..RSB....CAM各个位的意义如下:V:表示异常向量表所在的位置,0:异常向量在0x00000000;1:异常向量在0xFFFF0000I:0:关闭ICaches;1:开启ICachesR、S:用来与页表中的描述符一起确定内存的访问权限B:0:CPU为小字节序;1:CPU为大字节序C:0:关闭DCaches;1:开启DCachesA:0:数据访问时不进行地址对齐检查;1:数据访问时进行地址对齐检查M:0:关闭MMU;1:开启MMU332~337行代码将c1的M位置零,关闭了MMU。(8888)初始化RAMRAMRAMRAM控制寄存器其中的lowlevel_init就完成了内存初始化的工作,由于内存初始化是依赖于开发板的,因此lowlevel_init的代码一般放在board下面相应的目录中。对于mini2440,lowlevel_init在board/samsung/mini2440/lowlevel_init.S中定义如下:45#defineBWSCON0x48000000/*13个存储控制器的开始地址*/……129_TEXT_BASE:130.wordTEXT_BASE131132.globllowlevel_init133lowlevel_init:134/*memorycontrolconfiguration*/135/*maker0relativethecurrentlocationsothatit*/136/*readsSMRDATAoutofFLASHratherthanmemory!*/137ldrr0,=SMRDATA138ldrr1,_TEXT_BASE139subr0,r0,r1/*SMRDATA减_TEXT_BASE就是13个寄存器的偏移地址*/140ldrr1,=BWSCON/*BusWidthStatusController*/141addr2,r0,#13*41420:143ldrr3,[r0],#4/*将13个寄存器的值逐一赋值给对应的寄存器*/144strr3,[r1],#4145cmpr2,r0146bne0b147148/*everythingisfinenow*/149movpc,lr150151.ltorg152/*theliteralpoolsorigin*/153154SMRDATA:/*下面是13个寄存器的值*/155.word……156.word…………lowlevel_init初始化了13个寄存器来实现RAM时钟的初始化。lowlevel_init函数对于U-Boot从NANDFlash或NORFlash启动的情况都是有效的。U-Boot.lds链接脚本有如下代码:.text:{cpu/arm920t/start.o(.text)board/samsung/mini2440/lowlevel_init.o(.text)board/samsung/mini2440/nand_read.o(.text)……}board/samsung/mini2440/lowlevel_init.o将被链接到cpu/arm920t/start.o后面,因此board/samsung/mini2440/lowlevel_init.o也在U-Boot的前4KB的代码中。U-Boot在NANDFlash启动时,lowlevel_init.o将自动被读取到CPU内部4KB的内部RAM中。因此第137~146行的代码将从CPU内部RAM中复制寄存器的值到相应的寄存器中。对于U-Boot在NORFlash启动的情况,由于U-Boot连接时确定的地址是U-Boot在内存中的地址,而此时U-Boot还在NORFlash中,因此还需要在NORFlash中读取数据到RAM中。由于NORFlash的开始地址是0,而U-Boot的加载到内存的起始地址是TEXT_BASE,SMRDATA标号在Flash的地址就是SMRDATA-TEXT_BASE。综上所述,lowlevel_init的作用就是将SMRDATA开始的13个值复制给开始地址[BWSCON]的13个寄存器,从而完成了存储控制器的设置。(9999)复制U-BootU-BootU-BootU-Boot第二阶段代码到RAMRAMRAMRAMcpu/arm920t/start.S原来的代码是只支持从NORFlash启动的,经过修改现在U-Boot在NORFlash和NANDFlash上都能启动了,实现的思路是这样的:blbBootFrmNORFlash/*判断U-Boot是在NANDFlash还是NORFlash启动*/cmpr0,#0/*r0存放bBootFrmNORFlash函数返回值,若返回0表示NANDFlash启动,否则表示在NORFlash启动*/beqnand_boot/*跳转到NANDFlash启动代码*//*NORFlash启动的代码*/bstack_setup/*跳过NANDFlash启动的代码*/nand_boot:/*NANDFlash启动的代码*/stack_setup:/*其他代码*/其中bBootFrmNORFlash函数作用是判断U-Boot是在NANDFlash启动还是NORFlash启动,若在NORFlash启动则返回1,否则返回0。根据ATPCS规则,函数返回值会被存放在r0寄存器中,因此调用bBootFrmNORFlash函数后根据r0的值就可以判断U-Boot在NANDFlash启动还是NORFlash启动。bBootFrmNORFlash函数在board/samsung/mini2440/nand_read.c中定义如下:intbBootFrmNORFlash(void){volatileunsignedint*pdw=(volatileunsignedint*)0;unsignedintdwVal;dwVal=*pdw;/*先 记录 混凝土 养护记录下载土方回填监理旁站记录免费下载集备记录下载集备记录下载集备记录下载 下原来的数据*/*pdw=0x12345678;if(*pdw!=0x12345678)/*写入失败,说明是在NORFlash启动*/{return1;}else/*写入成功,说明是在NANDFlash启动*/{*pdw=dwVal;/*恢复原来的数据*/return0;}}无论是从NORFlash还是从NANDFlash启动,地址0处为U-Boot的第一条指令“bstart_code”。对于从NANDFlash启动的情况,其开始4KB的代码会被自动复制到CPU内部4K内存中,因此可以通过直接赋值的方法来修改。对于从NORFlash启动的情况,NORFlash的开始地址即为0,必须通过一定的命令序列才能向NORFlash中写数据,所以可以根据这点差别来分辨是从NANDFlash还是NORFlash启动:向地址0写入一个数据,然后读出来,如果发现写入失败的就是NORFlash,否则就是NANDFlash。下面来分析NORFlash启动部分代码:208adrr0,_start/*r0<-currentpositionofcode*/209ldrr1,_TEXT_BASE/*testifwerunfromflashorRAM*//*判断U-Boot是否是下载到RAM中运行,若是,则不用再复制到RAM中了,这种情况通常在调试U-Boot时才发生*/210cmpr0,r1/*_start等于_TEXT_BASE说明是下载到RAM中运行*/211beqstack_setup212/*以下直到nand_boot标号前都是NORFlash启动的代码*/213ldrr2,_armboot_start214ldrr3,_bss_start215subr2,r3,r2/*r2<-sizeofarmboot*/216addr2,r0,r2/*r2<-sourceendaddress*/217/*搬运U-Boot自身到RAM中*/218copy_loop:219ldmiar0!,{r3-r10}/*从地址为[r0]的NORFlash中读入8个字的数据*/220stmiar1!,{r3-r10}/*将r3至r10寄存器的数据复制给地址为[r1]的内存*/221cmpr0,r2/*untilsourceendaddreee[r2]*/222blecopy_loop223bstack_setup/*跳过NANDFlash启动的代码*/下面再来分析NANDFlash启动部分代码:nand_boot:movr1,#NAND_CTL_BASEldrr2,=((7<<12)|(7<<8)|(7<<4)|(0<<0))strr2,[r1,#oNFCONF]/*设置NFCONF寄存器*//*设置NFCONT,初始化ECC编/解码器,禁止NANDFlash片选*/ldrr2,=((1<<4)|(0<<1)|(1<<0))strr2,[r1,#oNFCONT]ldrr2,=(0x6)/*设置NFSTAT*/strr2,[r1,#oNFSTAT]/*复位命令,第一次使用NANDFlash前复位*/movr2,#0xffstrbr2,[r1,#oNFCMD]movr3,#0/*为调用C函数nand_read_ll准备堆栈*/ldrsp,DW_STACK_STARTmovfp,#0/*下面先设置r0至r2,然后调用nand_read_ll函数将U-Boot读入RAM*/ldrr0,=TEXT_BASE/*目的地址:U-Boot在RAM的开始地址*/movr1,#0x0/*源地址:U-Boot在NANDFlash中的开始地址*/movr2,#0x30000/*复制的大小,必须比u-boot.bin文件大,并且必须是NANDFlash块大小的整数倍,这里设置为0x30000(192KB)*/blnand_read_ll/*跳转到nand_read_ll函数,开始复制U-Boot到RAM*/tstr0,#0x0/*检查返回值是否正确*/beqstack_setupbad_nand_read:loop2:bloop2//infiniteloop.align2DW_STACK_START:.wordSTACK_BASE+STACK_SIZE-4其中NAND_CTL_BASE,oNFCONF等在include/configs/mini2440.h中定义如下:#defineNAND_CTL_BASE0x4E000000//NANDFlash控制寄存器基址#defineSTACK_BASE0x33F00000//baseaddressofstack#defineSTACK_SIZE0x8000//sizeofstack#defineoNFCONF0x00/*NFCONF相对于NAND_CTL_BASE偏移地址*/#defineoNFCONT0x04/*NFCONT相对于NAND_CTL_BASE偏移地址*/#defineoNFADDR0x0c/*NFADDR相对于NAND_CTL_BASE偏移地址*/#defineoNFDATA0x10/*NFDATA相对于NAND_CTL_BASE偏移地址*/#defineoNFCMD0x08/*NFCMD相对于NAND_CTL_BASE偏移地址*/#defineoNFSTAT0x20/*NFSTAT相对于NAND_CTL_BASE偏移地址*/#defineoNFECC0x2c/*NFECC相对于NAND_CTL_BASE偏移地址*/NANDFlash各个控制寄存器的设置在S3C2440的数据手册有详细说明,这里就不介绍了。代码中nand_read_ll函数的作用是在NANDFlash中搬运U-Boot到RAM,该函数在board/samsung/mini2440/nand_read.c中定义。NANDFlash根据page大小可分为2种:512B/page和2048B/page的。这两种NANDFlash的读操作是不同的。因此就需要U-Boot识别到NANDFlash的类型,然后采用相应的读操作,也就是说nand_read_ll函数要能自动适应两种NANDFlash。参考S3C2440的数据手册可以知道:根据NFCONF寄存器的Bit3(AdvFlash(Readonly))和Bit2(PageSize(Readonly))可以判断NANDFlash的类型。Bit2、Bit3与NANDFlash的block类型的关系如下表所示:表2.4NFCONF的Bit3、Bit2与NANDFlash的关系Bit2Bit3010256B/page512B/page11024B/page2048B/page由于的NANDFlash只有512B/page和2048B/page这两种,因此根据NFCONF寄存器的Bit3即可区分这两种NANDFlash了。完整代码见board/samsung/mini2440/nand_read.c中的nand_read_ll函数,这里给出伪代码:intnand_read_ll(unsignedchar*buf,unsignedlongstart_addr,intsize){//根据NFCONF寄存器的Bit3来区分2种NANDFlashif(NFCONF&0x8)/*Bit是1,表示是2KB/page的NANDFlash*/{////////////////////////////////////读取2Kblock的NANDFlash////////////////////////////////////}else/*Bit是0,表示是512B/page的NANDFlash*/{/////////////////////////////////////读取512Bblock的NANDFlash/////////////////////////////////////}return0;}(10101010)设置堆栈/*设置堆栈*/stack_setup:ldrr0,_TEXT_BASE/*upper128KiB:relocateduboot*/subr0,r0,#CONFIG_SYS_MALLOC_LEN/*mallocarea*/subr0,r0,#CONFIG_SYS_GBL_DATA_SIZE/*跳过全局数据区*/#ifdefCONFIG_USE_IRQsubr0,r0,#(CONFIG_STACKSIZE_IRQ+CONFIG_STACKSIZE_FIQ)#endifsubsp,r0,#12/*leave3wordsforabort-stack*/只要将sp指针指向一段没有被使用的内存就完成栈的设置了。根据上面的代码可以知道U-Boot内存使用情况了,如下图所示:图2.2U-Boot内存使用情况(11111111)清除BSSBSSBSSBSS段clear_bss:ldrr0,_bss_start/*BSS段开始地址,在u-boot.lds中指定*/ldrr1,_bss_end/*BSS段结束地址,在u-boot.lds中指定*/movr2,#0x00000000clbss_l:strr2,[r0]/*将bss段清零*/addr0,r0,#4cmpr0,r1bleclbss_l初始值为0,无初始值的全局变量,静态变量将自动被放在BSS段。应该将这些变量的初始值赋为0,否则这些变量的初始值将是一个随机的值,若有些程序直接使用这些没有初始化的变量将引起未知的后果。(12121212)跳转到第二阶段代码入口ldrpc,_start_armboot_start_armboot:.wordstart_armboot跳转到第二阶段代码入口start_armboot处。1.1.2U-Boot1.1.2U-Boot1.1.2U-Boot1.1.2U-Boot启动第二阶段代码分析启动第二阶段代码分析启动第二阶段代码分析启动第二阶段代码分析start_armboot函数在lib_arm/board.c中定义,是U-Boot第二阶段代码的入口。U-Boot启动第二阶段流程如下:图2.3U-Boot第二阶段执行流程在分析start_armboot函数前先来看看一些重要的数据结构:(1111)gd_tgd_tgd_tgd_t结构体U-Boot使用了一个结构体gd_t来存储全局数据区的数据,这个结构体在include/asm-arm/global_data.h中定义如下:typedefstructglobal_data{bd_t*bd;unsignedlongflags;unsignedlongbaudrate;unsignedlonghave_console;/*serial_init()wascalled*/unsignedlongenv_addr;/*AddressofEnvironmentstruct*/unsignedlongenv_valid;/*ChecksumofEnvironmentvalid?*/unsignedlongfb_base;/*baseaddressofframebuffer*/void**jt;/*jumptable*/}gd_t;U-Boot使用了一个存储在寄存器中的指针gd来记录全局数据区的地址:#defineDECLARE_GLOBAL_DATA_PTRregistervolatilegd_t*gdasm("r8")DECLARE_GLOBAL_DATA_PTR定义一个gd_t全局数据结构的指针,这个指针存放在指定的寄存器r8中。这个声明也避免编译器把r8分配给其它的变量。任何想要访问全局数据区的代码,只要代码开头加入“DECLARE_GLOBAL_DATA_PTR”一行代码,然后就可以使用gd指针来访问全局数据区了。根据U-Boot内存使用图中可以计算gd的值:gd=TEXT_BASE-CONFIG_SYS_MALLOC_LEN-sizeof(gd_t)(2222)bd_tbd_tbd_tbd_t结构体bd_t在include/asm-arm.u/u-boot.h中定义如下:typedefstructbd_info{intbi_baudrate;/*串口通讯波特率*/unsignedlongbi_ip_addr;/*IP地址*/structenvironment_s*bi_env;/*环境变量开始地址*/ulongbi_arch_number;/*开发板的机器码*/ulongbi_boot_params;/*内核参数的开始地址*/struct/*RAM配置信息*/{ulongstart;ulongsize;}bi_dram[CONFIG_NR_DRAM_BANKS];}bd_t;U-Boot启动内核时要给内核传递参数,这时就要使用gd_t,bd_t结构体中的信息来设置标记列表。(3333)init_sequenceinit_sequenceinit_sequenceinit_sequence数组U-Boot使用一个数组init_sequence来存储对于大多数开发板都要执行的初始化函数的函数指针。init_sequence数组中有较多的编译选项,去掉编译选项后init_sequence数组如下所示:typedefint(init_fnc_t)(void);init_fnc_t*init_sequence[]={board_init,/*开发板相关的配置--board/samsung/mini2440/mini2440.c*/timer_init,/*时钟初始化--cpu/arm920t/s3c24x0/timer.c*/env_init,/*初始化环境变量--common/env_flash.c或common/env_nand.c*/init_baudrate,/*初始化波特率--lib_arm/board.c*/serial_init,/*串口初始化--drivers/serial/serial_s3c24x0.c*/console_init_f,/*控制通讯台初始化阶段1--common/console.c*/display_banner,/*打印U-Boot版本、编译的时间--geditlib_arm/board.c*/dram_init,/*配置可用的RAM--board/samsung/mini2440/mini2440.c*/display_dram_config,/*显示RAM大小--lib_arm/board.c*/NULL,};其中的board_init函数在board/samsung/mini2440/mini2440.c中定义,该函数设置了MPLLCOM,UPLLCON,以及一些GPIO寄存器的值,还设置了U-Boot机器码和内核启动参数地址:/*MINI2440开发板的机器码*/gd->bd->bi_arch_number=MACH_TYPE_MINI2440;/*内核启动参数地址*/gd->bd->bi_boot_params=0x30000100;其中的dram_init函数在board/samsung/mini2440/mini2440.c中定义如下:intdram_init(void){/*由于mini2440只有*/gd->bd->bi_dram[0].start=PHYS_SDRAM_1;gd->bd->bi_dram[0].size=PHYS_SDRAM_1_SIZE;return0;}mini2440使用2片32MB的SDRAM组成了64MB的内存,接在存储控制器的BANK6,地址空间是0x30000000~0x34000000。在include/configs/mini2440.h中PHYS_SDRAM_1和PHYS_SDRAM_1_SIZE分别被定义为0x30000000和0x04000000(64M)。分析完上述的数据结构,下面来分析start_armboot函数:voidstart_armboot(void){init_fnc_t**init_fnc_ptr;char*s;……/*计算全局数据结构的地址gd*/gd=(gd_t*)(_armboot_start-CONFIG_SYS_MALLOC_LEN-sizeof(gd_t));……memset((void*)gd,0,sizeof(gd_t));gd->bd=(bd_t*)((char*)gd-sizeof(bd_t));memset(gd->bd,0,sizeof(bd_t));gd->flags|=GD_FLG_RELOC;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();}}/*armboot_start在cpu/arm920t/start.S中被初始化为u-boot.lds连接脚本中的_start*/mem_malloc_init(_armboot_start-CONFIG_SYS_MALLOC_LEN,CONFIG_SYS_MALLOC_LEN);/*NORFlash初始化*/#ifndefCONFIG_SYS_NO_FLASH/*configureavailableFLASHbanks*/display_flash_config(flash_init());#endif/*CONFIG_SYS_NO_FLASH*/……/*NANDFlash初始化*/#ifdefined(CONFIG_CMD_NAND)puts("NAND:");nand_init();/*goinittheNAND*/#endif……/*配置环境变量,重新定位*/env_relocate();……/*从环境变量中获取IP地址*/gd->bd->bi_ip_addr=getenv_IPaddr("ipaddr");stdio_init();/*getthedeviceslistgoing.*/jumptable_init();……console_init_r();/*fullyinitconsoleasadevice*/……/*enableexceptions*/enable_interrupts();#ifdefCONFIG_USB_DEVICEusb_init_slave();#endif/*Initializefromenvironment*/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……/*网卡初始化*/#ifdefined(CONFIG_CMD_NET)#ifdefined(CONFIG_NET_MULTI)puts("Net:");#endifeth_initialize(gd->bd);……#endif/*main_loop()canreturntoretryautoboot,ifsojustrunitagain.*/for(;;){main_loop();}/*NOTREACHED-nowayoutofcommandloopexceptbooting*/}main_loop函数在common/main.c中定义。一般情况下,进入main_loop函数若干秒内没有1.1.31.1.31.1.31.1.3U-BootU-BootU-BootU-Boot启动启动启动启动LinuxLinuxLinuxLinux过程过程过程过程U-Boot使用标记列表(taggedlist)的方式向Linux传递参数。标记的数据结构式是tag,在U-Boot源代码目录include/asm-arm/setup.h中定义如下:structtag_header{u32size;/*表示tag数据结构的联合u实质存放的数据的大小*/u32tag;/*表示标记的类型*/};structtag{structtag_headerhdr;union{structtag_corecore;structtag_mem32mem;structtag_videotextvideotext;structtag_ramdiskramdisk;structtag_initrdinitrd;structtag_serialnrserialnr;structtag_revisionrevision;structtag_videolfbvideolfb;structtag_cmdlinecmdline;/**Acornspecific*/structtag_acornacorn;/**DC21285specific*/structtag_memclkmemclk;}u;};U-Boot使用命令bootm来启动已经加载到内存中的内核。而bootm命令实际上调用的是do_bootm函数。对于Linux内核,do_bootm函数会调用do_bootm_linux函数来设置标记列表和启动内核。do_bootm_linux函数在lib_arm/bootm.c中定义如下:59intdo_bootm_linux(intflag,intargc,char*argv[],bootm_headers_t*images)60{61bd_t*bd=gd->bd;62char*s;63intmachid=bd->bi_arch_number;64void(*theKernel)(intzero,intarch,uintparams);6566#ifdefCONFIG_CMDLINE_TAG67char*commandline=getenv("bootargs");/*U-Boot环境变量bootargs*/68#endif……73theKernel=(void(*)(int,int,uint))images->ep;/*获取内核入口地址*/……86#ifdefined(CONFIG_SETUP_MEMORY_TAGS)||\87defined(CONFIG_CMDLINE_TAG)||\88defined(CONFIG_INITRD_TAG)||\89defined(CONFIG_SERIAL_TAG)||\90defined(CONFIG_REVISION_TAG)||\91defined(CONFIG_LCD)||\92defined(CONFIG_VFD)93setup_start_tag(bd);/*设置ATAG_CORE标志*/……100#ifdefCONFIG_SETUP_MEMORY_TAGS101setup_memory_tags(bd);/*设置内存标记*/102#endif103#ifdefCONFIG_CMDLINE_TAG104setup_commandline_tag(bd,commandline);/*设置命令行标记*/105#endif……113setup_end_tag(bd);/*设置ATAG_NONE标志*/114#endif115116/*weassumethatthekernelisinplace*/117printf("\nStartingkernel...\n\n");……126cleanup_before_linux();/*启动内核前对CPU作最后的设置*/127128theKernel(0,machid,bd->bi_boot_params);/*调用内核*/129/*doesnotreturn*/130131return1;132}其中的setup_start_tag,setup_memory_tags,setup_end_tag函数在lib_arm/bootm.c中定义如下:(1111)setup_start_tagsetup_start_tagsetup_start_tagsetup_start_tag函数staticvoidsetup_start_tag(bd_t*bd){params=(structtag*)bd->bi_boot_params;/*内核的参数的开始地址*/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);}标记列表必须以ATAG_CORE开始,setup_start_tag函数在内核的参数的开始地址设置了一个ATAG_CORE标记。((((2222)setup_memory_tagssetup_memory_tagssetup_memory_tagssetup_memory_tags函数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);}}setup_memory_tags函数设置了一个ATAG_MEM标记,该标记包含内存起始地址,内存大小这两个参数。(3333)setup_end_tagsetup_end_tagsetup_end_tagsetup_end_tag函数staticvoidsetup_end_tag(bd_t*bd){params->hdr.tag=ATAG_NONE;params->hdr.size=0;}标记列表必须以标记ATAG_NONE结束,setup_end_tag函数设置了一个ATAG_NONE标记,表示标记列表的结束。U-Boot设置好标记列表后就要调用内核了。但调用内核前,CPU必须满足下面的条件:(1)CPU寄存器的设置�r0=0�r1=机器码�r2=内核参数标记列表在RAM中的起始地址(2)CPU工作模式�禁止IRQ与FIQ中断�CPU为SVC模式(3)使数据Cache与指令Cache失效do_bootm_linux中调用的cleanup_before_linux函数完成了禁止中断和使Cache失效的功能。cleanup_before_linux函数在cpu/arm920t/cpu.中定义:intcleanup_before_linux(void){/**thisfunctioniscalledjustbeforewecalllinux*itpreparestheprocessorforlinux**weturnoffcachesetc...*/
本文档为【Uboot启动过程详解】,请使用软件OFFICE或WPS软件打开。作品中的文字与图均可以修改和编辑, 图片更改请在作品中右键图片并更换,文字修改请直接点击文字进行修改,也可以新增和删除文档中的内容。
该文档来自用户分享,如有侵权行为请发邮件ishare@vip.sina.com联系网站客服,我们会及时删除。
[版权声明] 本站所有资料为用户分享产生,若发现您的权利被侵害,请联系客服邮件isharekefu@iask.cn,我们尽快处理。
本作品所展示的图片、画像、字体、音乐的版权可能需版权方额外授权,请谨慎使用。
网站提供的党政主题相关内容(国旗、国徽、党徽..)目的在于配合国家政策宣传,仅限个人学习分享使用,禁止用于任何广告和商用目的。
下载需要: 免费 已有0 人下载
最新资料
资料动态
专题动态
is_375187
暂无简介~
格式:pdf
大小:522KB
软件:PDF阅读器
页数:35
分类:互联网
上传时间:2017-07-06
浏览量:283