start.S:
1.设置cpu模式为svc32
2.初始化看门狗
3.设置中断(根据手册)
4.设置时钟CLKDIV
5.Cpu_critical_setting, memory timing, flush cache
6.重新定位u-boot:
这里需要了解lds文件的作用,以及ldr,adr的区别,源码中这里是将代码从flash中copy到Ram中,如果移植到mini2440上的话,那这里就需要处理把u-boot copy到ram中。
7.留出需要的内存(CFG_MALLOC_LEN, GBL_DATA_SIZE, IRQ+FIQ),定义用户栈区
8.清理bss区
9.进入第二阶段
汇编基础:
ELF格式相关 参照 http://learn.akae.cn/media/ch18s05.html
汇编条令 参照:http://learn.akae.cn/media/ch18s01.html
代码解释:
.globl _start
/*_start为程序的入口点, 可以在连接脚本中修改Entry标签(即可以改用其他标签),_start是个全局变量,链接器会给它指定运行/加载地址。程序的第一条指定就存储在_start变量里*/
_start: b reset
/*上面说到_start这个变量(地址)里存放的就是第一条指令,所以程序启动即执行reset,实际就是跳转到reset这个标签指定的地址,这里的b指令执行跳转时需要的偏移量是编译器根据当前和目标计算出来的,是个相对值,所以也是相对跳转*/
ldr pc, _undefined_instruction
ldr pc, _software_interrupt
ldr pc, _prefetch_abort
ldr pc, _data_abort
ldr pc, _not_used
ldr pc, _irq
ldr pc, _fiq
/*上面这些是跳转到相应中断处理的命令,把各中断标签代表的地址中存放的值传给pc,cpu下条指令就会执行pc所指向地址的命令,也就是各中断标签代表的地址中所存放地址所指向的的命令。这是个绝对跳转,因为pc直接指向需要执行指令的存放的地址*/
_undefined_instruction: .word undefined_instruction
_software_interrupt: .word software_interrupt
_prefetch_abort: .word prefetch_abort
_data_abort: .word data_abort
_not_used: .word not_used
_irq: .word irq
_fiq: .word fiq
/*把各处理中断的程序序列地址赋给各标签,这里讨论下ARM汇编中的.word的用法。
官方解释:
.word
Syntax: .word expressions
This directive expects zero or more expressions, of any section, separated by commas. For each expression, as emits a 16-bit number for this target
就是把expression以16bit的方式存放在当前位置, 以上面的代码为例,_undefined_instruction: .word undefined_instruction, 是把undefined_instruction标号地址存放在_undefined_instruction标号地址处, .word就是赤裸裸的引用*/
.balignl 16,0xdeadbeef
/*强制对齐*/
_TEXT_BASE:
.word TEXT_BASE
/*TEXT_BASE(config.mk定义的一个常量)存放在_TEXT_BASE标号地址处*/
.globl _armboot_start
_armboot_start:
.word _start
/*定义全局变量_armboot_start,并把_start的地址赋给_armboot_start*/
.globl _bss_start
_bss_start:
.word __bss_start
.globl _bss_end
_bss_end:
.word _end
/*
* These are defined in the board-specific linker script.
*/
#ifdef CONFIG_USE_IRQ
/* IRQ stack memory (calculated at run-time) */
.globl IRQ_STACK_START
IRQ_STACK_START:
.word 0x0badc0de
/* IRQ stack memory (calculated at run-time) */
.globl FIQ_STACK_START
FIQ_STACK_START:
.word 0x0badc0de
#endif
reset:
/*
* set the cpu to SVC32 mode
*/
mrs r0,cpsr
bic r0,r0,#0x1f
orr r0,r0,#0xd3
msr cpsr,r0
****************
#ifndef CONFIG_SKIP_RELOCATE_UBOOT
relocate: /* relocate U-Boot to RAM */
adr r0, _start /* r0 <- current position of code */
ldr r1, _TEXT_BASE /* test if we run from flash or RAM */
cmp r0, r1 /* don't reloc during debug */
beq stack_setup
ldr r2, _armboot_start
ldr r3, _bss_start
sub r2, r3, r2 /* r2 <- size of armboot */
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
/*上面的代码块实现了u-boot把自己从flash cp到RAM中。
adr r0, _start /* r0 <- current position of code */
adr 是伪指令,是把_start的地址放到r0中,但是需要注意,这里获取_start的地址是汇编器通过计算当前PC到_start的偏移量得到的,我们可以看下u-boot的反汇编
33f80094 <relocate>:
33f80094: e24f009c sub r0, pc, #156 ; 0x9c
可以看出上面的伪指令在汇编器处理之后是一条sub语句,是用当前PC值减去156,以为ARM是流水线工作,预取指令,所以执行到这条命令的时候PC值跟程序运行区域有关系,如果u-boot此刻运行在ARM4kflash中,那么PC=0x33f80094 + 8 = 33f8009c,如果运行在RAM,那么PC=0x0 + 8.所以R0就是33f80000(运行在flash中)或者0(运行在RAM中),即_start的地址。*/
ldr r1, _TEXT_BASE /* test if we run from flash or RAM */
/*ldr 也是一条伪指令,是把_TEXT_BASE的值,最开始的代码段已经给_TEXT_BASE赋值为TEXT_BASE(/board/boadname/config.mk中赋值为33f80000),所以r1=0x33f80000
u-boot反汇编看此条指令为
33f80098: e51f1060 ldr r1, [pc, #-96] ; 33f80040 <_TEXT_BASE>
以此可以看出,可以对比r0和r1的值来确定u-boot现在运行的环境(RAM/Flash)/*
ldr r2, _armboot_start
ldr r3, _bss_start
sub r2, r3, r2 /* r2 <- size of armboot */
add r2, r0, r2 /* r2 <- source end address */
/*ldr 是个比mov更好用的数据传递指令,_armboot_start, _bass_start都是对_start, bss_start的引用,这样就可以把想要的地址传递到r2, r3中了。想要得到u-boot映像的大小,就需要知道起始和截止地址,_start和bss_start的地址在连接后都是确定的,所以r3-r2就是size of u-boot*/
ldr pc, _start_armboot
_start_armboot: .word start_armboot
/*跳转到第二阶段继续执行函数,此时_start_armboot存放的是全局变量start_armboot的地址,是链接后的地址,也就是运行地址,指向RAM。从而实现了跳到RAM继续执行后面的程序*/