Uboot启动流程(2)

目录

1._main()函数分析

 2.board_init_f()函数详解

 3. relocate_code 函数详解

4.relocate_vectors 函数详解

5.board_init_r 函数详解

从上篇分析可得,此时Uboot进入_main()中执行,以下详细分析一下_main()的执行过程。

1._main()函数分析

arch/arm/lib/crt0.S
/*
 * entry point of crt0 sequence
 */

ENTRY(_main)

/*
 * Set up initial C runtime environment and call board_init_f(0).
 */

#if defined(CONFIG_SPL_BUILD) && defined(CONFIG_SPL_STACK)
	        ldr	        sp, =(CONFIG_SPL_STACK)
#else
	        ldr	        sp, =(CONFIG_SYS_INIT_SP_ADDR) //设置 sp 指针为 CONFIG_SYS_INIT_SP_ADDR,也就是 sp 指向 0X0091FF00
#endif
#if defined(CONFIG_CPU_V7M)	        /* v7M forbids using SP as BIC destination */
	        mov	        r3, sp
	        bic	        r3, r3, #7
	        mov	        sp, r3
#else
	        bic	        sp, sp, #7	        //sp 做 8 字节对齐
#endif
	        mov	        r0, sp //读取 sp 到寄存器 r0 里面,此时 r0=0X0091FF00
	        bl	        board_init_f_alloc_reserve
	        mov	        sp, r0 //将 r0 写入到 sp 里面, r0 保存着函数board_init_f_alloc_reserve 的返回值,所以这一句也就是设置 sp=0X0091FA00
	        /* set up gd here, outside any C code */
	        mov	        r9, r0 //将 r0 寄存器的值写到寄存器 r9 里面,因为 r9 寄存器存放着全局变量 gd 的地址(gd 指向 0X0091FA00)
	        bl	        board_init_f_init_reserve //调用函数 board_init_f_alloc_reserve,此函数有一个参数,参数为 r0 中的值,也就是 0X0091FF00,

	        mov	        r0, #0 //设置 r0 为 0
	        bl	        board_init_f //主要用来初始化 DDR,定时器,完成代码拷贝等等

#if ! defined(CONFIG_SPL_BUILD)

