1.设置中断向量表
为了更清楚,彻底的理解u-boot,我会全部从反汇编的代码去分析
忘记的朋友先补下课啦,提示:arm-linux-objdump -D u-boot>u-boot.s
====================================================================================================
.globl _start
_start: b start_code
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
_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
.balignl 16,0xdeadbeef
====================================================================================================
33f80000 <_start>:
33f80000: ea000012 b 33f80050
33f80004: e59ff014 ldr pc, [pc, #20] ; 33f80020 <_undefined_instruction>
33f80008: e59ff014 ldr pc, [pc, #20] ; 33f80024 <_software_interrupt>
33f8000c: e59ff014 ldr pc, [pc, #20] ; 33f80028 <_prefetch_abort>
33f80010: e59ff014 ldr pc, [pc, #20] ; 33f8002c <_data_abort>
33f80014: e59ff014 ldr pc, [pc, #20] ; 33f80030 <_not_used>
33f80018: e59ff014 ldr pc, [pc, #20] ; 33f80034 <_irq>
33f8001c: e59ff014 ldr pc, [pc, #20] ; 33f80038 <_fiq>
33f80020 <_undefined_instruction>:
33f80020: 33f80140 mvnccs r0, #16 ; 0x10
33f80024 <_software_interrupt>:
33f80024: 33f801a0 mvnccs r0, #40 ; 0x28
33f80028 <_prefetch_abort>:
33f80028: 33f80200 mvnccs r0, #0 ; 0x0
33f8002c <_data_abort>:
33f8002c: 33f80260 mvnccs r0, #6 ; 0x6
33f80030 <_not_used>:
33f80030: 33f802c0 mvnccs r0, #12 ; 0xc
33f80034 <_irq>:
33f80034: 33f80320 mvnccs r0, #-2147483648 ; 0x80000000
33f80038 <_fiq>:
33f80038: 33f80380 mvnccs r0, #2 ; 0x2
33f8003c: deadbeef cdple 14, 10, cr11, cr13, cr15, {7}
====================================================================================================
这段比较好理解,先是在33f80000地址处定义了一个全局标号_start,这个地址是在board/smdk2410/config.mk中指定,他会被包含在Makefile文件中,链接时传递给GCC。当然,你还可以从System.map文件中找到他所标识的地址,如:33f80000 T _start,T标识他所在的段是text(code) section.
然后是一堆中断跳转指令,如:ldr pc, _undefined_instruction。他把_undefined_instruction标号处(即地址33f80020)的值(即33f80140)放入pc指针,就是跳转到相应的中断处理函数的地址处,从反汇编中(ldr pc, [pc, #20])我们可以看出,就是相对当前pc指针偏移20字节(计算时别忘了流水线哦!)。最后一句可能让人比较费解(.balignl 16,0xdeadbeef),他的定义是增加位置计数器(在当前子段)使它指向规定的存储边界。真是很拗口哦,不知所云。其实从反汇编中可以看出,他在这里做的事情很简单,就是在0x33f8003c地址处写入值0xdeadbeef。
有关.balignl 16,0xdeadbeef的详细解释请参考:http://haoyeren.blog.sohu.com/84511571.html
2设置特权模式
====================================================================================================
start_code:
/*
* set the cpu to SVC32 mode
*/
mrs r0,cpsr ;cpsr中的值放到人r0
bic r0,r0,#0x1f ;r0 的值与 0x1f的反码按位作逻辑与操作
orr r0,r0,#0xd3 ;r0的值与寄存器 0xd3的值按位作逻辑或操作
msr cpsr,r0 ;r0中的值放到cpsr
bl coloured_LED_init
bl red_LED_on
============================================================================
33f80050: e10f0000 mrs r0, CPSR
33f80054: e3c0001f bic r0, r0, #31 ; 0x1f
33f80058: e38000d3 orr r0, r0, #211 ; 0xd3
33f8005c: e129f000 msr CPSR_fc, r0
33f80060: eb000110 bl 33f804a8 <__coloured_LED_init>
33f80064: eb000110 bl 33f804ac <__red_LED_on>
============================================================================
执行结果:
禁止IRQ(bit7=1)
禁止FRQ(bit6=1)
ARM指令集(bit5=0)
特权模式(bit[4..0]=10010)
最后两句是有关LED处理的跳转指令,可用如下指令定位:
arm-linux-addr2line -e u-boot 33f804a8
在lib_arm/board.c文件中的第137行和139行:
void inline __coloured_LED_init (void) {}
void inline __red_LED_on (void) {}
其实都是空函数,可根据需要自己添加。
3关闭看门狗
====================================================================================================
/* turn off the watchdog */
# define pWTCON 0x53000000
ldr r0, =pWTCON ;把0x53000000放入r0
mov r1, #0x0 ;把0x00放入r1
str r1, [r0] ;把r1的值存入r0指向的地址处
====================================================================================================
33f80068: e3a00453 mov r0, #1392508928 ; 0x53000000
33f8006c: e3a01000 mov r1, #0 ; 0x0
33f80070: e5801000 str r1, [r0]
====================================================================================================
反汇编中#1392508928即是十六进制数0x53000000的十进制表示
4屏蔽所有中断
====================================================================================================
# define INTMSK 0x4A000008 /* Interupt-Controller base addresses */
# define INTSUBMSK 0x4A00001C
/*
* mask all IRQs by setting all bits in the INTMR - default
*/
mov r1, #0xffffffff
ldr r0, =INTMSK
str r1, [r0]
# if defined(CONFIG_S3C2410)
ldr r1, =0x7ff
ldr r0, =INTSUBMSK
str r1, [r0]
# endif
====================================================================================================
33f80074: e3e01000 mvn r1, #0 ; 0x0
33f80078: e59f0358 ldr r0, [pc, #856] ; 33f803d8 <.text+0x3d8>
33f8007c: e5801000 str r1, [r0]
33f80080: e59f1354 ldr r1, [pc, #852] ; 33f803dc <.text+0x3dc>
33f80084: e59f0354 ldr r0, [pc, #852] ; 33f803e0 <.text+0x3e0>
33f80088: e5801000 str r1, [r0]
。。。。。
33f803d8: 4a000008 bmi 33f80400
33f803dc: 000007ff streqd r0, [r0], -pc
33f803e0: 4a00001c bmi 33f80458
====================================================================================================
注意这里的立即数无法循环右移偶数位得到,所以汇编器先把他存在了后面的地址空间中,见反汇编代码。
5设置时钟工作频率
====================================================================================================
# define CLKDIVN 0x4C000014 /* clock divisor register */
/* FCLK:HCLK:PCLK = 1:2:4 */
/* default FCLK is 120 MHz ! */
ldr r0, =CLKDIVN
mov r1, #3
str r1, [r0]
====================================================================================================
33f8008c: e59f0350 ldr r0, [pc, #848] ; 33f803e4 <.text+0x3e4>
33f80090: e3a01003 mov r1, #3 ; 0x3
33f80094: e5801000 str r1, [r0]
。。。。。
33f803e4: 4c000014 stcmi 0, cr0, [r0], {20}
6刷新数据和指令缓存
====================================================================================================
bl cpu_init_crit ;跳转到子程序入口处
。。。。。。。
cpu_init_crit:
/*
* flush v4 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 */
====================================================================================================
33f80098: eb000018 bl 33f80100
。。。。。
33f80100: e3a00000 mov r0, #0 ; 0x0
33f80104: ee070f17 mcr 15, 0, r0, cr7, cr7, {0}
33f80108: ee080f17 mcr 15, 0, r0, cr8, cr7, {0}
====================================================================================================
记住这里有个子程序入口,第8部分子程序会返回。
7禁止MMU和缓存
====================================================================================================
/*
* 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
====================================================================================================
33f8010c: ee110f10 mrc 15, 0, r0, cr1, cr0, {0}
33f80110: e3c00c23 bic r0, r0, #8960 ; 0x2300
33f80114: e3c00087 bic r0, r0, #135 ; 0x87
33f80118: e3800002 orr r0, r0, #2 ; 0x2
33f8011c: e3800a01 orr r0, r0, #4096 ; 0x1000
33f80120: ee010f10 mcr 15, 0, r0, cr1, cr0, {0}
8配置存储控制相关寄存器
====================================================================================================
mov ip, lr ;保存lr到ip(r12)
bl lowlevel_init ;跳转到新的子程序入口处
mov lr, ip ;恢复lr
mov pc, lr ;子程序返回
。。。。。。。。
#define BWSCON 0x48000000
_TEXT_BASE:
.word TEXT_BASE
.globl lowlevel_init
lowlevel_init:
/* memory control configuration */
/* make r0 relative the current location so that it */
/* reads SMRDATA out of FLASH rather than memory ! */
ldr r0, =SMRDATA
ldr r1, _TEXT_BASE
sub r0, r0, r1
ldr r1, =BWSCON /* Bus Width Status Controller */
add r2, r0, #13*4
0:
ldr r3, [r0], #4
str r3, [r1], #4
cmp r2, r0
bne 0b
/* everything is fine now */
mov pc, lr
.ltorg
/* the literal pools origin */
SMRDATA:
.word (0+(B1_BWSCON<<4)+(B2_BWSCON<<8)+(B3_BWSCON<<12)+(B4_BWSCON<<16)+(B5_BWSCON<<20)+(B6_BWSCON<<24)+(B7_BWSCON<<28))
.word ((B0_Tacs<<13)+(B0_Tcos<<11)+(B0_Tacc<<8)+(B0_Tcoh<<6)+(B0_Tah<<4)+(B0_Tacp<<2)+(B0_PMC))
.word ((B1_Tacs<<13)+(B1_Tcos<<11)+(B1_Tacc<<8)+(B1_Tcoh<<6)+(B1_Tah<<4)+(B1_Tacp<<2)+(B1_PMC))
.word ((B2_Tacs<<13)+(B2_Tcos<<11)+(B2_Tacc<<8)+(B2_Tcoh<<6)+(B2_Tah<<4)+(B2_Tacp<<2)+(B2_PMC))
.word ((B3_Tacs<<13)+(B3_Tcos<<11)+(B3_Tacc<<8)+(B3_Tcoh<<6)+(B3_Tah<<4)+(B3_Tacp<<2)+(B3_PMC))
.word ((B4_Tacs<<13)+(B4_Tcos<<11)+(B4_Tacc<<8)+(B4_Tcoh<<6)+(B4_Tah<<4)+(B4_Tacp<<2)+(B4_PMC))
.word ((B5_Tacs<<13)+(B5_Tcos<<11)+(B5_Tacc<<8)+(B5_Tcoh<<6)+(B5_Tah<<4)+(B5_Tacp<<2)+(B5_PMC))
.word ((B6_MT<<15)+(B6_Trcd<<2)+(B6_SCAN))
.word ((B7_MT<<15)+(B7_Trcd<<2)+(B7_SCAN))
.word ((REFEN<<23)+(TREFMD<<22)+(Trp<<20)+(Trc<<18)+(Tchr<<16)+REFCNT)
.word 0x32
.word 0x30
.word 0x30
====================================================================================================
33f80124: e1a0c00e mov ip, lr
33f80128: eb005f76 bl 33f97f08
33f8012c: e1a0e00c mov lr, ip
33f80130: e1a0f00e mov pc, lr
。。。。。
33f97f04 <_TEXT_BASE>:
33f97f04: 33f80000 mvnccs r0, #0 ; 0x0
33f97f08
33f97f08: e59f0020 ldr r0, [pc, #32] ; 33f97f30 <.text+0x17f30>
33f97f0c: e51f1010 ldr r1, [pc, #-16] ; 33f97f04 <_TEXT_BASE>
33f97f10: e0400001 sub r0, r0, r1
33f97f14: e3a01312 mov r1, #1207959552 ; 0x48000000
33f97f18: e2802034 add r2, r0, #52 ; 0x34
33f97f1c: e4903004 ldr r3, [r0], #4
33f97f20: e4813004 str r3, [r1], #4
33f97f24: e1520000 cmp r2, r0
33f97f28: 1afffffb bne 33f97f1c
33f97f2c: e1a0f00e mov pc, lr
33f97f30: 33f97f34 mvnccs r7, #208 ; 0xd0
33f97f34
33f97f34: 2211d120 andcss sp, r1, #8 ; 0x8
33f97f38: 00000700 andeq r0, r0, r0, lsl #14
33f97f3c: 00000700 andeq r0, r0, r0, lsl #14
33f97f40: 00000700 andeq r0, r0, r0, lsl #14
33f97f44: 00001f4c andeq r1, r0, ip, asr #30
33f97f48: 00000700 andeq r0, r0, r0, lsl #14
33f97f4c: 00000700 andeq r0, r0, r0, lsl #14
33f97f50: 00018005 andeq r8, r1, r5
33f97f54: 00018005 andeq r8, r1, r5
33f97f58: 008e04f4 streqd r0, [lr], r4
33f97f5c: 00000032 andeq r0, r0, r2, lsr r0
33f97f60: 00000030 andeq r0, r0, r0, lsr r0
33f97f64: 00000030 andeq r0, r0, r0, lsr r0
====================================================================================================
注意由于前次子程序未返回,又用bl进入新的子程序,所以先要对lr进行保护。
本段主要完成的任务是,用一个循环把SMRDATA标号处定义的52字节数据依次存放到0x48000000以上的地址空间中 ,从而完成对存储控制相关寄存器的初始化工作。最后恢复lr,子程序返回。
然后来看下具体配置:(根据源代码中的宏定义)
/* BWSCON */
#define DW32 (0x2)
#define B1_BWSCON (DW32)
#define B6_BWSCON (DW32)
/* BANK0CON */
#define B0_Tacs 0x0 /* 0clk */
#define B0_Tcos 0x0 /* 0clk */
#define B0_Tacc 0x7 /* 14clk */
#define B0_Tcoh 0x0 /* 0clk */
#define B0_Tah 0x0 /* 0clk */
#define B0_Tacp 0x0
#define B0_PMC 0x0 /* normal */
/* BANK6CON */
#define B6_MT 0x3 /* SDRAM */
#define B6_Trcd 0x1
#define B6_SCAN 0x1 /* 9bit */
/* REFRESH parameter */
#define REFEN 0x1 /* Refresh enable */
#define TREFMD 0x0 /* CBR(CAS before RAS)/Auto refresh */
#define Trp 0x0 /* 2clk */
#define Trc 0x3 /* 7clk */
#define Tchr 0x2 /* 3clk */
#define REFCNT 1268 /* period=15.6us, HCLK=60Mhz, (2048+1-15.6*60)
BWSCON:配置总线宽度,等待状态控制寄存器
(0+(B1_BWSCON<<4)+(B2_BWSCON<<8)+(B3_BWSCON<<12)+(B4_BWSCON<<16)+(B5_BWSCON<<20)+(B6_BWSCON<<24)+(B7_BWSCON<<28))
配置结果:
BANK6设为32位数据总线,禁止WAIT,不用UB/LB
(BANK0通过外部引脚设定,其他BANK未使用,设置略。)
BANKCON0:BANK0控制寄存器
((B0_Tacs<<13)+(B0_Tcos<<11)+(B0_Tacc<<8)+(B0_Tcoh<<6)+(B0_Tah<<4)+(B0_Tacp<<2)+(B0_PMC))
配置结果:
nGCS0之前的地址建立时间0clocks,
在nOE之前片选的建立时间0clocks,
访问周期14clocks,
在nOE之后的片选的保持时间0clocks,
nGCS0之后的地址保持时间0clocks,
页模式访问周期2clocks,
页模式配置为标准(1 date),
BANKCON6:BANK6控制寄存器
((B6_MT<<15)+(B6_Trcd<<2)+(B6_SCAN))
配置结果:
BANK6设为类型DRAM,
RAS到CAS延迟3clocks,
纵向地址数9bit
REFRESH:SDRAM刷新控制寄存器
((REFEN<<23)+(TREFMD<<22)+(Trp<<20)+(Trc<<18)+(Tchr<<16)+REFCNT)
配置结果:
使能刷新
刷新模式为自动刷新
SDRAM RAS预改变时间2clocks
SDRAM Semi Row 周期7clocks
刷新计数值1268(即设定SDRAM刷新频率)
BANKSIZE:BANK大小寄存器
0x32
配置结果:
使能SDRAM掉电模式
SCLK仅在访问期间才有效
BANK6/7按128M映射
MRSRB6:BANK6模式寄存器设置寄存器
0x30
配置结果:
CAS反应时间3clocks
顺序触发类型
触发长度1
9将程序搬移到RAM空间
====================================================================================================
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
====================================================================================================
33f8009c
33f8009c: e24f00a4 sub r0, pc, #164 ; 0xa4
33f800a0: e51f1068 ldr r1, [pc, #-104] ; 33f80040 <_TEXT_BASE>
33f800a4: e1500001 cmp r0, r1
33f800a8: 0a000007 beq 33f800cc
33f800ac: e51f2070 ldr r2, [pc, #-112] ; 33f80044 <_armboot_start>
33f800b0: e51f3070 ldr r3, [pc, #-112] ; 33f80048 <_bss_start>
33f800b4: e0432002 sub r2, r3, r2
33f800b8: e0802002 add r2, r0, r2
33f800bc
33f800bc: e8b007f8 ldmia r0!, {r3, r4, r5, r6, r7, r8, r9, sl}
33f800c0: e8a107f8 stmia r1!, {r3, r4, r5, r6, r7, r8, r9, sl}
33f800c4: e1500002 cmp r0, r2
33f800c8: dafffffb ble 33f800bc
====================================================================================================
先测试是否是Debug中(注意adr相对寻址,ldr绝对寻址的不同),是则跳转到下一部分,否则按8字节循环搬移程序到RAM空间,注意结合上面给出的uboot存储器映射图学习。
10设置堆栈指针
====================================================================================================
/* 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 */
#ifdef CONFIG_USE_IRQ
sub r0, r0, #(CONFIG_STACKSIZE_IRQ+CONFIG_STACKSIZE_FIQ)
#endif
sub sp, r0, #12 /* leave 3 words for abort-stack */
====================================================================================================
33f800cc
33f800cc: e51f0094 ldr r0, [pc, #-148] ; 33f80040 <_TEXT_BASE>
33f800d0: e2400803 sub r0, r0, #196608 ; 0x30000
33f800d4: e2400080 sub r0, r0, #128 ; 0x80
33f800d8: e240d00c sub sp, r0, #12 ; 0xc
====================================================================================================
结合上面的映射图可以看出,_TEXT_BASE标号处地址(33f80000)以上就是上一步重定位的程序代码,他以下减去动态内存分配区大小,再减去bdinfo的大小(uboot第二段会有个结构体指向这个区域),从反汇编代码可以看出,我们未配置用户IRQ,所以再留12个字节给中断堆栈使用。现在指向的地址就是sp指针的位置了,从反汇编可以很容易的看出sp每次减去的数值。
11清零全局未初始化数据段,并跳转到第二阶段--C程序部分
====================================================================================================
clear_bss:
ldr r0, _bss_start /* find start of bss segment */
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
ldr pc, _start_armboot
_start_armboot: .word start_armboot
====================================================================================================
33f800dc
33f800dc: e51f009c ldr r0, [pc, #-156] ; 33f80048 <_bss_start>
33f800e0: e51f109c ldr r1, [pc, #-156] ; 33f8004c <_bss_end>
33f800e4: e3a02000 mov r2, #0 ; 0x0
33f800e8
33f800e8: e5802000 str r2, [r0]
33f800ec: e2800004 add r0, r0, #4 ; 0x4
33f800f0: e1500001 cmp r0, r1
33f800f4: dafffffb ble 33f800e8
33f800f8: e51ff004 ldr pc, [pc, #-4] ; 33f800fc <_start_armboot>
33f800fc <_start_armboot>:
33f800fc: 33f80578 mvnccs r0, #503316480 ; 0x1e000000
====================================================================================================
从board/smdk2410/u-boot.lds文件中,我们看到bss段被链接到了u_boot_cmd段的后面,即地址0x33f80048以后,现在我们要做的就通过一个循环把这段清空。然后重新赋值PC指针,跳转到标号_start_armboot指向的地址处,即第二阶段,C程序部分。