/* Set up the stack */ stack_setup: ldr r0, _TEXT_BASE /* upper 128 KiB: relocated uboot */ sub r0, r0, #CFG_MALLOC_LEN /* malloc area */ sub r0, r0, #CFG_GBL_DATA_SIZE /* bdinfo */
此句含义是,把地址为_TEXT_BASE的内存中的内容给r0,即,将所有的中断都mask了。 而查看前面的相关部分的代码,即: _TEXT_BASE: .word TEXT_BASE 得知,地址为_TEXT_BASE的内存中的内容,就是
中的: TEXT_BASE = 0x33D00000 所以,此处即: r0 = TEXT_BASE = 0x33D00000 而关于sub指令:
所以对应含义为: r0 = r0 - #CFG_MALLOC_LEN r0 = r0 - #CFG_GBL_DATA_SIZE 其中,对应的两个宏的值是:
中: #define CONFIG_64MB_Nand 0 //添加了对64MB Nand Flash支持 /* * Size of malloc() pool */ #define CFG_MALLOC_LEN (CFG_ENV_SIZE + 128*1024) #define CFG_GBL_DATA_SIZE 128 /* size in bytes reserved for initial data */ #if(CONFIG_64MB_Nand == 1) #define CFG_ENV_SIZE 0xc000 /* Total Size of Environment Sector */ #else #define CFG_ENV_SIZE 0x20000 /* Total Size of Environment Sector */ #endif 所以,从源码中的宏定义中可以看出, CFG_MALLOC_LEN = (CFG_ENV_SIZE + 128*1024) = 0x20000 + 128*1024 = 0x40000 = 256*1024 = 256KB CFG_GBL_DATA_SIZE = 128 所以,此三行的含义就是算出r0的值: r0 = (r0 - #CFG_MALLOC_LEN) - #CFG_GBL_DATA_SIZE = r0 - 0x40000 – 128 = r0 – 0x40080 = 33CBFF80 |
#ifdef CONFIG_USE_IRQ sub r0, r0, #(CONFIG_STACKSIZE_IRQ+CONFIG_STACKSIZE_FIQ) #endif sub sp, r0, #12 /* leave 3 words for abort-stack */
如果定义了CONFIG_USE_IRQ,即如果使用中断的话,那么再把r0的值减去IRQ和FIQ的堆栈的值, 而对应的宏的值也是在
中: /*------------------------------------------------------------------- * Stack sizes * * The stack sizes are set up in start.S using the settings below */ #define CONFIG_STACKSIZE (128*1024) /* regular stack */ #ifdef CONFIG_USE_IRQ #define CONFIG_STACKSIZE_IRQ (4*1024) /* IRQ stack */ #define CONFIG_STACKSIZE_FIQ (4*1024) /* FIQ stack */ #endif 所以,此时r0的值就是: #ifdef CONFIG_USE_IRQ r0 = r0 - #(CONFIG_STACKSIZE_IRQ+CONFIG_STACKSIZE_FIQ) = r0 – (4*1024 + 4*1024) = r0 – 8*1024 = 33CBFF80 – 8*1024 = 33CBDF80 #endif |
|
最后,再减去终止异常所用到的堆栈大小,即12个字节。 现在r0的值为: #ifdef CONFIG_USE_IRQ r0 = r0 – 12 = 33CBDF80 - 12 = 33CBDF74 #else r0 = r0 – 12 = 33CBFF80 - 12 = 33CBFF74 #endif 然后将r0的值赋值给sp,即堆栈指针。 关于: sp代表stack pointer,堆栈指针; 和后面要提到的ip寄存器: ip代表instruction pointer,指令指针。 更多详情参见下面的解释。 关于ARM的寄存器的别名和相关的APCS,参见本文后面的内容:第 3.5 节 “AMR寄存器的别名 + APCS” |
bl clock_init
在上面,经过计算,算出了堆栈的地址,然后赋值给了sp,此处,接着才去调用函数clock_init去初始化时钟。 其中此函数是在C文件:
中: void clock_init(void) { ...设置系统时钟clock的相关代码... } 看到这里,让我想起,关于其他人的关于此start.S代码解释中说到的,此处是先去设置好堆栈,即初始化sp指针,然后才去调用C语言的函数clock_init的。 而我们可以看到,前面那行代码: #ifndef CONFIG_SKIP_LOWLEVEL_INIT bl cpu_init_crit #endif 就不需要先设置好堆栈,再去进行函数调用。 其中cpu_init_crit对应的代码也在start.S中(详见后面对应部分的代码),是用汇编实现的。 而对于C语言,为何就需要堆栈,而汇编却不需要堆栈的原因,请参见本文后面的内容:第 3.6 节 “为何C语言(的函数调用)需要堆栈,而汇编语言却不需要堆栈” |
#ifndef CONFIG_SKIP_RELOCATE_UBOOT relocate: /* relocate U-Boot to RAM */ adr r0, _start /* r0 <- current position of code */
adr指令的语法和含义:
所以,上述: adr r0, _start 的意思其实很简单,就是将_start的地址赋值给r0.但是具体实现的方式就有点复杂了,对于用adr指令实现的话,说明_start这个地址,相对当前PC的偏移,应该是很小的,意思就是向前一段后者向后一段去找,肯定能找到_start这个标号地址的,此处,自己通过看代码也可以看到_start,就是在当前指令的前面,距离很近,编译后,对应汇编代码,也可以猜得出,应该是上面所说的,用sub来实现,即当前PC减去某个值,得到_start的值, 参照前面介绍的内容,去: arm-inux-objdump –d u-boot > dump_u-boot.txt 然后打开dump_u-boot.txt,可以找到对应的汇编代码,如下: 33d00000 <_start>: 33d00000: ea000014 b 33d00058 <reset> 。。。 33d000a4 <relocate>: 33d000a4: e24f00ac sub r0, pc, #172 ; 0xac 可以看到,这个相对当前PC的距离是0xac=172,细心的读者可以看到,那条指令的地址减去0xac,却并不等于_start的值,即 33d000a4 - 33d00000 = 0xa4 != 0xac 而0xac – 0xa4 = 8, 那是因为,由于ARM920T的五级流水线的缘故导致指令执行那一时刻的PC的值等于该条指令PC的值加上8,即 sub r0, pc, #172中的PC的值是 sub r0, pc, #172 指令地址:33d000a4,再加上8,即33d000a4+8 = 33d000ac, 所以,33d000ac – 0xac,才等于我们看到的33d00000,才是_start的地址。 这个由于流水线导致的PC的值和当前指令地址不同的现象,就是我们常说的,ARM中,PC=PC+8。 对于为何是PC=PC+8,请参见后面的内容:第 3.4 节 “为何ARM7中PC=PC+8” 对于此处为何不直接用mov指令,却使用adr指令,请参见后面内容:第 3.7 节 “关于为何不直接用mov指令,而非要用adr伪指令” 对于mov指令的操作数的取值范围,请参见后面内容:第 3.8 节 “mov指令的操作数的取值范围到底是多少” |
adr r0, _start
的伪代码,被翻译成实际汇编代码为:
33d000a4: e24f00ac sub r0, pc, #172 ; 0xac
其含义就是,通过计算PC+8-172 ⇒ _start的地址,
而_start的地址,即相对代码段的0地址,是这个地址在运行时刻的值,而当ARM920T加电启动后,,此处是从Nor Flash启动,对应的代码,也是在Nor Flash中,对应的物理地址是0x0,所以,此时_start的值就是0,而不是0x33d00000。
所以,此时:
r0 = 0x0
ldr r1, _TEXT_BASE /* test if we run from flash or RAM */ cmp r0, r1 /* don't reloc during debug */ beq clear_bss
这里的_TEXT_BASE的含义,前面已经说过了,那就是: _TEXT_BASE: .word TEXT_BASE 得知,地址为_TEXT_BASE的内存中的内容,就是
中的: TEXT_BASE = 0x33D00000 所以,此处就是 r1 = 0x33D00000 |
|
含义很简单,就是比较r0和r1。而 r0 = 0x0 r1 = 0x33D00000 所以不相等 |
|
因此beq发现两者不相等,就不会去跳转到clear_bss,不会去执行对应的将bss段清零的动作了。 |
ldr r2, _armboot_start ldr r3, _bss_start sub r2, r3, r2 /* r2 <- size of armboot */
这两行代码意思也很清楚,分别装载_armboot_start和_bss_start地址中的值,赋值给r2和r3 而_armboot_start和_bss_start的值,前面都已经提到过了,就是: .globl _armboot_start _armboot_start: .word _start .globl _bss_start _bss_start: .word __bss_start _TEXT_BASE: .word TEXT_BASE 而其中的_start,是我们uboot的代码的最开始的位置,而__bss_start的值,是在
中的: SECTIONS { . = 0x00000000; . = ALIGN(4); .text : ... . = ALIGN(4); .rodata : { *(.rodata) } . = ALIGN(4); .data : { *(.data) } ... . = ALIGN(4); __bss_start = .; .bss : { *(.bss) } _end = .; } 所以,可以看出,__bss_start的位置,是bss的start开始位置,同时也是text+rodata+data的结束位置,即代码段,只读数据和已初始化的可写的数据的最末尾的位置。 其实我们也可以通过前面的方法,objdump出来,看到对应的值: 33d00048 <_bss_start>: 33d00048: 33d339d4 .word 0x33d339d4 是0x33d339d4。
|
||||
此处的意思就很清楚了,就是r2 = r3-r2,计算出 text + rodata + data 的大小,即整个需要载入的数据量是多少,用于下面的函数去拷贝这么多的数据到对应的内存的位置。 这里的实际的值是 r2 = r3 –r2 = 0x33d339d4 - 0x33d00000 = 0x000339d4
|
#if 1 bl CopyCode2Ram /* r0: source, r1: dest, r2: size */ #else add r2, r0, r2 /* r2 <- source end address */ copy_loop: ldmia r0!, {r3-r10} /* copy from source address [r0] */ stmia r1!, {r3-r10} /* copy to target address [r1] */ cmp r0, r2 /* until source end addreee [r2] */ ble copy_loop #endif #endif /* CONFIG_SKIP_RELOCATE_UBOOT */
此处,代码很简单,只是注释掉了原先的那些代码,而单纯的只是去调用CopyCode2Ram这个函数。 CopyCode2Ram函数,前面也提到过了,是在:
中: int CopyCode2Ram(unsigned long start_addr, unsigned char *buf, int size) { unsigned int *pdwDest; unsigned int *pdwSrc; int i; if (bBootFrmNORFlash()) { pdwDest = (unsigned int *)buf; pdwSrc = (unsigned int *)start_addr; /* 从 NOR Flash启动 */ for (i = 0; i < size / 4; i++) { pdwDest[i] = pdwSrc[i]; } return 0; } else { /* 初始化NAND Flash */ nand_init_ll(); /* 从 NAND Flash启动 */ if (NF_ReadID() == 0x76 ) nand_read_ll(buf, start_addr, (size + NAND_BLOCK_MASK)&~(NAND_BLOCK_MASK)); else nand_read_ll_lp(buf, start_addr, (size + NAND_BLOCK_MASK_LP)&~(NAND_BLOCK_MASK_LP)); return 0; } } 可以看到,其有三个参数,start_addr,*buf和size,这三个参数,分别正好对应着我们刚才所总结的r0,r1和r2. 这些寄存器和参数的对应关系,也是APSC中定义的:
上面说的a1-a4,就是寄存器r0-r3。 而CopyCode2Ram函数的逻辑也很清晰,就是先去判断是从Nor Flash启动还是从Nand Flash启动,然后决定从哪里拷贝所需要的代码到对应的目标地址中。 |