UBoot启动流程

1. UBoot启动流程可以分为两个阶段:

(1)第一阶段

① 硬件设备初始化

② 加载UBoot第二阶段代码到RAM空间

③ 设置好栈

④ 跳转到第二阶段代码入口

(2)第二阶段

① 初始化本阶段使用的硬件设备

② 检测系统内存映射

③ 将内核从Flash读取到RAM中

④ 为内核设置启动参数

⑤ 调用内核

 

2. UBoot启动第一阶段

根据链接器脚本cpu/arm920t/u-boot.lds中指定的链接方式,可以看出,u-boot代码段的第一个链接的是cpu/arm920t/start.o,入口标号是_start,因此u-boot的入口代码在对应源文件cpu/arm920t/start.S中

(1)设置异常向量表:

.globl _start
_start:    b    start_code
    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

    .balignl 16,0xdeadbeef

(2)CPU进入SVC模式

    /*
     * set the cpu to SVC32 mode
     */
    mrs    r0, cpsr
    bic    r0, r0, #0x1f
    orr    r0, r0, #0xd3
    msr    cpsr, r0

(3)关闭MMU和Cache

/*
     * flush v4 I/D caches
     */
    mov    r0, #0
    mcr    p15, 0, r0, c7, c7, 0    /* flush v3/v4 cache */
    mcr    p15, 0, r0, c8, c7, 0    /* flush v4 TLB */

    /*
     * disable MMU stuff and caches
     */
    mrc    p15, 0, r0, c1, c0, 0
    bic    r0, r0, #0x00002300    @ clear bits 13, 9:8 (--V- --RS)
    bic    r0, r0, #0x00000087    @ clear bits 7, 2:0 (B--- -CAM)
    orr    r0, r0, #0x00000002    @ set bit 2 (A) Align
    orr    r0, r0, #0x00001000    @ set bit 12 (I) I-Cache
    mcr    p15, 0, r0, c1, c0, 0

(4)关闭看门狗

    /* 
     *turn off the watchdog 
     */
#define pWTCON    0x53000000
#define INTMSK    0x4A000008    /* Interupt-Controller base addresses */
#define INTSUBMSK    0x4A00001C
    ldr    r0, =pWTCON
    mov    r1, #0x0
    str    r1, [r0]

(5)屏蔽中断

    /*
     * mask all IRQs by setting all bits in the INTMR - default
     */
    mov    r1, #0xffffffff
    ldr    r0, =INTMSK
    str    r1, [r0]
    
    ldr    r1, =0x7fff
    ldr    r0, =INTSUBMSK
    str    r1, [r0]

(6)设置MPLLCON、UPLLCON、CLKDIVN

