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

Posted Mculover666

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了uboot研读笔记 | 14 - uboot启动流程分析(2016.03版本)相关的知识,希望对你有一定的参考价值。

【腾讯文档】uboot启动流程图(2016.03)https://docs.qq.com/flowchart/DR25oeU5KQmJydEhM

文章目录

一、第一行代码

要分析uboot启动流程,先得找到uboot启动的第一行代码,编译uboot,查看u-boot.map文件,找到Linker script and memory map这一节:

.text           0x0000000087800000    0x3e734
 *(.__image_copy_start)
 .__image_copy_start
                0x0000000087800000        0x0 arch/arm/lib/built-in.o
                0x0000000087800000                __image_copy_start
 *(.vectors)
 .vectors       0x0000000087800000      0x300 arch/arm/lib/built-in.o
                0x0000000087800000                _start
                0x0000000087800020                _undefined_instruction
                0x0000000087800024                _software_interrupt
                0x0000000087800028                _prefetch_abort
                0x000000008780002c                _data_abort
                0x0000000087800030                _not_used
                0x0000000087800034                _irq
                0x0000000087800038                _fiq
                0x0000000087800040                IRQ_STACK_START_IN

可以看到,uboot编译出的可执行程序中,最开始放置的还是中断向量表(vectors),第一行代码为 _start 函数,地址在0x87800000处,并把此地址作为起始地址赋给 __image_copy_start 变量用于镜像拷贝。

二、_start 函数

map文件中给出了 _start 函数在 arch/arm/lib 下面,搜索一下在arch/arm/lib/Vectors.S文件中:

/*
 *************************************************************************
 *
 * Exception vectors as described in ARM reference manuals
 *
 * Uses indirect branch to allow reaching handlers anywhere in memory.
 *
 *************************************************************************
 */
 
_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

可以看到,_start函数最先跳转到reset函数执行,不返回,接着依次执行后面的函数。

搜索一下,reset函数在arch/arm/cpu/armv7/start.S中:

/*************************************************************************
 *
 * Startup Code (reset vector)
 *
 * Do important init only if we don't start from memory!
 * Setup memory and board specific bits prior to relocation.
 * Relocate armboot to ram. Setup stack.
 *
 *************************************************************************/

	.globl	reset
	.globl	save_boot_params_ret

reset:
	/* Allow the board to save important registers */
	b	save_boot_params

1. save_boot_params函数

复位时允许CPU先保存重要的寄存器,save_boot_params函数定义如下:

/*************************************************************************
 *
 * void save_boot_params(u32 r0, u32 r1, u32 r2, u32 r3)
 *	__attribute__((weak));
 *
 * Stack pointer is not yet initialized at this moment
 * Don't save anything to stack even if compiled with -O0
 *
 *************************************************************************/
ENTRY(save_boot_params)
	b	save_boot_params_ret		@ back to my caller
ENDPROC(save_boot_params)
	.weak	save_boot_params

该函数跳转到 save_boot_params_ret 去执行,不返回。

2. save_boot_params_ret函数

save_boot_params_ret:
	/*
	 * disable interrupts (FIQ and IRQ), also set the cpu to SVC32 mode,
	 * except if in HYP mode already
	 */
	mrs	r0, cpsr
	and	r1, r0, #0x1f		@ mask mode bits
	teq	r1, #0x1a		@ test for HYP mode
	bicne	r0, r0, #0x1f		@ clear all mode bits
	orrne	r0, r0, #0x13		@ set SVC mode
	orr	r0, r0, #0xc0		@ disable FIQ and IRQ
	msr	cpsr,r0

/*
 * Setup vector:
 * (OMAP4 spl TEXT_BASE is not 32 byte aligned.
 * Continue to use ROM code vector only in OMAP4 spl)
 */
#if !(defined(CONFIG_OMAP44XX) && defined(CONFIG_SPL_BUILD))
	/* Set V=0 in CP15 SCTLR register - for VBAR to point to vector */
	mrc	p15, 0, r0, c1, c0, 0	@ Read CP15 SCTLR Register
	bic	r0, #CR_V		@ V = 0
	mcr	p15, 0, r0, c1, c0, 0	@ Write CP15 SCTLR Register

	/* Set vector address in CP15 VBAR register */
	ldr	r0, =_start
	mcr	p15, 0, r0, c12, c0, 0	@Set VBAR
#endif

	/* the mask ROM code should have PLL and others stable */
#ifndef CONFIG_SKIP_LOWLEVEL_INIT
	bl	cpu_init_cp15
	bl	cpu_init_crit
#endif

