您好,欢迎访问三七文档
当前位置:首页 > IT计算机/网络 > linux/Unix相关 > 嵌入式Linux2.6内核启动流程)
Linux内核构成(国嵌)Linux/arch/arm/boot/compressed/head.s1.解压缩2.初始化3.启动应用程序1arch/arm/boot/compressed/Makefilearch/arm/boot/compressed/vmlinux.lds2.arch/arm/kernel/vmlinux.ldsLinux内核启动流程(国嵌)arch/arm/boot/compressed/start.S(head.s—负责解压缩)Start:.typestart,#function.rept8movr0,r0.endrb1f.word0x016f2818@Magicnumberstohelptheloader.wordstart@absoluteload/runzImageaddress.word_edata@zImageendaddress1:movr7,r1@savearchitectureIDmovr8,r2@saveatagspointer这也标志着u-boot将系统完全的交给了OS,bootloader生命终止。之后代码在133行会读取cpsr并判断是否处理器处于supervisor模式——从u-boot进入kernel,系统已经处于SVC32模式;而利用angel进入则处于user模式,还需要额外两条指令。之后是再次确认中断关闭,并完成cpsr写入mrsr2,cpsr@getcurrentmodetstr2,#3@notuser?bnenot_angelmovr0,#0x17@angel_SWIreason_EnterSVCswi0x123456@angel_SWI_ARMnot_angel:mrsr2,cpsr@turnoffinterruptstoorrr2,r2,#0xc0@preventangelfromrunningmsrcpsr_c,r2然后在LC0地址处将分段信息导入r0-r6、ip、sp等寄存器,并检查代码是否运行在与链接时相同的目标地址,以决定是否进行处理。由于现在很少有人不使用loader和tags,将zImage烧写到rom直接从0x0位置执行,所以这个处理是必须的(但是zImage的头现在也保留了不用loader也可启动的能力)。arm架构下自解压头一般是链接在0x0地址而被加载到0x30008000运行,所以要修正这个变化。涉及到r5寄存器存放的zImage基地址r6和r12(即ip寄存器)存放的got(globaloffsettable)r2和r3存放的bss段起止地址sp栈指针地址很简单,这些寄存器统统被加上一个你也能猜到的偏移地址0x30008000。该地址是s3c2410相关的,其他的ARM处理器可以参考下表PXA2xx是0xa0008000IXP2x00和IXP4xx是0x00008000Freescalei.MX31/37是0x80008000TIdavinciDM64xx是0x80008000TIomap系列是0x80008000AT91RM/SAM92xx系列是0x20008000CirrusEP93xx是0x00008000这些操作发生在代码172行开始的地方,下面只粘贴一部分addr5,r5,r0addr6,r6,r0addip,ip,r0后面在211行进行bss段的清零工作not_relocated:movr0,#01:strr0,[r2],#4@clearbssstrr0,[r2],#4strr0,[r2],#4strr0,[r2],#4cmpr2,r3blo1b然后224行,打开cache,并为后面解压缩设置64KB的临时malloc空间blcache_onmovr1,sp@mallocspaceabovestackaddr2,sp,#0x10000@64kmax接下来238行进行检查,确定内核解压缩后的Image目标地址是否会覆盖到zImage头,如果是则准备将zImage头转移到解压出来的内核后面cmpr4,r2bhswont_overwritesubr3,sp,r5@compressedkernelsizeaddr0,r4,r3,lsl#2@allowfor4xexpansioncmpr0,r5blswont_overwritemovr5,r2@decompressaftermallocspacemovr0,r5movr3,r7bldecompress_kernel真实情况——在大多数的应用中,内核编译都会把压缩的zImage和非压缩的Image链接到同样的地址,s3c2410平台下即是0x30008000。这样做的好处是,人们不用关心内核是Image还是zImage,放到这个位置执行就OK,所以在解压缩后zImage头必须为真正的内核让路。在250行解压完毕,内核长度返回值存放在r0寄存器里。在内核末尾空出128字节的栈空间用,并且使其长度128字节对齐。addr0,r0,#127+128@alignment+stackbicr0,r0,#127@alignthekernellength算出搬移代码的参数:计算内核末尾地址并存放于r1寄存器,需要搬移代码原来地址放在r2,需要搬移的长度放在r3。然后执行搬移,并设置好sp指针指向新的栈(原来的栈也会被内核覆盖掉)addr1,r5,r0@endofdecompressedkerneladrr2,reloc_startldrr3,LC1addr3,r2,r31:ldmiar2!,{r9-r14}@copyrelocationcodestmiar1!,{r9-r14}ldmiar2!,{r9-r14}stmiar1!,{r9-r14}cmpr2,r3blo1baddsp,r1,#128@relocatethestack搬移完成后刷新cache,因为代码地址变化了不能让cache再命中被内核覆盖的老地址。然后跳转到新的地址继续执行blcache_clean_flushaddpc,r5,r0@callrelocationcode注意——zImage在解压后的搬移和跳转会给gdb调试内核带来麻烦。因为用来调试的符号表是在编译是生成的,并不知道以后会被搬移到何处去,只有在内核解压缩完成之后,根据计算出来的参数“告诉”调试器这个变化。以撰写本文时使用的zImage为例,内核自解压头重定向后,reloc_start地址由0x30008360变为0x30533e60。故我们要把vmlinux的符号表也相应的从0x30008000后移到0x30533b00开始,这样gdb就可以正确的对应源代码和机器指令。随着头部代码移动到新的位置,不会再和内核的目标地址冲突,可以开始内核自身的搬移了。此时r0寄存器存放的是内核长度(严格的说是长度外加128Byte的栈),r4存放的是内核的目的地址0x30008000,r5是目前内核存放地址,r6是CPUID,r7是machineID,r8是atags地址。代码从501行开始reloc_start:addr9,r5,r0subr9,r9,#128@donotcopythestackdebug_reloc_startmovr1,r41:.rept4ldmiar5!,{r0,r2,r3,r10-r14}@relocatekernelstmiar1!,{r0,r2,r3,r10-r14}.endrcmpr5,r9blo1baddsp,r1,#128@relocatethestack接下来在516行清除并关闭cache,清零r0,将machineID存入r1,atags指针存入r2,再跳入0x30008000执行真正的内核Imagecall_kernel:blcache_clean_flushblcache_offmovr0,#0@mustbezeromovr1,r7@restorearchitecturenumbermovr2,r8@restoreatagspointermovpc,r4@callkernel内核代码入口在arch/arm/kernel/head.S文件的83行。首先进入SVC32模式,并查询CPUID,检查合法性msrcpsr_c,#PSR_F_BIT|PSR_I_BIT|SVC_MODE@ensuresvcmode@andirqsdisabledmrcp15,0,r9,c0,c0@getprocessoridbl__lookup_processor_type@r5=procinfor9=cpuidmovsr10,r5@invalidprocessor(r5=0)?beq__error_p@yes,error'p'接着在87行进一步查询machineID并检查合法性bl__lookup_machine_type@r5=machinfomovsr8,r5@invalidmachine(r5=0)?beq__error_a@yes,error'a'其中__lookup_processor_type在linux-2.6.24-moko-linuxbj/arch/arm/kernel/head-common.S文件的149行,该函数首将标号3的实际地址加载到r3,然后将编译时生成的__proc_info_begin虚拟地址载入到r5,__proc_info_end虚拟地址载入到r6,标号3的虚拟地址载入到r7。由于adr伪指令和标号3的使用,以及__proc_info_begin等符号在linux-2.6.24-moko-linuxbj/arch/arm/kernel/vmlinux.lds而不是代码中被定义,此处代码不是非常直观,想弄清楚代码缘由的读者请耐心阅读这两个文件和adr伪指令的说明。r3和r7分别存储的是同一位置标号3的物理地址(由于没有启用mmu,所以当前肯定是物理地址)和虚拟地址,所以儿者相减即得到虚拟地址和物理地址之间的offset。利用此offset,将r5和r6中保存的虚拟地址转变为物理地址__lookup_processor_type:adrr3,3fldmdar3,{r5-r7}subr3,r3,r7@getoffsetbetweenvirt&physaddr5,r5,r3@convertvirtaddressestoaddr6,r6,r3@physicaladdressspace然后从proc_info中读出内核编译时写入的processorID和之前从cpsr中读到的processorID对比,查看代码和CPU硬件是否匹配(想在arm920t上运行为cortex-a8编译的内核?不让!)。如果编译了多种处理器支持,如versatile板,则会循环每种type依次检验,如果硬件读出的ID在内核中找不到匹配,则r5置0返回1:ldmiar5,{r3,r4}@value,maskandr4,r4,r9@maskwantedbitsteqr3,r4beq2faddr5,r5,#PROC_INFO_SZ@sizeof(proc_info_list)cmpr5,r6blo1bmovr5,#0@unknownprocessor2:movpc,lr__lookup_machine_type在linux-2.6.24-moko-linuxbj/arch/arm/kernel/head-common.S文件的197行,编码方法与检查processorID完全一样,请参考前段__lookup_machine_type:adrr3,3bldmiar3,{r4,r5,r6}subr3,r3,r4@getoffsetbetweenvirt&physaddr5,r5,r3@convertvirtaddressestoaddr6,r6,r3@physicaladdressspace1:ldrr3,[r5,#MACHINFO_TYPE]@getmachinetypeteqr3,r1@matchesloadernumber?beq2f@foundaddr5
本文标题:嵌入式Linux2.6内核启动流程)
链接地址:https://www.777doc.com/doc-7027473 .html