版本

uboot-2018.07

u-boot.lds

OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm")	/* 输出文件格式为 elf32-littlearm */
OUTPUT_ARCH(arm)  /* 输出架构为 ARM */
ENTRY(_start)     /* 指定整个程序的入口地址为 _start,这里跳转到 vector.S 执行 */
SECTIONS
{
 /DISCARD/ : { *(.rel._secure*) }
 . = 0x00000000;  /* 指定可执行文件的全局入口点,通常这个地址都放在 ROM(flash) 0x00000000 位置。必须使编译器知道这个地址,通常都是修改此处来完成的 */
 . = ALIGN(4);    /* 代码以 4 字节对齐 */
 .text :          /* 代码段 */
 {
  *(.__image_copy_start)  /* u-boot 将自己 copy 到 RAM,此为需要 copy 的程序的 start */
  *(.vectors)             /* 这里的 vectors 是 vector.S 链接的二进制文件的开头部分 */
  arch/arm/cpu/armv7/start.o (.text*)	/* 执行 start.S */
  *(.text*)               /* 剩下的所有未指出名字的 .o */
 }
...

代码流程

由链接脚本可知,程序首先执行 arch/arm/lib/vectors.S 中的 _start 函数,看下其内容

.globl _start

_start:
#ifdef CONFIG_SYS_DV_NOR_BOOT_CFG
	.word	CONFIG_SYS_DV_NOR_BOOT_CFG
#endif
	b	reset						/* 0x00000000 复位异常, 跳转到 reset 执行 */
	ldr	pc, _undefined_instruction	/* 0x00000004 为定义指令异常 */
	ldr	pc, _software_interrupt		/* 0x00000008 软中断异常 */
	ldr	pc, _prefetch_abort			/* 0x0000000C 预取异常 */
	ldr	pc, _data_abort				/* 0x00000010 数据异常 */
	ldr	pc, _not_used				/* 0x00000014 未使用异常,多余的指令 */
	ldr	pc, _irq					/* 0x00000018 外部中断异常 */
	ldr	pc, _fiq					/* 0x0000001C 快速中断异常 */

这里的代码全部都是异常向量表的定义,第一段代码中,执行 b reset,跳转到 reset 执行,该函数在 arch/arm/cpu/armv7/start.S 文件中,内容如下

	.globl	reset /* 这里便是 reset 函数的入口,CPU 一上电就是跳到这里执行的 */

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

	/*
	 * disable interrupts (FIQ and IRQ), also set the cpu to SVC32 mode,
	 * 设置 CPU 为管理员模式
	 * except if in HYP mode already
	 */
	/* 对状态寄存器 cpsr 的修改要按照: 读出-修改-写回 的顺序来执行 */
	mrs	r0, cpsr								/* 将 CPSR 状态寄存器读取保存到 r0 中 */
	and	r1, r0, #0x1f		@ mask mode bits	/* 清除低 5 位 */
	teq	r1, #0x1a		@ test for HYP mode
	bicne	r0, r0, #0x1f		@ clear all mode bits
	orrne	r0, r0, #0x13		@ set SVC mode		/* 设置为 SVC 模式 */
	orr	r0, r0, #0xc0		@ disable FIQ and IRQ	/* 禁止 FIQ、IRQ */
	msr	cpsr,r0		/* 写回 cpsr */

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

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

	/* the mask ROM code should have PLL and others stable */
#ifndef CONFIG_SKIP_LOWLEVEL_INIT
#ifdef CONFIG_CPU_V7A
	bl	cpu_init_cp15
#endif
#ifndef CONFIG_SKIP_LOWLEVEL_INIT_ONLY
	bl	cpu_init_crit		/* 执行 CPU 初始化,完成 DRAM 初始化操作 */
#endif
#endif

	bl	_main	/* 跳转到 _main */


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	/* 调用 lowlevel_init() */
ENDPROC(cpu_init_crit)

arch/arm/cpu/armv7/lowlevel_init.S

WEAK(lowlevel_init)
...
ENDPROC(lowlevel_init)

arch/arm/lib/crt0.S

ENTRY(_main)

/*
 * Set up initial C runtime environment and call board_init_f(0).
 * 初始化 C 运行环境并且调用 board_init_f(0) 函数
 */

#if defined(CONFIG_SPL_BUILD) && defined(CONFIG_SPL_STACK)
	ldr	r0, =(CONFIG_SPL_STACK)
#else
	ldr	r0, =(CONFIG_SYS_INIT_SP_ADDR)
#endif
	bic	r0, r0, #7	/* 8-byte alignment for ABI compliance */
	mov	sp, r0
	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	/* 跳转到 board_init_f,单板的初始化函数 */

...

    /* call board_init_r(gd_t *id, ulong dest_addr) */
	mov     r0, r9                  /* gd_t */
	ldr	r1, [r9, #GD_RELOCADDR]	/* dest_addr */			/* yes board_init_r */
	/* call board_init_r */
#if CONFIG_IS_ENABLED(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)

arch/arm/mach-sunxi/board.c

void board_init_f(ulong dummy)
{
	spl_init();
	preloader_console_init();

#ifdef CONFIG_SPL_I2C_SUPPORT
	/* Needed early by sunxi_board_init if PMU is enabled */
	i2c_init(CONFIG_SYS_I2C_SPEED, CONFIG_SYS_I2C_SLAVE);
#endif
	sunxi_board_init();
}

common/spl/spl.c

void preloader_console_init(void)
{
	gd->baudrate = CONFIG_BAUDRATE;

	serial_init();		/* serial communications setup */

	gd->have_console = 1;

#ifndef CONFIG_SPL_DISABLE_BANNER_PRINT
	puts("\nU-Boot SPL " PLAIN_VERSION " (" U_BOOT_DATE " - " \
			U_BOOT_TIME " " U_BOOT_TZ ")\n");
	puts("## aa\n");
#endif
#ifdef CONFIG_SPL_DISPLAY_PRINT
	spl_display_print();
#endif
}

arch/arm/mach-sunxi/board.c

#ifdef CONFIG_SPL_BUILD
void sunxi_board_init(void)
{
	int power_failed = 0;

#ifdef CONFIG_SY8106A_POWER
	power_failed = sy8106a_set_vout1(CONFIG_SY8106A_VOUT1_VOLT);
#endif

#if defined CONFIG_AXP152_POWER || defined CONFIG_AXP209_POWER || \
	defined CONFIG_AXP221_POWER || defined CONFIG_AXP809_POWER || \
	defined CONFIG_AXP818_POWER
	power_failed = axp_init();

#if defined CONFIG_AXP221_POWER || defined CONFIG_AXP809_POWER || \
	defined CONFIG_AXP818_POWER
	power_failed |= axp_set_dcdc1(CONFIG_AXP_DCDC1_VOLT);
#endif
	power_failed |= axp_set_dcdc2(CONFIG_AXP_DCDC2_VOLT);
	power_failed |= axp_set_dcdc3(CONFIG_AXP_DCDC3_VOLT);
#if !defined(CONFIG_AXP209_POWER) && !defined(CONFIG_AXP818_POWER)
	power_failed |= axp_set_dcdc4(CONFIG_AXP_DCDC4_VOLT);
#endif
#if defined CONFIG_AXP221_POWER || defined CONFIG_AXP809_POWER || \
	defined CONFIG_AXP818_POWER
	power_failed |= axp_set_dcdc5(CONFIG_AXP_DCDC5_VOLT);
#endif

#if defined CONFIG_AXP221_POWER || defined CONFIG_AXP809_POWER || \
	defined CONFIG_AXP818_POWER
	power_failed |= axp_set_aldo1(CONFIG_AXP_ALDO1_VOLT);
#endif
	power_failed |= axp_set_aldo2(CONFIG_AXP_ALDO2_VOLT);
#if !defined(CONFIG_AXP152_POWER)
	power_failed |= axp_set_aldo3(CONFIG_AXP_ALDO3_VOLT);
#endif
#ifdef CONFIG_AXP209_POWER
	power_failed |= axp_set_aldo4(CONFIG_AXP_ALDO4_VOLT);
#endif

#if defined(CONFIG_AXP221_POWER) || defined(CONFIG_AXP809_POWER) || \
	defined(CONFIG_AXP818_POWER)
	power_failed |= axp_set_dldo(1, CONFIG_AXP_DLDO1_VOLT);
	power_failed |= axp_set_dldo(2, CONFIG_AXP_DLDO2_VOLT);
#if !defined CONFIG_AXP809_POWER
	power_failed |= axp_set_dldo(3, CONFIG_AXP_DLDO3_VOLT);
	power_failed |= axp_set_dldo(4, CONFIG_AXP_DLDO4_VOLT);
#endif
	power_failed |= axp_set_eldo(1, CONFIG_AXP_ELDO1_VOLT);
	power_failed |= axp_set_eldo(2, CONFIG_AXP_ELDO2_VOLT);
	power_failed |= axp_set_eldo(3, CONFIG_AXP_ELDO3_VOLT);
#endif

#ifdef CONFIG_AXP818_POWER
	power_failed |= axp_set_fldo(1, CONFIG_AXP_FLDO1_VOLT);
	power_failed |= axp_set_fldo(2, CONFIG_AXP_FLDO2_VOLT);
	power_failed |= axp_set_fldo(3, CONFIG_AXP_FLDO3_VOLT);
#endif

#if defined CONFIG_AXP809_POWER || defined CONFIG_AXP818_POWER
	power_failed |= axp_set_sw(IS_ENABLED(CONFIG_AXP_SW_ON));
#endif
#endif
/* 111 */
	printf("DRAM:");	// yes
	gd->ram_size = sunxi_dram_init();
	printf(" %d MiB\n", (int)(gd->ram_size >> 20));
	if (!gd->ram_size)
		hang();

	/*
	 * Only clock up the CPU to full speed if we are reasonably
	 * assured it's being powered with suitable core voltage
	 */
	if (!power_failed)
		clock_set_pll1(CONFIG_SYS_CLK_FREQ);	/* yes */
	else
		printf("Failed to set core voltage! Can't set CPU frequency\n");
}
#endif

arch/arm/mach-sunxi/clock_sun4i.c

void clock_set_pll1(unsigned int hz)
{
	int i = 0;
	int axi, ahb, apb0;
	struct sunxi_ccm_reg * const ccm =
		(struct sunxi_ccm_reg *)SUNXI_CCM_BASE;

	/* Find target frequency */
	while (pll1_para[i].freq > hz)
		i++;

	hz = pll1_para[i].freq;
	if (! hz)
		hz = 384000000;

	/* Calculate system clock divisors */
	axi = DIV_ROUND_UP(hz, 432000000);	/* Max 450MHz */
	ahb = DIV_ROUND_UP(hz/axi, 204000000);	/* Max 250MHz */
	apb0 = 2;				/* Max 150MHz */
/* 222 */
	printf("CPU: %uHz, AXI/AHB/APB: %d/%d/%d\n", hz, axi, ahb, apb0);

	/* Map divisors to register values */
	axi = axi - 1;
	if (ahb > 4)
		ahb = 3;
	else if (ahb > 2)
		ahb = 2;
	else if (ahb > 1)
		ahb = 1;
	else
		ahb = 0;

	apb0 = apb0 - 1;

	/* Switch to 24MHz clock while changing PLL1 */
	writel(AXI_DIV_1 << AXI_DIV_SHIFT |
	       AHB_DIV_2 << AHB_DIV_SHIFT |
	       APB0_DIV_1 << APB0_DIV_SHIFT |
	       CPU_CLK_SRC_OSC24M << CPU_CLK_SRC_SHIFT,
	       &ccm->cpu_ahb_apb0_cfg);
	sdelay(20);

	/* Configure sys clock divisors */
	writel(axi << AXI_DIV_SHIFT |
	       ahb << AHB_DIV_SHIFT |
	       apb0 << APB0_DIV_SHIFT |
	       CPU_CLK_SRC_OSC24M << CPU_CLK_SRC_SHIFT,
	       &ccm->cpu_ahb_apb0_cfg);

	/* Configure PLL1 at the desired frequency */
	writel(pll1_para[i].pll1_cfg, &ccm->pll1_cfg);
	sdelay(200);

	/* Switch CPU to PLL1 */
	writel(axi << AXI_DIV_SHIFT |
	       ahb << AHB_DIV_SHIFT |
	       apb0 << APB0_DIV_SHIFT |
	       CPU_CLK_SRC_PLL1 << CPU_CLK_SRC_SHIFT,
	       &ccm->cpu_ahb_apb0_cfg);
	sdelay(20);
}

common/spl/spl.c

static int boot_from_devices(struct spl_image_info *spl_image,
			     u32 spl_boot_list[], int count)
{
	int i;

	for (i = 0; i < count && spl_boot_list[i] != BOOT_DEVICE_NONE; i++) {
		struct spl_image_loader *loader;

		loader = spl_ll_find_loader(spl_boot_list[i]);
#if defined(CONFIG_SPL_SERIAL_SUPPORT) && defined(CONFIG_SPL_LIBCOMMON_SUPPORT)
		if (loader)
			printf("Trying to boot from %s\n", loader->name);
		else
			puts("SPL: Unsupported Boot Device!\n");
#endif
		if (loader && !spl_load_image(spl_image, loader))
			return 0;
	}

	return -ENODEV;
}


void board_init_r(gd_t *dummy1, ulong dummy2)
{

	if (boot_from_devices(&spl_image, spl_boot_list,
			      ARRAY_SIZE(spl_boot_list))) {
		puts("SPL: failed to boot from all boot devices\n");
		hang();
	}

}

参考

四、u-boot 链接脚本

u-boot移植(二)—修改前工作:代码流程分析1 二、链接文件

u-boot移植(三)—修改前工作:代码流程分析2 _start 代码流程

u-boot移植(四)—修改前工作:代码流程分析3—代码重定位

bootrom/spl/uboot/linux逐级加载是如何实现的?