打开APP
userphoto
未登录

开通VIP,畅享免费电子书等14项超值服

开通VIP
转载-arch/arm/boot/compressed/head.S初试
现在虽然确定了以后要走的路线,但这条路很不好走啊,昨天一天就知道了kernel在start_kernel之前走过了哪些文件,今天打算开始象head.S下手,这还是zImage时的路线,我们要生成xipImage,好像是要不压缩的,那么就不会经过这步了,埃先暂时步管了,zImage是主线,先搞这个了;
是汇编写的,上午先看了嵌入式系统中关于arm处理器部分,才开始,又找到了篇分析arm启动过程的文章,文章中分析了bootloader的代码和head.S,head-xxx.S,还有接下来的head-armv.S,但后面三个文件它分析的不是仔细,可能对于arm熟手来差不多,但对我这样的,没感觉到多了多少帮助啊,自己慢慢看,里面很多命令,伪指令,查资料,勉强勉强看了下,里面很多细节不明所以,只知道大概讲了点什么,两个字,郁闷啊,又没有人讨论讨论,但讨论,也是很低级的啊,自己水平还太烂啊
arch/arm/boot/compressed/head.S:
.start节,可分配,可执行的
Start函数符号
r7存储体系ID,r8保存r0
1,在__ARM_ARCH_2__未定义条件下,判断是否从angel启动的,及判断cpsr中低两位是否为11,不是,则此刻处于用户态,不是从angel来的,关闭cpsr[7:6],中断和快中断位,是从angel来的,需要先进入SVC模式(mov   r0, #0x17                  swi     0x123456       这语句不怎么明白)然后在禁止中断; __ARM_ARCH_2__定义了,通过teqp    pc, #0x0c000003来关闭中断(不懂意思);
2,进入 0号正文子节 .text
把LC0的数据依次存入r1,r2,r3,r4,r5,r6,ip,sp,LC0中存储的正是
.word   LC0                @ r1
           .word  __bss_start          @ r2
           .word  _end               @ r3
           .word  _load_addr          @ r4
           .word  _start              @ r5
           .word  _got_start       @ r6
           .word  _got_end        @ ip
           .word  user_stack+4096       @ sp
因为此时r0是LC0的地址,r1中存放的是初试地址,跟它相减计算出链接后运行时的偏移地址,一样的说明没有被重定位,跳到not_relocated,不然已经被重定位,需要把zImage的基地址,GOT开始和结束地址加上这个偏移量
      add     r5, r5, r0
           add     r6, r6, r0
           add     ip, ip, r0
再看是否定义CONFIG_ZBOOT_ROM,未定义就是完全的要PIC,就还需要修正BSS域,相关的有,都加上r0
*   r2 - BSS start
            *   r3 - BSS end
            *   sp - stack pointer
再重定位GOT表中的条目,都加上r0;如果定义了CONFIG_ZBOOT_ROM则只重定位BSS域外面的GOT条目,程序好像是比较GOT条目是否在BSS_start和_end间,应该是在之外面的才重定位,cmphs,addlo指令看不懂
not_relocated:没有重定位就把bss清零
3,此时估计C运行环境已稳定,跳到cache_on去打开缓存,设置指针,开始解压缩
cache_on: r3中存入#8,跳到call_cache_fn, proc_types为缓存操作表格,一个条目有五条
*   - CPU ID match
*   - CPU ID mask
*   - 'cache on' method instruction
*   - 'cache off' method instruction
*   - 'cache flush' method instruction
r3的值决定了调用的是on的还是off的函数


06-09-24:
今天勉强又看了看,稍微明白的多了点,但还是很多细节不明白,对里面的内核载入地址,执行地址,解压缩地址什么的,不明白,于是里面就也搞不懂了,但还是稍微理了理过程.
注意在arch/arm/boot/Makefile中
ZTEXTADDR是zImage要被copy到哪里执行,我们的bootloader会负责把它搬过去。
ZRELADDR是指kernel要被解压缩到哪里,解压缩完成后会跳转到ZRELADDR。
Vmlinux.lds.in:   .text节中的 .start节在前,.text接后,再piggy.o,piggy.o是由TOPDIR下的vmlinux生成;  再到.got. .got.plt..data, .bss.
1,设置初始中断向量,保存体系ID和r0,进入svc模式,关闭中断
2,进入head-xscale.S部分,还属于 .start节;在当前PC值到pc+64K处取指令,确保缓存命中,在drain WB,flush I & D cache,关闭MMU和数据指令缓存;
3,正式的.text节;获得LC0中储存的信息,根据当前LC0的地址和LC0中存储的LC0地址判断是否已经经过relocate,没有,就直接把BSS节清零,否则,算出偏移,在zImage的基址,GOT节开始结束地址上都加上偏移,再根据是否支持ROM压缩,调整GOT中符号值,
4, 调用cache_on,打开缓存,由call_cache_fn实施,,注意方法,打开缓存,关闭缓存,冲掉缓存,都是通过r3索引proc_type表格中相应处理器id的条目,转到相应子程序;这里,调用__armv4_cache_on子程序,要打开cache,需先设置mmu,
5,设置MMU,主要是设置页表,在内核映象_load_addr前16K中,具体细节不明了,回到第4
4,cache_on: 抽干写缓冲,冲掉I,D,TLBs,载入页表指针,载入域存取寄存器,打开指令缓存,写缓冲,mmu,这里都是跟协处理器打交道
6, 在sp上分配空间
7,看是否会覆盖自己,不会的话,在r0中保存_load_addr,r3中保存体系id,调用解压缩内核例程,再调用call_kernle,否则在刚才6中分配的空间上调用解压缩内核例程,
8,对齐内核长度,拷贝relocate代码,冲掉缓存,调用relocate代码,经过relocate后,同样到达call_kernel  (这里细节不明了)
9,call_kernel,冲掉缓存,关闭缓存,r0置0,r1体系id,pc转到内核执行地址,应该就到了head-armv.S中去了


06-09-26:
今天再试着理解理解head。S,感觉勉强懂了点
1,        进入svc模式,关闭中断
2,        获得LC0中保存相关信息,并根据其物理地址和链接时的地址,修正GOT表,清空BSS节。这里关键理解r4,r5,r4就是所谓的ZRELADDR,是最后解压过的内核vmlinux的首址及物理地址0xA0008000(虚拟地址是0xC0008000),r5就是zImage的首址,也就是此head.S的第一条命令地址。
3,        打开cache,理解打开cache所用的方法,其中牵涉到proc_types表格的查找,关键是__setup_mm的理解,在0xA0004000处,及vmlinux首址前16k处设置页表,采取的是段映射,不要跟i386的映射机制混淆。这里采取的是最简单的1:1映射,在head-armv.S中会修改部分映射条目,加上一些参数,比如内核虚拟地址0xC0000000会被映射到0xA0000000物理地址,
4,        准备解压缩空间,sp到sp+64k,判断会不会重叠,解压到sp+64k处,并得到真正内核的长度
5,        把reloc代码拷贝到刚才解压的内涵的尾部,程序执行转到reloc
6,        由reloc把刚才解压过的内核搬到r4(ZRELADDR)处,执行r4处指令,再清除cache,关闭MMU和cache,转到arch/arm/kernel/head-armv.S中去了。
终于勉强搞懂此文件,可以进入head-armv.S了,总共耗时一星期!


07-03-10:
完全不知道当时是真搞懂还是假搞懂了,反正这两天再看时还是不懂,这次看的是2.6内核的,有两个问题,1,怎么做到位置无关的,2,怎么个解压内核过程

1答:要结合该目录下的Makefile看
Makefile,主要关注一下段:
#
# We now have a PIC decompressor implementation.  Decompressors running
# from RAM should not define ZTEXTADDR.  Decompressors running directly
# from ROM or Flash must define ZTEXTADDR (preferably via the config)
# FIXME: Previous assignment to ztextaddr-y is lost here. See SHARK
ifeq ($(CONFIG_ZBOOT_ROM),y)
ZTEXTADDR := $(CONFIG_ZBOOT_ROM_TEXT)
ZBSSADDR := $(CONFIG_ZBOOT_ROM_BSS)
else
ZTEXTADDR := 0
ZBSSADDR := ALIGN(4)  #这里我们普通的启动就是得到ZTEXTADDR为0了,ZTEXTADDR是指zImage中.text节开始的物理地址,也就是zImage的第一条指令的物理地址
endif
SEDFLAGS = s/TEXT_START/$(ZTEXTADDR)/;s/BSS_START/$(ZBSSADDR)/
#这里把链接教本中的TEXT_START换成了ZTEXTADDR,我们的例子来说就是0了,TEXT_ADDR是链接教本中引入,或者说zImage的虚拟首址
targets       := vmlinux vmlinux.lds piggy.gz piggy.o $(FONT) \
   head.o misc.o $(OBJS)
EXTRA_CFLAGS  := -fpic -fno-instrument-functions#这里说明会把zImage编译成PIC性质,及我们的zImage是position independant位置无关的
EXTRA_AFLAGS  :=
# Supply ZRELADDR, INITRD_PHYS and PARAMS_PHYS to the decompressor via
# linker symbols.  We only define initrd_phys and params_phys if the
# machine class defined the corresponding makefile variable.
LDFLAGS_vmlinux := --defsym zreladdr=$(ZRELADDR)
ifneq ($(INITRD_PHYS),)
LDFLAGS_vmlinux += --defsym initrd_phys=$(INITRD_PHYS)
endif
ifneq ($(PARAMS_PHYS),)
LDFLAGS_vmlinux += --defsym params_phys=$(PARAMS_PHYS)
endif
#这些说了INITRD_PHYS ,PARAMS_PHYS的运用

怎么实现PIC的了,主要要理解b指令和adr指令,b指令跳到给定的地址执行,这个地址是相当当前PC值的偏移量,所以zImage放在任何物理地址上,b能正常跳转的
而我们知道通常的PIC程序能做到“位置无关”,是动态连接器的功劳,能够根据运行时程序的实际地址修正GOT(地址值)&PLT(函数地址值)中的信息,这里根本没有ld.so,这个修正过程就得我们自己实施了,而这里的关键就是如下代码:(完全摘自head.S)
.text
  adr r0, LC0
  ldmia r0, {r1, r2, r3, r4, r5, r6, ip, sp}
  subs r0, r0, r1  @ calculate the delta offset
      @ if delta is zero, we are
  beq not_relocated  @ running at the address we
      @ were linked at.
...
  .type LC0, #object
LC0:  .word LC0   @ r1
  .word __bss_start  @ r2
  .word _end   @ r3
  .word zreladdr  @ r4
  .word _start   @ r5
  .word _got_start  @ r6
  .word _got_end  @ ip
  .word user_stack+4096  @ sp
LC1:  .word reloc_end - reloc_start
  .size LC0, . - LC0

这里LC0物体中相当r1保存了链接成zImage时LC0的地址值,及相对于TEXT_START的偏移量,而adr r0,LC0指令则把zImage真正运行时候LC0处的PC值,理解adr指令的作用,是将基于PC相对偏移的地址值保存到寄存器中,那么比较这个r0和LC0中保存的r1就可以知道并计算出真正运行是的首址和链接时的TEXT_START的偏差,如果zImage确实是在物理地址为TEXT_START处运行,就不用relocate而直接跳到not_relocated  处,否则要进行修正及relocate一下。

2答:
修正r4-zreladdr,Image的物理首址,r5,zImage物理首址,还有BSS节,SP等等后,清空BSS,打开cache,为了提高解压的速度,在SP后面开辟一块64K的空间,如下:
bl cache_on
  mov r1, sp   @ malloc space above stack
  add r2, sp, #0x10000 @ 64k max
注意r1指向所开辟空间的首址,r2指向空间的末址,再
/*
* Check to see if we will overwrite ourselves.
*   r4 = final kernel address
*   r5 = start of this image
*   r2 = end of malloc space (and therefore this image)
* We basically want:
*   r4 >= r2 -> OK #Image首址在所分配的空间之后
*   r4 + image length  OK#整个Image会在zImage之前
*/
  cmp r4, r2
  bhs wont_overwrite
  add r0, r4, #4096*1024 @ 4MB largest kernel size
  cmp r0, r5
  bls wont_overwrite#这里就是判断解压后的Image会会不覆写zImage
  mov r5, r2   @ decompress after malloc space  mov r0, r5
  mov r3, r7
  bl decompress_kernel#如果不会覆写zImage,就开始解压了,注意此时寄存器状态,r0和r2相等,都是那块64k空间的末址,但作用含义不同,r0是Image就会放在首址为r0处,r2是解压过程中用到的buff空间末址,而r1就是buff空间首址,r3则是bootloader传进来的体系结构ID,查看一下misc.c中decompress_kernel函数定义:
ulg
decompress_kernel(ulg output_start, ulg free_mem_ptr_p, ulg free_mem_ptr_end_p,
    int arch_id)

正好跟arm处理器传递参数的方式吻合,什么方式?四个参数以内的函数,r0传递第一个,r1传递第二个,r2传递第三个,r3传递第四个,此时明白了,Image会被解压到紧挨着所分配的64k buff空间末尾,解压完后,r0传回Image的长度值,把r0对齐后,得到Image末址r1=r5(Image首址)+r0,再把搬运工及reloc_start和reloc_end之间的代码放到r1开始处,及紧咬Image屁股,再跳到r1处就运行搬运工了,搬运工则可以把Image搬到zreladdr处,此时已不用管是否会覆写zImage了,搬完后就再跳到zreladdr,开始运行Image,这中间为了提高搬运速度也会打开cache什么的,要用到r3中储存的体系ID调用相应的打开cache函数,就不说了

..............
wont_overwrite: mov r0, r4
  mov r3, r7
  bl decompress_kernel
  b call_kernel
#如果解压时不会覆写zImage,事情就简单多了,不用什么搬运工了,可以直接解压,直接跳到zreladdr,
本站仅提供存储服务,所有内容均由用户发布,如发现有害或侵权内容,请点击举报
打开APP,阅读全文并永久保存 查看更多类似文章
猜你喜欢
类似文章
【热】打开小程序,算一算2024你的财运
linux内核启动地址,解压缩,内核参数问题探讨 - Embeded - todaygood
zImage解压过程
基于linux2.6.38.8内核zImage文件的自解压详解
UBOOT引导内核uImage问题
Android arm linux kernel启动流程(一)
链接器算法
更多类似文章 >>
生活服务
热点新闻
分享 收藏 导长图 关注 下载文章
绑定账号成功
后续可登录账号畅享VIP特权!
如果VIP功能使用有故障,
可点击这里联系客服!

联系客服