• 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这里去执行真正的内核了吧

  • 相关阅读:
    利用 img 和 script 发送跨域请求
    tomcat 内存配置
    servlet request.getParamter 有时获取参数为null
    windows本地无法启动sqlserver服务
    mac用virtualbox 装win7联网及分辨率设置
    奇怪的transform bug
    mysql 同时执行多条update语句
    二进制树形算法
    协议栈中使用crc校验函数
    引用布局
  • 原文地址:https://www.cnblogs.com/mingjie/p/kernelstart1.html
Copyright © 2020-2023  润新知