您的位置:首页 > 其它

u-boot启动过程分析(一)

2016-09-03 13:08 274 查看

u-boot启动过程分析(以u-boot-2012.0401为例)

u-boot的启动过程可以分为以下几步

将CPU设置为管理者模式(SVC32)

关看门狗

屏蔽中断

设置时钟

设置内存控制器(初始化SDRAM)

设置栈调用C函数board_init_f

调用函数数组init_sequence中的各个函数

重定位代码

清除bss段

调用C函数board_init_r
二.具体分析
1.将CPU设置为管理者模式(SVC32)
mrs
r0, cpsr
bic
r0, r0, #0x1f
orr
r0, r0, #0xd3
msr
cpsr, r0

关看门狗
#ifdef CONFIG_S3C24X0//这个宏已定义
/* turn off the watchdog */
# if defined(CONFIG_S3C2400)//这个宏未定义
#  define pWTCON
0x15300000
#  define INTMSK
0x14400008
/* Interrupt-Controller base addresses */
#  define CLKDIVN
0x14800014
/* clock divisor register */
#else//真正执行的语句
#  define pWTCON
0x53000000
#  define INTMSK
0x4A000008
/* Interrupt-Controller base addresses */
#  define INTSUBMSK
0x4A00001C
#  define CLKDIVN
0x4C000014
/* clock divisor register */
# endif
ldr
r0, =pWTCON//r0=0x53000000
mov
r1, #0x0//r1=0x0
str
r1, [r0]//将0x0写入PWTCON寄存器,即可关闭看门狗

屏蔽中断
mov
r1, #0xffffffff
ldr
r0, =INTMSK
str
r1, [r0]//将0xffffffff写入中断屏蔽寄存器INTMSK
# if defined(CONFIG_S3C2410)//这个宏已定义
ldr
r1, =0x3ff
ldr
r0, =INTSUBMSK
str
r1, [r0]//将0x3ff写入中断次级屏蔽寄存器INTSUBMSK
# endif

设置时钟比例
/* FCLK:HCLK:PCLK = 1:2:4 */
/* default FCLK is 120 MHz ! */
ldr
r0, =CLKDIVN
mov
r1, #3
str
r1, [r0]//将3写入时钟分频寄存器CLKDIVN,FCLK:HCLK:PCLK=1:2:4
#endif
/* CONFIG_S3C24X0 */

执行完上述语句后,跳转至cpu_init_crit中继续执行,在cpu_init_crit中所做的工作包括:

启动I/D caches

禁止MMU

跳转至执行lowlevel_init(初始化SDRAM)
 
3)   在cpu_init_crit中跳转至lowlevel_init初始化SDRAM(给存储器控制器赋值)
.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//0X0
sub
r0, r0, r1
ldr
r1, =BWSCON
/* Bus Width Status Controller */
add  r2, r0, #13*4
0:
ldr r3, [r0], #4//取r0存储的值赋入r1,r0对应SMRDATA中的值,r1对应存储器控制器的地址
str     r3, [r1], #4
cmp  r2, r0//r2是SMRDATA中的最后一个地址,当r0=r2时表示所有的值已全部取完
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

执行完cpu_init_crit(启动I/D caches,关闭MMU,初始化SDRAM)后,设置栈并调用board_init_f函数
call_board_init_f:
ldr
sp, =(CONFIG_SYS_INIT_SP_ADDR)//CONFIG_SYS_INIT_SP_ADDR=CONFIG_SYS_SDRAM_BASE+
0x1000 - GENERATED_GBL_DATA_SIZE=0X30000000+0X1000-0X80=0x30000f80
bic
sp, sp, #7 /* 8-byte alignment for ABI compliance */7=0x0111,清除sp中的0,1,2位
ldr
r0,=0x00000000
bl
board_init_f//跳转至board_init_f函数执行

board_init_f函数分析

划定一块空间给gd_t结构体,用于设置参数
gd = (gd_t *) ((CONFIG_SYS_INIT_SP_ADDR) & ~0x07);//0x30000f80地址被划作gd_t

 依次执行函数数组init_sequence中的函数
