微波EDA网,见证研发工程师的成长!
首页 > 硬件设计 > 嵌入式设计 > ARM linux解析之压缩内核zImage的启动过程

ARM linux解析之压缩内核zImage的启动过程

时间:11-10 来源:互联网 点击:

区别的,这就是为何前面要区分ZTEXTADDR和ZBSSADDR的原因了。

好了,再看下面这两句的区别,如果这个地方弄明白了,那么,下面的内容就会变得很简单,往下看:

restart:adr r0,LC0

addr0,pc,#0x10C

LC0:.wordLC0@ r1

dcd0x17C

故可知,当zImage加到0x28运行时,PC值为:0x28070,这个时候r0=0x2817C

而通过ldmiar0, {r1, r2, r3, r6, r10, r11, r12}加载内存值后,r1=0x17C

那么我们看一看这句:sub r0, r0, r1@ calculate the delta offset的值是多少?如下:

r0=0x2817C-0x17C =0x28

see~~~看出来什么没有,这个就是我们的加载zImage运行的内存起始地址,这个很重要,后面就要靠它知道我们当前的代码在哪里,搬移到哪里。然后再下一条指令把堆栈指针设置好。然后再把实际代码偏移量加在r6=_edata和(r10=input_data_end-4)上面,这就是实际的内存中的地址。好继续往下看:

ldrbr9, [r10, #0]

ldrblr, [r10, #1]

orr r9, r9, lr, lsl #8

ldrblr, [r10, #2]

ldrbr10, [r10, #3]

orr r9, r9, lr, lsl #16

orr r9, r9, r10, lsl #24

压缩的工具会把所压缩后的文件的最后加上用小端格式表示的4个字节的尾,用来存储所压内容的原始大小,这个信息很要,是我们后面分配空间,代码重定位的重要依据。这里为何要一个字节,一个字节地取,只因为要兼容ARM代码使用大端编译的情况,保证读取的正确无误。好了,再往下:

#ifndef CONFIG_ZBOOT_ROM

add sp, sp, r0

add r10, sp, #0x10

#else

movr10, r6

#endif

我们这里在RAM中运行,所以加上重定位SP的指针,加上偏移里,变成实际所在内存的堆栈指针地址。这里主要是为了后面的检查代码是否要进行重定位的时候所提前设置的,因为如果代码不重定位,就不会再设堆栈指针了,重定位的话,则还要重设一次。然后再在堆栈指针的上面开辟一块64K大小的空间,用于解压内核时的临时buffer。

再往下看:

add r10, r10, #16384//16K MMU页表也不能被覆盖哦,否则解压到复盖后,ARM就挂了。

cmpr4, r10

bhswont_overwrite

add r10, r4, r9

ARM(cmpr10, pc)

THUMB(movlr, pc)

THUMB(cmpr10, lr)

blswont_overwrite

这段的检测有点绕人,两种情况都画个图看一下,如图.6所示,下面我们来看分析两种不会覆盖的情况:

第一种情况是加载运行的zImage在下,解压后内核运行地址zreladdr在上,这种情况如果最上面的64k的解压buffer不会覆盖到内核前的16k页表的话,就不用重定位代码跳到wont_overwrite执行。

第二种情况是加载运行的zImage在上,而解压的内核运行地址zreladdr在下面,只要最后解压后的内核的大小加上zreladdr不会到当前pc值,则也不会出现代码覆盖的情况,这种情况下,也不用重位代码,直接跳到wont_overwrite执行就可以了。

图.6内核的两种解压不要重定位的情况

可以我们一般加载的zImage的地址,和最后解压的zreladdr的地址是相同的,那么,就必然会发生代码覆盖的问题,这时候就要进行代码的自搬移和重定位。具体实现如下:

add r10, r10, #((reloc_code_end-restart+ 256) & ~255)

bicr10, r10, #255

adr r5, restart

bicr5, r5, #31

sub r9, r6, r5@ size to copy

add r9, r9, #31@ rounded up to a multiple

bicr9, r9, #31@ ... of 32 bytes

add r6, r9, r5

add r9, r9, r10

1:ldmdbr6!, {r0 - r3, r10 - r12, lr}

cmpr6, r5

stmdbr9!, {r0 - r3, r10 - r12, lr}

bhi 1b

这段代码就是实现代码的自搬移,最开始两句是取得所要搬移代码的大小,进行了256字节的对齐,注释上说了,为了避免偏移很小时产生自我覆盖(这个地方暂没有想明白,不过不影响下面分析)。这里还是再画个图表示一下整个搬移过程吧,以zImage加载地下和zreladdr都为0x28为例,其他的类似。

图.7 zImage的代码自搬移和内核解压的全程图解

图.7中我已经标好了序号,代码的自搬移和内核解的整个过程都在这里面下面一步步来分解:

①.首先计算要搬移的代码的.text段代码的大小,从restart开始,到reloc_code_end结束,这个就是剩下的.text段的内容,这段内容是接在打开cache的函数之后的。然后把这段代码搬到核实际解压后256字节对齐的边界,然后进行搬移,搬移时一次搬运32个字节,故存有搬移大小的r9寄存器进行了一下32字节对齐的扩展。

②.搬移完成后,会保存一下新旧代码间的offset值,存于r6中。再重新设置一下新的堆栈的地址,位置如图所示,代码如下:

subr6, r9, r6

#ifndef CONFIG_ZBOOT_ROM

addsp, sp, r6

#endif

③.然后进行cache的flush,因为马上要进行代码的跳转了,接着就计算新的restart在哪里,接着跳过去执行新

Copyright © 2017-2020 微波EDA网 版权所有

网站地图

Top