(1) 失能中断(FIQ和IRQ)并且设置CPU为SVC32模式,除非已经处于HYP模式。

(2)设置中断向量表地址为_start函数的地址,在map文件中可以看到,为0x87800000。

(3)进行CPU初始化,调用函数初始化CP15和CRIT。

cpu_init_cp15函数的定义很长,截取片段如下:

/*************************************************************************
 *
 * cpu_init_cp15
 *
 * Setup CP15 registers (cache, MMU, TLBs). The I-cache is turned on unless
 * CONFIG_SYS_ICACHE_OFF is defined.
 *
 *************************************************************************/
ENTRY(cpu_init_cp15)
	/*
	 * 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
	mcr	p15, 0, r0, c7, c5, 6	@ invalidate BP array
	mcr     p15, 0, r0, c7, c10, 4	@ DSB
	mcr     p15, 0, r0, c7, c5, 4	@ ISB

	/*
	 * disable MMU stuff and caches
	 */
	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 11 (Z---) BTB
#ifdef CONFIG_SYS_ICACHE_OFF
	bic	r0, r0, #0x00001000	@ clear bit 12 (I) I-cache
#else
	orr	r0, r0, #0x00001000	@ set bit 12 (I) I-cache
#endif
	mcr	p15, 0, r0, c1, c0, 0

	mov	r5, lr			@ Store my Caller
	mrc	p15, 0, r1, c0, c0, 0	@ r1 has Read Main ID Register (MIDR)
	mov	r3, r1, lsr #20		@ get variant field
	and	r3, r3, #0xf		@ r3 has CPU variant
	and	r4, r1, #0xf		@ r4 has CPU revision
	mov	r2, r3, lsl #4		@ shift variant field for combined value
	orr	r2, r4, r2		@ r2 has combined CPU variant + revision

	mov	pc, r5			@ back to my caller
ENDPROC(cpu_init_cp15)

① 关闭 L1 I/D Cache。

② 禁用MMU和缓存。

③ 返回。

cpu_init_crit 函数的定义如下:

#ifndef CONFIG_SKIP_LOWLEVEL_INIT
/*************************************************************************
 *
 * CPU_init_critical registers
 *
 * setup important registers
 * setup memory timing
 *
 *************************************************************************/
ENTRY(cpu_init_crit)
	/*
	 * Jump to board specific initialization...
	 * The Mask ROM will have already initialized
	 * basic memory. Go here to bump up clock rate and handle
	 * wake up conditions.
	 */
	b	lowlevel_init		@ go setup pll,mux,memory
ENDPROC(cpu_init_crit)
#endif

可见,uboot完成CPU初始化后,调用cpu_init_crit函数,跳转到板级初始化函数 lowlevel_init。至此,Mask ROM已经完成了基础内存的初始化,到这里由 lowlevel_init 来设置pll、mux、memory,来提高时钟速度和处理唤醒条件

三、lowlevel_init函数

1. lowlevel_init说明

  • 目的:完成执行board_init_f() 函数之前必不可少的初始化。
  • 不要使用全局变量或者BSS段
  • 没有栈
  • 不要设置SDRAM或者使用控制台
  • 仅仅完成最低限度的工作(设置栈即可)使 board_init_f 可以执行就好

2. lowlevel_init函数ARM v7实现

搜索一下,lowlevel_init函数的定义在 arch/arm/cpu/armv7/lowlevel_init.S中。

ENTRY(lowlevel_init)
	/*
	 * Setup a temporary stack. Global data is not available yet.
	 */
	ldr	sp, =CONFIG_SYS_INIT_SP_ADDR
	bic	sp, sp, #7 /* 8-byte alignment for ABI compliance */
#ifdef CONFIG_SPL_DM
	mov	r9, #0
#else
	/*
	 * Set up global data for boards that still need it. This will be
	 * removed soon.
	 */
#ifdef CONFIG_SPL_BUILD
	ldr	r9, =gdata
#else
	sub	sp, sp, #GD_SIZE
	bic	sp, sp, #7
	mov	r9, sp
#endif
#endif
	/*
	 * Save the old lr(passed in ip) and the current lr to stack
	 */
	push	ip, lr

	/*
	 * Call the very early init function. This should do only the
	 * absolute bare minimum to get started. It should not:
	 *
	 * - set up DRAM
	 * - use global_data
	 * - clear BSS
	 * - try to start a console
	 *
	 * For boards with SPL this should be empty since SPL can do all of
	 * this init in the SPL board_init_f() function which is called
	 * immediately after this.
	 */
	bl	s_init
	pop	ip, pc
ENDPROC(lowlevel_init)

(1)设置临时堆栈,全局变量目前不可用;

