基于samsung的Exynos 4412
从链接脚本u-boot.lds中我们知道u-boot是从start.s这个汇编文件开始的,所以u-boot启动的第一阶段肯定也是从这里开始的,这个文件在cpu/arm_cortexa9/文件夹下,下面我们依照这个文件一步一步分析u-boot启动的第一阶段。
#include <config.h>
#include <version.h>
#if defined(CONFIG_ENABLE_MMU)
#include <asm/proc/domain.h>
#endif
#if defined(CONFIG_S5PV310)
#include <s5pv310.h>
#endif
#if defined(CONFIG_S5PC210)
#include <s5pc210.h>
#endif
上面的代码包含了一些必要的头文件。
.word 0x2000
.word 0x0
.word 0x0
.word 0x0
定义uboot程序开头的16字节校验头信息填充空间,头校验信息块内的值需要在后面写入。
.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
_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
*/
mrs r0, cpsr
bic r0, r0, #0x3f
orr r0, r0, #0xd3
msr cpsr, r0
#else//*****ly
mrs r0, cpsr
bic r0, r0, #0x1f
orr r0, r0, #0xd3
msr cpsr,r0
#endif
以上代码将CPU的工作模式设置为管理模式,并屏蔽IRQ和FIQ中断。
#if 1 //*****ly
cache_init:
mrc p15, 0, r0, c0, c0, 0 @ read main ID register
and r1, r0, #0x00f00000 @ variant
and r2, r0, #0x0000000f @ revision
orr r2, r2, r1, lsr #20-4 @ combine variant and revision
cmp r2, #0x30
mrceq p15, 0, r0, c1, c0, 1 @ read ACTLR
orreq r0, r0, #0x6 @ Enable DP1(2), DP2(1)
mcreq p15, 0, r0, c1, c0, 1 @ write ACTLR
/*
* Invalidate L1 I/D
*/
mov r0, #0 @ set up for MCR
mcr p15, 0, r0, c8, c7, 0 @ invalidate TLBs
mcr p15, 0, r0, c7, c5, 0 @ invalidate icache
/*
* disable MMU stuff and caches
*/
mrc p15, 0, r0, c1, c0, 0
bic r0, r0, #0x00002000 @ clear bits 13 (--V-)
bic r0, r0, #0x00000007 @ clear bits 2:0 (-CAM)
orr r0, r0, #0x00001000 @ set bit 12 (---I) Icache
orr r0, r0, #0x00000002 @ set bit 1 (--A-) Align
orr r0, r0, #0x00000800 @ set bit 11 (Z---) BTB
mcr p15, 0, r0, c1, c0, 0
#endif
关闭缓存和MMU,缓存和MMU是由CP15协处理器管理的,所以要做的就是设置CP15相应的寄存器
/* Read booting information */
ldr r0, =POWER_BASE
ldr r1, [r0,#OMR_OFFSET]
bic r2, r1, #0xffffffc1
cmp r2, #0xA
moveq r3, #BOOT_ONENAND
/* SD/MMC BOOT */
cmp r2, #0x4
moveq r3, #BOOT_MMCSD
/* eMMC4.3 BOOT */
cmp r2, #0x6
moveq r3, #BOOT_EMMC43
/* eMMC441 BOOT */
cmp r2, #0x28
moveq r3, #BOOT_EMMC441
ldr r0, =INF_REG_BASE
str r3, [r0, #INF_REG3_OFFSET]
读取boot信息,判断是从哪种启动介质启动
bl lowlevel_init /* go setup pll,mux,memory */
跳转到lowlevel_init进行初始化,下面我们进入lowlevel_init,由于它是和开发板相关的,所以是放在board这个文件夹里面
lowlevel_init:
#if 1//*****ly
/* use iROM stack in bl2 */
ldr sp, =0x02060000
#endif
push {lr}
/* check reset status */
ldr r0, =(INF_REG_BASE + INF_REG1_OFFSET)
ldr r1, [r0]
/* AFTR wakeup reset */
ldr r2, =S5P_CHECK_DIDLE
cmp r1, r2
beq exit_wakeup
/* Sleep wakeup reset */
ldr r2, =S5P_CHECK_SLEEP
cmp r1, r2
beq wakeup_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.
*/
ldr r0, =0xff000fff
bic r1, pc, r0 /* r0 <- current base addr of code */
ldr r2, _TEXT_BASE /* r1 <- original base addr in ram */
bic r2, r2, r0 /* r0 <- current base addr of code */
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
b 1f
1:
#ifdef CONFIG_EVT1___
/* store DMC density information in DRAM */
/* mem_ctrl_asm_init returns dmc_density in r6 */
ldr r0, =CFG_UBOOT_BASE
sub r0, r0, #4
str r6, [r0]
#endif
#if 0 //test for mt6620 turn off GPC1(0)
/*wenpin.cui: headphone and sw uart switch init*/
ldr r0, =0x11000C44
ldr r1, [r0]
and r1, r1, #0x4
cmp r1, #0x4 /*uart*/
beq out
ldr r0, =0x11400084 /* GPC1(0) */
ldr r1, [r0] /* read GPC1DAT status*/
orr r1, r1, #0x1 /* GPC1(0) output high */
str r1, [r0]
ldr r0, =0x11400080 /* GPC1(0) */
ldr r1, [r0]
and r1, 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文件中
ldr r0, =0x1002330C /* PS_HOLD_CONTROL register */
ldr r1, =0x00005300 /* PS_HOLD output high */
str r1, [r0]
设置开发板供电锁存
/* get ready to call C functions */
ldr sp, _TEXT_PHY_BASE /* setup temp stack pointer */
sub sp, sp, #12
mov fp, #0 /* no previous frame, so fp=0 */
在内存中为接下来的C函数设置栈
ldr r0, =0xff000fff
bic r1, pc, r0 /* r0 <- current base addr of code */
ldr r2, _TEXT_BASE /* r1 <- original base addr in ram */
bic r2, r2, r0 /* r0 <- current base addr of code */
cmp r1, r2 /* compare r0, r1 */
beq after_copy /* r0 == r1 then skip flash copy */
判断当前代码是否运行在SDRAM中,如果当前代码运行在SDRAM中,则跳过代码重定位。
ldr r0, =INF_REG_BASE
ldr r1, [r0, #INF_REG3_OFFSET]
cmp r1, #BOOT_NAND /* 0x0 => boot device is nand */
beq nand_boot
cmp r1, #BOOT_ONENAND /* 0x1 => boot device is onenand */
beq onenand_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:
mov r0, #0x1000
bl copy_from_nand
b after_copy
onenand_boot:
bl onenand_bl2_copy /*goto 0x1010*/
b after_copy
//ly
second_mmcsd_boot:
ldr r3, =BOOT_MMCSD
ldr r0, =INF_REG_BASE
str r3, [r0, #INF_REG3_OFFSET]
mmcsd_boot:
#ifdef CONFIG_CLK_1000_400_200
ldr r0, =CMU_BASE
ldr r2, =CLK_DIV_FSYS2_OFFSET
ldr r1, [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)
ldr r0, =CMU_BASE
ldr r2, =CLK_DIV_FSYS1_OFFSET
ldr r1, [r0, r2]
orr r1, r1, #0x3
str r1, [r0, r2]
#endif
bl emmc_uboot_copy
b after_copy
emmc441_boot:
#if defined(CONFIG_CLK_1000_400_200) || defined(CONFIG_CLK_1000_200_200) || defined(CONFIG_CLK_800_400_200)
ldr r0, =CMU_BASE
ldr r2, =CLK_DIV_FSYS3_OFFSET
ldr r1, [r0, r2]
orr r1, r1, #0x3
str r1, [r0, r2]
#endif
bl emmc441_uboot_copy
//ly 20110824
ldr r0, =0x43e00000
ldr r1, [r0]
ldr r2, =0x2000
cmp r1, r2
bne second_mmcsd_boot
b after_copy
nor_boot:
@ bl read_hword
b after_copy
不同的启动方式所对应的启动代码,注意,这部分的代码,只有当u-boot不在内存中时才会执行
#if defined(CONFIG_ENABLE_MMU)
enable_mmu:
/* enable domain access */
ldr r5, =0x0000ffff
mcr p15, 0, r5, c3, c0, 0 @load domain access register
/* Set the TTB register */
ldr r0, _mmu_table_base
ldr r1, =CFG_PHY_UBOOT_BASE
ldr r2, =0xfff00000
bic r0, r0, r2
orr r1, r0, r1
mcr p15, 0, r1, c2, c0, 0
/* Enable the MMU */
mmu_on:
mrc p15, 0, r0, c1, c0, 0
orr r0, r0, #1
mcr p15, 0, r0, c1, c0, 0
nop
nop
nop
nop
#endif
设置mmu,使能mmu
stack_setup:
#if defined(CONFIG_MEMORY_UPPER_CODE)
ldr sp, =(CFG_UBOOT_BASE + CFG_UBOOT_SIZE - 0x1000)
#else
ldr r0, _TEXT_BASE /* upper 128 KiB: relocated uboot */
sub r0, r0, #CONFIG_SYS_MALLOC_LEN /* malloc area */
sub r0, r0, #CFG_GBL_DATA_SIZE /* bdinfo */
#if defined(CONFIG_USE_IRQ)
sub r0, r0, #(CONFIG_STACKSIZE_IRQ+CONFIG_STACKSIZE_FIQ)
#endif
sub sp, r0, #12 /* leave 3 words for abort-stack */
#endif
通过对内存整体使用规划,在内存中合适的地方设置栈
clear_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
ldr pc, _start_armboot
_start_armboot:
.word start_armboot
清除bss段,跳转到start_armboot执行,u-boot启动第一阶段完成。
下面总结一下,u-boot第一阶段做了哪些事情:
- 设置异常向量(exception vector)
- 关闭IRQ、FIQ,设置SVC模式
- 关闭缓存、MMU
- 确定启动方式
- lowlevel_init(主要初始化系统时钟、SDRAM初始化、串口初始化等)
- 设置开发板供电锁存
- 设置内存中的栈
- 将uboot从EMMC拷贝到内存中
- 设置并开启MMU
- 通过对内存整体使用规划,在内存中合适的地方设置栈
- 清除bss段,远跳转到start_armboot执行,u-boot第一阶段执行完