Uboot流程分析

Posted maogefff

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Uboot流程分析相关的知识,希望对你有一定的参考价值。

1. uboot的配置分析

1).配置入口分析

首先分析配置:

从make mx6dl_sabresd_android_config可知配置项,搜索Makefile:

 

mx6solo_sabresd_android_config             \

mx6dl_sabresd_config                \

mx6dl_sabresd_mfg_config         \

mx6dl_sabresd_android_config                 \   

mx6q_sabresd_config                 \

mx6q_sabresd_android_config             \

mx6q_sabresd_mfg_config                \

mx6q_sabresd_iram_config  : unconfig

       @[ -z "$(findstring iram_,[email protected])" ] || \

              {echo "TEXT_BASE = 0x00907000" > $(obj)board/freescale/mx6q_sabresd/config.tmp ; \

                echo "... with iram configuration" ; \

              }

       @$(MKCONFIG) $(@:_config=) arm arm_cortexa8 mx6q_sabresd freescale mx6

 

其中MKCONFIG就是mkconfig

展开后结果为:mkconfig mx6dl_sabresd_android  arm arm_cortexa8 mx6q_sabresd freescale mx6

 

2). mkconfig

然后打开mkconfig查看:

 

APPEND=no   # Default: Create new config file

BOARD_NAME=""   # Name to print in make output

 

#循环查看参数并处理

while [ $# -gt 0 ] ; do

    case "$1" in

    --) shift ; break ;;

    -a) shift ; APPEND=yes ;;

    -n) shift ; BOARD_NAME="${1%%_config}" ; shift ;;

    *)  break ;;

    esac

done

 

[ "${BOARD_NAME}" ] || BOARD_NAME="$1"

#当参数数量小于4或者大于6个的时候退出

