uboot详细分析(一)
本文尽可能详细的分析uboot1.1.6.截止目前,作者仅仅将uboot1.1.6移植到TQ2440上,能够从NAND Flash自启动.本文的目的在于总结自己在移植过程中需要掌握的知识点.
.globl_start : .globl 是为了声明_start为全局的一个标号,为其他文件能够引用.
还记得在分析start.lds文中提到ENTRY(_start)吗,它指明了CPU上电启动的入口函数地址,就是此处的_start
_start: b reset
ldr pc,_undefined_instruction /* 未定义指令向量 */
ldr pc,_software_interrupt /* 软件中断向量 */
ldr pc,_prefetch_abort /* 预取指令异常向量 */
ldr pc,_data_abort /* 数据操作异常向量 */
ldr pc,_not_used /* 未使用 */
ldr pc,_irq /* irq 中断向量 */
ldr pc,_fiq /* fiq 中断向量 */
_undefined_instruction: .word undefined_instruction
_software_interrupt: .word software_interrupt
_prefetch_abort: .wordprefetch_abort
_data_abort: .word data_abort
_not_used: .word not_used
_irq: .word irq
_fiq: .word fiq
根据中断ARM异常处理规定,8个中断向量异常处理的地址依次为0x0(复位),0x4(未定义指令异常),0x8(软件中断异常),0xc(预取指令异常),0x10(数据操作异常),0x14(保留),0x18(irq中断向量),0x1c(fiq中断向量);所以必须将这八种异常指定的地址中存放我们自己的异常处理指令,我们的指令都是存放一条跳转指令来处理相应的异常,这个应该不难理解.
.balignl16,0xdeadbeef
可以参照这位仁兄的分析http://haoyeren.blog.sohu.com/84511571.html,这里0xdeadbeef只是一个标识.
_TEXT_BASE:
.word TEXT_BASE
这里定义了一个TEXT_BASE,这个标识的值我们可以在源代码的borad/smdk2410/config.mk里面找到,用它将来标识我们代码存放在内存中的位置.
.globl_armboot_start
_armboot_start:
.word _start
这里同样将_armboot_start声明为全局标号,它的值就是我们的首地址_start
.globl_bss_start
_bss_start:
.word __bss_start
.globl_bss_end
_bss_end:
.word _end
这里同样有声明两个全局的标识_bss_start,_bss_end,它们分别被定为__bss_start和_end,这两个值是外部的两个标识,它们的值指定在u-boot.lds文件指定的,查看section布局即可看到它们的位置.
#ifdefCONFIG_USE_IRQ
/*IRQ stack memory (calculated at run-time) */
.globlIRQ_STACK_START
IRQ_STACK_START:
.word 0x0badc0de
/*IRQ stack memory (calculated at run-time) */
.globlFIQ_STACK_START
FIQ_STACK_START:
.word 0x0badc0de
#endif
由于CONFIG_USE_IRQ没有定义所以以上的一段略过!
reset:
/*
* setthe cpu to SVC32 mode
*/
mrs r0,cpsr
bic r0,r0,#0x1f
orr r0,r0,#0xd3
msr cpsr,r0
终于看到了reset了,这个在我们的_start标号后面的第一条语句就是
_start: b reset
我们上电给CPU后,立即执行这个复位指令
首先我们看下cpsr,cpsr是程序状态寄存器,它的定义如下所示:
31 30 29 28 27 -- 8 7 6 5 4 3 2 1 0
N Z C V ( DNM(RAZ) ) I F T M4 M3 M2 M1 M0
N:N = 1 表示运算结果为负数;N = 0 表示结果为正数
Z:Z = 1 表示运算结果为0;Z = 0 表示运算结果不为0
C: C= 0 表示在加法指令中当结果产生进位,无符号数运算发生上溢出
C = 1 表示在减法指令中当结果产生借位,无符号数运算发生下溢出
V:在加减法运算指令中,当操作数和运算结果为二进制补码表示的带符号数时,V = 1表示符号溢出
I:I = 1 时禁止IRQ中断
F:F = 1 时禁止FIQ中断
T:T = 0 表示执行ARM指令
T = 1 表示Thumb指令
M4 M3 M2 M1 M0
1 0 0 0 0 用户模式
1 0 0 0 1 快速中断模式
1 0 0 1 0 外部中断模式
1 0 0 1 1 特权模式
1 0 1 1 1 数据访问中止模式
1 1 0 1 1 未定义指令中止模式
1 1 1 1 1 系统模式
mrs r0,cpsr 指令是将cpsr中的值读到r0中
bic r0,r0,#0x1f
orr r0,r0,#0xd3
将读出来的值修改,后再次写回r0,此时欲将FIQ,IRQ关闭,处理器模式设定为特权模式
msr cpsr,r0 将修改后的值再次写回到cpsr中
接下来设置:看门狗,中断控制器,以及时钟
以下是s3c2440/s3c2410芯片这些外设的地址
#define pWTCON 0x53000000
#define INTMSK 0x4A000008
#define INTSUBMSK 0x4A00001C
#define CLKDIVN 0x4C000014
ldr r0, =pWTCON
mov r1, #0x0
str r1, [r0]
以上三条指令是为了关闭看门狗,如果此处不关闭的话会出现uboot正常启动以后不停重启的现象.
mov r1,#0xffffffff
ldr r0,=INTMSK
str r1,[r0]
将中断源屏蔽
ldr r1,=0x3ff
ldr r0,=INTSUBMSK
str r1,[r0]
将子中断源屏蔽
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 */
/*
*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
以上代码是使数据cache和指令cache无效,关闭MMU.具体寄存器设置是怎么操作的,我想有必要另外开一个专题进行研究.
/*
*before relocating, we have to setup RAM timing
*because memory timing is board-dependend, you will
*find a lowlevel_init.S in your board directory.
*/
mov ip,lr
bl lowlevel_init
mov lr,ip
mov pc,lr
现在看上面代码,需要注意的是以上代码是cpu_init_crit:段的一部分,所以在执行完以后需要函数返回,所以在最后会有mov pc, lr指令.但是期间又一次调用了lowlevel_init,所以需要首先将lr中的值保存起来,这里这个ip感觉怪怪的,其实就是r12,它只不过是个别名罢了.当从lowlevel_init返回后再将原来的lr恢复.
下面我们进入lowlevel_init中分析.该函数在
board/smdk2410/lowlevel_init.S文件中.
_TEXT_BASE:
.word TEXT_BASE
此处也定义了一个_TEXT_BASE,它的值也是在/board/smdk2410/config.mk
下面来分析代码
.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
首先将lowlevel_init声明为全局标识,为其他模块能够引用.
接下来 ldr r0, =SMRDATA
ldr r1, _TEXT_BASE
sub r0,r0, r1
这三条指令指的我们去分析一下,从指令的表面看来将来执行完毕后r0中保存的就是SMRDATA相对_TEXT_BASE的偏移地址,但是为什么需要的是偏移地址而不是绝对地址呢?其实我们回过头来想想,我们是从start.S文件开始执行的,从u-boot.lds连接脚本文件中可以知道,我们的start.S是被连接在前4K的,如果从NandFlash启动,S3C2440会将前4K的内容自动复制到片内RAM中,当然片内RAM的地址是从0开始,所以我们的代码执行时从0地址开始执行的.如果从NorFlash启动,NorFlash启动地址本来就映射在S3C2440的0地址空间,同样代码是从0地址启动.当然我们会把lowlevel_init也连接在前4K的空间内,这样看来我们r0中虽然是一个偏移地址,但是相对于从0地址空间开始的代码这个偏移地址其实就是一个绝对地址(0+偏移量).(啰嗦写这么多不知道解释清楚没有).
接着往下看
ldr r1, =BWSCON /* Bus Width Status Controller */
add r2, r0, #13*4
r1装入内存控制器的首地址,r2装入内存控制器的末地址
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 (此处放BWSCON的配置)
.word (此处放BANKCON0的配置)
.word (此处放BANKCON1的配置)
.word (此处放BANKCON2的配置)
.word (此处放BANKCON3的配置)
.word (此处放BANKCON4的配置)
.word (此处放BANKCON5的配置)
.word (此处放BANKCON6的配置)
.word (此处放BANKCON7的配置)
.word (此处放REFRESH的配置)
.word (此处放BANKSIZE的配置)
.word (此处放MRSR的配置)
上面的一段代码将SMDATA处所有关于内存控制器的配置保存到内存控制器中,关于这些配置uboot已经给出了一些默认的值,但是我们需要根据自己的开发板来调整这些配置.尤其是要注意bank6/7的配置,因为我们的SDRAM都挂载在这两个bank上,bank6的起始地址是0x30000000,所以我们下载代码的地址都是在这个地址之后.
接下来我们返回到start.S中,继续往下执行
stack_setup:
ldr r0, _TEXT_BASE
sub r0, r0, #CFG_MALLOC_LEN
sub r0, r0,#CFG_GBL_DATA_SIZE
sub sp, r0, #12 /* leave 3 words for abort-stack */
这是进行堆栈的分配,此处为malloc分配128k的堆,分配128个字节的栈区,r0就是指向的栈顶的指针,再预留3个word空间大小,然后把这个栈顶给真正用来管理堆栈的指针sp寄存器.此处另外在多预留3个word不知道用意是什么,还没有弄清楚.
本节先暂时分析至此!