U-Boot之启动第一阶段

基于samsung的Exynos 4412,U-Boot版本为2010.03

从链接脚本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第一阶段做了哪些事情:

  1. 设置异常向量(exception vector)
  2. 关闭IRQ、FIQ,设置SVC模式
  3. 关闭缓存、MMU
  4. 确定启动介质
  5. lowlevel_init(主要初始化系统时钟、内存初始化、串口初始化等)
  6. 设置开发板供电锁存
  7. 设置内存中的栈
  8. 将uboot从EMMC拷贝到内存中
  9. 设置并开启MMU
  10. 通过对内存整体使用规划,在内存中合适的地方设置栈
  11. 清除bss段,远跳转到start_armboot执行,u-boot第一阶段执行完
posted @ 2019-12-11 11:44  ShijiaYin  阅读(400)  评论(0编辑  收藏  举报