u-boot-2016.5启动流程

来源:互联网 发布:minecraft java版 编辑:程序博客网 时间:2024/05/01 03:05

0、由u-boot-2016.05\arch\arm\cpu\u-boot.lds链接文件中ENTRY(_start) 可知程序的入口在_start,在SourceInsight中查找可发现程序的入口_start在u-boot-2016.05\arch\arm\lib\vectors.S中。

...ENTRY(_start)SECTIONS{    ...    . = 0x00000000;    . = ALIGN(4);    .text :    {        *(.__image_copy_start)        *(.vectors)        CPUDIR/start.o (.text*)        *(.text*)    }    ...    . = ALIGN(4);    .rodata : { *(SORT_BY_ALIGNMENT(SORT_BY_NAME(.rodata*))) }    . = ALIGN(4);    .data : {        *(.data*)    }    . = ALIGN(4);    . = .;    ...    .bss_start __rel_dyn_start (OVERLAY) : {        KEEP(*(.__bss_start));        __bss_base = .;    }    .bss __bss_base (OVERLAY) : {        *(.bss*)         . = ALIGN(4);         __bss_limit = .;    }    .bss_end __bss_limit (OVERLAY) : {        KEEP(*(.__bss_end));    }    ...}

进入boot-2016.05\arch\arm\lib\vectors.S中,可以看到从_start开始后就跳转到reset去执行:

....globl _start..._start:#ifdef CONFIG_SYS_DV_NOR_BOOT_CFG    .word   CONFIG_SYS_DV_NOR_BOOT_CFG#endif    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...

1、从u-boot-2016.05\arch\arm\cpu\arm920t\start.S中reset执行
主要执行流程:reset -> cpu_init_crit -> lowlevel_init -> _main

reset:...#ifndef CONFIG_SKIP_LOWLEVEL_INIT    bl  cpu_init_crit#endif    bl  _main...#ifndef CONFIG_SKIP_LOWLEVEL_INITcpu_init_crit:...    bl  lowlevel_init...#endif /* CONFIG_SKIP_LOWLEVEL_INIT */

2、由bl _main跳转到u-boot-2016.05\arch\arm\lib\crt0.S中从入口_main开始执行
主要执行流程:board_init_f -> relocate_code -> board_init_r

ENTRY(_main)        ...    bl  board_init_f_alloc_reserve        ...    bl  board_init_f_init_reserve        ...    bl  board_init_f#if ! defined(CONFIG_SPL_BUILD)        ...    b   relocate_code        ...#endif#if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_FRAMEWORK)         ...#if defined(CONFIG_SYS_THUMB_BUILD)        ...#else    ldr pc, =board_init_r   #endif#endifENDPROC(_main)

这部分有三点说明:
⑴、u-boot-2016.05\common\board_f.c:board_init_f通过initcall_run_list(init_sequence_f)函数执行一系列初始化函数以实现前半部分板级初始化。全局结构体gd在u-boot-2016.05\arch\arm\include\asm\global_data.h中声明:

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

⑵、u-boot-2016.05\arch\arm\lib\relocate.S:relocate_code实现uboot代码的重定位,此部分如果觉得源代码不是简单明了可自己改写。
⑶、去重定位uboot有两种路径:
一种是将gd->flags设为0,在初始化函数序列init_sequence_f中的jump_to_copy函数中去跳转到relocate_code:

