uboot结构分析

uboot启动分为两个阶段

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                                     /*  irq中断向量  */

       ldr   pc, _fiq                                     /*  fiq中断向量  */

    1),关闭看门狗 (start.S)

嵌入式系统上电,首先运行的的是uboot程序,uboot开始运行,首先就要对系统硬件进行相关的初始化,比如时钟,比如mmu,比如调试串口,在这个初始化过程中,基本不会出现程序跑飞,需要看门狗照顾的情况,所以,一般的做法就是关闭看门狗,避免喂狗的麻烦,等一切就绪后,正常运行时,再打开看门狗。  

 /* turn off the watchdog */

# if defined(CONFIG_S3C2400)
#  define pWTCON    0x15300000
#  define INTMSK    0x14400008    /* Interrupt-Controller base addresses */
#  define CLKDIVN    0x14800014    /* clock divisor register */
#else
#  define pWTCON    0x53000000
#  define INTMSK    0x4A000008    /* Interrupt-Controller base addresses */
#  define INTSUBMSK    0x4A00001C
#  define CLKDIVN    0x4C000014    /* clock divisor register */
# endif

    2),设置cpu模式 (start.S)

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

    3),关闭mmu和cache     

数据cache必须关闭 指令cache可以关闭也可以启动 Bootloader主要是装载内核镜像,镜像数据必须真实写回SDRAM中,所以数据cache必须关闭,而对于指令cache,不存在强制性的规定,但在一般情况下,推荐关闭cache  

在板子启动的时候是没有对mmu进行初始化的,而且这个时候也用不到mmu,为了避免他们影响启动时的初始化,所以需要先关闭mmu和缓存。

 /*
     * 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),  关闭中断

      uboot只是完成硬件初始化,环境参数设置,代码搬运等工作,用不到中断。屏蔽中断是为了避免因为意外中断使得boot失败,毕竟很多外设还没有初始化,对应中断代码也都没有准备好。

     * mask all IRQs by setting all bits in the INTMR - default
     */
    mov    r1, #0xffffffff
    ldr    r0, =INTMSK
    str    r1, [r0]
# if defined(CONFIG_S3C2410)
    ldr    r1, =0x3ff
    ldr    r0, =INTSUBMSK
    str    r1, [r0]
# endif

    5), 设置系统时钟,初始化内存

     由于SDRAM受到系统时钟的影响,初始化sdram是为第二阶段代码准备运行空间

    /* FCLK:HCLK:PCLK = 1:2:4 */
    /* default FCLK is 120 MHz ! */
    ldr    r0, =CLKDIVN
    mov    r1, #3
    str    r1, [r0]

 

 

/* memory control configuration */lowlevel_init.S
    /* 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

    /* everything is fine now */
    mov    pc, lr

    .ltorg
/* the literal pools origin */

    6), 为运行第二阶段代码设置好堆栈(start.S)

       只要将sp指针指向一段没有被使用的内存就完成栈的设置了
/*
 *************************************************************************
 *
 * Startup Code (called from the ARM reset exception vector)
 *
 * do important init only if we don't start from memory!
 * relocate armboot to ram
 * setup stack
 * jump to second stage
 *
 *************************************************************************
 */

.globl _TEXT_BASE
_TEXT_BASE:
#if defined(CONFIG_SPL_BUILD) && defined(CONFIG_SPL_TEXT_BASE)
    .word    CONFIG_SPL_TEXT_BASE
#else
    .word    CONFIG_SYS_TEXT_BASE
#endif

/*
 * These are defined in the board-specific linker script.
 * Subtracting _start from them lets the linker put their
 * relative position in the executable instead of leaving
 * them null.
 */
.globl _bss_start_ofs
_bss_start_ofs:
    .word __bss_start - _start

.globl _bss_end_ofs
_bss_end_ofs:
    .word __bss_end - _start

.globl _end_ofs
_end_ofs:
    .word _end - _start

#ifdef CONFIG_USE_IRQ
/* IRQ stack memory (calculated at run-time) */
.globl IRQ_STACK_START
IRQ_STACK_START:
    .word    0x0badc0de

................................................

    7), 跳转到二阶段

start.S _main  ----> crt0.S  board_init_f  --->board.c 

 

2.代码跳转,引导内核启动

uboot二阶段执行,找到个大神画的流程图

uboot的第二阶段是在做什么呢?

1), 首先要做的就是初始化一阶段还未初始化的硬件,主要是soc的外部硬件,像网卡什么的,

2), 初始化自身的命令,环境变量等,然后进入uboot命令行模式循环,主要是接收命令,解析命令,执行命令

 

在一阶段结束后代码跳转到了board_init_f,代码到这后具体是怎样的执行流程?