[ $# -lt 4 ] && exit 1

[ $# -gt 6 ] && exit 1

 

#这里就是配置时窗口显示的Configuring for mx6dl_sabresd_android board...

echo "Configuring for ${BOARD_NAME} board..."

 

#

# Create link to architecture specific headers

# 根据不同的架构,将不同的文件进行软连接

if [ "$SRCTREE" != "$OBJTREE" ] ; then

    mkdir -p ${OBJTREE}/include

    mkdir -p ${OBJTREE}/include2

    cd ${OBJTREE}/include2

    rm -f asm

    ln -s ${SRCTREE}/include/asm-$2 asm

    LNPREFIX="../../include2/asm/"

    cd ../include

    rm -rf asm-$2

    rm -f asm

    mkdir asm-$2

    ln -s asm-$2 asm

else

    cd ./include

    rm -f asm

    ln -s asm-$2 asm

fi

 

rm -f asm-$2/arch

 

if [ -z "$6" -o "$6" = "NULL" ] ; then

    ln -s ${LNPREFIX}arch-$3 asm-$2/arch

else

    ln -s ${LNPREFIX}arch-$6 asm-$2/arch

fi

 

if [ "$2" = "arm" ] ; then

    rm -f asm-$2/proc

    ln -s ${LNPREFIX}proc-armv asm-$2/proc

fi

 

#创建一个/include/config.mk,并将传进来的参数放入里面

echo "ARCH   = $2" >  config.mk

echo "CPU    = $3" >> config.mk

echo "BOARD  = $4" >> config.mk

 

[ "$5" ] && [ "$5" != "NULL" ] && echo "VENDOR = $5" >> config.mk

 

[ "$6" ] && [ "$6" != "NULL" ] && echo "SOC    = $6" >> config.mk

 

#创建/include/config.h并将两个架构相关的头文件放入其中

if [ "$APPEND" = "yes" ]    # Append to existing config file

then

    echo >> config.h

else

    > config.h      # Create new config file

fi

echo "/* Automatically generated - do not edit */" >>config.h

echo "#include <configs/$1.h>" >>config.h

echo "#include <asm/config.h>" >>config.h

 

exit 0

 

查看MKCONFIG可知,最后这五个会生成include/config.mk中

 

ARCH   = arm

CPU    = arm_cortexa8

BOARD  = mx6q_sabresd

VENDOR = freescale

SOC    = mx6

 

由此可以推出./board/freescale/mx6q_sabresd/u-boot.lds

 

由此可知入口为flash_header.S

3).编译过程分析

通过之前分析查看/include/config.mk

ARCH   = arm

CPU    = arm_cortexa8

BOARD  = mx6q_sabresd

VENDOR = freescale

SOC    = mx6

 

通过全局搜索可以找到/board/freescale/mx6q_sabresd/u-boot.lds为链接脚本文件

 

OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm")

OUTPUT_ARCH(arm)

ENTRY(_start)

SECTIONS

{

    . = 0x00000000;

 

    . = ALIGN(4);

    .text      :

    {

      /* WARNING - the following is hand-optimized to fit within    */

      /* the sector layout of our flash chips!  XXX FIXME XXX   */

      board/freescale/mx6q_sabresd/flash_header.o   (.text.flasheader)

      cpu/arm_cortexa8/start.o

      board/freescale/mx6q_sabresd/libmx6q_sabresd.a    (.text)

      lib_arm/libarm.a      (.text)

      net/libnet.a          (.text)

      drivers/mtd/libmtd.a      (.text)

      drivers/mmc/libmmc.a      (.text)

 

      . = DEFINED(env_offset) ? env_offset : .;

      common/env_embedded.o(.text)

 

      *(.text)

    }

 

    . = ALIGN(4);

    .rodata : { *(SORT_BY_ALIGNMENT(SORT_BY_NAME(.rodata*))) }

 

    . = ALIGN(4);

    .data : { *(.data) }

 

    . = ALIGN(4);

    .got : { *(.got) }

 

    . = .;

    __u_boot_cmd_start = .;

    .u_boot_cmd : { *(.u_boot_cmd) }

    __u_boot_cmd_end = .;

 

    . = ALIGN(4);

    _end_of_copy = .; /* end_of ROM copy code here */

 

    /* Extend to align to 0x1000, then put the Hab Data */

    . = ALIGN(0x1000);

    __hab_data = .;

    . = . + 0x2000;

    __data_enc_key = .;

    /* actually, only 64bytes are needed, but this generates

        a size multiple of 512bytes, which is optimal for SD boot */

    . = . + 0x200;

    __hab_data_end = .;

    /* End of Hab Data, Place it before BSS section */

 

    __bss_start = .;

    .bss : { *(.bss) }

    _end = .;

}

 

从这个文件可知,代码的入口为_start

text为代码段,rodata为只读数据段,u_boot_cmd为命令的存放空间。

 

发现第一个文件为board/freescale/mx6q_sabresd/flash_header.S,他是保存在(.text.flasheader)段中

2. uboot的第一阶段

从flash_header.S中可知

.section ".text.flasheader", "x"

       b     _start

 

这个时候跳入了_start中了,其在cpu/arm_cortexa8/start.S当中

 

/*第一步:首先进行复位*/

.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

_pad:           .word 0x12345678 /* now 16*4=64 */

.global _end_vect

_end_vect:

 

.balignl 16,0xdeadbeef

 

。。。省略。。。

 

/*复位,设置CPSR寄存器*/

reset:

    /*

     * set the cpu to SVC32 mode

     */

    mrs r0, cpsr

    bic r0, r0, #0x1f

    orr r0, r0, #0xd3

    msr cpsr,r0

   

    /*没有定义CONFIG_SKIP_LOWLEVEL_INIT ,所以进入cpu_init_crit */

#ifndef CONFIG_SKIP_LOWLEVEL_INIT

    bl  cpu_init_crit

#endif

。。。省略。。。

 

cpu_init_crit:

    /*

     * 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

 

    /*关闭mmu和catch*/

    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, #0x00000002 @ set bit 1 (--A-) Align

    orr r0, r0, #0x00000800 @ set bit 12 (Z---) BTB

    mcr p15, 0, r0, c1, c0, 0

 

    mov ip, lr          @ persevere link reg across call

    bl  lowlevel_init  @这里是跳转到lowlevel_init去初始化一些基本的板级信息。

    mov lr, ip          @ restore link

    mov pc, lr          @ back to my caller

 

通过全局搜索可知lowlevel_init在board\freescale\mx6q_sabresd目录中的lowlevel_init.S中,打开lowlevel_init.S

 

.globl lowlevel_init

lowlevel_init:

       /* 禁止D-CACHE */

       inv_dcache

       /*禁止L2Cache */

       init_l2cc

       init_aips

       /*初始化时钟*/

       init_clock

       /*跳回去*/

       mov pc, lr

 

返回到start.S中,如下代码可知,返回到了cpu_init_crit调用之后的代码

 

#ifndef CONFIG_SKIP_RELOCATE_UBOOT

/*比较_start 的值和_TEXT_BASE的值是否一致,如果不一致的话则进行拷贝,

 *在uboot的第一阶段是不一致的,因为_start为nor flash的地址值,而

 *_TEST_BASE为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

 

    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:              @ copy 32 bytes at a time

    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

#endif  /* CONFIG_SKIP_RELOCATE_UBOOT */

 

/*重定位之后,设置堆栈,,初始化C语言环境 */

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

#ifdef CONFIG_USE_IRQ

    sub r0, r0, #(CONFIG_STACKSIZE_IRQ + CONFIG_STACKSIZE_FIQ)

#endif

    sub sp, r0, #12     @ leave 3 words for abort-stack

    and sp, sp, #~7     @ 8 byte alinged for (ldr/str)d

 

    /* Clear BSS (if any). Is below tx (watch load addr - need space) */

clear_bss:

    ldr r0, _bss_start      @ find start of bss segment

    ldr r1, _bss_end        @ stop here

    mov r2, #0x00000000     @ clear value

clbss_l:

    str r2, [r0]        @ clear BSS location

    cmp r0, r1          @ are we at the end yet

    add r0, r0, #4      @ increment clear index pointer

    bne clbss_l         @ keep clearing till at end

 

#ifdef CONFIG_ARCH_MMU

    bl board_mmu_init

#endif

    ldr pc, _start_armboot  @ jump to C code

 /*从这里跳入uboot的第二阶段*/

_start_armboot: .word start_armboot

 

 

第一阶段流程总结:

flash_header.S     b   _start     

start.S            _start: b reset       //复位        

start.S            cpu_init_crit        //关闭mmu和catch             

lowlevel_init.S     lowlevel_init        //关闭D-CACHE和L2Cache,初始化时钟

start.S            relocate           //查看是否需要代码进行重定位

start.S            copy_loop         //代码进行重定位

start.S            stack_setup        //设置堆栈,清BSS等初始化C语言环境

start.S            start_armboot      //跳入第二阶段

 

3. uboot的第二阶段

通过搜索可知第二阶段代码在:lib_arm/board.c中的start_armboot

 

void start_armboot (void)

{

    init_fnc_t **init_fnc_ptr;

    char *s;

#if defined(CONFIG_VFD) || defined(CONFIG_LCD)

    unsigned long addr;

#endif

 

    /* 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;

    /*这里的init_sequence是一个函数指针数组,他将分别对硬件进行初始化*/

    for (init_fnc_ptr = init_sequence; *init_fnc_ptr; ++init_fnc_ptr) {

        if ((*init_fnc_ptr)() != 0) {

            hang ();

        }

    }

 

在lib_arm/board.c中查看所初始化的函数:

init_fnc_t *init_sequence[] = {

#if defined(CONFIG_ARCH_CPU_INIT)

    arch_cpu_init,      /* CPU基本的初始化:打开icache,dcache,清看门狗,和一些外设掉电 */

#endif

    board_init,     /* 板子的基本信息:机器码=3980,LCD基本信息 */

#if defined(CONFIG_USE_IRQ)

    interrupt_init,     /* set up exceptions */

#endif

    timer_init,     /*初始化定时器*/

    env_init,       /* 初始化环境变量,所在文件为common/env_sf.c */

    init_baudrate,      /* 设置波特率 */

    serial_init,        /* 设置串口 */

    console_init_f,     /* 设置是否有无控制台 */

    display_banner,     /*打印一些基本信息给控制台*/

#if defined(CONFIG_DISPLAY_CPUINFO)

    print_cpuinfo,      /* display cpu info (and speed) */

#endif

#if defined(CONFIG_DISPLAY_BOARDINFO)

    checkboard,     /* display board info */

#endif

#if defined(CONFIG_HARD_I2C) || defined(CONFIG_SOFT_I2C)

    init_func_i2c,

#endif

    dram_init,      /* 设置DRAM的大小和地址 */

#if defined(CONFIG_CMD_PCI) || defined (CONFIG_PCI)

    arm_pci_init,

#endif

    display_dram_config,  /*打印配置的DRAM的基本信息*/

    NULL,

};

 

其中env_init为环境变量的初始化,所在文件为common/env_sf.c

int env_init(void)

{

    /* SPI flash isn‘t usable before relocation */

    gd->env_addr = (ulong)&default_environment[0];

    gd->env_valid = 1;

 

    return 0;

}

 

default_environment则为默认环境变量在/common/env_common.c中

uchar default_environment[] = {

#ifdef  CONFIG_BOOTARGS

    "bootargs=" CONFIG_BOOTARGS         "\0"

#endif

#ifdef  CONFIG_BOOTCOMMAND

    "bootcmd="  CONFIG_BOOTCOMMAND      "\0"

#endif

#ifdef  CONFIG_RAMBOOTCOMMAND

    "ramboot="  CONFIG_RAMBOOTCOMMAND       "\0"

#endif

#ifdef  CONFIG_NFSBOOTCOMMAND

    "nfsboot="  CONFIG_NFSBOOTCOMMAND       "\0"

#endif

#if defined(CONFIG_BOOTDELAY) && (CONFIG_BOOTDELAY >= 0)

    "bootdelay="    MK_STR(CONFIG_BOOTDELAY)    "\0"

#endif

#if defined(CONFIG_BAUDRATE) && (CONFIG_BAUDRATE >= 0)

    "baudrate=" MK_STR(CONFIG_BAUDRATE)     "\0"

#endif

#ifdef  CONFIG_LOADS_ECHO

    "loads_echo="   MK_STR(CONFIG_LOADS_ECHO)   "\0"

#endif

#ifdef  CONFIG_ETHADDR

    "ethaddr="  MK_STR(CONFIG_ETHADDR)      "\0"

#endif

#ifdef  CONFIG_ETH1ADDR

    "eth1addr=" MK_STR(CONFIG_ETH1ADDR)     "\0"

#endif

#ifdef  CONFIG_ETH2ADDR

    "eth2addr=" MK_STR(CONFIG_ETH2ADDR)     "\0"

#endif

#ifdef  CONFIG_ETH3ADDR

    "eth3addr=" MK_STR(CONFIG_ETH3ADDR)     "\0"

#endif

#ifdef  CONFIG_ETH4ADDR

    "eth4addr=" MK_STR(CONFIG_ETH4ADDR)     "\0"

#endif

#ifdef  CONFIG_ETH5ADDR

    "eth5addr=" MK_STR(CONFIG_ETH5ADDR)     "\0"

#endif

#ifdef  CONFIG_IPADDR

    "ipaddr="   MK_STR(CONFIG_IPADDR)       "\0"

#endif

#ifdef  CONFIG_SERVERIP

    "serverip=" MK_STR(CONFIG_SERVERIP)     "\0"

#endif

#ifdef  CONFIG_SYS_AUTOLOAD

    "autoload=" CONFIG_SYS_AUTOLOAD         "\0"

#endif

#ifdef  CONFIG_PREBOOT

    "preboot="  CONFIG_PREBOOT          "\0"

#endif

#ifdef  CONFIG_ROOTPATH

    "rootpath=" MK_STR(CONFIG_ROOTPATH)     "\0"

#endif

#ifdef  CONFIG_GATEWAYIP

    "gatewayip="    MK_STR(CONFIG_GATEWAYIP)    "\0"

#endif

#ifdef  CONFIG_NETMASK

    "netmask="  MK_STR(CONFIG_NETMASK)      "\0"

#endif

#ifdef  CONFIG_HOSTNAME

    "hostname=" MK_STR(CONFIG_HOSTNAME)     "\0"

#endif

#ifdef  CONFIG_BOOTFILE

    "bootfile=" MK_STR(CONFIG_BOOTFILE)     "\0"

#endif

#ifdef  CONFIG_LOADADDR

    "loadaddr=" MK_STR(CONFIG_LOADADDR)     "\0"

#endif

#ifdef CONFIG_RD_LOADADDR

    "rd_loadaddr="  MK_STR(CONFIG_RD_LOADADDR)  "\0"

#endif

#ifdef  CONFIG_CLOCKS_IN_MHZ

    "clocks_in_mhz=1\0"

#endif

#if defined(CONFIG_PCI_BOOTDELAY) && (CONFIG_PCI_BOOTDELAY > 0)

    "pcidelay=" MK_STR(CONFIG_PCI_BOOTDELAY)    "\0"

#endif

#ifdef  CONFIG_EXTRA_ENV_SETTINGS

    CONFIG_EXTRA_ENV_SETTINGS

#endif

    "\0"

};

 

返回到lib_arm/board.c中的start_armboot接着往下看

   /* armboot_start is defined in the board-specific linker script */

    mem_malloc_init (_armboot_start - CONFIG_SYS_MALLOC_LEN);

 

#ifndef CONFIG_SYS_NO_FLASH

    /* 初始化Flash */

    display_flash_config (flash_init ());

#endif /* CONFIG_SYS_NO_FLASH */

 

。。。省略。。。

 

#ifdef CONFIG_LCD

    /* 在之前arch_cpu_init进行过设置gd->fb_base = CONFIG_FB_BASE=(TEXT_BASE + 0x300000)

       所以这里就不进入了*/

    if (!gd->fb_base) {

#       ifndef PAGE_SIZE

#         define PAGE_SIZE 4096

#       endif

        /*

         * reserve memory for LCD display (always full pages)

         */

        /* bss_end is defined in the board-specific linker script */

        addr = (_bss_end + (PAGE_SIZE - 1)) & ~(PAGE_SIZE - 1);

        lcd_setmem (addr);

        gd->fb_base = addr;

    }

#endif /* CONFIG_LCD */

 

。。。省略。。。

 

#ifdef CONFIG_GENERIC_MMC

    /*初始化MMC设备*/

    puts ("MMC:   ");

    mmc_initialize (gd->bd);

#endif

 

    /* 加载环境变量,如果没有则使用默认的 */

    env_relocate ();

 

。。。省略。。。

 

    /* IP Address */

    gd->bd->bi_ip_addr = getenv_IPaddr ("ipaddr");

 

#if defined CONFIG_SPLASH_SCREEN && defined CONFIG_VIDEO_MX5

    setup_splash_image();

#endif

    /*对设备进行初始化,并将其加入链表进行统一的管理*/

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

 

在stdio_init中,采用了uboot的设备管理模型对设备进行了管理,对于设备的具体操作方式统一了接口,并将它们放在了链表上进行统一的管理。如下:

int stdio_init (void)

{

#ifndef CONFIG_ARM  /* already relocated for current ARM implementation */

    ulong relocation_offset = gd->reloc_off;

    int i;

 

    /* relocate device name pointers */

    for (i = 0; i < (sizeof (stdio_names) / sizeof (char *)); ++i) {

        stdio_names[i] = (char *) (((ulong) stdio_names[i]) +

                        relocation_offset);

    }

#endif

 

    /* 初始化设备链表 */

    INIT_LIST_HEAD(&(devs.list));

 

#ifdef CONFIG_ARM_DCC_MULTI

    drv_arm_dcc_init ();

#endif

#if defined(CONFIG_HARD_I2C) || defined(CONFIG_SOFT_I2C)

    /*初始化I2C设备*/

    i2c_init (CONFIG_SYS_I2C_SPEED, CONFIG_SYS_I2C_SLAVE);

#endif

#ifdef CONFIG_LCD

    /*初始化LCD设备*/

    drv_lcd_init ();

#endif

#if defined(CONFIG_VIDEO) || defined(CONFIG_CFB_CONSOLE)

    /*初始化视频设备*/

    drv_video_init ();     

#endif

#ifdef CONFIG_KEYBOARD

    /*初始化按键设备*/

    drv_keyboard_init ();  

#endif

#ifdef CONFIG_LOGBUFFER

    drv_logbuff_init ();

#endif

    /*初始化系统设备*/

    drv_system_init ();

#ifdef CONFIG_SERIAL_MULTI

    serial_stdio_init ();

#endif

#ifdef CONFIG_USB_TTY

    /*初始化usbtty设备*/

    drv_usbtty_init ();

#endif

#ifdef CONFIG_NETCONSOLE

    drv_nc_init ();

#endif

#ifdef CONFIG_JTAG_CONSOLE

    drv_jtag_console_init ();

#endif

 

    return (0);

}

 

继续接下来start_armboot中的代码。

    /*将一些常用的函数统一放入gd->jt当中*/

    jumptable_init ();

 

。。。省略。。。

    /*控制台*/

    console_init_r ();  /* fully init console as a device */

 

。。。省略。。。

 

    /* enable exceptions */

    enable_interrupts ();

 

    /* Perform network card initialisation if necessary */

。。。省略。。。

 

#if defined(CONFIG_DRIVER_SMC91111) || defined (CONFIG_DRIVER_LAN91C96)

    /* XXX: this needs to be moved to board init */

    if (getenv ("ethaddr")) {

        uchar enetaddr[6];

        eth_getenv_enetaddr("ethaddr", enetaddr);

        smc_set_mac_addr(enetaddr);

    }

#endif /* CONFIG_DRIVER_SMC91111 || CONFIG_DRIVER_LAN91C96 */

 

#if defined(CONFIG_ENC28J60_ETH) && !defined(CONFIG_ETHADDR)

    extern void enc_set_mac_addr (void);

    enc_set_mac_addr ();

#endif /* CONFIG_ENC28J60_ETH && !CONFIG_ETHADDR*/

 

    /* Initialize from environment */

    if ((s = getenv ("loadaddr")) != NULL) {

        load_addr = simple_strtoul (s, NULL, 16);

    }

#if defined(CONFIG_CMD_NET)

    if ((s = getenv ("bootfile")) != NULL) {

        copy_filename (BootFile, s, sizeof (BootFile));

    }

#endif

 

#ifdef BOARD_LATE_INIT

    board_late_init ();

#endif

 

#ifdef CONFIG_ANDROID_RECOVERY

    check_recovery_mode();

#endif

 

#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

#ifdef CONFIG_FASTBOOT

    check_fastboot_mode();

#endif

    /* main_loop() can return to retry autoboot, if so just run it again. */

    for (;;) {

        main_loop ();

    }

}

 

在main_loop后就进入了死循环,不停的接收命令,执行命令,其代码在/common/main.c当中

void main_loop (void)

{

#ifndef CONFIG_SYS_HUSH_PARSER

    static char lastcommand[CONFIG_SYS_CBSIZE] = { 0, };

    int len;

    int rc = 1;

    int flag;

#endif

 

#if defined(CONFIG_BOOTDELAY) && (CONFIG_BOOTDELAY >= 0)

    char *s;

    int bootdelay;

#endif

 

。。。省略。。。

 

#ifdef CONFIG_SYS_HUSH_PARSER

    u_boot_hush_start ();

#endif

 

#ifdef CONFIG_AUTO_COMPLETE      

    install_auto_complete();

#endif

 

#if defined(CONFIG_BOOTDELAY) && (CONFIG_BOOTDELAY >= 0)

    /*从环境变量中获取到bootdelay的值*/

    s = getenv ("bootdelay");

   

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

 

    debug ("### main_loop entered: bootdelay=%d\n\n", bootdelay);

   

。。。省略。。。   

 

    /*从环境变量中获取到bootcmd的值*/

    s = getenv ("bootcmd");

 

    debug ("### main_loop: bootcmd=\"%s\"\n", s ? s : "<UNDEFINED>");

    /*等待bootdelay秒并且没有按下确认键则进入if*/

    if (bootdelay >= 0 && s && !abortboot (bootdelay)) {

 

。。。省略。。。

   

# ifndef CONFIG_SYS_HUSH_PARSER

        run_command (s, 0);

# else

        /*在这里执行bootcmd,正常流程就是进入到启动内核程序中了*/

        parse_string_outer(s, FLAG_PARSE_SEMICOLON |

                    FLAG_EXIT_FROM_LOOP);

# endif

 

。。。省略。。。

 

    }

   

#endif  /* CONFIG_BOOTDELAY */

 

#ifdef CONFIG_SYS_HUSH_PARSER

    /*这里进入循环的命令解析执行*/

    parse_file_outer();   

    /* 不会到这里 */

    for (;;);

#else

。。。省略。。。

#endif /*CONFIG_SYS_HUSH_PARSER*/

}

 

4. uboot启动kernel

首先注意比较重要的结构体cmd_tbl_s

struct cmd_tbl_s {

    char    *name;      /* 命令的名字 */

    int     maxargs;    /* 最大参数个数  */

    int     repeatable; /* 是否可以重复,也就是按确认键,是否会支持  */

    int     (*cmd)(struct cmd_tbl_s *, int, int, char *[]);/*执行函数*/

    char        *usage;     /* 用户帮助信息  (简略的) */

#ifdef  CONFIG_SYS_LONGHELP

    char        *help;      /* 用户帮助信息   (详细的)  */

#endif

#ifdef CONFIG_AUTO_COMPLETE

    /* do auto completion on the arguments */

    int     (*complete)(int argc, char *argv[], char last_char, int maxv, char *cmdv[]);

#endif

};

 

Bootm的宏为:

U_BOOT_CMD(

    bootm,  CONFIG_SYS_MAXARGS, 1,  do_bootm,

    。。。省略。。。

);

 

宏展开后可以推出:

 

cmd_tbl_t __u_boot_cmd_bootm = {"bootm", CONFIG_SYS_MAXARGS, 1, do_bootm, 。。。}

 

启动流程:

lib_arm/bootm.c

 

int do_bootm (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])

