ARM linxu启动过程分析(二)

时间:2021-10-26 04:55:40

*****解压缩内核,分三种情况,下面一一解释:

说明:这一段分析中所提到的vmlinux指的是基本内核映像vmlinux

/*检查当前地址间的相互关系,防止解压缩过程中出现地址重叠或者说地址冲突

 *  r4 = final kernel address   //最终解压后的内核首地址

 *  r5 = start of this image zImage的运行时首地址,一般为0x30008000,当然也可以不同,

 *  r2 = end of malloc space (and therefore this image)

 *We basically want:

 *  r4 >= r2 -> OK

 *  r4 + image length <= r5 -> OK   

decompress_kernel(ulgoutput_start, ulg free_mem_ptr_p, ulg free_mem_ptr_end_p,

              intarch_id */

第一种情况:

      理论上讲,这种情况下vmlinux的起始地址大于zImage运行时所需的最大地址(r2,那么直接将zImage解压到vmlinux的目标地址也是没有问题的,但是实际上有点意外,看下面的分析。

             cmp r4,r2

             bhs wont_overwrite   //r4大于r2不会发生地址冲突

分析:依据链接脚本arch/arm/boot/compressed/vmlinux.lds分析

.data      : { *(.data) }

 _edata =.;

 . =ALIGN(4);

 __bss_start= .;

 .bss: {*(.bss) }

 _end =.;

 .stack(NOLOAD): { *(.stack) }

Sp的装载地址在zImage映像上面的bss段之上,那么从上面的代码知道r2=sp+0x10000,

那么我们得出结论:第一种情况不可能发生:因为r4=0x30008000,内存的起始地址为0x30000000,zImage的大小最小约1MB,0x100000,所以r4一定小于r2

第二种情况:

      这种情况下zImage的起始地址大于vmlinux的目标起始地址加上vmlinux大小(4M)的地址,所以将zImage直接解压到vmlinux的目标地址是没有问题的。

             add r0, r4, #4096*1024 @ 4MB largest kernel size

             cmp r0,r5          

             bls  wont_overwrite     //r4+zImage size <=r5不会发生地址冲突

 

 

分析如下:假如r5>r4+0x400000,其根本意思就是u-bootzImage重定位到r4+0x400000(我们这里就是0x30008000+0x400000=0x30408000的位置),这一方案是可行的。其内存分布图如下所示:

ARM linxu启动过程分析(二)

第三种情况:也是我们最常使用的方式,将作详细解释

      这种情况下vmlinux的目标位置刚好和zImage的当前位置重合,所以解决方案就是先将zImage解压到zImage的上面,再将其重定位或者说搬移到目标位置。当然这其中就有个问题,vmlinux的搬移同样会覆盖掉正在运行的重定位及其后面的第一阶段启动代码,所以在搬移vmlinux到目标位置之前我们需要先将重定位代码段(事实上包含了重定位代码和后面的内核启动第一阶段的代码)搬移到vmlinux的上面,这样就避免了地址的冲突。

             mov r5,r2                   @ decompress after malloc space

             mov r0,r5           //zImage解压到r2(就是sp+0x10000)起始的地址

             mov r3,r7                   //Archtecture ID-->r3

             bl    decompress_kernel 

             add r0, r0, #127   

             bic  r0, r0, #127          @ align the kernel length

/*r0     = decompressed kernel length

 *r1-r3  = unused

 *r4     = kernel execution address

 *r5     = decompressed kernel start

 *r6     = processor ID

 *r7     = architecture ID

 *r8-r14 = unused    */

 //将内核重定位代码段搬移到解压后的内核的上面

             add r1, r5, r0        @ end ofdecompressed kernel

             adr  r2, reloc_start   //reloc_start代码段的起始地址

             ldr  r3, LC1      //reloc_start段代码的大小

             add r3, r2, r3  

1:           ldmia       r2!, {r8 -r13}             @ copy relocation code

             stmia      r1!, {r8 - r13}

             ldmia      r2!, {r8 - r13}

             stmia      r1!, {r8 - r13}

             cmp r2,r3

             blo  1b

             bl    cache_clean_flush   //cache


             add pc, r5, r0             @ call relocation code  //跳转到新的reloc_start代码位置执行解压后内核的重定位,  将解压后的内核搬移到r4=0x30008000位置

ARM linxu启动过程分析(二)

/* * We're not in danger of overwriting ourselves.  Do this the simple way.

 * r4     = kernel execution address

 * r7     = architecture ID

 解压缩过程中不需要传递压缩映像的起始地址,这些时在编译链接时

 就已经由arch/arm/boot/compressed/piggy.S决定好了。 */

第一、二种情况下的解压代调用部分

wont_overwrite:     mov r0, r4    //设置解压缩目的起始地址,也就是内核入口地址

//在arm linux中这一地址一般为0x30008000

              mov r3, r7    //architecture ID 解压缩时需要该参数

              bl     decompress_kernel   //调用解压缩c程序

              b     call_kernel 

第三种情况下后面这部分代码的位置已经不是zImage最初装载的位置了,而是在搬移重定位代码段时,被搬移到vmlinux上面了,否则就会出错,因为vmlinux搬移之后将会彻底覆盖zImage最初装载的位置。

       debug_reloc_end     //relocate 代码段紧接着就是call_kernel

call_kernel:     bl     cache_clean_flush   //清cache

              bl     cache_off  //关cache

              mov r0, #0

              mov r1, r7                    @ restore architecture number

              mov pc, r4                    @ call kernel  跳转到内核启动第二阶段开始执行。