#define CLKDIVN            0x4C000014        /* Clock divisor register */
#define CLK_CTL_BASE     0x4C000000
#define MDIV_405         (0x7f << 12)    /* Default FCLK is 405 MHz! */
#define PSDIV_405         0x21
#define MDIV MDIV_405
#define PSDIV PSDIV_405
#define CLKDIV             0x05            /* FCLK:HCLK:PCLK = 1:4:8 */
    
    ldr    r0, =CLKDIVN
    mov    r1, #CLKDIV
    str    r1, [r0]

    /* Set arm920t to Asynchronous Clock Mode (405 MHz)*/
    mrc p15, 0, r1, c1, c0, 0
    orr r1, r1, #0xc0000000
    mcr p15, 0, r1, c1, c0, 0
    
    mov r1, #CLK_CTL_BASE
    mov r2, #MDIV
    add r2, r2, #PSDIV
    str r2, [r1, #0x04]        /* MPLLCON */

(7)Nand初始化(Nand启动)

ldr r0, =S3C2440_NAND_BASE
    ldr r1, =0x001210
    str r1, [r0, #NFCONF_OFFSET]
    
    mov r1, #0x03
    str r1, [r0, #NFCONT_OFFSET]
    
    mov pc, lr

(8)存储控制器初始化(Memeory)

/* 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
    sub    r0, r0, r1
    ldr    r1, =BWSCON    /* Bus Width Status Controller */
    add     r2, r0, #13*4
0:
    ldr     r3, [r0], #4
    str     r3, [r1], #4
    cmp     r2, r0
    bne     0b

    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

(9)复制u-boot第二阶段的代码到RAM

relocate:                /* relocate U-Boot to RAM        */
    adr    r0, _start        /* r0 <- current position of code   */
    ldr    r1, _TEXT_BASE        /* test if we run from flash or RAM */
    cmp    r0, r1            /* don't reloc during debug         */
    beq    stack_setup
    
    mov r2, #0
    str r2, [r2]
    ldr r2, [r2]
    cmp r2, #0
    bne copy_to_ram_from_nor

    mov sp, #0x1000
    bl copy_to_ram_from_nand
    b stack_setup
    
copy_to_ram_from_nor:    
    ldr    r2, _armboot_start
    ldr    r3, _bss_start
    sub    r2, r3, r2        /* r2 <- size of armboot            */
    add    r2, r0, r2        /* r2 <- source end address         */

copy_loop:
    ldmia    r0!, {r3-r10}        /* copy from source address [r0]    */
    stmia    r1!, {r3-r10}        /* copy to   target address [r1]    */
    cmp    r0, r2            /* until source end addreee [r2]    */
    ble    copy_loop

(10)设置栈

stack_setup:
    ldr    r0, _TEXT_BASE        /* upper 128 KiB: relocated uboot   */
    sub    r0, r0, #CONFIG_SYS_MALLOC_LEN    /* malloc area              */
    sub    r0, r0, #CONFIG_SYS_GBL_DATA_SIZE /* bdinfo                 */

(11)清除BSS段

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

(12)跳转到第二阶段代码入口

ldr    pc, _start_armboot

(13)UBoot内存分配图

UBoot启动流程

 

3. UBoot启动第二阶段

(1)gd_t结构体:存储全局数据区的数据

typedef    struct    global_data {
    bd_t        *bd;
    unsigned long    flags;
    unsigned long    baudrate;
    unsigned long    have_console;    /* serial_init() was called */
    unsigned long    env_addr;    /* Address  of Environment struct */
    unsigned long    env_valid;    /* Checksum of Environment valid? */
    unsigned long    fb_base;    /* base address of frame buffer */
#ifdef CONFIG_VFD
    unsigned char    vfd_type;    /* display type */
#endif
#ifdef CONFIG_FSL_ESDHC
    unsigned long    sdhc_clk;
#endif
#if 0
    unsigned long    cpu_clk;    /* CPU clock in Hz!        */
    unsigned long    bus_clk;
    phys_size_t    ram_size;    /* RAM size */
    unsigned long    reset_status;    /* reset status register at boot */
#endif
    void        **jt;        /* jump table */
} gd_t;

① u-boot使用了一个存储在寄存器中的指针gd来记录全局数据区的地址(指针存放在寄存器r8中)

#define DECLARE_GLOBAL_DATA_PTR     register volatile gd_t *gd asm ("r8")

② 根据u-boot内存分布图,可以计算gd的值

gd = TEXT_BASE - CONFIG_SYS_MALLOC_LEN - sizeof(gd_t)

与start_armboot()函数中的计算方法一致

gd = (gd_t*)(_armboot_start - CONFIG_SYS_MALLOC_LEN - sizeof(gd_t));

(2)bd_t结构:存放板级相关的全局数据

typedef struct bd_info {
    int            bi_baudrate;    /* serial console baudrate */
    unsigned long    bi_ip_addr;    /* IP Address */
    struct environment_s           *bi_env;
    ulong            bi_arch_number;    /* unique id for this board */
    ulong            bi_boot_params;    /* where this board expects params */
    struct                /* RAM configuration */
    {
    ulong start;
    ulong size;
    }            bi_dram[CONFIG_NR_DRAM_BANKS];
} bd_t;

(3)init_sequence数组

① u-boot使用一个数组init_sequence来存储大多数开发板都要执行的初始化函数的函数指针

init_fnc_t *init_sequence[] = {

    board_init,        /* basic board dependent setup */

    timer_init,        /* initialize timer */
    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 */

    dram_init,        /* configure available RAM banks */

    display_dram_config,
    NULL,
};

② board_init函数配置了MPLLCON、UPLLCON和部分GPIO寄存器的值,还设置了u-boot的机器码和内核启动参数地址

gd->bd->bi_arch_number = MACH_TYPE_MINI2440 ;
gd->bd->bi_boot_params = 0x30000100;

③ dram_init函数

int dram_init (void)
{
    gd->bd->bi_dram[0].start = PHYS_SDRAM_1;
    gd->bd->bi_dram[0].size = PHYS_SDRAM_1_SIZE;

    return 0;
}

(4)start_armboot()顺序分析

void start_armboot (void)
{
    init_fnc_t **init_fnc_ptr;
    char *s;
    ...
    /* Pointer is writable since we allocated a register for it */
    gd = (gd_t*)(_armboot_start - CONFIG_SYS_MALLOC_LEN - sizeof(gd_t));
    /* compiler optimization barrier needed for GCC >= 3.4 */
    __asm__ __volatile__("": : :"memory");

    memset ((void*)gd, 0, sizeof (gd_t));
    gd->bd = (bd_t*)((char*)gd - sizeof(bd_t));
    memset (gd->bd, 0, sizeof (bd_t));

    gd->flags |= GD_FLG_RELOC;

    monitor_flash_len = _bss_start - _armboot_start;

    for (init_fnc_ptr = init_sequence; *init_fnc_ptr; ++init_fnc_ptr) {
        if ((*init_fnc_ptr)() != 0) {
            hang ();
        }
    }

    /* armboot_start is defined in the board-specific linker script */
    mem_malloc_init (_armboot_start - CONFIG_SYS_MALLOC_LEN,
            CONFIG_SYS_MALLOC_LEN);

#ifndef CONFIG_SYS_NO_FLASH
    /* configure available FLASH banks */
    display_flash_config (flash_init ());
#endif /* CONFIG_SYS_NO_FLASH */
    ...
#if defined(CONFIG_CMD_NAND)
    puts ("NAND:  ");
    nand_init();        /* go init the NAND */
#endif
    ...
    /* initialize environment */
    env_relocate ();
    ...
    /* IP Address */
    gd->bd->bi_ip_addr = getenv_IPaddr ("ipaddr");

    stdio_init ();    /* get the devices list going. */

    jumptable_init ();
    ...
    console_init_r ();    /* fully init console as a device */
    ...
    /* enable exceptions */
    enable_interrupts ();
    ...
#if defined(CONFIG_CMD_NET)
#if defined(CONFIG_NET_MULTI)
    puts ("Net:   ");
#endif
    eth_initialize(gd->bd);
#if defined(CONFIG_RESET_PHY_R)
    debug ("Reset Ethernet PHY\n");
    reset_phy();
#endif
#endif
    ...
    /* main_loop() can return to retry autoboot, if so just run it again. */
    for (;;) {
        main_loop ();
    }

    /* NOTREACHED - no way out of command loop except booting */
}

(5)main_loop函数分析

① 设置启动次数有关参数

② 启动Modem功能

③ 设置版本号、初始化命令自动完成功能

④ 启动延时和启动菜单

⑤ 执行命令循环

 

上一篇:php imagerotate()在png上破坏alpha?


下一篇:php – codeigniter调整图像大小并创建缩略图