这里将告诉您解压内核镜像,教程操作步骤:
步骤 0
uboot 将 zImage 复制到内存之后,跳转到 zImage 处开始执行,首先执行的代码是 arch/arm/boot/compressed/head.S 文件,首先是一些涉及不同体系结构调试相关的汇编宏定义
#ifdefDEBUG#ifdefined(CONFIG_DEBUG_ICEDCC)#ifdefined(CONFIG_CPU_V6)||defined(CONFIG_CPU_V6K)||defined(CONFIG_CPU_V7).macroloadsp,rb,tmp.endm.macrowriteb,ch,rbmcrp14,0,ch,c0,c5,0.endm...省略...#endif#endif#endif
步骤 1
首先是保存 bootloader 传递过来的机器 ID 和 atags 起始地址
@设置段名.section".start",#alloc,#execinstr.align.arm@设置指令为arm模式start:.typestart,#function@声明为函数标签.rept7movr0,r0.endrARM(movr0,r0)ARM(b1f)@向下跳转THUMB(adrr12,BSYM(1f))THUMB(bxr12).word0x016f2818@Magicnumberstohelptheloader.wordstart@程序其实地址,即zImage的地址.word_edata@zImage结束地址THUMB(.thumb)1:movr7,r1@保存bootloader传递过来的机器IDmovr8,r2@保存bootloader传递过来的atags起始地址
步骤 2
关闭中断、进入 SVC 模式
#ifndef__ARM_ARCH_2__mrsr2,cpsr@getcurrentmodetstr2,#3@notuser?bnenot_angelmovr0,#0x17@进入SVC模式ARM(swi0x123456)@angel_SWI_ARMTHUMB(svc0xab)@angel_SWI_THUMBnot_angel:mrsr2,cpsrorrr2,r2,#0xc0@关闭FIQ和IRQmsrcpsr_c,r2#elseteqppc,#0x0c000003@turnoffinterrupts
步骤 3
为了加速解压过程,打开缓存
#ifdefCONFIG_AUTO_ZRELADDR@如果配置了运行时自动计算重定位地址@则根据当前pc位置和TEXT_OFFSET计算出解压位置@TEXT_OFFSET在arch/arm/Makefile中指定,表示的是相对于内存起始地址的偏移@定义为textofs-y:=0x00008000TEXT_OFFSET:=$(textofs-y)movr4,pcandr4,r4,#0xf8000000@这里假设zImage是被放在内存的前128MB内的addr4,r4,#TEXT_OFFSET@得到zImage解压的物理地址#else@这里是直接在arch/arm/mach-s5p4418/Makefile.boot中定义的@定义为zreladdr-y:=0x40008000ldrr4,=zreladdr@r4存放解压后内核存放的起始地址#endif@打开缓存和MMUblcache_on
步骤 4
检查解压后的内核镜像是否与当前镜像发生覆盖
restart:adrr0,LC0ldmiar0,{r1,r2,r3,r6,r10,r11,r12}@将LC0数据放入寄存器中ldrsp,[r0,#28]@更新栈指针位置subr0,r0,r1@计算当前程序位置和链接地址间的偏移量addr6,r6,r0@得到zImage运行地址的结束位置addr10,r10,r0@存放未压缩内核大小的运行地址/**内核编译系统会将未压缩的内核大小追加到压缩后的内核数据之后*并以小端格式存储*/@取出未压缩内核大小放入r9ldrbr9,[r10,#0]ldrblr,[r10,#1]orrr9,r9,lr,lsl#8ldrblr,[r10,#2]ldrbr10,[r10,#3]orrr9,r9,lr,lsl#16orrr9,r9,r10,lsl#24#ifndefCONFIG_ZBOOT_ROM/*在栈指针之上分配内存空间放到r10中(64kmax)*/addsp,sp,r0addr10,sp,#0x10000#elsemovr10,r6#endifmovr5,#0@initdtbsizeto0/**检查解压后的内核是否会覆盖当前代码*r4=最终解压后的内核起始地址*r9=解压后的内核镜像大小*r10=当前镜像结束地址,其中包括所分配的内存区域*Webasicallywant:*r4-16k页表>=r10->OK*r4+imagelength<=addressofwont_overwrite->OK*/addr10,r10,#16384@内核镜像前的16KB页表cmpr4,r10bhswont_overwrite@解压后的内核起始地址大于等于r10addr10,r4,r9adrr9,wont_overwritecmpr10,r9@或者解压后的内核结束地址在wont_overwrite地址之前blswont_overwrite/**r6=_edata*r10=endofthedecompressedkernel*/@当前代码段向后移动到的目的地址,不足256字节的补足256字节,向上取整addr10,r10,#((reloc_code_end-restart+256)&~255)bicr10,r10,#255@当前代码段起始地址,以32字节为单位,向下取整adrr5,restartbicr5,r5,#31subr9,r6,r5@需要移动镜像的大小addr9,r9,#31@以32字节为单位向上取整bicr9,r9,#31addr6,r9,r5@循环结束地址addr9,r9,r10@目的地址结束位置@循环移动当前镜像1:ldmdbr6!,{r0-r3,r10-r12,lr}cmpr6,r5stmdbr9!,{r0-r3,r10-r12,lr}bhi1bsubr6,r9,r6@代码重定位的偏移地址#ifndefCONFIG_ZBOOT_ROMaddsp,sp,r6@对栈指针也进行重定位#endifblcache_clean_flush@跳转到重定位后的镜像restart处重新执行,检查是否存在覆盖adrr0,BSYM(restart)addr0,r0,r6movpc,r0
步骤 5
清理 bss 段、解压内核、关闭缓存,最后启动内核
/*至此,当前镜像和解压后的内核不会发生覆盖问题*/wont_overwrite:/**Ifdeltaiszero,wearerunningattheaddresswewerelinkedat.*r0=delta*r2=BSSstart*r3=BSSend*r4=kernelexecutionaddress*r5=appendeddtbsize(0ifnotpresent)*r7=architectureID*r8=atagspointer*r11=GOTstart*r12=GOTend*sp=stackpointer*/orrsr1,r0,r5beqnot_relocatedaddr11,r11,r0addr12,r12,r0not_relocated:movr0,#01:strr0,[r2],#4@清理bss段strr0,[r2],#4strr0,[r2],#4strr0,[r2],#4cmpr2,r3blo1b/**至此,C语言运行环境已经初始化完成*r4=解压后的内核起始地址*r7=机器ID*r8=atags指针*/movr0,r4movr1,sp@在栈指针之上分配内存空间addr2,sp,#0x10000@64kmaxmovr3,r7bldecompress_kernel@解压内核blcache_clean_flushblcache_off@关闭缓存movr0,#0@mustbezeromovr1,r7@restorearchitecturenumbermovr2,r8@restoreatagspointerARM(movpc,r4)@启动内核THUMB(bxr4)@entrypointisalwaysARM.align2.typeLC0,#objectLC0:.wordLC0@r1:LC0的链接地址.word__bss_start@r2:bss段的起始地址.word_end@r3:bss段的结束地址.word_edata@r6:zImage的结束地址.wordinput_data_end-4@r10:存放未压缩的内核大小的链接地址.word_got_start@r11:全局偏移表起始位置.word_got_end@ip:全局偏移表结束位置.word.L_user_stack_end@sp:栈指针.sizeLC0,.-LC0@该LC0数据的大小解压内核镜像就为您介绍到这里,感谢您关注懒咪学编程c.lanmit.com.
本文地址:https://c.lanmit.com/czxt/Linux/119424.html