for (init_fnc_ptr = init_sequence; *init_fnc_ptr; ++init_fnc_ptr) {
if
((*init_fnc_ptr)() != 0) {
hang
();
}
init_fnc_t *init_sequence[] = {
#if defined(CONFIG_BOARD_EARLY_INIT_F)
board_early_init_f,//设置时钟(MPLLCON、UPLLCON)、设置引脚
#endif
timer_init,
/* initialize timer */出现了FCLK,HCLK,PCLK之间的取值关系
env_init,
/* initialize environment */环境变量初始化
init_baudrate,
/* initialze baudrate settings */波特率初始化
serial_init,
/* serial communications setup */串口初始化
console_init_f,
/* stage 1 init of console */
display_banner,
/* say that we are here */
#if defined(CONFIG_DISPLAY_CPUINFO)
print_cpuinfo,
/* display cpu info (and speed) */
#endif
dram_init
NULL,
};

重新分配内存——因为要重定位代码,所以在重定位之前先要重新分配内存
addr = CONFIG_SYS_SDRAM_BASE + gd->ram_size;//0x30000000+64M=0x34000000
#if !(defined(CONFIG_SYS_ICACHE_OFF) && defined(CONFIG_SYS_DCACHE_OFF))//这两个宏均未定义
/* reserve TLB table */
addr -= (4096 * 4);//addr=addr-4k=0x33fff000,划出4k用作TLB
/* round down to next 64 kB limit */
addr &= ~(0x10000 - 1);//addr=addr&~FFFF=0X33FF0000,64K对齐
gd->tlb_addr = addr;
debug("TLB table at: %08lx\n", addr);
#endif
addr &= ~(4096 - 1);//addr=0x33ff0000,4K对齐
debug("Top of RAM usable for U-Boot at: %08lx\n", addr);
addr -= gd->mon_len;//addr=addr-gd->mon_len=0x33ff0000-_bss_end_ofs(≈697k),划出一段用于存放u-boot的代码段、数据段、bss段
addr &= ~(4096 - 1);//addr=addr&~(4096-1)=0x33f41000
addr=0x33f41000,这个地址也是SDRAM中代码复制的目标地址
#ifndef CONFIG_SPL_BUILD//这个宏未定义
/*
 * reserve memory for malloc() arena
 */
addr_sp = addr - TOTAL_MALLOC_LEN;//addr_sp=0x33f41000-(4*1024*1024)=0x33b41000,划出一段作为堆区
debug("Reserving %dk for malloc() at: %08lx\n",
TOTAL_MALLOC_LEN
>> 10, addr_sp);
/*
 * (permanently) allocate a Board Info struct
 * and a permanent copy of the "global" data
 */
addr_sp -= sizeof (bd_t);//addr_sp=addr_sp-bd_t,划出一段用于存储bd_t结构体
bd = (bd_t *) addr_sp;
gd->bd = bd;
debug("Reserving %zu Bytes for Board Info at: %08lx\n",
sizeof
(bd_t), addr_sp);
addr_sp -= sizeof (gd_t);//addr_sp=addr_sp-gd_t,划出一段用于存储gd_t结构体
id = (gd_t *) addr_sp;
debug("Reserving %zu Bytes for Global Data at: %08lx\n",
sizeof
(gd_t), addr_sp);
/* setup stackpointer for exeptions */
gd->irq_sp = addr_sp;
/* leave 3 words for abort-stack    */
addr_sp -= 12;//addr_sp=addr_sp-12
/* 8-byte alignment for ABI compliance */
addr_sp &= ~0x07;
 
memcpy(id, (void *)gd, sizeof(gd_t));//把gd从原地址0x30000f80复制到重新划出的gd_t
(4)relocate_code(addr_sp,
id, addr);//内存分配好,执行代码重定位

id指向了gd_t,addr=0x33f41000是u-boot代码复制到SDRAM中的目标地址,addr_sp是用于重新设置栈(sp)的新地址

重定位
.globl
 relocate_code
relocate_code:
mov
r4, r0
/* save addr_sp */r4=r0=addr_sp
mov
r5, r1
/* save addr of gd */r5=r1=id,指向gd_t结构体
mov
r6, r2
/* save addr of destination */r6=r2=addr=0x33f41000,目标地址
/* Set up the stack*/重新设置栈
stack_setup:
mov
sp, r4//sp=r4=addr_sp
adr
r0, _start//r0=_start=0x0
cmp
r0, r6//r6=addr,比较r6与r0是否相等,相等则直接清除bss段
beq
clear_bss
/* skip relocation */相等,跳转至clear_bss段
拷贝代码时从r0拷贝到r1(addr)
mov
r1, r6
 /* r1 <- scratch for copy_loop */r1=r6=addr=0x33f41000,r1指向了目标地址
ldr
r3, _bss_start_ofs//r3=_bss_start_ofs= __bss_start - _start,bss段在flash中的起始地址相对于0地址的偏移量
add
r2, r0, r3  /* r2 <- source end address
*/r2=r0+r3=0x0+_bss_start_ofs,bss段在flash中的起始地址,因为bss段无需复制到SDRAM,所以复制代码时复制到这个地址就可以停止了
copy_loop://开始复制
ldmia
 r0!, {r9-r10} /* copy from source address [r0] */将r0中的值存入r9,r10两个寄存器(r0起始为0x0,从flash中的0x0地址开始取)
stmia
 r1!, {r9-r10}/* copy to target address [r1] */将r9,r10中的值存入r1,r1=r6=addr=0x33f41000,指向目标地址
cmp
r0, r2
/* until source end address [r2] */比较r0与r2是否相等,r2的值为所要复制代码的结束地址,当相等时代表已经全部复制完成
blo
copy_loop//不相等,继续复制
重定位除将NOR中的代码复制到SDRAM外,由于以前代码的链接地址是0地址,访问全局变量、静态变量、调用函数时使用的是“基于0地址编译得到的地址”,而复制后,链接地址已经改变,因此需要修改代码,把“基于0地址编译得到的地址”改为新地址
其基本原理是:
(1)fixrel:对于在编译时就可确定的地址,首先在rel段中取值,该值代表所要修改变量的存储地址,将该地址加上目标地址addr,得到的就是该变量在SDRAM中的新地址,同时还要修改该变量,因为该变量代表基于flash
0地址编译得到的相对地址,而复制后,链接地址已经改变了,因此该变量也要修改,将该变量加上addr目标地址存入相应的新地址即可
(2)fixabs:对于在链接时不能确定的地址,直接将其写入对应的新地址即可
(3)如何判断某一个值属于第一种还是第二种情况?
在rel中存储的数据大约如下图所示
 
 
其格式为每存一个数据,下一个数据都是标记位,是0x00000017的代表属于第一种情况,是2的代表属于第二种情况
#ifndef CONFIG_SPL_BUILD
/*
 * fix .rel.dyn relocations
 */
ldr
r0, _TEXT_BASE
/* r0 <- Text base */r0=0x0,代码段基地址
sub
r9, r6, r0
/* r9 <- relocation offset */r9=r6-r0=0x33f41000(addr)-0x0
求_dynsym_start,动态符号表起始地址:r10=0x73608
ldr
r10, _dynsym_start_ofs
/* r10 <- sym table ofs */
add
r10, r10, r0
/* r10 <- sym table in FLASH */
求_rel_dyn_start,所有文件相对信息段在flash中的相对起始地址:r2=0006b568
ldr
r2, _rel_dyn_start_ofs
/* r2 <- rel dyn start ofs */
add
r2, r2, r0
/* r2 <- rel dyn start in FLASH */
求_rel_dyn_end,所有文件相对信息段在flash中的相对结束地址:r3=00073608
ldr
r3, _rel_dyn_end_ofs
/* r3 <- rel dyn end ofs */
add
r3, r3, r0
/* r3 <- rel dyn end in FLASH */
fixloop:
ldr
r0, [r2]
/* r0 <- location to fix up, IN FLASH! */r0=[r2]=[0006b568]=00000020
add
r0, r0, r9
/* r0 <- location to fix up in RAM */r0=r0+r9=0x33f41000+00000020=0x33f41020
ldr
r1, [r2, #4]//r1=[r2+4]=[0006b568+4]=00000017(标记位),取标记位判断属于第一种还是第二种情况
and
r7, r1, #0xff//r7=r1&0xff=00000017
cmp
r7, #23
/* relative fixup? */第一种情况,执行fixrel
beq
fixrel
cmp
r7, #2
/* absolute fixup? */第二种情况,执行fixabs
beq
fixabs
/* ignore unknown type of fixup */
b
fixnext
fixabs:
/* absolute fix: set location to (offset) symbol value */
mov
r1, r1, LSR #4
/* r1 <- symbol index in .dynsym */
add
r1, r10, r1
/* r1 <- address of symbol in table */
ldr
r1, [r1, #4]
/* r1 <- symbol value */
add
r1, r1, r9
/* r1 <- relocated sym addr */
b
fixnext
fixrel:
/* relative fix: increase location by offset */
ldr
r1, [r0]//r1=[r0]=[00000020]=000001e0
add
r1, r1, r9//r1=r1+r9=000001e0+0x33f41000=33f411e0,修改变量
fixnext:
str
r1, [r0]//[r0]=r1=>[0x33f41020]=33f411e0,将修改后的变量存入相应的新地址
add
r2, r2, #8
/* each rel.dyn entry is 8 bytes */取下一个变量,加4取得的是标记位,加8才是真正待修改的变量
cmp
r2, r3//r3=_rel_dyn_end,当r2=r3时表示rel段中所有数据均已修改完毕
blo
fixloop//未修改完继续循环
#endif
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: