armlinux启动流程之解压内核

armlinux启动流程之解压内核
 
    从后往前看下编译生成zImage的过程,我们可以找到程序的入口还是那个很重要

    链接文件,找到它,生成zImage所在的目录是kernel\arch\arm\boot\compressed\

    Make过程为....ld -p -X -T vmlinux.lds head.o misc.o head-s3c2410.o piggy.o

    libgcc.o -o vmlinux

    然后是用二进制工具objcopy把vmlinux制作成可执行的二进制映像文件zImage

    这样在我们就去kernel\arch\arm\boot\compressed\目录下去找到vmlinux.lds文件

    如果没有编译就不会有这个文件,因为它也是在编译过程生成的,由同一目录下的

    vmlinux.lds.in生成,打开这个文件

    ENTRY(_start)

    SECTIONS

    {

     . = LOAD_ADDR;

     _load_addr = .;

     . = TEXT_START;

     _text = .;

     .text : {

     _start = .;

     *(.start)

     *(.text)

    ........

    入口是_start,而且入口就直接定义在这个文件中了

    入口直接接着.start段,所以程序开始是从.start段开始执行的

    如果看看vmlinux.lds的生成过程就应该能找到LOAD_ADDR和TEXT_START的值

    实际上这两个值是由其他两个变量赋给的 ZRELADDR 和 ZTEXTADDR

    在kernel\arch\arm\boot\Makefile中我们可以找到这两个变量的值

    ifeq ($(CONFIG_ARCH_S3C2410),y)

    ZTEXTADDR = 0x30008000

    ZRELADDR = 0x30008000

    endif

    所以

    LOAD_ADDR = 0x30008000

    TEXT_START = 0x30008000

    看一下vmlinux.lds吧

    ENTRY(_start)

    SECTIONS

    {

     . = 0x30008000;

     _load_addr = .;

     . = 0;

     _text = .;

    显然LOAD_ADDR被赋值了0x30008000

    看一下TEXT_START怎么成0了,我想这应该是一个偏移吧,偏移是0

    所以它还是0x30008000

    接着下来就从head.s来开始看代码吧

     .section ".start", #alloc, #execinstr

    /*

     * sort out different calling conventions

     */

     .align

    start:

     .type start,#function

     .rept 8

     mov r0, r0

     .endr

     b 1f

     .word 0x016f2818 @ Magic numbers to help the loader

     .word start @ absolute load/run zImage address

     .word _edata @ zImage end address

    1: mov r7, r1 @ save architecture ID

    这里一定就是程序的入口了,一般汇编程序的含义就看看英文注释就是了

    有一个要注意的地方,不是一个汇编文件就是属于一个段的,不是说先执行完了

    head.s再去执行head-s3c2410.s,还是要注意链接的段,显然head.s

    不一会就开始了另一个段.text

     .text

     adr r0, LC0

     ldmia r0, {r1, r2, r3, r4, r5, r6, ip, sp}

     subs r0, r0, r1 @ calculate the delta offset

    而我们的head-s3c2410.s呢

     .section ".start", #alloc, #execinstr

    __S3C2410_start:

     bic r2, pc, #0x1f

     add r3, r2, #0x4000 @ 16 kb is quite enough...

    还是属于.start段的,所以顺序执行下来时先执行head-s3c2410.s,然后再去执行

    .text段。head-s3c2410.s主要是cpu的一些初始化工作。接着下来我们会需要把内核

    接压缩,先说说为什么吧。还是注意到上面生成zImage的文件中有一个piggy.o,往上

    追寻可以看到是piggy.o由那个真正的内核vmlinux生成的,这个vmlinux才是启动后一直在

    运行的内核,原本很大,压缩以后可以方便地放在flash中,当然其实不压缩跳到它的

    入口也就可以运行了。解压的内核是准备从LOAD_ADDR = 0x30008000开始的4M空间,会覆盖

    我们的当前运行的代码,那样就先把内核解压到我们这个zImage+分配堆栈0x10000的最后

     cmp r4, r2 //r4 是LOAD_ADDR=0x30008000

     bhs wont_overwrite //r2 是当前代码的最底部 这里当然不会跳转

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

     cmp r0, r5 //r5 也是0x30008000

     bls wont_overwrite //不会跳转

     mov r5, r2 //r2是(user_stack+4096)在zImage的最后+0x10000

     mov r0, r5

     mov r3, r7 //machine type

     bl decompress_kernel

    有了r5,r0,r7作为参数,就可以调用misc.c中的decompress_kernel函数进行解压缩了

    这个函数调用的gunzip函数时gcc的库函数,所以在源码中找不到的

    解压在r5开始的地方,函数返回的是r0解压得到的长度。这时候我们需要对代码经行调整

     add r1, r5, r0 @ end of decompressed kernel

     adr r2, reloc_start

     ldr r3, LC1 //LC1: .word reloc_end - 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 //这里就把从reloc_start到reloc_end这段我们需要的代码放到了

     blo 1b //解压内核的最后,而在下面我们会将zImage都覆盖掉

     bl cache_clean_flush

     add pc, r5, r0 //调到调整后的reloc_start,在decompressed kernel后

    reloc_start: add r8, r5, r0 //r5解压内核开始的地方 r0解压内核的长度

     debug_reloc_start

     mov r1, r4 //r4=0x30008000

    1:

     .rept 4

     ldmia r5!, {r0, r2, r3, r9 - r13} @ relocate kernel

     stmia r1!, {r0, r2, r3, r9 - r13}

     .endr

     cmp r5, r8

     blo 1b //这样就又把解压的真正内核移到了0x30008000处

    call_kernel: bl cache_clean_flush

     bl cache_off

     mov r0, #0

     mov r1, r7 @ restore architecture number

     mov pc, r4 @ call kernel

    上面就是跳到0x30008000这里去执行真正的内核了吧

posted @ 2012-10-18 13:06  梦来是缘  阅读(350)  评论(0)    收藏  举报