(2)为仍然需要的板子设置全局变量,这段代码不久将会被移除;

(3)保存旧的lr寄存器和当前lr寄存器到栈;

(4)调用非常早期的初始化函数,它应该只做最基本的事情,不应该做:

  • 设置DRAM
  • 使用全局数据
  • 清除BSS
  • 尝试启动控制台

对于使用SPL启动的开发板,它应该为空,因为SPI可以完成所有的这些初始化在 SPL board_init_f() 函数中,该函数将在之后被立即调用。

3. 栈地址CONFIG_SYS_INIT_SP_ADDR

该宏定义表示要初始化的栈顶指针地址,在开发板BSP中的头文件include/configs/mx6ullatk.h定义:

#define CONFIG_SYS_INIT_SP_OFFSET \\
	(CONFIG_SYS_INIT_RAM_SIZE - GENERATED_GBL_DATA_SIZE)
#define CONFIG_SYS_INIT_SP_ADDR \\
	(CONFIG_SYS_INIT_RAM_ADDR + CONFIG_SYS_INIT_SP_OFFSET)

继续找定义,可以看到,栈目前是定义在内存RAM的:

#define CONFIG_SYS_INIT_RAM_ADDR	IRAM_BASE_ADDR
#define CONFIG_SYS_INIT_RAM_SIZE	IRAM_SIZE

找到RAM大小的定义,在arch/arm/include/asm/arch-mx6/imx-regs.h文件中:

#if !(defined(CONFIG_MX6SX) || defined(CONFIG_MX6UL) || \\
	defined(CONFIG_MX6SLL) || defined(CONFIG_MX6SL))
#define IRAM_SIZE                    0x00040000
#else
#define IRAM_SIZE                    0x00020000
#endif

我们使用的是imx6ull,内部RAM大小为0x00020000(128KB)。

接着找IRAM_BASE_ADDR的定义,同样在该文件中,为0x00900000。

#define IRAM_BASE_ADDR			0x00900000


查阅参考手册的memory map,可以看到0x00900000这个地址是内部OCRAM的起始地址。

最后还有一个GENERATED_GBL_DATA_SIZE,搜索一下,在include/generated/generic-asm-offsets.h文件中,<font=“red”>该文件是uboot编译时动态生成的,表示

#define GENERATED_GBL_DATA_SIZE 256 /* (sizeof(struct global_data) + 15) & ~15	@ */

这样,我们就可以计算出栈顶地址了:

CONFIG_SYS_INIT_SP_OFFSET = 0x00020000 - 0x100 = 0x1FF00
CONFIG_SYS_INIT_SP_ADDR = 0x00900000 + 0x1FF00 = 0x0091FF00

再回到lowlevel_init函数,对栈顶指针做了8字节对齐处理:

bic	sp, sp, #7 /* 8-byte alignment for ABI compliance */

对0x0091FF00做8字节对齐,变为0x0091FE08,这就是最终设置的栈顶指针SP,栈空间大小为CONFIG_SYS_INIT_SP_OFFSET。

4. s_init函数imx6实现

经过搜索,s_init函数也是由不同的芯片厂商实现,imx6系列处理器的在arch/arm/cpu/armv7/mx6/soc.c文件中,s_init函数部分实现如下,看上去像是要设置时钟

void s_init(void)

	struct anatop_regs *anatop = (struct anatop_regs *)ANATOP_BASE_ADDR;
	struct mxc_ccm_reg *ccm = (struct mxc_ccm_reg *)CCM_BASE_ADDR;
	u32 mask480;
	u32 mask528;
	u32 reg, periph1, periph2;

	if (is_cpu_type(MXC_CPU_MX6SX) || is_cpu_type(MXC_CPU_MX6UL) ||
	    is_cpu_type(MXC_CPU_MX6ULL) || is_cpu_type(MXC_CPU_MX6SLL))
		return;
	...

5. 一路返回

s_iinit函数执行完成后返回 lowlevel_init 函数,lowlevel_init 函数将之前存储的lr寄存器值恢复到pc,程序返回。

接着一路返回到 cpu_init_crit 函数被调用时候的返回地址(因为该函数调用时使用了bl指令),也就是start.S汇编文件中:

	/* the mask ROM code should have PLL and others stable */
#ifndef CONFIG_SKIP_LOWLEVEL_INIT
	bl	cpu_init_cp15
	bl	cpu_init_crit
#endif

	bl	_main

可以看到,cpu_init_crit执行完毕后,接下来就跳转到_main函数执行。

四、__main函数

