http://blog.csdn.net/yongbudl2012/article/details/28614305
本打算直接移植uboot来着,但想了想,还是把uboot整个整理一下吧,集网上资料之大全,好好分析下,慢慢编辑,争取弄细些,采用TQ2440的uboot,是因为自己的开发板就是天嵌的。方便以后查阅。
分析uboot代码应该从start.s开始,从名称就可以看出来,start,开始的地方,但为什么从start.s开始呢,这是因为u-boot.lds中有这样的一段代码:
.......
.......
ENTRY(_start)
.......
cpu/arm920t/start.o (.text)
.......
这就决定了start为整个uboot的入口地址,这个_start,就是start.s开始处的
.globl _start
_start: b reset
下面,先分析start.s,以后再看看u-boot.lds.
#include <config.h>
#include <version.h>
.globl _start
_start: b reset
*ldr用法:
*1,LDR Rd,[Rn]
*2, LDR Rd,[Rn,Flexoffset]
*3, LDR Rd,[Rn],Flexoffset
*4, LDR Rd,label
*ldr只能在当前PC的4KB范围内跳转
*_undefined_instruction、_software_interrupt等都是标号(label),就是把标号地址中的内容赋给pc指针。
*还有一种用法:
*#define WTCON (*(volatile unsigned *)0x53000000)
*ldr r1,=WTCON,则WTCON是一个变量,并不是标号
*/
ldr pc, _undefined_instruction //未定义指令,arm处理器遇到不能处理的指令时会发生该异常,
//可以通过软件仿真扩展ARM或Thumb指令集。
ldr pc, _software_interrupt //该异常由软件中断指令(SWI)产生,用于进入管理模式,常用于
//请求执行特定的管理工能
ldr pc, _prefetch_abort //指令预取中止
ldr pc, _data_abort //数据访问中止
ldr pc, _not_used //没有使用
ldr pc, _irq //外部中断请求
ldr pc, _fiq //快速中断请求
_undefined_instruction: .word undefined_instruction
//.word的含义是,在_undefined_instruction这个标号(就是在地址0x0000,0004)处放置一个 word型的值,
//即放置undefined_instruction这个值,而 undefined_instruction 就是一个地址值( word 型,32位),
//也就是说地址0x0000,0004中存放的内容为undefined_instruction这个值,这样,
//就可以用ldr pc,_undefined_instruction,
//将_undefined_instruction地址(即0x0000,0004)中的内容装入pc中,跳转到undefined_instruction处,
//执行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
.balignl 16,0xdeadbeef
//在以当前地址开始,在地址为16的倍数的位置的前面填入四个字节内容为0xdeadbeef,.balign的用法见
_TEXT_BASE:
.word TEXT_BASE
//TEXT_BASE在board\EmbedSky\config.mk中定义,定义了代码在运行时所在的内存地址
.globl _armboot_start
_armboot_start:
.word _start
// 声明_bss_start并用__bss_start来初始化,其中__bss_start定义在与板相关的u-boot.lds中。
// _bss_start保存的是__bss_start这个标号所在的地址, 是程序链接时被链接器所确定的值,这里涉及到当前
//代码所在
// 的地址不是编译时的地址的情况, 这里直接取得该标号对应的地址, 不受编译时
// 地址的影响. _bss_end也是同样的道理.
//未初始化数据区。亦称 BSS 区(uninitialized data segment),存入的是全局未初始化
//变量。BSS 这个叫法是根据一个早期的汇编运算符而来,这个汇编运算符标志着一个块的开
//始。BSS 区的数据在程序开始执行之前被内核初始化为 0 或者空指针(NULL)。
//__bss_start的值是多少,这是由连接脚本lds决定的,是一个肯定大于0x33D80000(TQ所规定的TEXT_BASE的
//值)
.globl _bss_start
_bss_start:
.word __bss_start
.globl _bss_end
_bss_end:
.word _end //道理同_bss_start
.globl FREE_RAM_END //由于使用usb DNW下载功能
FREE_RAM_END: //在cpu\arm920t\cpu.c中,int cpu_init(void)定义,此处以后还会详细分析。
.word 0x0badc0de
.globl FREE_RAM_SIZE
FREE_RAM_SIZE:
.word 0x0badc0de
#ifdef CONFIG_USE_IRQ
.globl IRQ_STACK_START
IRQ_STACK_START:
.word 0x0badc0de
.globl FIQ_STACK_START
FIQ_STACK_START:
.word 0x0badc0de
#endif
reset:
mrs r0,cpsr //mrs指令:专用寄存器到通过寄存器的存取,把CPSR(程序状态寄存器)内容存入r0。
bic r0,r0,#0x1f //bic指令(bit clear): r0= r0 & (~0x1f)。该指令目的是把bit0~bit4清零。
orr r0,r0,#0xd3 //r0:= r0 | 0xd3 . r0值为:**** **** **** **** **** ***** 11*1 0011
msr cpsr,r0 //msr指令是专用的通用寄存器到特殊功能寄存器的指令与mrs对应
//将r0赋于cpsr。进入了SVC32模式。
#if defined(CONFIG_S3C2400)
# define pWTCON 0x15300000
# define INTMSK 0x14400008
# define CLKDIVN 0x14800014
#elif defined(CONFIG_S3C2410)
# define pWTCON 0x53000000 //WTCON(看门狗定时器控制寄存器)的地址是0x53000000
# define INTMOD 0X4A000004 //中断方式寄存器
# define INTMSK 0x4A000008 //中断屏蔽寄存器
# define INTSUBMSK 0x4A00001C //中断屏蔽寄存器
# define CLKDIVN 0x4C000014 //时钟分频控制寄存器
#endif
#if defined(CONFIG_S3C2400) || defined(CONFIG_S3C2410)
ldr r0, =pWTCON //将0x53000000放入r0中。
mov r1, #0x0 //将立即数0x0存入r1.
str r1, [r0] //str指令用于从源寄存器中将一个32位的字数据写入存储器,即将0x0000 0000
//写入0x53000000地址单元中,WTCON寄存器被赋值0.
mov r1, #0xffffffff
ldr r0, =INTMSK
str r1, [r0] //将0xffffffff写入INTMSK寄存器,中断被屏蔽。
# if defined(CONFIG_S3C2410)
ldr r1, =0x3ff //ldr在这里是伪指令,将0x3ff赋给r1,但这里确实应该是0x7fff。
ldr r0, =INTSUBMSK
str r1, [r0]
# endif
#if 0 //此处不编译
ldr r0, =CLKDIVN
mov r1, #3
str r1, [r0]
#endif
#endif
//cpu_init_crit 主要完成内存管理相关的寄存器设置,CP15协处理器,配置内存区控制寄存器。另外这段代码
//中调用了lowlevel_init 函数,进行寄存器的具体设置,与采用的内存芯片有关
#ifndef CONFIG_SKIP_LOWLEVEL_INIT //没有定义CONFIG_SKIP_LOWLEVEL_INIT,跳转
bl cpu_init_crit //bl是带跳转返回指令,执行完后再跳回此处。
#endif
stack_setup: //堆栈是进入C函数前必须初始化的
ldr r0, _TEXT_BASE
sub r0, r0, #CFG_MALLOC_LEN //CFG_MALLOC_LEN是堆大小+环境数据区大小
sub r0, r0, #CFG_GBL_DATA_SIZE
#ifdef CONFIG_USE_IRQ
sub r0, r0, #(CONFIG_STACKSIZE_IRQ+CONFIG_STACKSIZE_FIQ)
#endif
sub sp, r0, #12 //1字=4Byte,3字=12Byte,为abort-stack预留3个字。
bl clock_init
#ifndef CONFIG_SKIP_RELOCATE_UBOOT
relocate:
adr r0, _start //链接时_start的地址确实被链接为0x33D80000,但程序的地址有加载地址和运行地
//址两种,uboot被下载到nandflash中,nand boot时,被加载到steppingstone中,这个sram的起始地址是
//0x0000 0000,所以uboot被加载到此处,即uboot的加载地址是0x00000000,这就出现个问题,uboot在链接时被链接到0x33D80000开始的地方,即内存中的地址,按理说得把uboot下载到0x33D80000处才能正常工作,那为什么加载到0x00000000时也可以运行呢,这是因为uboot运用了arm的位置无关程序设计(PIC),跳转都是基于pc指针的。详见......
这就保证了uboot被下载任何地方都可以运行,而adr r0, _start就是取_start此刻所在的地址,即0x00000000,
ldr r1, _TEXT_BASE //把_TEXT_BASE标号的地址里存的是TEXT_BASE=0x33D80000赋给r1
cmp r0, r1
beq clear_bss
ldr r2, _armboot_start
ldr r3, _bss_start
sub r2, r3, r2
#if 1
bl CopyCode2Ram
#else
add r2, r0, r2
copy_loop:
ldmia r0!, {r3-r10}
stmia r1!, {r3-r10}
cmp r0, r2
ble copy_loop
#endif
#endif
clear_bss:
ldr r0, _bss_start
ldr r1, _bss_end
mov r2, #0x00000000
clbss_l:str r2, [r0]
add r0, r0, #4
cmp r0, r1
ble clbss_l
#if 0
ldr r0, =pWTCON
mov r1, #0x0
str r1, [r0]
mov r1, #0xffffffff
ldr r0, =INTMR
str r1, [r0]
ldr r0, =CLKDIVN
mov r1, #3
str r1, [r0]
#endif
ldr pc, _start_armboot
_start_armboot: .word start_armboot
#ifndef CONFIG_SKIP_LOWLEVEL_INIT
cpu_init_crit:
mov r0, #0
mcr p15, 0, r0, c7, c7, 0
mcr p15, 0, r0, c8, c7, 0 // 刷新TLB
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
mov ip, lr
bl lowlevel_init
mov lr, ip
mov pc, lr
#endif
@
@ 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
.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
.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
@ HJ
.globl Launch
.align 4
Launch:
mov r7, r0
@ diable interrupt
@ disable watch dog timer
mov r1, #0x53000000
mov r2, #0x0
str r2, [r1]
ldr r1,=INTMSK
ldr r2,=0xffffffff @ all interrupt disable
str r2,[r1]
ldr r1,=INTSUBMSK
ldr r2,=0x7ff @ all sub interrupt disable
str r2,[r1]
ldr r1, = INTMOD
mov r2, #0x0 @ set all interrupt as IRQ (not FIQ)
str r2, [r1]
@
mov ip, #0
mcr p15, 0, ip, c13, c0, 0 @
mcr p15, 0, ip, c7, c7, 0 @
mcr p15, 0, ip, c7, c10, 4 @
mcr p15, 0, ip, c8, c7, 0 @
mrc p15, 0, ip, c1, c0, 0 @
bic ip, ip, #0x0001 @
mcr p15, 0, ip, c1, c0, 0 @
@ MMU_EnableICache
@mrc p15,0,r1,c1,c0,0
@orr r1,r1,#(1<<12)
@mcr p15,0,r1,c1,c0,0
#ifdef CONFIG_SURPORT_WINCE
bl Wince_Port_Init
#endif
@ clear SDRAM: the end of free mem(has wince on it now) to the end of SDRAM
ldr r3, FREE_RAM_END
ldr r4, =PHYS_SDRAM_1+PHYS_SDRAM_1_SIZE @ must clear all the memory unused to zero
mov r5, #0
ldr r1, _armboot_start
ldr r2, =On_Steppingstone
sub r2, r2, r1
mov pc, r2
On_Steppingstone:
2: stmia r3!, {r5}
cmp r3, r4
bne 2b
@ set sp = 0 on sys mode
mov sp, #0
@ add by HJ, switch to SVC mode
msr cpsr_c, #0xdf @ set the I-bit = 1, diable the IRQ interrupt
msr cpsr_c, #0xd3 @ set the I-bit = 1, diable the IRQ interrupt
ldr sp, =0x31ff5800
nop
nop
nop
nop
mov pc, r7 @ Jump to PhysicalAddress
nop
mov pc, lr
#ifdef CONFIG_USE_IRQ
.align 5
irq:
sub lr, lr, #4 @ the return address
ldr sp, IRQ_STACK_START @ the stack for irq
stmdb sp!, { r0-r12,lr } @ save registers
ldr lr, =int_return @ set the return addr
ldr pc, =IRQ_Handle @ call the isr
int_return:
ldmia sp!, { r0-r12,pc }^ @ return from interrupt
.align 5
fiq:
get_fiq_stack
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