1. 为gd数据结构分配地址,并清零

2. 执行init_fnc_ptr函数指针数组中的各个初始化函数

3. 分配SDRAM下一单元为U-BOOT代码段,数据段,BSS段

分配存放bd , gd , 3个字大小的异常堆的空间

关于二阶段的一些初始化:PS(转载)

init_fnc_ptr函数指针数组中的各个初始化函数:

board_early_init_f函数在board/samsung/smdk2410目录下的smdk2410.c文件内timer_init函数在arch/arm/cpu/arm920t/s3c24x0目录下的timer.c文件内

env_init函数在common目录下的env_flash.c文件内

 init_baudrate函数在arch/arm/lib目录下的board.c文件内

serial_init函数在drivers/serial目录下的serial_s3c24x0.c文件内,在include/configs/smdk2410.h中定义了CONFIG_S3C24X0_SERIAL

console_init_f函数在common目录下的console.c文件内

display_banner函数在arch/arm/lib目录下的board.c文件内

dram_init函数在board/samsung/smdk2410目录下的smdk2410.c文件内

各种外设的初始化:

flash_init函数是在drivers/mtd目录下的cfi_flash.c文件内(因为include/configs/smdk2410.h中定义了CONFIG_FLASH_CFI_DRIVER)

nand_init函数是在divers/mtd/nand目录下的nand.c文件内定义的

env_relocate函数是在common目录下的env_common.c文件中定义的

stdio_init ()在common目录下的stdio.c文件中定义的

 jumptable_init ()在common目录下的exports.c文件中定义的

console_init_r ()是在common目录下的console.c文件中定义的

 interrupt_init () enable_interrupts ()都是在arch/arm/lib目录下的interrupts.c文件中定义

eth_initialize()函数是在net目录下的eth.c文件的第209行至第298行定义的

main_loop()在common目录下的main.c文件内定义的

接着执行 relocate_code, board_init_r

我们主要关注的是:board_init_r

board_init_r 中的主要函数 initcall_run_list(init_sequence_f)

在这个里面主要