__main函数定义在arch/arm/lib/crt0.S文件中,这个文件处理U-Boot 启动过程中与目标无关的阶段,其中需要C运行时环境(设置好栈顶指针)。

__main函数的执行流程如下。

1.设置C语言运行环境并调用board_init_f函数

/*
 * 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)
#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	/* 8-byte alignment for ABI compliance */
#endif
	mov	r0, sp
	bl	board_init_f_alloc_reserve
	mov	sp, r0
	/* set up gd here, outside any C code */
	mov	r9, r0
	bl	board_init_f_init_reserve

	mov	r0, #0
	bl	board_init_f

(1)设置栈顶指针

C语言运行环境其实就是堆栈,栈用于函数调用和局部变量,设置栈的过程可以分为三步。

① 设置栈顶指针SP为CONFIG_SYS_INIT_SP_ADDR,并进行8字节对齐,值之前计算过。

② 将SP的值作为函数参数,调用board_init_f_alloc_reserve函数,该函数是一个通用函数,可以被各个架构调用,用于分配预留空间,定义在common/init/board_init.c文件中:

/**
 * ulong board_init_f_alloc_reserve - allocate reserved area
 *
 * This function is called by each architecture very early in the start-up
 * code to allow the C runtime to reserve space on the stack for writable
 * 'globals' such as GD and the malloc arena.
 *
 * @top:	top of the reserve area, growing down.
 * @return:	bottom of reserved area
 */
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;

board_init_f_alloc_reserve函数的传入参数是栈顶地址,返回参数是预留空间的底部。

可以看到,预留空间分为两部分,如果开启了malloc,则为malloc预留一部分空间,大小为CONFIG_SYS_MALLOC_F_LEN;其次为GD变量(global_data结构体类型)预留空间,并且对齐到16个字节的倍数。

③ 将新的栈顶地址写入到SP中,设置完成。

(2)设置全局变量gd的地址。

在文件arch/arm/include/asm/global_data.h中,定义了使用r9寄存器作为指向全局变量gd的指针:

#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

所以此处将r0寄存器的值写入r9寄存器,因为栈是向下增长的,所以此时的栈顶指针就是gd存储空间的基地址,也就是0x0091FA00。

gd_t类型在include/asm-generic/global_data.h中定义,用于管理全局变量,部分代码如下:

typedef struct global_data 
	bd_t *bd;
	unsigned long flags;
	unsigned int baudrate;
	unsigned long cpu_clk;	/* CPU clock in Hz!		*/
	unsigned long bus_clk;
	//代码省略
	struct udevice *cur_serial_dev;	/* current serial device */
	struct arch_global_data arch;	/* architecture-specific data */
 gd_t;

设置完gd变量的地址后,调用函数board_init_f_init_reserve,初始化第(1)步中预留的空间,在common/init/board_init.c文件中定义:

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);

	/*
	 * record early malloc arena start.
	 * Use gd as it is now properly set for all architectures.
	 */

#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

(3)清空r0寄存器,调用board_init_f函数,board_init_f 函数非常重要,完了下一节详细讲述。

2. 设置新的sp指针和gd指针,设置中间环境,调用重定位代码

