您的位置:首页 > 运维架构 > Linux

IMX6Solo启动流程-Linux 内核启动 三

2015-09-21 10:32 591 查看

写在前头

*.版权声明:本篇文章为原创,可随意转载,转载请注明出处,谢谢!另我创建一个QQ群82642304,欢迎加入!

*.目的:整理一下RIotBoard开发板的启动流程,对自己的所学做一个整理总结,本系列内核代码基于linux-3.0.35-imx。

*.备注:整个系列只是对我所学进行总结,记录我认为是关键的点,另我能力有限,难免出现疏漏错误,如果读者有发现请多指正,以免我误导他人!

解压时会不会出现覆盖

在RIotBoard板上,内核被Uboot加载到的地址为0x10800000,而内核被解压的地址也是0x10800000,所以根据上篇的分析,目前内存上面的分布为:




我们可以看出,解压的时候会导致解压后的数据覆盖掉待解压的数据,所以解压之前我们先要处理一下,防止覆盖情况的发生。

接着上篇文章分析的代码位置往下看

[code]/*
 * Check to see if we will overwrite ourselves.
 *   r4  = final kernel address
 *   r9  = size of decompressed image
 *   r10 = end of this image, including  bss/stack/malloc space if non XIP
 * We basically want:
 *   r4 - 16k page directory >= r10 -> OK
 *   r4 + image length <= current position (pc) -> OK
 */
        add r10, r10, #16384
        cmp r4, r10
        bhs wont_overwrite
        add r10, r4, r9
   ARM(     cmp r10, pc     )
 THUMB(     mov lr, pc      )
 THUMB(     cmp r10, lr     )
        bls wont_overwrite


寄存器r4保存的是内核解压地址,r9保存的是解压之后的内核长度,r10保存的是镜像结束地址(镜像的内存分布可以参考vmlinux.lds,在压缩内核数据后面还有bss段以及堆栈等数据)

我们要求:

r4 - 16k page directory >= r10 -> OK

即整个镜像数据在内核解压地址-16K的地方之下。

或者:

r4 + image length <= current position (pc) -> OK

解压之后不会覆盖当前PC位置,由于待解压的数据都在PC之上,所以更不会覆盖掉待解压的数据。

了解这两个要求,上面那段汇编就容易理解了,分别判断上面两个要求,如果满足就跳转到wont_overwrite,跳过重定位镜像,如果不满足就继续执行。此时我们是不满足的,所以需要移动镜像位置。

移动镜像

如果无法满足上述的任一条件,需要将镜像重新移动到高位去,避免解压内核时出现覆盖现象。

[code]/*
 * Relocate ourselves past the end of the decompressed kernel.
 *   r6  = _edata
 *   r10 = end of the decompressed kernel
 * Because we always copy ahead, we need to do it from the end and go
 * backward in case the source and destination overlap.
 */
        /*
         * Bump to the next 256-byte boundary with the size of
         * the relocation code added. This avoids overwriting
         * ourself when the offset is small.
         */
        add r10, r10, #((reloc_code_end - restart + 256) & ~255)
        bic r10, r10, #255;255字节对齐

        /* Get start of code we want to copy and align it down. */
        adr r5, restart      ;从restart开始拷贝
        bic r5, r5, #31      ;32位对齐

        sub r9, r6, r5      @ size to copy;拷贝restart到__edata间的这段代码
        add r9, r9, #31     @ rounded up to a multiple
        bic r9, r9, #31     @ ... of 32 bytes;32位对齐
        add r6, r9, r5      ;r6保存待拷贝数据的最顶端
        add r9, r9, r10     ;r9保存目的区域的最顶端

;拷贝数据
1:      ldmdb   r6!, {r0 - r3, r10 - r12, lr}
        cmp r6, r5
        stmdb   r9!, {r0 - r3, r10 - r12, lr}
        bhi 1b

        /* Preserve offset to relocated code. */
        sub r6, r9, r6      ;计算原先restart和拷贝后的restart之间的偏移量

#ifndef CONFIG_ZBOOT_ROM
        /* cache_clean_flush may use the stack, so relocate it */
        add sp, sp, r6
#endif

        bl  cache_clean_flush
;r6保存的是原先restart和拷贝后的restart之间的偏移量,接下来是计算拷贝后的restart地址并且跳转到restart
        adr r0, BSYM(restart)
        add r0, r0, r6
        mov pc, r0


拷贝后的内存分布为:




计算出拷贝数据长度与拷贝源、目的地址后,将镜像拷贝到原镜像的上面,然后计算出拷贝后的restart入口地址,并跳转到该处,重新执行加载LC0表,重新分配堆,计算待解压内核长度、判断是否重叠等操作,此时判断是不会重叠的,所以跳过移动镜像的操作,进行下面的指令。

总结

解压缩之后内核数据长度变大以及当前PC位置跟内核解压位置的关系,解压内核有可能会覆盖掉执行镜像,所以在解压之前先要判断一下是否出现覆盖现象。

参考

暂无
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: