U-Boot之启动第一阶段

Posted yinshijia

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了U-Boot之启动第一阶段相关的知识,希望对你有一定的参考价值。

基于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第一阶段做了哪些事情:

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

以上是关于U-Boot之启动第一阶段的主要内容,如果未能解决你的问题,请参考以下文章

U-Boot之LINUX内核引导

u-boot 启动过程

Linux内核启动及根文件系统载入过程

Linux嵌入式驱动学习之路⑤u-boot启动流程分析

u-boot 启动流程分成哪两个阶段?分别要完成哪些功能

u-boot.2012.10——mini2440(启动流程分析)