这两天在分析uboot,昨天分析完了makefile之后,今天分析了第一阶段的源码,故而分享之。新手望各位指教
开始先插一下,uboot
移植的思路:
1
首先来确定UBOOT
的每一步动作,以及这个动作所使用的文件,函数等等
2
再来确定哪一个动作需要修改的
下来进行分析:分析的前提是假设所有的配置都是打开的。
首先,看start.S
.globl_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
ldr pc,_fiq
知道上电后执行的是 b reset
找到rest
如下:
reset:
/*
*set the cpu to SVC32 mode
*/
mrs r0,cpsr
bic r0,r0,#0x1f
orr r0,r0,#0xd3
msr cpsr,r0
/*turn off the watchdog */
#if defined(CONFIG_S3C2400)
#define pWTCON 0x15300000
#define INTMSK 0x14400008 /* Interupt-Controller base addresses */
#define CLKDIVN 0x14800014 /* clock divisor register */
#elifdefined(CONFIG_S3C2410)
#define pWTCON 0x53000000
#define INTMSK 0x4A000008 /* Interupt-Controller base addresses */
#define INTSUBMSK 0x4A00001C
#define CLKDIVN 0x4C000014 /* clock divisor register */
#endif
#ifdefined(CONFIG_S3C2400) || defined(CONFIG_S3C2410)
ldr r0, =pWTCON
mov r1, #0x0
str r1, [r0]
/*
*mask all IRQs by setting all bits in the INTMR - default
*/
mov r1,#0xffffffff
ldr r0,=INTMSK
str r1,[r0]
# ifdefined(CONFIG_S3C2410)
ldr r1,=0x3ff
ldr r0,=INTSUBMSK
str r1,[r0]
#endif
/* FCLK:HCLK:PCLK = 1:2:4 */
/* default FCLK is 120 MHz ! */
ldr r0,=CLKDIVN
mov r1,#3
str r1,[r0]
#endif /* CONFIG_S3C2400 || CONFIG_S3C2410 */
/*
*we do sys-critical inits only at reboot,
*not when booting from ram!
*/
#ifndefCONFIG_SKIP_LOWLEVEL_INIT
bl cpu_init_crit
#endif
很清楚地看到,首先上电后执行的是:
1. 设为SVC MODE
2. 关闭watchdog
3. 屏蔽IRQ中断
4. 设置时钟
5. 跳转到cpu_init_crit
好了,我们来找到cpu_init_crit在cpu/arm920t/start.S中。
看一看代码如下:
#ifndefCONFIG_SKIP_LOWLEVEL_INIT
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
/*
* before relocating, we have to setup RAM timing
* because memory timing is board-dependend,you will
* find a lowlevel_init.S in your boarddirectory.
*/
mov ip,lr
bl lowlevel_init
mov lr,ip
mov pc,lr
#endif /*CONFIG_SKIP_LOWLEVEL_INIT */
代码的注释十分详细,不用看代码都可以知道,这个函数主要做的事情有
:
1 flush v4 I/D caches
2 disable MMU stuff and caches
3这句话很重要:
/*
*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.
*/
我们可以知道,接下来他要做的事情是初始化RAM,原因是because memory timing isboard-dependend,,所以我们要根据自己的板子来设置好RAM,以进行代码的重定位。
4
bl lowlevel_init
跳转到lowlevel_init,根据这句you will find alowlevel_init.S in your board directory.,我们就到/board去找找看。
果然找到了
file:///C:/Users/ADMINI~1/AppData/Local/Temp/msohtml1/01/clip_image002.jpg
代码有点长,所以节选来看。
#include<config.h>
#include <version.h>
首先它包含了两个头文件,第一个是我们的配置文件主要是选择打开的配置,是我们手动创建生成的。
第二 个是版本信息。没什么值得分析的
接下来定义了一个
_TEXT_BASE:
.word TEXT_BASE
这个TEXT_BASE其实才是镜像文件的真正其实地址。我们来看,连接器脚本,look一下,
SECTIONS
{
. = 0x00000000;
. = ALIGN(4);
.text :
{
cpu/arm920t/start.o (.text)
*(.text)
}
. = ALIGN(4);
.rodata : { *(.rodata) }
. = ALIGN(4);
.data : { *(.data) }
. = ALIGN(4);
.got : { *(.got) }
. = .;
__u_boot_cmd_start = .;
.u_boot_cmd : { *(.u_boot_cmd) }
__u_boot_cmd_end = .;
. = ALIGN(4);
__bss_start = .;
.bss : { *(.bss) }
_end = .;
}
看起来好像是0地址开始的,可是......我们来看看map文件吧,look一下
Linker script andmemory map
0x00000000 . = 0x0
0x00000000 . = ALIGN (0x4)
.text 0x33f80000 0x12844
cpu/arm920t/start.o(.text)
.text 0x33f80000 0x400cpu/arm920t/start.o
0x33f80048 _bss_start
0x33f8004c _bss_end
0x33f80044 _armboot_start
0x33f80000 _start
看吧,地址是从0x33f80000开始的,为什么会这样呢?
其实奥妙就在makefile 当中.
我们看看实际上makefile 是怎么链接的
这一句cd $(LNDIR) &&$(LD) $(LDFLAGS) $$UNDEF_SYM $(__OBJS) \
而在mkconfig脚本生成的config.mk文件中可以看到:
LDFLAGS += -Bstatic-T $(LDSCRIPT) -Ttext $(TEXT_BASE) $(PLATFORM_LDFLAGS)
吓尿了有木有!事实上连接起始地址是TEXT_BASE这个变量在board/smdk2410中定义是:
TEXT_BASE =0x33F80000
为什么要这么做?因为我们的代码最终要拷贝到RAM 内以提高速度,而SMDK2410这块板子的RAM起始地址是0x30000000,所以0x3ff80000是RAM的地址代码就被链接到了RAM里面啦。
路人甲发问:为毛不是从0x30000000开始?
我:……不要在意这些细节…….
如果非要弄明白这个问题,那就请看start.S的代码:
stack_setup:
ldr r0,_TEXT_BASE /* upper 128 KiB:relocated uboot */
sub r0,r0, #CFG_MALLOC_LEN /* mallocarea */
sub r0,r0, #CFG_GBL_DATA_SIZE /* bdinfo */
#ifdefCONFIG_USE_IRQ
sub r0,r0, #(CONFIG_STACKSIZE_IRQ+CONFIG_STACKSIZE_FIQ)
#endif
sub sp,r0, #12 /* leave 3 words forabort-stack */
很明白了,RAM起始部分的地址是留给栈和MALLOC,全局参数,以及ABORT异常的。
路人乙:那为毛链接到RAM里面的代码在NOR上跑起来了
我:卧槽!你有种就再说一遍!
咳咳咳,咱们不能再开叉了,不然就跑题了,如果想要明白这个问题的,请谷歌“位置无关码”“GUN程序加载数据方式”
好了花了那么多口舌去讲一个TEXT_BASE可见其很重要,为毛重要,是因为移植的时候我们必须要注意到这一点,不然写程序的时候没有意识到链接地址是TEXT_BASE开始的,那么我们自己添加的代码就会作废了。
继续分析
Lowlevel,刚刚分析到第四步,现在是第五步啦
5第五步就是初始化RAM啦
lowlevel_init:
/* memory control configuration */
/* make r0 relative the current locationso that it */
/* reads SMRDATA out of FLASH rather thanmemory ! */
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
其中SMRDATA是RAM相关寄存器设置值的存放起始地址,一共十三个。因为这时候代码所在的位置是0起始的,而实际上连接的地址是0x33f80000,所以直接去访问数据肯定是错误的,为什么?因为程序在连接的时候已经确定了所有的符号地址,比如说你去访问SMRDATA这个数组,因为连接时地址已经确定,所以访问的地址应该是RAM中的地址,但是现在我们的镜像是在NOR里的,访问自然理所当然会出错。故而我们应该找到SMRDATA这个数组在NOR的位置,于是:sub r0,r0,r1.。然后做基址寻址,ok!大功告成。
6最后一步,事情都完成了,要返回咯
/* everything is fine now */
mov pc,lr
嗯?这个代码注释挺有喜感的,德国人都这么幽默吗?
返回哪里?我的天,当然是哪里调用的lowlevel_init就返回哪里呀。返回cpu_init_crit。
返回后我们看到这么一段代码
mov ip, lr
bl lowlevel_init
mov lr,ip
mov pc,lr
嗯,从cpu_init_crit返回。返回到rest中。
看看接下来会发生什么吧。
好了现在我们回到rest的bl cpu_init_crit处接着执行。
回顾一下我们已经完成的工作:
1
设为SVC MODE
2
关闭watchdog
3
屏蔽IRQ
中断
4
设置时钟
5
清缓冲
6
关闭
MMU
7
初始化
RAM
既然更舒适温馨的房子
----RAM
已经准备好了,那么接下来就搬到
RAM
里面去住吧。
代码很简单,就是平常我们写的
copy
函数:
relocate: /* relocate U-Boot to RAM */
adr r0,_start /* r0 <- current positionof code */
ldr r1,_TEXT_BASE /* test if we run fromflash or RAM */
cmp r0, r1 /* don'treloc during debug */
beq stack_setup
ldr r2,_armboot_start
ldr r3,_bss_start
sub r2,r3, r2 /* r2 <- size ofarmboot */
add r2,r0, r2 /* r2 <- source endaddress */
copy_loop:
ldmia r0!,{r3-r10} /* copy from sourceaddress [r0] */
stmia r1!,{r3-r10} /* copy to target address [r1] */
cmp r0,r2 /* until source endaddreee [r2] */
ble copy_loop
首先取出来当前代码所在的位置adr r0,_start 然后判断一下代码是不是在RAM里面,cmp r0,r1如果在RAM里面就直接跳到下一步,如果不在就copy_loop吧。
接下来,设置好栈,然后清BSS段,接着跳转到第二阶段(RAM内),开始新的旅程了。
跳转的代码如下:
ldr pc,_start_armboot
_start_armboot: .word start_armboot
这个_start_armboot就是第二阶段的入口,在/lib_arm/board.c内。
———第一阶段分析结束