|
U-boot移植心得《二》
下面我开始分析start.S,在分析之前,我们要了解,u-boot引导linux加载的过程分为2个阶段,第一个阶段的终极目的就是设置好各种硬件环境,将代码从nor flash或NAND flash中拷贝到外部RAM中,然后跳转到C函数中去。第二阶段的终极目标就是在RAM中运行u-boot代码,执行u-boot的各种命令,加载引导linux映像并,最终跳到linux系统中,至此u-boot的生命结束。
好,我们看第一阶段的代码:
_start: b reset @u-boot首先从_start处开始运行,跳转到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
reset:
/ * set the cpu to SVC32 mode 置cpu为管理模式 */
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 */
#elif
defined(CONFIG_S3C2410)
# define pWTCON 0x53000000
# define INTMOD 0X4A000004
# define INTMSK 0x4A000008 /* Interupt-Controller base addresses */
# define INTSUBMSK 0x4A00001C
# define CLKDIVN 0x4C000014 /* clock divisor register */
#endif
#if
defined(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 */
③屏蔽中断,此处只设置了2410,如果是2440或其他处理器型号,需要设置修改
mov r1,
#0xffffffff
ldr r0,
=INTMSK
str r1,
[r0]
# if
defined(CONFIG_S3C2410)
ldr r1,
=0x3ff
ldr r0,
=INTSUBMSK
str r1,
[r0]
# endif
没经修改的u-boot源码中没有屏蔽此处,此处的功能是设置2410的时钟,如果是2410的开发板此处可不改动,但我们移植的这个u-boot要同时兼容2410和2440,所以讲此处屏蔽掉重新写一个时钟初始化的函数clock_init,让其即支持2410又支持2440.放到代码后面某个合适的地方去。
#if 0
/*
FCLK:HCLK:PCLK = 1:2:4 */
/*
default FCLK is 120 MHz ! */
ldr r0, =CLKDIVN
mov r1, #3
str r1, [r0]
#endif
比较_start和_TEXT_BASE的地址判断代码运行在外部RAM下还是内部ROM,如果代码地址相同,则目前运行在RAM中,忽略此步骤中的所有工作。否则运行在内部ROM中,跳转到cpu_init_crit。
#ifndef
CONFIG_SKIP_LOWLEVEL_INIT
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 */
blne cpu_init_crit /* 如果运行在ROM中(内部4K),则跳转到 cpu_init_crit ,代码见下节*/
#endif
/* 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 */
#ifndef
CONFIG_SKIP_LOWLEVEL_INIT
bl clock_init 设置系统时钟,代码见下节
#endif
重新定位,判断代码在不在RAM中运行,如果不在,则复制代码到RAM中运行。
#ifndef
CONFIG_SKIP_RELOCATE_UBOOT
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
clear_bss 判断,跳过复制代码程序
ldr r2,
_armboot_start
ldr r3,
_bss_start
sub r2,
r3, r2 /* r2 <-
size of armboot */
#if 1
bl
CopyCode2Ram /* r0: source,
r1: dest, r2: size */ 复制NAND或NOR代码到RAM,代码见下节。
#else 下面else下的代码不执行,跳过
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
#endif
#endif /* CONFIG_SKIP_RELOCATE_UBOOT */
clear_bss: 清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
SetLoadFlag:
/* Set a global flag, PreLoadedONRAM */
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 */
ldr r2, =PreLoadedONRAM
mov r3, #1
streq r3, [r2]
关于: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 */
看了下网上的帖子,adr指令,网上很多人被这这个指令弄郁闷,我看杜春雷的<<arm体系结构与编程>>P143讲,这个指令是基于PC或者寄存器的,读到是地址无关的,一般被编译器替换为SUB
r0, pc,#offset ,不要理解为读取符合表中_start符号的地址(0x33f80000).在我们上电开始执行时,pc从0开始,所以现在r0值为0 +offset,不等于_TEXT_BASE(0x33f80000).接下来要用到链接时确定的符号地址了,_armboot_start(0x33f80000).,
_bss_start(0x33f97954)这些可以在u-boot.map里面的看到, size of armboot
=0x33f97954-0x33f80000 ,把_start:0x0
(norflsh)把.text ,.data的代码往SDRAM里_TEXT_BASE确定的地址:
0x33f80000搬运.s3c2410的SDRAM基地址是0x3000_0000,由于uboot支持的这个board SDRAM是64M,(0x3000_0000---0x3400_0000),所以把u-boot.bin搬运到内存的高端地址.然后跳到内存中执行,提高速度.
之后就relocate 
stack_setup  clear_bss  ldr pc, _start_armboot (
ROMRAM)
_start_armboot: .word start_armboot ( u-boot-1.1.4\lib_arm\board.c)
stack_setup , clear_bss设置堆栈清bss段,都是为进入C语言做初始化准备,通过对start_armboot链接后以及把这个函数地址已经绑定在RAM中,当执行完ldr
pc, label 指令,程序将从标号绑定地址开始执行,从而实现了从地址无关程序到地址相关的转变,我们做代码搬移也是为了跳转做准备,如果没有搬移,直接访问地址相关,由于RAM中都是随机值,一跳转就马上飞了.当进入start_armboot C函数,剩下的都没什么难度了.可以慢分析源码搞定.2410没有remap寄存器, relocate时候要容易些,有remap寄存器的芯片在relocate时候进行remap会让情况更复杂些.不过原理都差不多.
在进入board.c后,uboot还做了一次代码搬运如下,大概如下图,不过分两种,一种是把pc机传的image通过串口或者网络传到内存开始执行,或者从nandflash里把应用搬到内存开始执行,不过原理都差不多.
#if 0 屏蔽不执行,跳过
/* try doing this stuff after the
relocation */
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,
=INTMR
str r1,
[r0]
/* FCLK:HCLK:PCLK = 1:2:4 */
/* default FCLK is 120 MHz ! */
ldr r0,
=CLKDIVN
mov r1,
#3
str r1,
[r0]
/* END stuff after relocation */
#endif
ldr pc,
_start_armboot 进入u-boot第二阶段,跳转到start_armboot
_start_armboot: .word start_armboot
在cpu/arm920t/start.S中,将text relocate 到Ram后,其代码段的最后1行有条语句:
ldr pc
_start_armboot
_start_armboot:
.word start_armboot
start_armboot是一个函数指针,这个symbol对应了符号表里的函数地址,这个函数是一个C语言的函数,他就是u-boot的stage2的入口点,这个stage2应该是在RAM里面执行的。问题就来了,既然我们只是手动将text relocate到了RAM里面,此时FLASH和RAM里面都有start_armboot的代码,为什么程序就要跳转到RAM里,而不是依然在FLASH里?
我最初感觉除非是ld在连接的时候,修改了符号表里的内容,指定start_armboot符号的地址是RAM里的地址,这样只要我们执行时取其地址,取到的肯定是RAM中的地址。
经查阅资料,有如下一段解释:
转自:http://www.mail-archive.com/u-boot-users@lists.sourceforge.net/msg04018.html
----- Original
Message ----
> > >
> From: Vishal Oliyil Kunnil
> > >
> To: Tiju
> > >
>
> > >
> Sent: Monday, 31 March, 2008 2:58:12 PM
> > >
> Subject: Re: [U-Boot-Users] s3c2440 -- serial_init
> > >
>
> > >
> TEXT_BASE is the address for which u-boot is linked for.If you take an
> > >
> objdump of u-boot
> > >
> elf, you will see that it links for address beginning with that
> > >
> specified by TEXT_BASE.
> > >
> Meaning, you link for the address thus specified.
> > >
> Typically the binary will be run from the reset vector of the
> > >
> processor, which is not necessarily
> > >
> TEXT_BASE : say 0x0 flash address. U-boot starts executing from the
> > >
> reset vector,
> > >
> relocates to RAM and since it is linked for TEXT_BASE, the ldr pc,
> > >
> _start_armboot
> > >
> will branch to the start_armboot which is in RAM.
> > >
> -------snip - start.S --------------
> > >
> ldr pc, _start_armboot
> > >
> _start_armboot: .word start_armboot
> > >
> -------snip - start.S --------------
> > >
> Regards,
> > > > Vishal 上面的意思是说,我们在u-boot的C代码的编译环境里制定了TEXT_BASE的值,然后所有生成的C函数的可执行代码都是以TEXT_BASE作为连接基地址,而不是我们的0x00000000(u-boot在FLASH开始执行的起始地址),也就是说这是我们u-boot的代码中在FLASH里,除了最开始的一部分汇编码是在FLASH里执行的,其他由C语言实现的部分都只能在RAM里执行,因为我们给他们定好的基地址就是RAM的地址。