{

    ulong       iflag;

    ulong       load_end = 0;

    int     ret;

    boot_os_fn  *boot_fn;

 

    /* relocate boot function table */

    if (!relocated) {

        int i;

        for (i = 0; i < ARRAY_SIZE(boot_os); i++)

            if (boot_os[i] != NULL)

                boot_os[i] += gd->reloc_off;

        relocated = 1;

    }

。。。省略。。。

    /*取得镜像信息*/

    if (bootm_start(cmdtp, flag, argc, argv))

        return 1;

    /*关中断*/

    iflag = disable_interrupts();

 

#if defined(CONFIG_CMD_USB)

    usb_stop();

#endif

 

#ifdef CONFIG_AMIGAONEG3SE

    icache_disable();

    dcache_disable();

#endif

    /*加载内核,在这里面首先会判断内核镜像是否需要解压缩。我们把解压缩放在了kernel的开头进行,所以在这里并不需要,然后再把内核加载到指定的地址*/

    ret = bootm_load_os(images.os, &load_end, 1);

 

    if (ret < 0) {

        if (ret == BOOTM_ERR_RESET)

            do_reset (cmdtp, flag, argc, argv);

        if (ret == BOOTM_ERR_OVERLAP) {

            if (images.legacy_hdr_valid) {

                if (image_get_type (&images.legacy_hdr_os_copy) == IH_TYPE_MULTI)

                    puts ("WARNING: legacy format multi component "

                        "image overwritten\n");

            } else {

                puts ("ERROR: new format image overwritten - "

                    "must RESET the board to recover\n");

                show_boot_progress (-113);

                do_reset (cmdtp, flag, argc, argv);

            }

        }

        if (ret == BOOTM_ERR_UNIMPLEMENTED) {

            if (iflag)

                enable_interrupts();

            show_boot_progress (-7);

            return 1;

        }

    }

 

    lmb_reserve(&images.lmb, images.os.load, (load_end - images.os.load));

 

。。。省略。。。

 

    show_boot_progress (8);

 

#ifdef CONFIG_SILENT_CONSOLE

    if (images.os.os == IH_OS_LINUX)

        fixup_silent_linux();

#endif

    /*获取到启动函数的函数指针,在这里为do_bootm_linux*/

    boot_fn = boot_os[images.os.os];

 

    /*跳转至do_bootm_linux*/

    boot_fn(0, argc, argv, &images);

    /*不会运行到这里*/

    show_boot_progress (-9);

#ifdef DEBUG

    puts ("\n## Control returned to monitor - resetting...\n");

#endif

    do_reset (cmdtp, flag, argc, argv);

 

    return 1;

}

 

