SPL 启动流程
版本
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移植(二)—修改前工作:代码流程分析1 二、链接文件
u-boot移植(三)—修改前工作:代码流程分析2 _start 代码流程