文章详情

  • 游戏榜单
  • 软件榜单
关闭导航
热搜榜
热门下载
热门标签
php爱好者> php文档>arch/arm/boot/compressed/head.S初试(已试完)

arch/arm/boot/compressed/head.S初试(已试完)

时间:2006-09-21  来源:augustusqing

06-09-21:

现在虽然确定了以后要走的路线,但这条路很不好走啊,昨天一天就知道了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 <= r5 -> 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,

 

 

 

 

 

相关阅读 更多 +
排行榜 更多 +
极简城市

极简城市

音乐节奏 下载
可可星动营

可可星动营

休闲益智 下载
疯狂投掷达人

疯狂投掷达人

休闲益智 下载