lowlevel_init.s解析 uboot中start.s与lowlevel_init.s中绝对地址与相对地址

解释说明:

我们的程序是放在Flash中的,这里面的地址我们叫做加载地址,当然是从0x0这个地址开始的。而程序中所用的标号编译时都是基于_TEXT_BASE 地址,我们称为连接或运行地址,这时,加载地址和运行地址不相同,所以要求我们在代码还没有搬移到_TEXT_BASE(0x3eff8000 )这个位置以前是不能使用这些标号的,如果直接使用这些标号,程序就飞了,只有程序运行在SDRAM中时,才可以使用这些标号,因为0x3eff8000在SDRAM中。

在看uboot的源码中,在启动阶段,有两段代码自己没有理解到位,现在记录下来。

  1. 在/cpu/arm920t/start.s中(部分):

    adr     r0, _start
     ldr      r1, _TEXT_BASE            @//0x3eff8000
     cmp  r0, r1
     blne  xxxx
    
     ...
    
  2. 在/board/…/lowlevel_init.s中

    ldr r0, =SMRDATA
    ldr r1, _TEXT_BASE
    sub r0, r0, r1

     ...
    

_TEXT_BASE是定义的一个字变量0x3eff8000,存放代码在内存中重定位的首地址。

在1中,是比较_start的地址与0x3eff800地址是否相等。

在2中,是求SMRDATA标签的地址与0x3eff800的差值,也就是0x00000000的相对位置。

因为在代码连接的时候,整个代码都已经确定了被定向到首地址0x3eff8000位置,并且_start又是入口地址。所以按道理来说,_start的地址值应该是0x3eff8000,并且SMRDATA的地址应该是0x3eff8000+offset的值。但是事实却不是。

区别在于adr 与 ldr的寻址方式。

adr的寻址方式是: 在当前pc值加减一定小范围的值(具体多少不记得了),所以它得到的地址,是与程序运行的实际环境相关的。在代码1中,因为这是从nand启动,所以nand的前4K放在了arm的内置4Ksdram里面运行了。所以这时PC的值只可能在0x0000-0x1000这个地址空间了。所以代码1中的r0的值不会超过0x1000.

ldr的寻址方式是:取label中的绝对地址。这个绝对地址在程序被连接(link)的时候就已经确定了的。证据: 因为ldr是伪指令(看arm 指令手册解释),如果地址是无法用循环右移得到的,编译时会在段尾(在没有.ltrog的时候)自动补上一个字存放这个地址,然后ldr换位pc加偏移寻址(书上有介绍)。所以,这个地址其实在编译连接时就确定了。所以代码2中取"=SMRDATA"的绝对地址,应该是0x3eff8000加上偏移的值。

所以代码2的意思是,这个时候因为这段代码运行在arm的内置4Ksdram里面,首地址是重0x00000000开始,所以必须要将=SMRDATA的绝对值减去重定向的首地址,才能到的相对于0x0地址的位置。

看了一下lowlevel_init.s里面的代码,发现最难懂的地方当属这里了:

ldr     r0, =SMRDATA
ldr	r1, _TEXT_BASE
sub	r0, r0, r1
ldr	r1, =BWSCON	/* Bus Width Status Controller */
add     r2, r0, #13*4

第一条语句是获取SMRDATA的地址,SMRDATA的定义在此文件的末尾。第二条语句是获取_TEXT_BASE的值,在start.s中,这个值被定义为TEXT_BASE。而TEXT_BASE在目标板目录100ask24x0下的config.mk中定义,然后在根目录的config.mk中的以下语句中被包含进来。

CPPFLAGS := $(DBGFLAGS) $(OPTFLAGS) $(RELFLAGS)  \
	-D__KERNEL__ -DTEXT_BASE=$(TEXT_BASE)		\

然后查看u-boot.lds文件,就知道整个u-boot最后都被编译到TEXT_BASE这个地址之后,包括代码段、数据段等内容。所以SMRDATA也必定是在这个地址之后。而第三句话就是计算SMRDATA相对于TEXT_BASE的偏移量。接下来的两句就是把控制寄存器的地址赋给r1,将SMRDATA的结束地址赋给r2。

计算好地址之后,后面的代码是一个循环,按照预先的设计设定每个BANK的属性。

计算偏移量涉及到芯片的启动过程。现以上面的SDRDATA为例进行说明。

首先使用objdump –d lowlevel_init.o进行反汇编,查看这一部分代码被预编译为以下内容。

lowlevel_init.o:     file format elf32-littlearm

Disassembly of section .text:

00000000 <_TEXT_BASE>:
   0:	33f80000 	.word	0x33f80000

00000004 :
   4:	e59f0020 	ldr	r0, [pc, #32]	; 2c 
   8:	e51f1010 	ldr	r1, [pc, #-16]	; 0 <_TEXT_BASE>
   c:	e0400001 	sub	r0, r0, r1
  10:	e3a01312 	mov	r1, #1207959552	; 0x48000000
  14:	e2802034 	add	r2, r0, #52	; 0x34
  18:	e4903004 	ldr	r3, [r0], #4
  1c:	e4813004 	str	r3, [r1], #4
  20:	e1520000 	cmp	r2, r0
  24:	1afffffb 	bne	18 
  28:	e1a0f00e 	mov	pc, lr
  2c:	00000030 	.word	0x00000030

00000030 :
  30:	2211d120 	.word	0x2211d120
  34:	00000700 	.word	0x00000700
  38:	00000700 	.word	0x00000700
  3c:	00000700 	.word	0x00000700
  40:	00001f4c 	.word	0x00001f4c
  44:	00000700 	.word	0x00000700
  48:	00000700 	.word	0x00000700
  4c:	00018005 	.word	0x00018005
  50:	00018005 	.word	0x00018005
  54:	008e0459 	.word	0x008e0459
  58:	00000032 	.word	0x00000032
  5c:	00000030 	.word	0x00000030
  60:	00000030 	.word	0x00000030

可知SMRDATA的地址就是当前PC值偏移32,在实际运行的时候就是lowlevel的入口地址加32。

再在根目录下使用objdump –d u-boot就可以查到最终的生成文件中lowlevel_init的入口地址了,由于在链接的时候指定了代码段的首地址为0x33f80000,所以lowlevel的入口地址是代码段的首址加上一个偏移量,这样就可以算出SMRDATA的地址就是0x33f80000+lowlevel_init入口的偏移量+SMRDATA本身的段内偏移量。查看uboot根目录底下的System.map文件可知lowlevel_init的地址是0x33f80bf0,SMRDATA的地址是0x33f80c1c。

弄清楚了这些地址之后再回到开头的问题,arm9在上电的时候如果硬件跳线选择的是从NAND FLASH启动的话,会将NAND FLASH开头的4K内容拷贝到一个叫“stepping stone”的空间中,其首地址是0x0。假设执行到了lowlevel_init里面,还是按照原来的地址去寻找SMRDATA的话,那肯定就找不到了,因为这个时候的代码是运行在0x0~0x00001000这个地址内的,应该去找0x00000c1c这个地址才是正确的。

你可能感兴趣的:(lowlevel_init.s解析 uboot中start.s与lowlevel_init.s中绝对地址与相对地址)