转自:http://zqwt.012.blog.163.com/blog/static/12044684201332663357278/
copy_loop:
ldmia r0!, {r3-r10}
/* copy from source address [r0] _start,即uboot的入口地址 */
stmia r1!, {r3-r10} /* copy to target address [r1](_TEXT_BASE) */
cmp r0, r2 /* until source end address [r2] */
ble copy_loop //Branch if Less than or Equal
/* 在这个循环中,r0的值是不断增大的,直到大于r2,r2也就是BSS段的起始地址(即uboot代码存放的结束地址)*/
#endif /* CONFIG_SKIP_RELOCATE_UBOOT */
l 设置堆栈(设置uboot在SDRAM里的基地址(即加载地址)_TEXT_BASE = 0x33F80000)
这里需要注意的是堆栈在代码段的下方!宏CFG_MALLOC_LEN和CFG_GBL_DATA_SIZE在目标板的头文件中定义u-boot\include\configs\smdk2410.h,可以按下面这张图来理解:
stack_setup:
ldr r0, _TEXT_BASE
/* upper 128 KiB: relocated uboot 这句话意思是,_TEXT_BASE上面是128 KiB重定位之后的u-boot */
sub r0, r0, #CFG_MALLOC_LEN
/* malloc area _TEXT_BASE向下是内存分配空间 */
sub r0, r0, #CFG_GBL_DATA_SIZE
/* bdinfo 然后是bdinfo结构体地址空间 */
#ifdef CONFIG_USE_IRQ
sub r0, r0, #(CONFIG_STACKSIZE_IRQ+CONFIG_STACKSIZE_FIQ)
#endif
sub sp, r0, #12
/* leave 3 words for abort-stack 给堆栈异常留3个字的空间 */
l 这里是所谓BSS段清零
BSS(Block Started by Symbol)段是可执行文件中的一种数据段。通常ARM编译器生成的可执行文件由两部分数据组成,分别是代码段和数据段。代码段又分为可执行代码段(text)和只读数据段(rodata);数据段又分为初始化数据段(data)和未初始化数据段(bss)。
clear_bss:
ldr r0, _bss_start /* 找到bss段的开始地址 */
ldr r1, _bss_end /* stop here */
mov r2, #0x00000000 /* clear */
clbss_l:
str r2, [r0] /* clear loop... 用一个循环,做清零工作 */
add r0, r0, #4
cmp r0, r1
ble clbss_l
… …
l 跳转到start_armboot函数入口(去sdram执行代码),_start_armboot保存函数入口指针(其实,可以在这一句之前添加vivi的copy_myself函数,通过c语言来将nandflash的前4k代码搬运到sdram中)
ldr pc, _start_armboot
_start_armboot: .word start_armboot
/* start_armboot()在lib_arm\board.c中定义,
* 它类似于Linux内核的start_kernel(),
* 它们都是一种系统初始化的接口函数:
* 在start_kernel()中集中完成了内核几乎所有资源的初始化,
* 包括CPU相关的资源和外设接口等;
* 而在start_armboot()中也要完成一些初始化工作。
*/
/*
* CPU_init_critical registers
* CPU_init_critical临界区寄存器
* setup important registers
* setup memory timing
* 此处要设置一些重要的寄存器,并进行内存测试。*/
*************************************************************************
*/
#ifndef CONFIG_SKIP_LOWLEVEL_INIT
l 设置CP15寄存器
CP15寄存器是系统控制协处理器,用于连接在内存中的页表描述符,此外还用于决定对MMU的操作。设置CP15寄存器的目的是失效Icache(指令cache)和Dcache(数据cache),然后禁止MMU和cache。
cpu_init_crit:
/*
* flush v4 I/D caches
使I/D caches失效
*/
mov r0, #0
mcr p15, 0, r0, c7, c7, 0 /* flush v3/v4 cache */
mcr p15, 0, r0, c8, c7, 0 /* flush v4 TLB */
/*
* disable MMU stuff and caches
*/
mrc p15, 0, r0, c1, c0, 0
bic r0, r0, #0x00002300 @ clear bits 13, 9:8 (--V- --RS)
bic r0, r0, #0x00000087 @ clear bits 7, 2:0 (B--- -CAM)
orr r0, r0, #0x00000002 @ set bit 2 (A) Align
orr r0, r0, #0x00001000 @ set bit 12 (I) I-Cache
mcr p15, 0, r0, c1, c0, 0
l 想进一步了解协处理器CP15,可以点击链接:
http://zqwt.012.blog.163.com/blog/static/120446842010473171279/
/*
* before relocating, we have to setup RAM timing
在搬运代码之前,我们必须设置好sram的时钟
* because memory timing is board-dependend, you will
因为sram时序是依赖于具体的开发板的,你将会
* find a lowlevel_init.S in your board directory.
在自己的开发板目录board目录下发现一个
lowlevel_init.S文件。实际上,这个文件所做的工作主要就是内存的初始化。
*/
l 配置内存控制寄存器(第三个要修改的地方)
因为内存控制寄存器是和开发板密切相关的,寄存器的具体值由开发商或者硬件工程师提供,如果对总线周期和外围芯片非常熟悉,也可以自己定义。在uboot中的设置文件是board/diamond2410/lowlevel_init.S,该文件包含lowlevel_init程序段,用于内存控制配置。
l lr为链接寄存器,看了这段文字之后,就很容易理解了:
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
相对寻址
与基址变址寻址方式相类似,相对寻址以程序计数器PC的当前值为基地址,指令中的地址标号作为偏移量,将两者相加之后得到操作数的有效地址。以下程序段完成子程序的调用和返回,跳转指令BL采用了相对寻址方式:
l BL NEXT @跳转到子程序,到NEXT处执行
@此时LR自动接收来自R15(PC)的拷贝,即程序当前运行的地址,以便之后从子程序返回
……
NEXT
……
MOV PC,LR @从子程序返回
注意:
l pc: 总是包含下一个要被执行的指令的位置。
l lr: 当程序Branch和Link(BL)的时候,总是接受一个来自R15(PC)的拷贝
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@此时lr里应该是”bl cpu_init_crit”这条指令后面紧接着的一条指令的地址
mov ip, lr
bl lowlevel_init /*此时lr里应该是” bl lowlevel_init(start.S中)”这条指令后面紧接着的一条指令的地址*/
mov lr, ip /*此时lr里应该是”bl cpu_init_crit”这条指令后面紧接着的一条指令的地址*/
mov pc, lr
@此时,程序从”bl cpu_init_crit”这条指令后面紧接着的一条指令开始执行
#endif /* CONFIG_SKIP_LOWLEVEL_INIT */
/* Interrupt handling 下面的代码为中断处理 */
@ IRQ stack frame.
#define S_FRAME_SIZE 72
#define S_OLD_R0 68
#define S_PSR 64
#define S_PC 60
#define S_LR 56
#define S_SP 52
#define S_IP 48
#define S_FP 44
#define S_R10 40
#define S_R9 36
#define S_R8 32
#define S_R7 28
#define S_R6 24
#define S_R5 20
#define S_R4 16
#define S_R3 12
#define S_R2 8
#define S_R1 4
#define S_R0 0
#define MODE_SVC 0x13
#define I_BIT 0x80
/*
* use bad_save_user_regs for abort/prefetch/undef/swi ...
* use irq_save_user_regs / irq_restore_user_regs for IRQ/FIQ handling
*/
.macro bad_save_user_regs
sub sp, sp, #S_FRAME_SIZE
stmia sp, {r0 - r12} @ Calling r0-r12
ldr r2, _armboot_start
sub r2, r2, #(CONFIG_STACKSIZE+CFG_MALLOC_LEN)
sub r2, r2, #(CFG_GBL_DATA_SIZE+8) @ set base 2 words into abort stack
ldmia r2, {r2 - r3} @ get pc, cpsr
add r0, sp, #S_FRAME_SIZE @ restore sp_SVC
add r5, sp, #S_SP
mov r1, lr
stmia r5, {r0 - r3} @ save sp_SVC, lr_SVC, pc, cpsr
mov r0, sp
.endm
.macro irq_save_user_regs
sub sp, sp, #S_FRAME_SIZE
stmia sp, {r0 - r12} @ Calling r0-r12
add r8, sp, #S_PC
stmdb r8, {sp, lr}^ @ Calling SP, LR
str lr, [r8, #0] @ Save calling PC
mrs r6, spsr
str r6, [r8, #4] @ Save CPSR
str r0, [r8, #8] @ Save OLD_R0
mov r0, sp
.endm
.macro irq_restore_user_regs
ldmia sp, {r0 - lr}^ @ Calling r0 - lr
mov r0, r0
ldr lr, [sp, #S_PC] @ Get PC
add sp, sp, #S_FRAME_SIZE
subs pc, lr, #4 @ return & move spsr_svc into cpsr
.endm
.macro get_bad_stack
ldr r13, _armboot_start @ setup our mode stack
sub r13, r13, #(CONFIG_STACKSIZE+CFG_MALLOC_LEN)
sub r13, r13, #(CFG_GBL_DATA_SIZE+8) @ reserved a couple spots in abort stack
str lr, [r13] @ save caller lr / spsr
mrs lr, spsr
str lr, [r13, #4]
mov r13, #MODE_SVC @ prepare SVC-Mode
@ msr spsr_c, r13
msr spsr, r13
mov lr, pc
movs pc, lr
.endm
.macro get_irq_stack @ setup IRQ stack
ldr sp, IRQ_STACK_START
.endm
.macro get_fiq_stack @ setup FIQ stack
ldr sp, FIQ_STACK_START
.endm
/*
* exception handlers 异常处理程序
*/
.align 5
undefined_instruction:
get_bad_stack
bad_save_user_regs
bl do_undefined_instruction
.align 5
software_interrupt:
get_bad_stack
bad_save_user_regs
bl do_software_interrupt
.align 5
prefetch_abort:
get_bad_stack
bad_save_user_regs
bl do_prefetch_abort
.align 5
data_abort:
get_bad_stack
bad_save_user_regs
bl do_data_abort
.align 5
not_used:
get_bad_stack
bad_save_user_regs
bl do_not_used
#ifdef CONFIG_USE_IRQ
.align 5
irq:
get_irq_stack
irq_save_user_regs
bl do_irq
irq_restore_user_regs
.align 5
fiq:
get_fiq_stack
/* someone ought to write a more effiction fiq_save_user_regs */
irq_save_user_regs
bl do_fiq
irq_restore_user_regs
#else
.align 5
irq:
get_bad_stack
bad_save_user_regs
bl do_irq
.align 5
fiq:
get_bad_stack
bad_save_user_regs
bl do_fiq
#endif