写在前头
*.版权声明:本篇文章为原创,可随意转载,转载请注明出处,谢谢!另我创建一个QQ群82642304,欢迎加入!
*.目的:整理一下RIotBoard开发板的启动流程,对自己的所学做一个整理总结,本系列内核代码基于linux-3.0.35-imx。
*.备注:整个系列只是对我所学进行总结,记录我认为是关键的点,另我能力有限,难免出现疏漏错误,如果读者有发现请多指正,以免我误导他人!
解压时会不会出现覆盖
在RIotBoard板上,内核被Uboot加载到的地址为0x10800000,而内核被解压的地址也是0x10800000,所以根据上篇的分析,目前内存上面的分布为:
我们可以看出,解压的时候会导致解压后的数据覆盖掉待解压的数据,所以解压之前我们先要处理一下,防止覆盖情况的发生。
接着上篇文章分析的代码位置往下看
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 1/*
2 * Check to see if we will overwrite ourselves.
3 * r4 = final kernel address
4 * r9 = size of decompressed image
5 * r10 = end of this image, including bss/stack/malloc space if non XIP
6 * We basically want:
7 * r4 - 16k page directory >= r10 -> OK
8 * r4 + image length <= current position (pc) -> OK
9 */
10 add r10, r10, #16384
11 cmp r4, r10
12 bhs wont_overwrite
13 add r10, r4, r9
14 ARM( cmp r10, pc )
15 THUMB( mov lr, pc )
16 THUMB( cmp r10, lr )
17 bls wont_overwrite
18
19
寄存器r4保存的是内核解压地址,r9保存的是解压之后的内核长度,r10保存的是镜像结束地址(镜像的内存分布可以参考vmlinux.lds,在压缩内核数据后面还有bss段以及堆栈等数据)
我们要求:
r4 – 16k page directory >= r10 -> OK
即整个镜像数据在内核解压地址-16K的地方之下。
或者:
r4 + image length <= current position (pc) -> OK
解压之后不会覆盖当前PC位置,由于待解压的数据都在PC之上,所以更不会覆盖掉待解压的数据。
了解这两个要求,上面那段汇编就容易理解了,分别判断上面两个要求,如果满足就跳转到wont_overwrite,跳过重定位镜像,如果不满足就继续执行。此时我们是不满足的,所以需要移动镜像位置。
移动镜像
如果无法满足上述的任一条件,需要将镜像重新移动到高位去,避免解压内核时出现覆盖现象。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45 1/*
2 * Relocate ourselves past the end of the decompressed kernel.
3 * r6 = _edata
4 * r10 = end of the decompressed kernel
5 * Because we always copy ahead, we need to do it from the end and go
6 * backward in case the source and destination overlap.
7 */
8 /*
9 * Bump to the next 256-byte boundary with the size of
10 * the relocation code added. This avoids overwriting
11 * ourself when the offset is small.
12 */
13 add r10, r10, #((reloc_code_end - restart + 256) & ~255)
14 bic r10, r10, #255;255字节对齐
15
16 /* Get start of code we want to copy and align it down. */
17 adr r5, restart ;从restart开始拷贝
18 bic r5, r5, #31 ;32位对齐
19
20 sub r9, r6, r5 @ size to copy;拷贝restart到__edata间的这段代码
21 add r9, r9, #31 @ rounded up to a multiple
22 bic r9, r9, #31 @ ... of 32 bytes;32位对齐
23 add r6, r9, r5 ;r6保存待拷贝数据的最顶端
24 add r9, r9, r10 ;r9保存目的区域的最顶端
25
26;拷贝数据
271: ldmdb r6!, {r0 - r3, r10 - r12, lr}
28 cmp r6, r5
29 stmdb r9!, {r0 - r3, r10 - r12, lr}
30 bhi 1b
31
32 /* Preserve offset to relocated code. */
33 sub r6, r9, r6 ;计算原先restart和拷贝后的restart之间的偏移量
34
35#ifndef CONFIG_ZBOOT_ROM
36 /* cache_clean_flush may use the stack, so relocate it */
37 add sp, sp, r6
38#endif
39
40 bl cache_clean_flush
41;r6保存的是原先restart和拷贝后的restart之间的偏移量,接下来是计算拷贝后的restart地址并且跳转到restart
42 adr r0, BSYM(restart)
43 add r0, r0, r6
44 mov pc, r0
45
拷贝后的内存分布为:
计算出拷贝数据长度与拷贝源、目的地址后,将镜像拷贝到原镜像的上面,然后计算出拷贝后的restart入口地址,并跳转到该处,重新执行加载LC0表,重新分配堆,计算待解压内核长度、判断是否重叠等操作,此时判断是不会重叠的,所以跳过移动镜像的操作,进行下面的指令。
总结
解压缩之后内核数据长度变大以及当前PC位置跟内核解压位置的关系,解压内核有可能会覆盖掉执行镜像,所以在解压之前先要判断一下是否出现覆盖现象。
参考
暂无