#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 */
#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	/* 8-byte alignment for ABI compliance */
#endif
	ldr	r9, [r9, #GD_BD]		/* r9 = gd->bd */
	sub	r9, r9, #GD_SIZE		/* new GD is below bd */

	adr	lr, here
	ldr	r0, [r9, #GD_RELOC_OFF]		/* r0 = gd->reloc_off */
	add	lr, lr, r0
#if defined(CONFIG_CPU_V7M)
	orr	lr, #1				/* As required by Thumb-only */
#endif
	ldr	r0, [r9, #GD_RELOCADDR]		/* r0 = gd->relocaddr */
	b	relocate_code

board_init_f函数中会初始化开发板的许多外设,包括DRAM,并将栈顶指针和sp放置到DRAM中,所以这里主要做了三件事情:

(1)设置新的栈顶指针为sp = gd->start_addr_sp
(2)设置新的gd指针为r9 = gd->bd
(3)设置r0寄存器的值为 gd->relocaddr跳转到重定位代码relocate_code,并且返回到here标号处

3. 重定位向量表

重定位代码完成后,返回到here标号处,调用relocate_vectors函数,开始进行向量表重定位

here:
/*
 * now relocate vectors
 */

	bl	relocate_vectors

4. 设置最终的环境

/* Set up final (full) environment */
	bl	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
	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) */
	mov     r0, r9                  /* gd_t */
	ldr	r1, [r9, #GD_RELOCADDR]	/* dest_addr */
	/* call 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

这里面主要调用函数 c_runtime_cpu_setup,然后清除BSS段,设置board_init_r函数的两个参数,最终调用函数board_init_r

五、board_init_f 函数

common/board_f.c文件中,找到 board_init_f 函数定义:

void board_init_f(ulong boot_flags)

#ifdef 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->have_console = 0;

	if (initcall_run_list(init_sequence_f))
		hang();

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

这里面的核心代码只有两行:

	if (initcall_run_list(init_sequence_f))
		hang();

这两行代码完成了一系列板级外设的初始化

init_sequence_f 如下,初始化函数表省略其中部分代码。

static init_fnc_t init_sequence_f[] = 
	setup_mon_len,
	initf_malloc,
	initf_console_record,
	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,
#endif
#if defined(CONFIG_ARM) || defined(CONFIG_MIPS) || \\
		defined(CONFIG_BLACKFIN) || defined(CONFIG_NDS32) || \\
		defined(CONFIG_SPARC)
	timer_init,		/* initialize timer */
#endif
#if defined(CONFIG_BOARD_POSTCLK_INIT)
	board_postclk_init,
#endif
#if defined(CONFIG_SYS_FSL_CLK) || defined(CONFIG_M68K)
	get_clocks,
#endif
	env_init,		/* initialize environment */
	init_baud_rate,		/* initialze baudrate settings */
	serial_init,		/* serial communications setup */
	console_init_f,		/* stage 1 init of console */
	display_options,	/* say that we are here */
	display_text_info,	/* show debugging info if required */
	print_cpuinfo,		/* display cpu info (and speed) */
#if defined(CONFIG_DISPLAY_BOARDINFO)
	show_board_info,
#endif
	INIT_FUNC_WATCHDOG_INIT
	INIT_FUNC_WATCHDOG_RESET
#if defined(CONFIG_HARD_I2C) || defined(CONFIG_SYS_I2C)
	init_func_i2c,
#endif
	announce_dram_init,
	/* TODO: unify all these dram functions? */
#if defined(CONFIG_ARM) || defined(CONFIG_X86) || defined(CONFIG_NDS32) || \\
		defined(CONFIG_MICROBLAZE) || defined(CONFIG_AVR32)
	dram_init,		/* configure available RAM banks */
#endif
	INIT_FUNC_WATCHDOG_RESET
	INIT_FUNC_WATCHDOG_RESET
	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,
	reserve_round_4k,
#if !(defined(CONFIG_SYS_ICACHE_OFF) && defined(CONFIG_SYS_DCACHE_OFF)) && \\
		defined(CONFIG_ARM)
	reserve_mmu,
#endif
	reserve_trace,
#if !defined(CONFIG_BLACKFIN)
	reserve_uboot,
#endif
#ifndef CONFIG_SPL_BUILD
	reserve_malloc,
	reserve_board,
#endif
	setup_machine,
	reserve_global_data,
	reserve_fdt,
	reserve_arch,
	reserve_stacks,
	setup_dram_config,
	show_dram_config,
	display_new_sp,
	INIT_FUNC_WATCHDOG_RESET
	reloc_fdt,
	setup_reloc,
	NULL,
;

这其中比较重要的一些初始化函数如下。

(1)setup_mon_len:设置gd的mon_len成员变量,也就是整个代码的长度;
(2)initf_malloc:设置gd中和malloc有关的成员变量;
(3) board_early_init_f:板子早期的一些初始化设置,imx6ull中用来初始化串口的IO配置(在board/freescale/mx6ullatk/mx6ullatk.c中实现);

int board_early_init_f(void)

	setup_iomux_uart();

	return 0;

(4)timer_init:初始化内核定时器,为uboot提供时钟节拍,在arch/arm/imx-common/timer.c中实现;
(5)board_postclk_init:imx6ull中用来设置VDDSOC电压,在arch/arm/cpu/armv7/mx6/soc.c中实现;

int board_postclk_init(void)

	/* NO LDO SOC on i.MX6SLL */
	if (is_cpu_type(MXC_CPU_MX6SLL)uboot研读笔记 | 01 - 下载uboot源码并使用VSCode远程查看源码编译uboot(2012.04.01版本)

uboot研读笔记 | 03 - 初步移植uboot 2012.04到JZ2440(修改时钟,配置串口)

uboot研读笔记 | 04 - 移植uboot 2012.04到JZ2440(支持Nor Flash读写)

uboot研读笔记 | 00 - 嵌入式Linux系统中Bootloader的作用和基本运行原理

uboot研读笔记 | 13 - uboot编译构建Makefile分析研读(2016.03版本)

uboot研读笔记 | 12 - uboot目录结构分析(2016.03版本)