/*
 * Set up intermediate environment (new sp and gd) and call
 * relocate_code(addr_moni). Trick here is that we'll return
 * 'here' but relocated.
 */

	        ldr	        sp, [r9, #GD_START_ADDR_SP]	        /* sp = gd->start_addr_sp */ //重新设置环境(sp 和 gd) sp=0X9EF44E90,0X9EF44E90 是 DDR 中的地址,说明新的 sp 和 gd 将会存放到 DDR 中,而不是内部的 RAM 了
#if defined(CONFIG_CPU_V7M)	        /* v7M forbids using SP as BIC destination */
	        mov	        r3, sp
	        bic	        r3, r3, #7
	        mov	        sp, r3
#else
	        bic	        sp, sp, #7	        //sp 做 8 字节对齐
#endif
	        ldr	        r9, [r9, #GD_BD]	        //获取 gd->bd 的地址赋给 r9,此时 r9 存放的是老的 gd,这里通过获取 gd->bd 的地址来计算出新的 gd 的位置。	        /* r9 = gd->bd */
	        sub	        r9, r9, #GD_SIZE	        //新的 gd 在 bd 下面,所以 r9 减去 gd 的大小就是新的 gd 的位置	        /* new GD is below bd */

	        adr	        lr, here 设置 lr 寄存器为 here,这样后面执行其他函数返回的时候就返回到 here 位置处。
	        ldr	        r0, [r9, #GD_RELOC_OFF]	        //读取 gd->reloc_off 的值复制给 r0 寄存器, GD_RELOC_OFF=68	        /* r0 = gd->reloc_off */
	        add	        lr, lr, r0 //lr 寄存器的值加上 r0 寄存器的值,重新赋值给 lr 寄存器。
#if defined(CONFIG_CPU_V7M)
	        orr	        lr, #1	        	        	        	        /* As required by Thumb-only */
#endif
	        ldr	        r0, [r9, #GD_RELOCADDR]	        	        //读取 gd->relocaddr 的值赋给 r0 寄存器,此时 r0 寄存器就保存着 uboot 要拷贝的目的地址,为 0X9FF47000/* r0 = gd->relocaddr */
	        b	        relocate_code //调用函数 relocate_code,也就是代码重定位函数,此函数负责将 uboot 拷贝到新的地方去
here:
/*
 * now relocate vectors
 */

	        bl	        relocate_vectors //继续回到示例代码 32.2.4.1 第 127 行,调用函数 relocate_vectors,对中断向量表做重定位

/* Set up final (full) environment */

	        bl	        c_runtime_cpu_setup	        //调用函数 c_runtime_cpu_setup/* we still call old routine here */
#endif
#if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_FRAMEWORK)
# ifdef CONFIG_SPL_BUILD
	        /* Use a DRAM stack for the rest of SPL, if requested */
	        bl	        spl_relocate_stack_gd
	        cmp	        r0, #0
	        movne	        sp, r0
	        movne	        r9, r0
# endif
//清除 BSS 段。
	        ldr	        r0, =__bss_start	        /* this is auto-relocated! */

#ifdef CONFIG_USE_ARCH_MEMSET
	        ldr	        r3, =__bss_end	        	        /* this is auto-relocated! */
	        mov	        r1, #0x00000000	        	        /* prepare zero to clear BSS */

	        subs	        r2, r3, r0	        	        /* r2 = memset len */
	        bl	        memset
#else
	        ldr	        r1, =__bss_end	        	        /* this is auto-relocated! */
	        mov	        r2, #0x00000000	        	        /* prepare zero to clear BSS */

clbss_l:cmp	        r0, r1	        	        	        /* while not at end of BSS */
#if defined(CONFIG_CPU_V7M)
	        itt	        lo
#endif
	        strlo	        r2, [r0]	        	        /* clear 32-bit BSS word */
	        addlo	        r0, r0, #4	        	        /* move to next */
	        blo	        clbss_l
#endif

#if ! defined(CONFIG_SPL_BUILD)
	        bl coloured_LED_init
	        bl red_led_on
#endif
	        /* call board_init_r(gd_t *id, ulong dest_addr) */
	        //设置函数 board_init_r 的两个参数
	        mov     r0, r9                  /* gd_t */
	        ldr	        r1, [r9, #GD_RELOCADDR]	        /* dest_addr */
	        /* call board_init_r *///调用函数 board_init_r
#if defined(CONFIG_SYS_THUMB_BUILD)
	        ldr	        lr, =board_init_r	        /* this is auto-relocated! */
	        bx	        lr
#else
	        ldr	        pc, =board_init_r	        /* this is auto-relocated! */
#endif
	        /* we should not return here. */
#endif

ENDPROC(_main)

其中调用了board_init_f_alloc_reserve()函数:此时sp 指向 0X0091FF00,即r0 = 0X0091FF00,传入的参数top=0x0091FF00:

//留出早期的 malloc 内存区域和 gd 内存区域,其中CONFIG_SYS_MALLOC_F_LEN=0X400
ulong board_init_f_alloc_reserve(ulong top)
{
	        /* Reserve early malloc arena */
#if defined(CONFIG_SYS_MALLOC_F)
	        top -= CONFIG_SYS_MALLOC_F_LEN;
#endif
	        /* LAST : reserve GD (rounded up to a multiple of 16 bytes) */
	        top = rounddown(top-sizeof(struct global_data), 16);

	        return top;
}
#define CONFIG_SYS_MALLOC_F_LEN 0x400

此时的Top指向:

 当从该函数返回后,将r0的值赋给了sp:

 mov r9, r0,将r0的值赋值给r9,r9是gd的位置:uboot 中定义了一个指向 gd_t 的指针 gd, gd 存放在寄存器 r9 里面的,因此 gd 是个全局变量。 gd_t 是个结构体。

#ifdef CONFIG_ARM64
#define DECLARE_GLOBAL_DATA_PTR	        	        register volatile gd_t *gd asm ("x18")
#else
#define DECLARE_GLOBAL_DATA_PTR	        	        register volatile gd_t *gd asm ("r9")
#endif

接下来调用board_init_f_init_reserve()初始化gd:

//用于初始化 gd
void board_init_f_init_reserve(ulong base)
{
	        struct global_data *gd_ptr;
#ifndef _USE_MEMCPY
	        int *ptr;
#endif

	        /*
	         * clear GD entirely and set it up.
	         * Use gd_ptr, as gd may not be properly set yet.
	         */

	        gd_ptr = (struct global_data *)base;
	        /* zero the area */
#ifdef _USE_MEMCPY //清零处理
	        memset(gd_ptr, '\0', sizeof(*gd));
#else
	        for (ptr = (int *)gd_ptr; ptr < (int *)(gd_ptr + 1); )
	        	        *ptr++ = 0;
#endif
	        /* set GD unless architecture did it already */
#if !defined(CONFIG_ARM)
	        arch_setup_gd(gd_ptr);
#endif
	        /* next alloc will be higher by one GD plus 16-byte alignment */
	        base += roundup(sizeof(struct global_data), 16); //16字节对齐

	        /*
	         * record early malloc arena start.
	         * Use gd as it is now properly set for all architectures.
	         */
	        //设置了gd->malloc_base 为 gd 基地址+gd 大小=0X0091FA00+248=0X0091FAF8,
#if defined(CONFIG_SYS_MALLOC_F)
	        /* go down one 'early malloc arena' */
	        gd->malloc_base = base;
	        /* next alloc will be higher by one 'early malloc arena' size */
	        base += CONFIG_SYS_MALLOC_F_LEN;
#endif
}

_mian的执行流程:

 2.board_init_f()函数详解

(1)初始化一系列外设,比如串口、定时器,或者打印一些消息等;

(2)初始化 gd 的各个成员变量, uboot 会将自己重定位到 DRAM 最后面的地址区域,也就是将自己拷贝到 DRAM 最后面的内存区域中。这么做的目的是给 Linux 腾出空间,防止 Linux kernel 覆盖掉 uboot,将 DRAM 前面的区域完整的空出来。

void board_init_f(ulong boot_flags)
{
#ifdef CONFIG_SYS_GENERIC_GLOBAL_DATA //没有定义CONFIG_SYS_GENERIC_GLOBAL_DATA
	        /*
	         * For some archtectures, global data is initialized and used before
	         * calling this function. The data should be preserved. For others,
	         * CONFIG_SYS_GENERIC_GLOBAL_DATA should be defined and use the stack
	         * here to host global data until relocation.
	         */
	        gd_t data;

	        gd = &data;

	        /*
	         * Clear global data before it is accessed at debug print
	         * in initcall_run_list. Otherwise the debug print probably
	         * get the wrong vaule of gd->have_console.
	         */
	        zero_global_data();
#endif

	        gd->flags = boot_flags; //初始化 gd->flags=boot_flags=r0=0。
	        gd->have_console = 0;

	        if (initcall_run_list(init_sequence_f))  //initcall_run_list 来运行初始化序列 init_sequence_f 里面的一些
            //列函数, init_sequence_f 里面包含了一系列的初始化函数
	        	        hang();

#if !defined(CONFIG_ARM) && !defined(CONFIG_SANDBOX) && \
	        	        !defined(CONFIG_EFI_APP)
	        /* NOTREACHED - jump_to_copy() does not return */
	        hang();
#endif

	        /* Light up LED1 */
	        imx6_light_up_led1();
}

通过调用initcall_run_list()函数运行了一系列函数:

int initcall_run_list(const init_fnc_t init_sequence[])
{
	        const init_fnc_t *init_fnc_ptr;

	        for (init_fnc_ptr = init_sequence; *init_fnc_ptr; ++init_fnc_ptr) {
	        	        unsigned long reloc_ofs = 0;
	        	        int ret;

	        	        if (gd->flags & GD_FLG_RELOC)
	        	        	        reloc_ofs = gd->reloc_off;
#ifdef CONFIG_EFI_APP
	        	        reloc_ofs = (unsigned long)image_base;
#endif
	        	        debug("initcall: %p", (char *)*init_fnc_ptr - reloc_ofs);
	        	        if (gd->flags & GD_FLG_RELOC)
	        	        	        debug(" (relocated to %p)\n", (char *)*init_fnc_ptr);
	        	        else
	        	        	        debug("\n");
	        	        ret = (*init_fnc_ptr)();
	        	        if (ret) {
	        	        	        printf("initcall sequence %p failed at call %p (err=%d)\n",
	        	        	               init_sequence,
	        	        	               (char *)*init_fnc_ptr - reloc_ofs, ret);
	        	        	        return -1;
	        	        }
	        }
	        return 0;
}
static init_fnc_t init_sequence_f[] = {
#ifdef CONFIG_SANDBOX
	        setup_ram_buf,
#endif
	        setup_mon_len, //设置 gd 的 mon_len 成员变量,此处为__bss_end -_start,也就是整个代码的长度。 0X878A8E74-0x87800000=0XA8E74
#ifdef CONFIG_OF_CONTROL
	        fdtdec_setup,
#endif
#ifdef CONFIG_TRACE
	        trace_early_init,
#endif
	        initf_malloc, //初始化 gd 中跟 malloc 有关的成员变量,比如 malloc_limit,
	        	        	        	          //此函数会设置 gd->malloc_limit = CONFIG_SYS_MALLOC_F_LEN=0X400。 malloc_limit 表示 malloc内存池大小。
	        initf_console_record,
#if defined(CONFIG_MPC85xx) || defined(CONFIG_MPC86xx)
	        /* TODO: can this go into arch_cpu_init()? */
	        probecpu,
#endif
#if defined(CONFIG_X86) && defined(CONFIG_HAVE_FSP)
	        x86_fsp_init,
#endif
	        arch_cpu_init,	        	        /* basic arch cpu dependent setup */
	        initf_dm, //驱动模型的一些初始化
	        arch_cpu_init_dm, //未实现
	        mark_bootstage,	        	        //应该是和啥标记有关的/* need timer, go after init dm */
#if defined(CONFIG_BOARD_EARLY_INIT_F)
	        board_early_init_f, //,板子相关的早期的一些初始化设置, I.MX6ULL 用来初始化串口的 IO 配置
#endif
	        /* TODO: can any of this go into arch_cpu_init()? */
#if defined(CONFIG_PPC) && !defined(CONFIG_8xx_CPUCLK_DEFAULT)
	        get_clocks,	        	        /* get CPU and bus clocks (etc.) */
#if defined(CONFIG_TQM8xxL) && !defined(CONFIG_TQM866M) \
	        	        && !defined(CONFIG_TQM885D)
	        adjust_sdram_tbs_8xx,
#endif
	        /* TODO: can we rename this to timer_init()? */
	        init_timebase,
#endif
#if defined(CONFIG_ARM) || defined(CONFIG_MIPS) || \
	        	        defined(CONFIG_BLACKFIN) || defined(CONFIG_NDS32) || \
	        	        defined(CONFIG_SPARC)
	        timer_init,	        	        //初始化定时器, Cortex-A7 内核有一个定时器,这里初始化的就是 CortexA 内核的那个定时器/* initialize timer */
#endif
#ifdef CONFIG_SYS_ALLOC_DPRAM
#if !defined(CONFIG_CPM2)
	        dpram_init,
#endif
#endif
#if defined(CONFIG_BOARD_POSTCLK_INIT)
	        board_postclk_init, // I.MX6ULL 来说是设置 VDDSOC 电压
#endif
#if defined(CONFIG_SYS_FSL_CLK) || defined(CONFIG_M68K)
	        get_clocks, //用于获取一些时钟值, I.MX6ULL 获取的是 sdhc_clk 时钟,也就是 SD 卡外设的时钟
#endif
	        env_init,	        //设置 gd 的成员变量 env_addr,也就是环境变量的保存地址。/* initialize environment */
#if defined(CONFIG_8xx_CPUCLK_DEFAULT)
	        /* get CPU and bus clocks according to the environment variable */
	        get_clocks_866,
	        /* adjust sdram refresh rate according to the new clock */
	        sdram_adjust_866,
	        init_timebase,
#endif
	        init_baud_rate,	        	        //用于初始化波特率,根据环境变量 baudrate 来初始化 gd->baudrate。/* initialze baudrate settings */
	        serial_init,	        //初始化串口	        /* serial communications setup */
	        console_init_f,	        //设置 gd->have_console 为 1,表示有个控制台,此函数也将前面暂存在缓冲区中的数据通过控制台打印出来。	        /* stage 1 init of console */
#ifdef CONFIG_SANDBOX
	        sandbox_early_getopt_check,
#endif
#ifdef CONFIG_OF_CONTROL
	        fdtdec_prepare_fdt,
#endif
	        display_options,	        //通过串口输出一些信息/* say that we are here */
	        display_text_info,	        //打印一些文本信息,如果开启 UBOOT 的 DEBUG 功能的话就会输出 text_base、 bss_start、 bss_end/* show debugging info if required */
#if defined(CONFIG_MPC8260)
	        prt_8260_rsr,
	        prt_8260_clks,
#endif /* CONFIG_MPC8260 */
#if defined(CONFIG_MPC83xx)
	        prt_83xx_rsr,
#endif
#if defined(CONFIG_PPC) || defined(CONFIG_M68K)
	        checkcpu,
#endif
	        print_cpuinfo,	        //print_cpuinfo 函数用于打印 CPU 信息	        /* display cpu info (and speed) */
#if defined(CONFIG_MPC5xxx)
	        prt_mpc5xxx_clks,
#endif /* CONFIG_MPC5xxx */
#if defined(CONFIG_DISPLAY_BOARDINFO)
	        show_board_info,
#endif
	        INIT_FUNC_WATCHDOG_INIT
#if defined(CONFIG_MISC_INIT_F)
	        misc_init_f,
#endif
	        INIT_FUNC_WATCHDOG_RESET
#if defined(CONFIG_HARD_I2C) || defined(CONFIG_SYS_I2C)
	        init_func_i2c, //初始化 I2C,初始化完成以后会输出
#endif
#if defined(CONFIG_HARD_SPI)
	        init_func_spi,
#endif
	        announce_dram_init, //输出字符串“DRAM:”
	        /* TODO: unify all these dram functions? */
#if defined(CONFIG_ARM) || defined(CONFIG_X86) || defined(CONFIG_NDS32) || \
	        	        defined(CONFIG_MICROBLAZE) || defined(CONFIG_AVR32)
	        dram_init,	        //设置 gd->ram_size 的值	        /* configure available RAM banks */
#endif
#if defined(CONFIG_MIPS) || defined(CONFIG_PPC) || defined(CONFIG_M68K)
	        init_func_ram,
#endif
#ifdef CONFIG_POST
	        post_init_f, //用来完成一些测试,初始化 gd->post_init_f_time
#endif
	        INIT_FUNC_WATCHDOG_RESET
#if defined(CONFIG_SYS_DRAM_TEST)
	        testdram,
#endif /* CONFIG_SYS_DRAM_TEST */
	        INIT_FUNC_WATCHDOG_RESET

#ifdef CONFIG_POST
	        init_post,
#endif
	        INIT_FUNC_WATCHDOG_RESET
	        /*
	         * Now that we have DRAM mapped and working, we can
	         * relocate the code and continue running from DRAM.
	         *
	         * Reserve memory at end of RAM for (top down in that order):
	         *  - area that won't get touched by U-Boot and Linux (optional)
	         *  - kernel log buffer
	         *  - protected RAM
	         *  - LCD framebuffer
	         *  - monitor code
	         *  - board info struct
	         */
	        setup_dest_addr,//设置目的地址,设置gd->ram_size, gd->ram_top, gd->relocaddr
#if defined(CONFIG_BLACKFIN)
	        /* Blackfin u-boot monitor should be on top of the ram */
	        reserve_uboot,
#endif
#if defined(CONFIG_SPARC)
	        reserve_prom,
#endif
#if defined(CONFIG_LOGBUFFER) && !defined(CONFIG_ALT_LB_ADDR)
	        reserve_logbuffer,
#endif
#ifdef CONFIG_PRAM
	        reserve_pram,
#endif
	        reserve_round_4k, //用 于 对 gd->relocaddr 做 4KB 对 齐
#if !(defined(CONFIG_SYS_ICACHE_OFF) && defined(CONFIG_SYS_DCACHE_OFF)) && \
	        	        defined(CONFIG_ARM)
	        reserve_mmu,//留出 MMU 的 TLB 表的位置,分配 MMU 的 TLB 表内存以后会对 gd->relocaddr 做 64K 字节对齐。
#endif
#ifdef CONFIG_DM_VIDEO
	        reserve_video,
#else
# ifdef CONFIG_LCD
	        reserve_lcd,
# endif
	        /* TODO: Why the dependency on CONFIG_8xx? */
# if defined(CONFIG_VIDEO) && (!defined(CONFIG_PPC) || defined(CONFIG_8xx)) && \
	        	        !defined(CONFIG_ARM) && !defined(CONFIG_X86) && \
	        	        !defined(CONFIG_BLACKFIN) && !defined(CONFIG_M68K)
	        reserve_legacy_video,
# endif
#endif /* CONFIG_DM_VIDEO */
	        reserve_trace, //留出跟踪调试的内存
#if !defined(CONFIG_BLACKFIN)
	        reserve_uboot, //留出重定位后的 uboot 所占用的内存区域, 
	                      //uboot 所占用大小由gd->mon_len 所指定,留出 uboot 的空间以后还要对 gd->relocaddr 做 4K 字节对齐,并且重新设
                 //置 gd->start_addr_sp
#endif
#ifndef CONFIG_SPL_BUILD
	        reserve_malloc, //留出 malloc 区域,调整 gd->start_addr_sp 位置
	        reserve_board, //留出板子 bd 所占的内存区, bd 是结构体 bd_t, bd_t 大小为80B
#endif
	        setup_machine, //设置机器 ID, linux 启动的时候会和这个机器 ID 匹配,如果匹配的话 linux 就会启动正常,IMX使用设备树
	        reserve_global_data, //保留出 gd_t 的内存区域, gd_t 结构体大小为 248B
	        reserve_fdt, //留出设备树相关的内存区域, I.MX6ULL 的 uboot 没有用到
	        reserve_arch,
	        reserve_stacks,//留出栈空间,先对 gd->start_addr_sp 减去 16
	        setup_dram_config, //设置 dram 信息,就是设置 gd->bd->bi_dram[0].start 和
	        	        	        	        	        	        //gd->bd->bi_dram[0].size,后面会传递给 linux 内核,告诉 linux DRAM 的起始地址和大小
	        show_dram_config, //显示 DRAM 的配置
#if defined(CONFIG_PPC) || defined(CONFIG_M68K) || defined(CONFIG_MIPS)
	        setup_board_part1,
#endif
#if defined(CONFIG_PPC) || defined(CONFIG_M68K)
	        INIT_FUNC_WATCHDOG_RESET
	        setup_board_part2,
#endif
	        display_new_sp, //显示新的 sp 位置,也就是 gd->start_addr_sp
#ifdef CONFIG_SYS_EXTBDINFO
	        setup_board_extra,
#endif
	        INIT_FUNC_WATCHDOG_RESET
	        reloc_fdt, //reloc_fdt 函数用于重定位 fdt
	        setup_reloc, //设置 gd 的其他一些成员变量,供后面重定位的时候使用,并且将以
	        	        	        	        //前的 gd 拷贝到 gd->new_gd 处
#if defined(CONFIG_X86) || defined(CONFIG_ARC)
	        copy_uboot_to_ram,
	        clear_bss,
	        do_elf_reloc_fixups,
#endif
#if !defined(CONFIG_ARM) && !defined(CONFIG_SANDBOX)
	        jump_to_copy,
#endif
	        NULL,
};

最终形成的内存布局:

 3. relocate_code 函数详解

主要作用是拷贝代码。

 arch/arm/lib/relocate.S
ENTRY(relocate_code)
	        ldr	        r1, =__image_copy_start	        //r1=__image_copy_start=0x87800000/* r1 <- SRC &__image_copy_start */
	        subs	        r4, r0, r1	        	        //r4=r0-r1=0X9FF47000-0X87800000=0X18747000 计算偏移/* r4 <- relocation offset */
	        beq	        relocate_done //源地址和目的地址是相同的,不用拷贝	        	        /* skip relocation */
	        ldr	        r2, =__image_copy_end //__image_copy_end =0x8785dd54/* r2 <- SRC &__image_copy_end */

copy_loop: //完成代码拷贝工作
	        ldmia	        r1!, {r10-r11}	        //从 r1,也就是__image_copy_start 开始,读取 uboot 代码保存到 r10 和 r11 中/* copy from source address [r1]    */
	        stmia	        r0!, {r10-r11}	        //0X9FF47000 /* copy to   target address [r0]    */
	        cmp	        r1, r2	        //直到拷贝完	        	        /* until source end address [r2]    */
	        blo	        copy_loop

//重定位.rel.dyn 段, .rel.dyn 段是存放.text 段中需要重定位地址的
//集合。重定位就是 uboot 将自身拷贝到 DRAM 的另一个地放去继续运行
	        /*
	         * fix .rel.dyn relocations
	         */
	        ldr	        r2, =__rel_dyn_start	        //.rel.dyn 段的起始地址/* r2 <- SRC &__rel_dyn_start */
	        ldr	        r3, =__rel_dyn_end	        //.rel.dyn 段的终止地址/* r3 <- SRC &__rel_dyn_end */
fixloop:
	        //从.rel.dyn 段起始地址开始,每次读取两个 4 字节的数据存放到 r0 和 r1 寄存器
	        //中, r0 存放低 4 字节的数据,也就是 Label 地址; r1 存放高 4 字节的数据,也就是 Label 标志
	        ldmia	        r2!, {r0-r1}	        	        /* (r0,r1) <- (SRC location,fixup) */
	        and	        r1, r1, #0xff //取 r1 的低 8 位。
	        cmp	        r1, #23	         //r1 中的值是否等于 23(0X17)	        	        /* relative fixup? */
	        bne	        fixnext //执行函数 fixnext

	        /* relative fix: increase location by offset */
	        add	        r0, r0, r4 //r0+r4 就得到了重定位后的Label 值
	        ldr	        r1, [r0] //读取重定位后 Label 所保存的变量地址
	        add	        r1, r1, r4 // 得 到 重 定 位 后 的 变 量 地 址
	        str	        r1, [r0] //重定位后的变量地址写入到重定位后的 Label 
fixnext:
	        cmp	        r2, r3 //.rel.dyn 段重定位是否完成
	        blo	        fixloop

relocate_done:

#ifdef __XSCALE__
	        /*
	         * On xscale, icache must be invalidated and write buffers drained,
	         * even with cache disabled - 4.2.7 of xscale core developer's manual
	         */
	        mcr	        p15, 0, r0, c7, c7, 0	        /* invalidate icache */
	        mcr	        p15, 0, r0, c7, c10, 4	        /* drain write buffer */
#endif

	        /* ARMv4- don't know bx lr but the assembler fails to see that */

#ifdef __ARM_ARCH_4__
	        mov	        pc, lr
#else
	        bx	        lr
#endif

ENDPROC(relocate_code)

4.relocate_vectors 函数详解

重定位向量表:

ENTRY(relocate_vectors)

#ifdef CONFIG_CPU_V7M
	        /*
	         * On ARMv7-M we only have to write the new vector address
	         * to VTOR register.
	         */
	        ldr	        r0, [r9, #GD_RELOCADDR]	        /* r0 = gd->relocaddr */
	        ldr	        r1, =V7M_SCB_BASE
	        str	        r0, [r1, V7M_SCB_VTOR]
#else
#ifdef CONFIG_HAS_VBAR
	        /*
	         * If the ARM processor has the security extensions,
	         * use VBAR to relocate the exception vectors.
	         */
	        ldr	        r0, [r9, #GD_RELOCADDR]	        //r0=gd->relocaddr,也就是重定位后 uboot 的首地址/* r0 = gd->relocaddr */
	        mcr     p15, 0, r0, c12, c0, 0  //r0 的值写入到 CP15 的 VBAR 寄存器/* Set VBAR */
#else
	        /*
	         * Copy the relocated exception vectors to the
	         * correct address
	         * CP15 c1 V bit gives us the location of the vectors:
	         * 0x00000000 or 0xFFFF0000.
	         */
	        ldr	        r0, [r9, #GD_RELOCADDR]	        /* r0 = gd->relocaddr */
	        mrc	        p15, 0, r2, c1, c0, 0	        /* V bit (bit[13]) in CP15 c1 */
	        ands	        r2, r2, #(1 << 13)
	        ldreq	        r1, =0x00000000	        	        /* If V=0 */
	        ldrne	        r1, =0xFFFF0000	        	        /* If V=1 */
	        ldmia	        r0!, {r2-r8,r10}
	        stmia	        r1!, {r2-r8,r10}
	        ldmia	        r0!, {r2-r8,r10}
	        stmia	        r1!, {r2-r8,r10}
#endif
#endif
	        bx	        lr

ENDPROC(relocate_vectors)

5.board_init_r 函数详解

board_init_f 并没有初始化所有的外设,还需要做一些后续工作,这些后续工作就是由函数 board_init_r 来完成的。

common/board_r.c
void board_init_r(gd_t *new_gd, ulong dest_addr)
{
#ifdef CONFIG_NEEDS_MANUAL_RELOC
	        int i;
#endif

#ifdef CONFIG_AVR32
	        mmu_init_r(dest_addr);
#endif

#if !defined(CONFIG_X86) && !defined(CONFIG_ARM) && !defined(CONFIG_ARM64)
	        gd = new_gd;
#endif

#ifdef CONFIG_NEEDS_MANUAL_RELOC
	        for (i = 0; i < ARRAY_SIZE(init_sequence_r); i++)
	        	        init_sequence_r[i] += gd->reloc_off;
#endif

	        if (initcall_run_list(init_sequence_r))//调用 initcall_run_list 函数来执行初始化序列 init_sequence_r
	        	        hang();

	        /* NOTREACHED - run_main_loop() does not return */
	        hang();
}
init_fnc_t init_sequence_r[] = {
	        initr_trace, //初始化和调试跟踪有关的内容
	        initr_reloc, //用于设置 gd->flags,标记重定位完成
	        /* TODO: could x86/PPC have this also perhaps? */
#ifdef CONFIG_ARM
	        initr_caches, //初始化 cache,使能 cache
	        /* Note: For Freescale LS2 SoCs, new MMU table is created in DDR.
	         *	         A temporary mapping of IFC high region is since removed,
	         *	         so environmental variables in NOR flash is not availble
	         *	         until board_init() is called below to remap IFC to high
	         *	         region.
	         */
#endif
	        initr_reloc_global_data, //初始化重定位后 gd 的一些成员变量
#if defined(CONFIG_SYS_INIT_RAM_LOCK) && defined(CONFIG_E500)
	        initr_unlock_ram_in_cache,
#endif
	        initr_barrier,
	        initr_malloc, //初始化 malloc
	        initr_console_record, //初始化控制台相关的内容
#ifdef CONFIG_SYS_NONCACHED_MEMORY
	        initr_noncached,
#endif
	        bootstage_relocate, //启动状态重定位
#ifdef CONFIG_DM
	        initr_dm,
#endif
	        initr_bootstage, //初始化 bootstage 什么的
#if defined(CONFIG_ARM) || defined(CONFIG_NDS32)
	        board_init,	        /* Setup chipselects */ //板级初始化,包括 74XX 芯片, I2C、 FEC、 USB 和 QSPI 等
#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
	        stdio_init_tables, //stdio 相关初始化
	        initr_serial, //初始化串口
	        initr_announce, //与调试有关,通知已经在 RAM 中运行
	        INIT_FUNC_WATCHDOG_RESET
#ifdef CONFIG_NEEDS_MANUAL_RELOC
	        initr_manual_reloc_cmdtable,
#endif
#if defined(CONFIG_PPC) || defined(CONFIG_M68K)
	        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_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
#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_M68K) || defined(CONFIG_X86) || \
	        defined(CONFIG_SPARC)
	        /* initialize higher level parts of CPU like time base and timers */
	        cpu_init_r,
#endif
#ifdef CONFIG_PPC
	        initr_spi,
#endif
#ifdef CONFIG_CMD_NAND
	        initr_nand, //初始化 NAND
#endif
#ifdef CONFIG_CMD_ONENAND
	        initr_onenand,
#endif
#ifdef CONFIG_GENERIC_MMC
	        initr_mmc, //初始化 EMMC
#endif
#ifdef CONFIG_HAS_DATAFLASH
	        initr_dataflash,
#endif
	        initr_env, //初始化环境变量
#ifdef CONFIG_SYS_BOOTPARAMS_LEN
	        initr_malloc_bootparams,
#endif
	        INIT_FUNC_WATCHDOG_RESET
	        initr_secondary_cpu, //初始化其他 CPU 核
#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_add_devices, //各种输入输出设备的初始化,如 LCD drive
	        initr_jumptable, //初始化跳转表
#ifdef CONFIG_API
	        initr_api,
#endif
	        console_init_r,	        	        //控 制 台 初 始 化/* fully init console as a device */
#ifdef CONFIG_DISPLAY_BOARDINFO_LATE
	        show_board_info,
#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
	        INIT_FUNC_WATCHDOG_RESET
#ifdef CONFIG_CMD_KGDB
	        initr_kgdb,
#endif
	        interrupt_init, //初始化中断
#if defined(CONFIG_ARM) || defined(CONFIG_AVR32)
	        initr_enable_interrupts, //使能中断
#endif
#if defined(CONFIG_MICROBLAZE) || defined(CONFIG_AVR32) || defined(CONFIG_M68K)
	        timer_init,	        	        /* initialize timer */
#endif
#if defined(CONFIG_STATUS_LED)
	        initr_status_led,
#endif
	        /* PPC has a udelay(20) here dating from 2002. Why? */
#ifdef CONFIG_CMD_NET
	        initr_ethaddr, //初始化网络地址,也就是获取 MAC 地址
#endif
#ifdef CONFIG_BOARD_LATE_INIT
	        board_late_init, //板子后续初始化
#endif
#ifdef CONFIG_FSL_FASTBOOT
	        initr_fastboot_setup,
#endif
#if defined(CONFIG_CMD_AMBAPP)
	        ambapp_init_reloc,
#if defined(CONFIG_SYS_AMBAPP_PRINT_ON_STARTUP)
	        initr_ambapp_print,
#endif
#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
#if defined(CONFIG_SPARC)
	        prom_init,
#endif
#ifdef CONFIG_FSL_FASTBOOT
	        initr_check_fastboot,
#endif

	        /* Light up LED2 */
        imx6_light_up_led2,

	        run_main_loop,//主循环,处理命令。
};

初始完设备后,进入主循环run_main_loop():uboot 启动以后会进入 3 秒倒计时,如果在 3 秒倒计时结束之前按下按下回车键,那么就会进入 uboot 的命令模式,如果倒计时结束以后都没有按下回车键,那么就会自动启动 Linux 内核 , 这 个 功 能 就 是 由 run_main_loop 函 数 来 完 成 的 。 run_main_loop 函 数 定 义 在 文 件common/board_r.c 中:

static int run_main_loop(void)
{
#ifdef CONFIG_SANDBOX
	        sandbox_main_loop_init();
#endif
	        /* main_loop() can return to retry autoboot, if so just run it again */
	        for (;;)
	        	        main_loop();
	        return 0;
}
/* 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");//打印出启动进度

#ifndef CONFIG_SYS_GENERIC_BOARD
	        puts("Warning: Your board does not use generic board. Please read\n");
	        puts("doc/README.generic-board and take action. Boards not\n");
	        puts("upgraded by the late 2014 may break or be removed.\n");
#endif

#ifdef CONFIG_VERSION_VARIABLE
	        setenv("ver", version_string);  /* set version variable *///setenv,设置将变量 ver 的值为 version_string,也就是设置版本号环境变量
#endif /* CONFIG_VERSION_VARIABLE */

	        cli_init();
	        //获取环境变量 perboot 的内容, perboot
	        //是一些预启动命令,一般不使用这个环境变量
	        run_preboot_environment_command();

#if defined(CONFIG_UPDATE_TFTP)
	        update_tftp(0UL, NULL, NULL);
#endif /* CONFIG_UPDATE_TFTP */
	        //读取环境变量 bootdelay 和 bootcmd 的内容,
	        //然后将 bootdelay 的值赋值给全局变量 stored_bootdelay,返回值为环境变量 bootcmd 的值
	        s = bootdelay_process();
	        if (cli_process_fdt(&s))
	        	        cli_secure_boot_cmd(s);

	        autoboot_command(s);//检查倒计时是否结束?倒计时结束之前有没有被打断?

	        cli_loop();
}
void autoboot_command(const char *s)
{
	        debug("### main_loop: bootcmd=\"%s\"\n", s ? s : "<UNDEFINED>");
	        //stored_bootdelay 等于环境变量 bootdelay 的值; s 是环境变量 bootcmd 的值
	        if (stored_bootdelay != -1 && s && !abortboot(stored_bootdelay)) {
#if defined(CONFIG_AUTOBOOT_KEYED) && !defined(CONFIG_AUTOBOOT_KEYED_CTRLC)
	        	        int prev = disable_ctrlc(1);	        /* disable Control C checking */
#endif

	        	        run_command_list(s, -1, 0);

#if defined(CONFIG_AUTOBOOT_KEYED) && !defined(CONFIG_AUTOBOOT_KEYED_CTRLC)
	        	        disable_ctrlc(prev);	        /* restore Control C checking */
#endif
	        }

#ifdef CONFIG_MENUKEY
	        if (menukey == CONFIG_MENUKEY) {
	        	        s = getenv("menucmd");
	        	        if (s)
	        	        	        run_command_list(s, -1, 0);
	        }
#endif /* CONFIG_MENUKEY */
}
static int abortboot(int bootdelay)
{
#ifdef CONFIG_AUTOBOOT_KEYED //无定义
	        return abortboot_keyed(bootdelay);
#else
	        return abortboot_normal(bootdelay); //执行normal
#endif
}
static int abortboot_normal(int bootdelay)
{
	int abort = 0; //变量 abort 是函数 abortboot_normal 的返回值,默认值为 0
	unsigned long ts;

#ifdef CONFIG_MENUPROMPT
	printf(CONFIG_MENUPROMPT);
#else
	if (bootdelay >= 0) //通过串口输出“Hit any key to stop autoboot”字样
		printf("Hit any key to stop autoboot: %2d ", bootdelay);
#endif

#if defined CONFIG_ZERO_BOOTDELAY_CHECK
	/*
	 * Check if key already pressed
	 * Don't check if bootdelay < 0
	 */
	if (bootdelay >= 0) {
		if (tstc()) {	/* we got a key press	*///判断键盘是否有按下,也就是是否打断了倒计时
			(void) getc();  /* consume input	*/
			puts("\b\b\b 0");
			abort = 1;	/* don't auto boot	*/ //设置 abort 为 1,设置 bootdelay 为 0 等,最后跳出倒计时循环
		}
	}
#endif

	while ((bootdelay > 0) && (!abort)) {
		--bootdelay;
		/* delay 1000 ms */
		ts = get_timer(0);
		do {
			if (tstc()) {	/* we got a key press	*/
				abort  = 1;	/* don't auto boot	*/
				bootdelay = 0;	/* no more delay	*/
# ifdef CONFIG_MENUKEY
				menukey = getc();
# else
				(void) getc();  /* consume input	*/
# endif
				break;
			}
			udelay(10000);
		} while (!abort && get_timer(ts) < 1000);

		printf("\b\b\b%2d ", bootdelay);
	}

	putc('\n');

#ifdef CONFIG_SILENT_CONSOLE
	if (abort)
		gd->flags &= ~GD_FLG_SILENT;
#endif

	return abort; //返回 abort 的值,如果倒计时自然结束,没有被打断 abort 就为 0
}

如果倒计时自然结束那么就执行函数run_command_list,此函数会执行参数 s 指定的一系列命令,也就是环境变量 bootcmd 的命令,bootcmd 里面保存着默认的启动命令,因此 linux 内核启动。如果倒计时结束之前按下按键,那么就会执行main_loop的 cli_loop 函数,这个就是命令处理函数,负责接收好处理输入的命令。

void cli_loop(void)
{
#ifdef CONFIG_SYS_HUSH_PARSER
	        parse_file_outer(); //调用函数 parse_file_outer
	        /* This point is never reached */
	        for (;;); //永远不会执行到这里
#else
	        cli_simple_loop();
#endif /*CONFIG_SYS_HUSH_PARSER*/
}
#ifndef __U_BOOT__
static int parse_file_outer(FILE *f)
#else
int parse_file_outer(void)
#endif
{
	int rcode;
	struct in_str input;
#ifndef __U_BOOT__
	setup_file_in_str(&input, f); //初始化变量 input 的成员变量
#else
	setup_file_in_str(&input);
#endif
	rcode = parse_stream_outer(&input, FLAG_PARSE_SEMICOLON); //hush shell 的命令解释器,负责接收命令行输入,然后解析并执行相应的命令,
	return rcode;
}
static int parse_stream_outer(struct in_str *inp, int flag)
{

	struct p_context ctx;
	o_string temp=NULL_O_STRING;
	int rcode;
#ifdef __U_BOOT__
	int code = 1;
#endif
	do {
		ctx.type = flag;
		initialize_context(&ctx);
		update_ifs_map();
		if (!(flag & FLAG_PARSE_SEMICOLON) || (flag & FLAG_REPARSING)) mapset((uchar *)";$&|", 0);
		inp->promptmode=1;
		rcode = parse_stream(&temp, &ctx, inp,
				     flag & FLAG_CONT_ON_NEWLINE ? -1 : '\n');//parse_stream 进行命令解析
#ifdef __U_BOOT__
		if (rcode == 1) flag_repeat = 0;
#endif
		if (rcode != 1 && ctx.old_flag != 0) {
			syntax();
#ifdef __U_BOOT__
			flag_repeat = 0;
#endif
		}
		if (rcode != 1 && ctx.old_flag == 0) {
			done_word(&temp, &ctx);
			done_pipe(&ctx,PIPE_SEQ);
#ifndef __U_BOOT__
			run_list(ctx.list_head);
#else
			code = run_list(ctx.list_head); //执行解析出来的命令
			if (code == -2) {	/* exit */
				b_free(&temp);
				code = 0;
				/* XXX hackish way to not allow exit from main loop */
				if (inp->peek == file_peek) {
					printf("exit not allowed from main input shell.\n");
					continue;
				}
				break;
			}
			if (code == -1)
			    flag_repeat = 0;
#endif
		} else {
			if (ctx.old_flag != 0) {
				free(ctx.stack);
				b_reset(&temp);
			}
#ifdef __U_BOOT__
			if (inp->__promptme == 0) printf("<INTERRUPT>\n");
			inp->__promptme = 1;
#endif
			temp.nonnull = 0;
			temp.quote = 0;
			inp->p = NULL;
			free_pipe_list(ctx.list_head,0);
		}
		b_free(&temp);
	/* loop on syntax errors, return on EOF */
	} while (rcode != -1 && !(flag & FLAG_EXIT_FROM_LOOP) &&
		(inp->peek != static_peek || b_peek(inp)));
#ifndef __U_BOOT__
	return 0;
#else
	return (code != 0) ? 1 : 0;
#endif /* __U_BOOT__ */
}

函数 run_list 会经过一系列的函数调用,最终通过调用 cmd_process 函数来处理命令。先看Uboot中的命令是如何定义的?先看U_BOOT_CMD:

#define U_BOOT_CMD(_name, _maxargs, _rep, _cmd, _usage, _help)	        	        \
	        U_BOOT_CMD_COMPLETE(_name, _maxargs, _rep, _cmd, _usage, _help, NULL)

可以看出U_BOOT_CMD是U_BOOT_CMD_COMPLETE的一个特例:

#define U_BOOT_CMD_COMPLETE(_name, _maxargs, _rep, _cmd, _usage, _help, _comp) \
	ll_entry_declare(cmd_tbl_t, _name, cmd) =			\
		U_BOOT_CMD_MKENT_COMPLETE(_name, _maxargs, _rep, _cmd,	\
						_usage, _help, _comp);

引用了ll_eatry_declar和U_BOOT_CMD_MKENT_COMPLETE:

 /* Example:
 * ll_entry_declare(struct my_sub_cmd, my_sub_cmd, cmd_sub) = {
 *         .x = 3,
 *         .y = 4,
 * };
 */
#define ll_entry_declare(_type, _name, _list)				\
	_type _u_boot_list_2_##_list##_2_##_name __aligned(4)		\
			__attribute__((unused,				\
			section(".u_boot_list_2_"#_list"_2_"#_name)))

_type 为 cmd_tbl_t,因此 ll_entry_declare 就是定义了一个 cmd_tbl_t 变量,这里用到了 C 语 言中的“##”连接符符。其中的“##_list”表示用_list 的值来替换,“##_name”就是用_name 的 值来替换。

#define U_BOOT_CMD_MKENT_COMPLETE(_name, _maxargs, _rep, _cmd,	        	        \
	        	        	        	        _usage, _help, _comp)	        	        	        \
	        	        { #_name, _maxargs, _rep, _cmd, _usage,	        	        	        \
	        	        	        _CMD_HELP(_help) _CMD_COMPLETE(_comp) }
#ifdef CONFIG_AUTO_COMPLETE
# define _CMD_COMPLETE(x) x,
#else
# define _CMD_COMPLETE(x)
#endif
#ifdef CONFIG_SYS_LONGHELP
# define _CMD_HELP(x) x,
#else
# define _CMD_HELP(x)
#endif

Uboot命令例子:以dhcp为例

U_BOOT_CMD(
	        dhcp,	        3,	        1,	        do_dhcp,
	        "boot image via network using DHCP/TFTP protocol",
	        "[loadAddress] [[hostIPaddr:]bootfilename]"
);
1.将U_BOOT_CMD展开后:
#define U_BOOT_CMD(_name, _maxargs, _rep, _cmd, _usage, _help)	        	        \
	                                        U_BOOT_CMD_COMPLETE(dhcp, 3, 1,do_dhcp, 
                                        "boot image via network using DHCP/TFTP protocol", 
                                        "[loadAddress] [[hostIPaddr:]bootfilename]", NULL)
2.将U_BOOT_CMD_COMPLETE展开后:
   #define U_BOOT_CMD_COMPLETE(_name, _maxargs, _rep, _cmd, _usage, _help, _comp) \
	        ll_entry_declare(cmd_tbl_t, dhcp, do_dhcp) =	        	        	        \
	        	        U_BOOT_CMD_MKENT_COMPLETE(dhcp, 3, 1,do_dhcp,	        \
	        	        	        "boot image via network using DHCP/TFTP protocol", 
                                    "[loadAddress] [[hostIPaddr:]bootfilename]", NULL);
3.将ll_entry_declare和U_BOOT_CMD_MKENT_COMPLETE展开后:
#define ll_entry_declare(_type, _name, _list)	        	        	        	        \
	        cmd_tbl_t _u_boot_list_2_cmd_2_dhcp __aligned(4)	        	        \
	        	        	        __attribute__((unused,	        	        	        	        \
	        	        	        section( .u_boot_list_2_cmd_2_dhcp)))

#define U_BOOT_CMD_MKENT_COMPLETE(_name, _maxargs, _rep, _cmd,	        	        \
	        	        	        	        _usage, _help, _comp)	        	        	        \
	                              { "dhcp", 3, 1,do_dhcp, 
                            "boot image via network using DHCP/TFTP protocol",             	                                	                                \
	        	        	        "[loadAddress] [[hostIPaddr:]bootfilename]", NULL }
4.最终结果:
 cmd_tbl_t _u_boot_list_2_cmd_2_dhcp __aligned(4)	                	                \
	                	                	                __attribute__((unused,	                	                	                	                \
                                 section( .u_boot_list_2_cmd_2_dhcp))){ "dhcp", 3, 1,do_dhcp, 
                            "boot image via network using DHCP/TFTP protocol",             	                                        	                                        \
	                	                	                "[loadAddress] [[hostIPaddr:]bootfilename]", NULL }

使 用 __attribute__ 关 键 字 设 置 变 量 _u_boot_list_2_cmd_2_dhcp 存 储在.u_boot_list_2_cmd_2_dhcp 段中。 u-boot.lds 链接脚本中有一个名为“.u_boot_list”的段,所有.u_boot_list 开头的段都存放到.u_boot.list 中,cmd_tbl_t是一个结构体:

struct cmd_tbl_s {
	char		*name;		/* Command Name			*/
	int		maxargs;	/* maximum number of arguments	*/
	int		repeatable;	/* autorepeat allowed?		*/
					/* Implementation function	*/
	int		(*cmd)(struct cmd_tbl_s *, int, int, char * const []);
	char		*usage;		/* Usage message	(short)	*/
#ifdef	CONFIG_SYS_LONGHELP
	char		*help;		/* Help  message	(long)	*/
#endif
#ifdef CONFIG_AUTO_COMPLETE
	/* do auto completion on the arguments */
	int		(*complete)(int argc, char * const argv[], char last_char, int maxv, char *cmdv[]);
#endif
};

调用command_process()函数处理命令:

enum command_ret_t cmd_process(int flag, int argc, char * const argv[],
			       int *repeatable, ulong *ticks)
{
	enum command_ret_t rc = CMD_RET_SUCCESS;
	cmd_tbl_t *cmdtp;

	/* Look up command in command table */
	cmdtp = find_cmd(argv[0]);  //find_cmd 在命令表中找到指定的命令
	if (cmdtp == NULL) {
		printf("Unknown command '%s' - try 'help'\n", argv[0]);
		return 1;
	}

	/* found - check max args */
	if (argc > cmdtp->maxargs)
		rc = CMD_RET_USAGE;

#if defined(CONFIG_CMD_BOOTD)
	/* avoid "bootd" recursion */
	else if (cmdtp->cmd == do_bootd) {
		if (flag & CMD_FLAG_BOOTD) {
			puts("'bootd' recursion detected\n");
			rc = CMD_RET_FAILURE;
		} else {
			flag |= CMD_FLAG_BOOTD;
		}
	}
#endif

	/* If OK so far, then do the command */
	if (!rc) {
		if (ticks)
			*ticks = get_timer(0);
		rc = cmd_call(cmdtp, flag, argc, argv);//执行具体的命令
		if (ticks)
			*ticks = get_timer(*ticks);
		*repeatable &= cmdtp->repeatable;
	}
	if (rc == CMD_RET_USAGE)
		rc = cmd_usage(cmdtp);
	return rc;
}
cmd_tbl_t *find_cmd(const char *cmd)
{
	cmd_tbl_t *start = ll_entry_start(cmd_tbl_t, cmd);//得到命令表数组的第一个元素
	const int len = ll_entry_count(cmd_tbl_t, cmd);//命令表的长度
    return find_cmd_tbl(cmd, start, len);//将参数 cmd 与命令表中每个成员的 name 字段都对比
}
static int cmd_call(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
{
	int result;

	result = (cmdtp->cmd)(cmdtp, flag, argc, argv); //具体的命令处理函数
	if (result)
		debug("Command failed, result=%d\n", result);
	return result;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小吴伴学者

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值