其中比较关键的函数为bootm_start(获取镜像信息)和bootm_load_os(加载内核)还有在bootm.c当中的do_bootm_linux,下面我们来看一下bootm_start实现的功能:

static int bootm_start(cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])

{

    ulong       mem_start;

    phys_size_t mem_size;

    void        *os_hdr;

    int     ret;

    /*先将信息清零,然后再填充*/

    memset ((void *)&images, 0, sizeof (images));

    images.verify = getenv_yesno ("verify");

 

    lmb_init(&images.lmb);

    /*获取到dram的起始地址*/

mem_start = getenv_bootm_low();

/*获取到dram的大小*/

    mem_size = getenv_bootm_size();

    /*应该是对dram进行某些管理*/

    lmb_add(&images.lmb, (phys_addr_t)mem_start, mem_size);

 

    arch_lmb_reserve(&images.lmb);

    board_lmb_reserve(&images.lmb);

 

   

/* 得到内核镜像的头,启动地址和长度 ,在我们这里是由自己固定的,从0x10800000处取出,在得到内核后,接下来会调用image_get_kernel对内核镜像进行检测:包括对魔数、头的CRC、内核架构,image_start和image_len为函数在去掉内核镜像头部信息以后的地址和长度,如果正确,则返回内核地址,错误返回NULL*/

    os_hdr = boot_get_kernel (cmdtp, flag, argc, argv,

            &images, &images.os.image_start, &images.os.image_len);

    if (images.os.image_len == 0) {

        puts ("ERROR: can‘t get kernel image!\n");

        return 1;

    }

 

    /* 得到镜像的一些参数 */

    switch (genimg_get_format (os_hdr)) {

    case IMAGE_FORMAT_LEGACY:

        images.os.type = image_get_type (os_hdr);

        images.os.comp = image_get_comp (os_hdr);

        images.os.os = image_get_os (os_hdr);

 

        images.os.end = image_get_image_end (os_hdr);

        images.os.load = image_get_load (os_hdr);

        break;

       。。。省略。。。

    default:

        puts ("ERROR: unknown image format type!\n");

        return 1;

    }

 

    /* find kernel entry point */

    if (images.legacy_hdr_valid) {

        images.ep = image_get_ep (&images.legacy_hdr_os_copy);

#if defined(CONFIG_FIT)

    } else if (images.fit_uname_os) {

        ret = fit_image_get_entry (images.fit_hdr_os,

                images.fit_noffset_os, &images.ep);

        if (ret) {

            puts ("Can‘t get entry point property!\n");

            return 1;

        }

#endif

    } else {

        puts ("Could not find kernel entry point!\n");

        return 1;

    }

 

    if (images.os.os == IH_OS_LINUX) {

        /* find ramdisk */

        ret = boot_get_ramdisk (argc, argv, &images, IH_INITRD_ARCH,

                &images.rd_start, &images.rd_end);

        if (ret) {

            puts ("Ramdisk image is corrupt or invalid\n");

            return 1;

        }

 

#if defined(CONFIG_OF_LIBFDT)

#if defined(CONFIG_PPC) || defined(CONFIG_M68K) || defined(CONFIG_SPARC)

        /* find flattened device tree */

        ret = boot_get_fdt (flag, argc, argv, &images,

                    &images.ft_addr, &images.ft_len);

        if (ret) {

            puts ("Could not find a valid device tree\n");

            return 1;

        }

 

        set_working_fdt_addr(images.ft_addr);

#endif

#endif

    }

 

    images.os.start = (ulong)os_hdr;

    images.state = BOOTM_STATE_START;

 

    return 0;

}

 