static int jump_to_copy(void){    if (gd->flags & GD_FLG_SKIP_RELOC)        return 0;    ...#if defined(CONFIG_X86) || defined(CONFIG_ARC)    ...#else    relocate_code(gd->start_addr_sp, gd->new_gd, gd->relocaddr);#endif    return 0;}

另一种就是不宏定义CONFIG_SPL_BUILD,然后在u-boot-2016.05\arch\arm\lib\crt0.S中通过

#if ! defined(CONFIG_SPL_BUILD)        ...    b   relocate_code        ...#endif

来跳转到relocate_code。以上两种方法选其一,另一种就得去掉。
3、在上一步通过ldr pc, =board_init_r指令进入u-boot-2016.05\common\board_r.c:board_init_r函数,进而调用initcall_run_list(init_sequence_r)函数执行一系列初始化函数以实现后半部分板级初始化,并在initcall_run_list函数里进入run_main_loop不再返回。

void board_init_r(gd_t *new_gd, ulong dest_addr){    ...    if (initcall_run_list(init_sequence_r))        hang();    /* NOTREACHED - run_main_loop() does not return */    hang();}

init_sequence_r是一个函数指针数组,里面存放了很多初始化函数指针,里面有两个重要的函数指针initr_announce和run_main_loop:

init_fnc_t init_sequence_r[] = {    ...    initr_announce,    ...    run_main_loop,};

initr_announce函数声明从此处开始程序就将跳转到RAM中运行:

static int initr_announce(void){    debug("Now running in RAM - U-Boot at: %08lx\n", gd->relocaddr);    return 0;}

最后一项是run_main_loop ,进入run_main_loop 后便不再返回。

4、在run_main_loop 里会进入u-boot-2016.05\common\main.c:main_loop函数

static int run_main_loop(void){    ...    for (;;)        main_loop();    return 0;}

进入main_loop之前就已经完成初始化,接下来准备去处理命令

/* We come here after U-Boot is initialised and ready to process commands */void main_loop(void){    const char *s;    bootstage_mark_name(BOOTSTAGE_ID_MAIN_LOOP, "main_loop");    ...    /* get environment_variable: s = getenv("bootcmd"); -> bootcmd */    s = bootdelay_process();    ...    autoboot_command(s);    ...}

main_loop函数里有两个重要的过程:
⑴、首先在bootdelay_process函数里通过s = getenv(“bootcmd”)得到bootcmd参数并返回bootcmd参数,

const char *bootdelay_process(void){    char *s;    int bootdelay;    ...    s = getenv("bootdelay");    ...    debug("### main_loop entered: bootdelay=%d\n\n", bootdelay);    ...        s = getenv("bootcmd");    ...    stored_bootdelay = bootdelay;    return s;}

其中,bootcmd参数通过以下方式指定:
先在u-boot-2016.05\include\env_default.h中

#ifdef  CONFIG_BOOTCOMMAND    "bootcmd="  CONFIG_BOOTCOMMAND      "\0"#endif

再在u-boot-2016.05\include\configs\smdk2440.h中指定

#define CONFIG_BOOTCOMMAND "nand read 30000000 kernel;bootm 30000000"

⑵、然后进入autoboot_command函数,并将bootcmd参数传入,继而进入run_command_list函数,继续将bootcmd参数传入

void autoboot_command(const char *s){    ...    if (stored_bootdelay != -1 && s && !abortboot(stored_bootdelay)) {        ...        run_command_list(s, -1, 0);        ...    }    ...}

5、从autoboot_command函数进入u-boot-2016.05\common\cli.c:run_command_list函数后,接着会调用board_run_command函数去执行命令

int run_command_list(const char *cmd, int len, int flag){    int need_buff = 1;    char *buff = (char *)cmd;   /* cast away const */    int rcode = 0;    if (len == -1) {        len = strlen(cmd);#ifdef CONFIG_SYS_HUSH_PARSER        ...#else        /* the built-in parser will change our string if it sees \n */        need_buff = strchr(cmd, '\n') != NULL;#endif    }    if (need_buff) {        buff = malloc(len + 1);        if (!buff)            return 1;        memcpy(buff, cmd, len);        buff[len] = '\0';    }#ifdef CONFIG_SYS_HUSH_PARSER    ...#ifdef CONFIG_CMDLINE    ...#else    rcode = board_run_command(buff);#endif#endif    ...}

那么,board_run_command如何去执行命令?
首先,board_run_command函数通过bootcmd参数中的bootm命令找到u-boot-2016.05\cmd\bootm.c中的

U_BOOT_CMD(    bootm,  CONFIG_SYS_MAXARGS, 1,  do_bootm,    "boot application image from memory", bootm_help_text);

然后,根据这个信息找到执行bootm命令的处理函数指针do_bootm,并进入do_bootm函数执行相关代码,而U_BOOT_CMD在u-boot-2016.05\include\command.h中定义:

#define U_BOOT_CMD(_name, _maxargs, _rep, _cmd, _usage, _help)      \    U_BOOT_CMD_COMPLETE(_name, _maxargs, _rep, _cmd, _usage, _help, NULL)
#define U_BOOT_CMD_COMPLETE(_name, _maxargs, _rep, _cmd, _usage, _help, \                _comp)              \    _CMD_REMOVE(sub_ ## _name, _cmd)
#define _CMD_REMOVE(_name, _cmd)                    \    int __remove_ ## _name(void)                    \    {                               \        if (0)                          \            _cmd(NULL, 0, 0, NULL);             \        return 0;                       \    }

在此,board_run_command函数还会将bootm命令中的参数(内核映像所在地址)30000000赋给bootm_headers_t结构体变量images,则images首地址就是30000000,images在u-boot-2016.05\cmd\bootm.c中定义:

bootm_headers_t images; 

6、根据U_BOOT_CMD信息进入u-boot-2016.05\cmd\bootm.c:do_bootm函数

int do_bootm(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]){    ...    return do_bootm_states(cmdtp, flag, argc, argv, BOOTM_STATE_START |        BOOTM_STATE_FINDOS | BOOTM_STATE_FINDOTHER |        BOOTM_STATE_LOADOS |#if defined(CONFIG_PPC) || defined(CONFIG_MIPS)        BOOTM_STATE_OS_CMDLINE |#endif        BOOTM_STATE_OS_PREP | BOOTM_STATE_OS_FAKE_GO |        BOOTM_STATE_OS_GO, &images, 1);}

其中BOOTM_STATE_START 、BOOTM_STATE_FINDOS 、BOOTM_STATE_FINDOTHER 、BOOTM_STATE_LOADOS 、BOOTM_STATE_OS_PREP 、BOOTM_STATE_OS_FAKE_GO 这些在u-boot-2016.05\include\image.h中bootm_headers结构体中指定:

#define BOOTM_STATE_START   (0x00000001)#define BOOTM_STATE_FINDOS  (0x00000002)#define BOOTM_STATE_FINDOTHER   (0x00000004)#define BOOTM_STATE_LOADOS  (0x00000008)#define BOOTM_STATE_RAMDISK (0x00000010)#define BOOTM_STATE_FDT     (0x00000020)#define BOOTM_STATE_OS_CMDLINE  (0x00000040)#define BOOTM_STATE_OS_BD_T (0x00000080)#define BOOTM_STATE_OS_PREP (0x00000100)#define BOOTM_STATE_OS_FAKE_GO  (0x00000200)    /* 'Almost' run the OS */#define BOOTM_STATE_OS_GO   (0x00000400)

7、从do_bootm进入u-boot-2016.05\common\bootm.c:do_bootm_states函数,Now run the OS!

int do_bootm_states(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[],            int states, bootm_headers_t *images, int boot_progress){    boot_os_fn *boot_fn;    ulong iflag = 0;    int ret = 0, need_boot_fn;    images->state |= states;    /*     * Work through the states and see how far we get. We stop on     * any error.     */    if (states & BOOTM_STATE_START)        ret = bootm_start(cmdtp, flag, argc, argv);    if (!ret && (states & BOOTM_STATE_FINDOS))        ret = bootm_find_os(cmdtp, flag, argc, argv);    if (!ret && (states & BOOTM_STATE_FINDOTHER)) {        ret = bootm_find_other(cmdtp, flag, argc, argv);        argc = 0;   /* consume the args */    }    /* Load the OS */    if (!ret && (states & BOOTM_STATE_LOADOS)) {        ulong load_end;        iflag = bootm_disable_interrupts();        ret = bootm_load_os(images, &load_end, 0);        if (ret == 0)            lmb_reserve(&images->lmb, images->os.load,                    (load_end - images->os.load));        else if (ret && ret != BOOTM_ERR_OVERLAP)            goto err;        else if (ret == BOOTM_ERR_OVERLAP)            ret = 0;#if defined(CONFIG_SILENT_CONSOLE) && !defined(CONFIG_SILENT_U_BOOT_ONLY)        if (images->os.os == IH_OS_LINUX)            fixup_silent_linux();#endif    }    /* Relocate the ramdisk */#ifdef CONFIG_SYS_BOOT_RAMDISK_HIGH    if (!ret && (states & BOOTM_STATE_RAMDISK)) {        ulong rd_len = images->rd_end - images->rd_start;        ret = boot_ramdisk_high(&images->lmb, images->rd_start,            rd_len, &images->initrd_start, &images->initrd_end);        if (!ret) {            setenv_hex("initrd_start", images->initrd_start);            setenv_hex("initrd_end", images->initrd_end);        }    }#endif#if IMAGE_ENABLE_OF_LIBFDT && defined(CONFIG_LMB)    if (!ret && (states & BOOTM_STATE_FDT)) {        boot_fdt_add_mem_rsv_regions(&images->lmb, images->ft_addr);        ret = boot_relocate_fdt(&images->lmb, &images->ft_addr,                    &images->ft_len);    }#endif    /* From now on, we need the OS boot function */    if (ret)        return ret;    boot_fn = bootm_os_get_boot_func(images->os.os);    need_boot_fn = states & (BOOTM_STATE_OS_CMDLINE |            BOOTM_STATE_OS_BD_T | BOOTM_STATE_OS_PREP |            BOOTM_STATE_OS_FAKE_GO | BOOTM_STATE_OS_GO);    if (boot_fn == NULL && need_boot_fn) {        if (iflag)            enable_interrupts();        printf("ERROR: booting os '%s' (%d) is not supported\n",               genimg_get_os_name(images->os.os), images->os.os);        bootstage_error(BOOTSTAGE_ID_CHECK_BOOT_OS);        return 1;    }    /* Call various other states that are not generally used */    if (!ret && (states & BOOTM_STATE_OS_CMDLINE))        ret = boot_fn(BOOTM_STATE_OS_CMDLINE, argc, argv, images);    if (!ret && (states & BOOTM_STATE_OS_BD_T))        ret = boot_fn(BOOTM_STATE_OS_BD_T, argc, argv, images);    if (!ret && (states & BOOTM_STATE_OS_PREP))        ret = boot_fn(BOOTM_STATE_OS_PREP, argc, argv, images);#ifdef CONFIG_TRACE    /* Pretend to run the OS, then run a user command */    if (!ret && (states & BOOTM_STATE_OS_FAKE_GO)) {        char *cmd_list = getenv("fakegocmd");        ret = boot_selected_os(argc, argv, BOOTM_STATE_OS_FAKE_GO,                images, boot_fn);        if (!ret && cmd_list)            ret = run_command_list(cmd_list, -1, flag);    }#endif    /* Check for unsupported subcommand. */    if (ret) {        puts("subcommand not supported\n");        return ret;    }    /* Now run the OS! We hope this doesn't return */    if (!ret && (states & BOOTM_STATE_OS_GO))        ret = boot_selected_os(argc, argv, BOOTM_STATE_OS_GO,                images, boot_fn);    /* Deal with any fallout */err:    if (iflag)        enable_interrupts();    if (ret == BOOTM_ERR_UNIMPLEMENTED)        bootstage_error(BOOTSTAGE_ID_DECOMP_UNIMPL);    else if (ret == BOOTM_ERR_RESET)        do_reset(cmdtp, flag, argc, argv);    return ret;}

do_bootm_states函数总共分8个部分:
⑴、 Work through the states and see how far we get. We stop on any error.
其中主要函数bootm_find_os实现三个功能:get kernel image header, start address and length,get image parameters。大概过程是:bootm_find_os -> boot_get_kernel -> image_get_kernel 。

static int bootm_find_os(cmd_tbl_t *cmdtp, int flag, int argc,             char * const argv[]){    const void *os_hdr;    bool ep_found = false;    int ret;    /* get kernel image header, start address and length */    os_hdr = boot_get_kernel(cmdtp, flag, argc, argv,            &images, &images.os.image_start, &images.os.image_len);    ...    /* get image parameters */    switch (genimg_get_format(os_hdr)) {#if defined(CONFIG_IMAGE_FORMAT_LEGACY)    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);        images.os.arch = image_get_arch(os_hdr);        break;#endif#if IMAGE_ENABLE_FIT    case IMAGE_FORMAT_FIT:        ...#endif#ifdef CONFIG_ANDROID_BOOT_IMAGE    case IMAGE_FORMAT_ANDROID:        ...#endif    default:        puts("ERROR: unknown image format type!\n");        return 1;    }    ...    if (images.os.type == IH_TYPE_KERNEL_NOLOAD) {        images.os.load = images.os.image_start;        images.ep += images.os.load;    }    ...

关于boot_get_kernel 、image_get_kernel 的说明:
boot_get_kernel - find kernel image(returns:
pointer to image header if valid image was found, plus kernel start address and length, otherwise NULL)

image_get_kernel - verify legacy format kernel image(returns:
pointer to a legacy image header if valid image was found otherwise return NULL)
⑵、Load the OS
⑶、Relocate the ramdisk
⑷、From now on, we need the OS boot function
boot_fn = bootm_os_get_boot_func(images->os.os);得到boot处理函数指针并赋给boot_fn。
①、关于参数images->os.os,可以由下列定义得知它是系统内核的类型,并在(2)中被赋值,若系统类型为linux,则images->os.os=5。

typedef struct bootm_headers {    ...#ifndef USE_HOSTCC  /*USE_HOSTCC 没有宏定义*/    image_info_t    os;     /* os image info */    ulong       ep;     /* entry point of OS */    ulong       rd_start, rd_end;/* ramdisk start/end */    char        *ft_addr;   /* flat dev tree address */    ulong       ft_len;     /* length of flat device tree */    ulong       initrd_start;    ulong       initrd_end;    ulong       cmdline_start;    ulong       cmdline_end;    bd_t        *kbd;#endif    ...} bootm_headers_t;
bootm_headers_t images;
typedef struct image_info {    ulong       start, end;     /* start/end of blob */    ulong       image_start, image_len; /* start of image within blob, len of image */    ulong       load;           /* load addr for the image */    uint8_t     comp, type, os;     /* compression, type of image, os type */    uint8_t     arch;           /* CPU architecture */} image_info_t;

得到images.os.os的值:

static int bootm_find_os(cmd_tbl_t *cmdtp, int flag, int argc,             char * const argv[]){    const void *os_hdr;    ...    /* get kernel image header, start address and length */    os_hdr = boot_get_kernel(cmdtp, flag, argc, argv,            &images, &images.os.image_start, &images.os.image_len);    ...    /* get image parameters */    switch (genimg_get_format(os_hdr)) {#if defined(CONFIG_IMAGE_FORMAT_LEGACY)    case IMAGE_FORMAT_LEGACY:                       /*旧系统格式的内核映像*/        ...        images.os.os = image_get_os(os_hdr);        ...        break;#endif

②、bootm_os_get_boot_func中会用到函数指针数组boot_os,该数组利用传入的images.os.os=5的值得到boot处理函数指针do_bootm_linux返回给boot_fn 。

boot_fn = bootm_os_get_boot_func(images->os.os);
boot_os_fn *bootm_os_get_boot_func(int os){    ...    return boot_os[os];}
static boot_os_fn *boot_os[] = {    [IH_OS_U_BOOT] = do_bootm_standalone,#ifdef CONFIG_BOOTM_LINUX    [IH_OS_LINUX] = do_bootm_linux,#endif#ifdef CONFIG_BOOTM_NETBSD    [IH_OS_NETBSD] = do_bootm_netbsd,#endif    ...};

操作系统代号可在u-boot-2016.05\include\image.h中查看

/* * Operating System Codes */#define IH_OS_INVALID       0   /* Invalid OS   */#define IH_OS_OPENBSD       1   /* OpenBSD  */#define IH_OS_NETBSD        2   /* NetBSD   */#define IH_OS_FREEBSD       3   /* FreeBSD  */#define IH_OS_4_4BSD        4   /* 4.4BSD   */#define IH_OS_LINUX     5   /* Linux    */        ...

⑸、Call various other states that are not generally used
⑹、Check for unsupported subcommand
⑺、Now run the OS! We hope this doesn’t return

if (!ret && (states & BOOTM_STATE_OS_GO))        ret = boot_selected_os(argc, argv, BOOTM_STATE_OS_GO,                images, boot_fn);

从do_bootm_states进入u-boot-2016.05\common\bootm_os.c:boot_selected_os函数,执行boot_fn(state, argc, argv, images);

int boot_selected_os(int argc, char * const argv[], int state,             bootm_headers_t *images, boot_os_fn *boot_fn){    ...    boot_fn(state, argc, argv, images);    ...}

⑻、Deal with any fallout
8、执行boot_fn(state, argc, argv, images),因为boot_fn=do_bootm_linux,所以相当于执行do_bootm_linux(state, argc, argv, images),程序跳到u-boot-2016.05\arch\arm\lib\bootm.c:

/* Main Entry point for arm bootm implementation*/int do_bootm_linux(int flag, int argc, char * const argv[],           bootm_headers_t *images){    ...    boot_jump_linux(images, flag);    ...}

do_bootm_linux -> boot_jump_linux -> kernel_entry(0, machid, r2);

static void boot_jump_linux(bootm_headers_t *images, int flag){    ...    unsigned long machid = gd->bd->bi_arch_number;    char *s;    void (*kernel_entry)(int zero, int arch, uint params);    unsigned long r2;    int fake = (flag & BOOTM_STATE_OS_FAKE_GO);    kernel_entry = (void (*)(int, int, uint))images->ep; /* ep:entry point of OS*/    s = getenv("machid");    if (s) {        if (strict_strtoul(s, 16, &machid) < 0) {            debug("strict_strtoul failed!\n");            return;        }        printf("Using machid 0x%lx from environment\n", machid);    }    ...    if (IMAGE_ENABLE_OF_LIBFDT && images->ft_len)        r2 = (unsigned long)images->ft_addr;    else        r2 = gd->bd->bi_boot_params;    if (!fake) {        ...            kernel_entry(0, machid, r2);    }#endif}

run the OS!
说明:
关于kernel_entry = (void (*)(int, int, uint))images->ep;中的images->ep在u-boot-2016.05\common\bootm.c:bootm_find_os函数中被赋值。

static int bootm_find_os(cmd_tbl_t *cmdtp, int flag, int argc,             char * const argv[]){    ...    if (images.os.type == IH_TYPE_KERNEL_NOLOAD) {        images.os.load = images.os.image_start;        images.ep += images.os.load;    }    ...}
1 0
原创粉丝点击