从链接脚本u-boot.lds中我们知道u-boot是从start.s这个汇编文件开始的,所以u-boot启动的第一阶段肯定也是从这里开始的,这个文件在cpu/arm_cortexa9/文件夹下,下面我们依照这个文件一步一步分析u-boot启动的第一阶段。
#include
#include
#if defined(CONFIG_ENABLE_MMU)
#include
#endif
#if defined(CONFIG_S5PV310)
#include
#endif
#if defined(CONFIG_S5PC210)
#include
#endif
上面的代码包含了一些必要的头文件。
.word 0x2000
.word 0x0
.word 0x0
.word 0x0
定义uboot程序开头的16字节校验头信息填充空间,头校验信息块内的值需要在后面写入。
.globl _start
_start: breset
ldrpc, _undefined_instruction
ldrpc, _software_interrupt
ldrpc, _prefetch_abort
ldrpc, _data_abort
ldrpc, _not_used
ldrpc, _irq
ldrpc, _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
中断向量表定义,这里我们看到有reset标号,但是它并没有像其它的中断向量一样放在向量表中,而是通过start跳转来实现的。下面我们分析reset
#if 0
/*
* set the cpu to SVC32 mode and IRQ & FIQ disable
*/
mrsr0, cpsr
bicr0, r0, #0x3f
orrr0, r0, #0xd3
msrcpsr, r0
#else//*****ly
mrsr0, cpsr
bicr0, r0, #0x1f
orrr0, r0, #0xd3
msrcpsr,r0
#endif
以上代码将CPU的工作模式设置为管理模式,并屏蔽IRQ和FIQ中断。
#if 1 //*****ly
cache_init:
mrcp15, 0, r0, c0, c0, 0@ read main ID register
andr1, r0, #0x00f00000@ variant
andr2, r0, #0x0000000f@ revision
orrr2, r2, r1, lsr #20-4@ combine variant and revision
cmpr2, #0x30
mrceqp15, 0, r0, c1, c0, 1@ read ACTLR
orreqr0, r0, #0x6@ Enable DP1(2), DP2(1)
mcreqp15, 0, r0, c1, c0, 1@ write ACTLR
/*
* Invalidate L1 I/D
*/
movr0, #0@ set up for MCR
mcrp15, 0, r0, c8, c7, 0@ invalidate TLBs
mcrp15, 0, r0, c7, c5, 0@ invalidate icache
/*
* disable MMU stuff and caches
*/
mrcp15, 0, r0, c1, c0, 0
bicr0, r0, #0x00002000@ clear bits 13 (--V-)
bicr0, r0, #0x00000007@ clear bits 2:0 (-CAM)
orrr0, r0, #0x00001000@ set bit 12 (---I) Icache
orrr0, r0, #0x00000002@ set bit 1 (--A-) Align
orrr0, r0, #0x00000800@ set bit 11 (Z---) BTB
mcrp15, 0, r0, c1, c0, 0
#endif
关闭缓存和MMU,缓存和MMU是由CP15协处理器管理的,所以要做的就是设置CP15相应的寄存器
/* Read booting information */
ldrr0, =POWER_BASE
ldrr1, [r0,#OMR_OFFSET]
bicr2, r1, #0xffffffc1
cmp r2, #0xA
moveq r3, #BOOT_ONENAND
/* SD/MMC BOOT */
cmp r2, #0x4
moveq r3, #BOOT_MMCSD
/* eMMC4.3 BOOT */
cmpr2, #0x6
moveqr3, #BOOT_EMMC43
/* eMMC441 BOOT */
cmpr2, #0x28
moveqr3, #BOOT_EMMC441
ldrr0, =INF_REG_BASE
strr3, [r0, #INF_REG3_OFFSET]
读取boot信息,判断是从哪种启动介质启动
bllowlevel_init/* go setup pll,mux,memory */
跳转到lowlevel_init进行初始化,下面我们进入lowlevel_init,由于它是和开发板相关的,所以是放在board这个文件夹里面
lowlevel_init:
#if 1//*****ly
/* use iROM stack in bl2 */
ldrsp, =0x02060000
#endif
push{lr}
/* check reset status */
ldr r0, =(INF_REG_BASE + INF_REG1_OFFSET)
ldr r1, [r0]
/* AFTR wakeup reset */
ldrr2, =S5P_CHECK_DIDLE
cmpr1, r2
beqexit_wakeup
/* Sleep wakeup reset */
ldrr2, =S5P_CHECK_SLEEP
cmpr1, r2
beqwakeup_reset
/* PS-Hold high */
ldr r0, =0x1002330c
ldr r1, [r0]
orr r1, r1, #0x300
str r1, [r0]
ldr r0, =0x11000c08
ldr r1, =0x0
str r1, [r0]
/* Clear MASK_WDT_RESET_REQUEST */
ldr r0, =0x1002040c
ldr r1, =0x00
str r1, [r0]
#ifdef check_mem /*liyang 20110822*/
/* when we already run in ram, we don't need to relocate U-Boot.
* and actually, memory controller must be configured before U-Boot
* is running in ram.
*/
ldrr0, =0xff000fff
bicr1, pc, r0/* r0
ldrr2, _TEXT_BASE/* r1
bicr2, r2, r0/* r0
cmp r1, r2 /* compare r0, r1 */
beq 1f/* r0 == r1 then skip sdram init */
#endif
/* add by cym 20130218 */
/* init system clock */
bl system_clock_init_scp
/* Memory initialize */
bl mem_ctrl_asm_init_ddr3
/* end add */
bl tzpc_init
b1f
1:
#ifdef CONFIG_EVT1___
/* store DMC density information in DRAM */
/* mem_ctrl_asm_init returns dmc_density in r6 */
ldrr0, =CFG_UBOOT_BASE
subr0, r0, #4
strr6, [r0]
#endif
#if 0 //test for mt6620 turn off GPC1(0)
/*wenpin.cui: headphone and sw uart switch init*/
ldrr0, =0x11000C44
ldrr1, [r0]
andr1, r1, #0x4
cmpr1, #0x4/*uart*/
beqout
ldr r0, =0x11400084 /* GPC1(0) */
ldr r1, [r0]/* read GPC1DAT status*/
orrr1, r1, #0x1/* GPC1(0) output high */
str r1, [r0]
ldr r0, =0x11400080 /* GPC1(0) */
ldrr1, [r0]
andr1, r1, #0xfffffff0
orr r1, r1, #0x1/* GPC1(0) output */
str r1, [r0]
#endif
out:
/* for UART */
bl uart_asm_init
它做的主要事情有:判断代码是否在内存中(如果u-boot已经在内存中,则直接跳过下面三步),系统时钟初始化,内存初始化,tzpc初始化,最后是串口初始化,回到start.s文件中
ldrr0, =0x1002330C /* PS_HOLD_CONTROL register */
ldrr1, =0x00005300 /* PS_HOLD output high*/
strr1, [r0]
设置开发板供电锁存
/* get ready to call C functions */
ldrsp, _TEXT_PHY_BASE/* setup temp stack pointer */
subsp, sp, #12
movfp, #0/* no previous frame, so fp=0 */
在内存中为接下来的C函数设置栈
ldrr0, =0xff000fff
bicr1, pc, r0/* r0
ldrr2, _TEXT_BASE/* r1
bicr2, r2, r0/* r0
cmp r1, r2 /* compare r0, r1 */
beq after_copy/* r0 == r1 then skip flash copy */
判断当前代码是否运行在SDRAM中,如果当前代码运行在SDRAM中,则跳过代码重定位。
ldrr0, =INF_REG_BASE
ldrr1, [r0, #INF_REG3_OFFSET]
cmpr1, #BOOT_NAND/* 0x0 => boot device is nand */
beqnand_boot
cmpr1, #BOOT_ONENAND/* 0x1 => boot device is onenand */
beqonenand_boot
cmp r1, #BOOT_EMMC441
beq emmc441_boot
cmp r1, #BOOT_EMMC43
beq emmc_boot
cmp r1, #BOOT_MMCSD
beq mmcsd_boot
cmp r1, #BOOT_NOR
beq nor_boot
cmp r1, #BOOT_SEC_DEV
beq mmcsd_boot
判断启动方式
nand_boot:
movr0, #0x1000
blcopy_from_nand
bafter_copy
onenand_boot:
blonenand_bl2_copy /*goto 0x1010*/
bafter_copy
//ly
second_mmcsd_boot:
ldr r3, =BOOT_MMCSD
ldrr0, =INF_REG_BASE
strr3, [r0, #INF_REG3_OFFSET]
mmcsd_boot:
#ifdef CONFIG_CLK_1000_400_200
ldrr0, =CMU_BASE
ldrr2, =CLK_DIV_FSYS2_OFFSET
ldrr1, [r0, r2]
orr r1, r1, #0xf
str r1, [r0, r2]
#endif
bl movi_uboot_copy
b after_copy
emmc_boot:
#if defined(CONFIG_CLK_1000_400_200) || defined(CONFIG_CLK_1000_200_200) || defined(CONFIG_CLK_800_400_200)
ldrr0, =CMU_BASE
ldrr2, =CLK_DIV_FSYS1_OFFSET
ldrr1, [r0, r2]
orr r1, r1, #0x3
str r1, [r0, r2]
#endif
blemmc_uboot_copy
bafter_copy
emmc441_boot:
#if defined(CONFIG_CLK_1000_400_200) || defined(CONFIG_CLK_1000_200_200) || defined(CONFIG_CLK_800_400_200)
ldrr0, =CMU_BASE
ldrr2, =CLK_DIV_FSYS3_OFFSET
ldrr1, [r0, r2]
orr r1, r1, #0x3
str r1, [r0, r2]
#endif
blemmc441_uboot_copy
//ly 20110824
ldr r0, =0x43e00000
ldr r1, [r0]
ldr r2, =0x2000
cmp r1, r2
bne second_mmcsd_boot
bafter_copy
nor_boot:
@blread_hword
bafter_copy
不同的启动方式所对应的启动代码,注意,这部分的代码,只有当u-boot不在内存中时才会执行
#if defined(CONFIG_ENABLE_MMU)
enable_mmu:
/* enable domain access */
ldrr5, =0x0000ffff
mcrp15, 0, r5, c3, c0, 0@load domain access register
/* Set the TTB register */
ldrr0, _mmu_table_base
ldrr1, =CFG_PHY_UBOOT_BASE
ldrr2, =0xfff00000
bicr0, r0, r2
orrr1, r0, r1
mcrp15, 0, r1, c2, c0, 0
/* Enable the MMU */
mmu_on:
mrcp15, 0, r0, c1, c0, 0
orrr0, r0, #1
mcrp15, 0, r0, c1, c0, 0
nop
nop
nop
nop
#endif
设置mmu,使能mmu
stack_setup:
#if defined(CONFIG_MEMORY_UPPER_CODE)
ldrsp, =(CFG_UBOOT_BASE + CFG_UBOOT_SIZE - 0x1000)
#else
ldrr0, _TEXT_BASE/* upper 128 KiB: relocated uboot */
subr0, r0, #CONFIG_SYS_MALLOC_LEN/* malloc area */
subr0, r0, #CFG_GBL_DATA_SIZE /* bdinfo */
#if defined(CONFIG_USE_IRQ)
subr0, r0, #(CONFIG_STACKSIZE_IRQ+CONFIG_STACKSIZE_FIQ)
#endif
subsp, r0, #12/* leave 3 words for abort-stack */
#endif
通过对内存整体使用规划,在内存中合适的地方设置栈
clear_bss:
ldrr0, _bss_start/* find start of bss segment */
ldrr1, _bss_end/* stop here */
mov r2, #0x00000000/* clear */
clbss_l:
strr2, [r0]/* clear loop... */
addr0, r0, #4
cmpr0, r1
bleclbss_l
ldrpc, _start_armboot
_start_armboot:
.word start_armboot
清除bss段,跳转到start_armboot执行,u-boot启动第一阶段完成。
下面总结一下,u-boot第一阶段做了哪些事情:
设置异常向量(exception vector)
关闭IRQ、FIQ,设置SVC模式
关闭缓存、MMU
确定启动介质
lowlevel_init(主要初始化系统时钟、内存初始化、串口初始化等)
设置开发板供电锁存
设置内存中的栈
将uboot从EMMC拷贝到内存中
设置并开启MMU
通过对内存整体使用规划,在内存中合适的地方设置栈
清除bss段,远跳转到start_armboot执行,u-boot第一阶段执行完
领取专属 10元无门槛券
私享最新 技术干货