从do_bootm可知,最后会跳转至lib_arm/bootm.c中的do_bootm_linux

int do_bootm_linux(int flag, int argc, char *argv[], bootm_headers_t *images)

{

    bd_t    *bd = gd->bd;

    char    *s;

    int machid = bd->bi_arch_number;

    void    (*theKernel)(int zero, int arch, uint params);

 

#ifdef CONFIG_CMDLINE_TAG

    char *commandline = getenv ("bootargs");

#endif

 

    if ((flag != 0) && (flag != BOOTM_STATE_OS_GO))

        return 1;

    /*得到函数的地址*/

    theKernel = (void (*)(int, int, uint))images->ep;

    /*获取到机器码*/

    s = getenv ("machid");

    if (s) {

        machid = simple_strtoul (s, NULL, 16);

        printf ("Using machid 0x%x from environment\n", machid);

    }

 

    show_boot_progress (15);

 

    debug ("## Transferring control to Linux (at address %08lx) ...\n",

           (ulong) theKernel);

 

#if defined (CONFIG_SETUP_MEMORY_TAGS) || \

    defined (CONFIG_CMDLINE_TAG) || \

    defined (CONFIG_INITRD_TAG) || \

    defined (CONFIG_SERIAL_TAG) || \

    defined (CONFIG_REVISION_TAG) || \

    defined (CONFIG_LCD) || \

    defined (CONFIG_VFD)

    setup_start_tag (bd);

#ifdef CONFIG_SERIAL_TAG

    setup_serial_tag (&params);

#endif

#ifdef CONFIG_REVISION_TAG

    setup_revision_tag (&params);

#endif

#ifdef CONFIG_SETUP_MEMORY_TAGS

    setup_memory_tags (bd);

#endif

#ifdef CONFIG_CMDLINE_TAG

    setup_commandline_tag (bd, commandline);

#endif

#ifdef CONFIG_INITRD_TAG

    if (images->rd_start && images->rd_end)

        setup_initrd_tag (bd, images->rd_start, images->rd_end);

#endif

#if defined (CONFIG_VFD) || defined (CONFIG_LCD)

    setup_videolfb_tag ((gd_t *) gd);

#endif

    setup_end_tag (bd);

#endif

 

    /* we assume that the kernel is in place */

    printf ("\nStarting kernel ...\n\n");

 

#ifdef CONFIG_USB_DEVICE

    {

        extern void udc_disconnect (void);

        udc_disconnect ();

    }

#endif

    /*在启动kernel之前进行一些清理工作*/

    cleanup_before_linux ();

    /*从这个地方就正式跳入了kernel,并将机器码和参数传入kernel,机器码用于kernel的比对,参数用于指导kernel的启动。到此,uboot的所有功能就结束了*/

    theKernel (0, machid, bd->bi_boot_params);

    /* does not return */

 

    return 1;

}

 

以上是关于Uboot流程分析的主要内容,如果未能解决你的问题,请参考以下文章

Uboot流程分析

一,移植uboot,分析uboot启动流程

uboot研读笔记 | 14 - uboot启动流程分析(2016.03版本)

从0移植uboot _启动流程分析

uboot学习之uboot-spl的程序流程分析

新版本uboot启动流程分析