init_fnc_t init_sequence_r[] = {
    initr_trace,
    initr_reloc,
    /* TODO: could x86/PPC have this also perhaps? */
#ifdef CONFIG_ARM
    initr_caches,
    board_init,    /* Setup chipselects */
#endif
    /*
     * TODO: printing of the clock inforamtion of the board is now
     * implemented as part of bdinfo command. Currently only support for
     * davinci SOC's is added. Remove this check once all the board
     * implement this.
     */
#ifdef CONFIG_CLOCKS
    set_cpu_clk_info, /* Setup clock information */
#endif
    initr_reloc_global_data,
    initr_serial,
    initr_announce,
    INIT_FUNC_WATCHDOG_RESET
#ifdef CONFIG_PPC
    initr_trap,
#endif
#ifdef CONFIG_ADDR_MAP
    initr_addr_map,
#endif
#if defined(CONFIG_BOARD_EARLY_INIT_R)
    board_early_init_r,
#endif
    INIT_FUNC_WATCHDOG_RESET
#ifdef CONFIG_LOGBUFFER
    initr_logbuffer,
#endif
#ifdef CONFIG_POST
    initr_post_backlog,
#endif
    INIT_FUNC_WATCHDOG_RESET
#ifdef CONFIG_SYS_DELAYED_ICACHE
    initr_icache_enable,
#endif
#if defined(CONFIG_SYS_INIT_RAM_LOCK) && defined(CONFIG_E500)
    initr_unlock_ram_in_cache,
#endif
#if defined(CONFIG_PCI) && defined(CONFIG_SYS_EARLY_PCI_INIT)
    /*
     * Do early PCI configuration _before_ the flash gets initialised,
     * because PCU ressources are crucial for flash access on some boards.
     */
    initr_pci,
#endif
#ifdef CONFIG_WINBOND_83C553
    initr_w83c553f,
#endif
    initr_barrier,
    initr_malloc,
    bootstage_relocate,
#ifdef CONFIG_ARCH_EARLY_INIT_R
    arch_early_init_r,
#endif
    power_init_board,
#ifndef CONFIG_SYS_NO_FLASH
    initr_flash,
#endif
    INIT_FUNC_WATCHDOG_RESET
#if defined(CONFIG_PPC) || defined(CONFIG_X86)
    /* initialize higher level parts of CPU like time base and timers */
    cpu_init_r,
#endif
#ifdef CONFIG_PPC
    initr_spi,
#endif
#if defined(CONFIG_X86) && defined(CONFIG_SPI)
    init_func_spi,
#endif
#ifdef CONFIG_CMD_NAND
    initr_nand,
#endif
#ifdef CONFIG_CMD_ONENAND
    initr_onenand,
#endif
#ifdef CONFIG_GENERIC_MMC
    initr_mmc,
#endif
#ifdef CONFIG_HAS_DATAFLASH
    initr_dataflash,
#endif
    initr_env,
    INIT_FUNC_WATCHDOG_RESET
    initr_secondary_cpu,
#ifdef CONFIG_SC3
    initr_sc3_read_eeprom,
#endif
#ifdef    CONFIG_HERMES
    initr_hermes,
#endif
#if defined(CONFIG_ID_EEPROM) || defined(CONFIG_SYS_I2C_MAC_OFFSET)
    mac_read_from_eeprom,
#endif
    INIT_FUNC_WATCHDOG_RESET
#if defined(CONFIG_PCI) && !defined(CONFIG_SYS_EARLY_PCI_INIT)
    /*
     * Do pci configuration
     */
    initr_pci,
#endif
    stdio_init,
    initr_jumptable,
#ifdef CONFIG_API
    initr_api,
#endif
    console_init_r,        /* fully init console as a device */
#ifdef CONFIG_DISPLAY_BOARDINFO_LATE
    show_model_r,
#endif
#ifdef CONFIG_ARCH_MISC_INIT
    arch_misc_init,        /* miscellaneous arch-dependent init */
#endif
#ifdef CONFIG_MISC_INIT_R
    misc_init_r,        /* miscellaneous platform-dependent init */
#endif
#ifdef CONFIG_HERMES
    initr_hermes_start,
#endif
    INIT_FUNC_WATCHDOG_RESET
#ifdef CONFIG_CMD_KGDB
    initr_kgdb,
#endif
#ifdef CONFIG_X86
    board_early_init_r,
#endif
    interrupt_init,
#if defined(CONFIG_ARM) || defined(CONFIG_x86)
    initr_enable_interrupts,
#endif
#ifdef CONFIG_X86
    timer_init,        /* initialize timer */
#endif
#if defined(CONFIG_STATUS_LED) && defined(STATUS_LED_BOOT)
    initr_status_led,
#endif
    /* PPC has a udelay(20) here dating from 2002. Why? */
#ifdef CONFIG_CMD_NET
    initr_ethaddr,
#endif
#ifdef CONFIG_BOARD_LATE_INIT
    board_late_init,
#endif
#ifdef CONFIG_CMD_SCSI
    INIT_FUNC_WATCHDOG_RESET
    initr_scsi,
#endif
#ifdef CONFIG_CMD_DOC
    INIT_FUNC_WATCHDOG_RESET
    initr_doc,
#endif
#ifdef CONFIG_BITBANGMII
    initr_bbmii,
#endif
#ifdef CONFIG_CMD_NET
    INIT_FUNC_WATCHDOG_RESET
    initr_net,
#endif
#ifdef CONFIG_POST
    initr_post,
#endif
#if defined(CONFIG_CMD_PCMCIA) && !defined(CONFIG_CMD_IDE)
    initr_pcmcia,
#endif
#if defined(CONFIG_CMD_IDE)
    initr_ide,
#endif
#ifdef CONFIG_LAST_STAGE_INIT
    INIT_FUNC_WATCHDOG_RESET
    /*
     * Some parts can be only initialized if all others (like
     * Interrupts) are up and running (i.e. the PC-style ISA
     * keyboard).
     */
    last_stage_init,
#endif
#ifdef CONFIG_CMD_BEDBUG
    INIT_FUNC_WATCHDOG_RESET
    initr_bedbug,
#endif
#if defined(CONFIG_PRAM) || defined(CONFIG_LOGBUFFER)
    initr_mem,
#endif
#ifdef CONFIG_PS2KBD
    initr_kbd,
#endif
#ifdef CONFIG_MODEM_SUPPORT
    initr_modem,
#endif
    run_main_loop,
};
run_main_loop 是个循环函数,也就是前面说的uboot进入命令行模式

 s = getenv("bootcmd"); //获取bootcmd命令内容

                   /* bootcmd=usbupdate;tftpupdate; fsload; bootm

                     usbupdate: 扫描U盘,升级U盘文件,自己添加不进行说明

                     tftpupdate: tftp自动化升级,自己添加不进行说明

                     fsload:加载内核文件, 自己修改,

                     bootm:引导内核,

common/env_common.c 定义 :bootcmd 

在这里会根据我们设定的参数或者默认去启动内核

bootdelay = s ? (int)simple_strtol(s, NULL, 10) : CONFIG_BOOTDELAY;

我们进入内核引导前所说的前3s按任意键可中断启动内核引导时间设置宏定义 CONFIG_BOOTDELAY ,uboot默认配置是3s