uboot版本https://github.com/linux-sunxi/u-boot-sunxi.git v2014.04-rc2
由uboot顶层makefile中
spl/u-boot-spl: tools prepare
$(Q)$(MAKE) obj=spl -f $(srctree)/spl/Makefile all可知,进入spl子目录进行make。是怎么找到spl/u-boot-spl: tools prepare的呢?
由cubietruck的可启动SD卡格式可知要用到u-boot-spl.bin,从而得到spl/u-boot-spl.bin: spl/u-boot-spl,
而该目标的命令是@:(什么都不做),所以寻找其依赖spl/u-boot-spl,就得到了spl/u-boot-spl: tools prepare
#---------------------------------------------------------------------------------------------------------------------------------#
进入spl目录分析makefile
ALL-y += $(obj)/$(SPL_BIN).bin——>$(obj)/$(SPL_BIN).bin: $(obj)/$(SPL_BIN) FORCE——>
$(obj)/$(SPL_BIN): $(u-boot-spl-init) $(u-boot-spl-main) $(obj)/u-boot-spl.lds
$(call cmd,u-boot-spl)
——>
cmd_u-boot-spl = cd $(obj) && $(LD) $(LDFLAGS) $(LDFLAGS_$(@F)) \
$(patsubst $(obj)/%,%,$(u-boot-spl-init)) --start-group \
$(patsubst $(obj)/%,%,$(u-boot-spl-main)) --end-group \
$(PLATFORM_LIBS) -Map $(SPL_BIN).map -o $(SPL_BIN)
——>u-boot-spl-init := $(head-y)——>head-y := $(START_PATH)/start.o
——>
ifdef CONFIG_SPL_START_S_PATH
START_PATH := $(CONFIG_SPL_START_S_PATH:"%"=%)
else
START_PATH := $(CPUDIR)
endif
可知SPL启动代码在$(CPUDIR)也就是arch/arm/cpu/armv7下的start.S
#---------------------------------------------------------------------------------------------------------------------------------#
分析start.S
.globl _start
_start: 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
#ifdef CONFIG_SPL_BUILD
_undefined_instruction: .word _undefined_instruction
_software_interrupt: .word _software_interrupt
_prefetch_abort: .word _prefetch_abort
_data_abort: .word _data_abort
_not_used: .word _not_used
_irq: .word _irq
_fiq: .word _fiq
_pad: .word 0x12345678 /* now 16*4=64 */
#else
.globl _undefined_instruction
_undefined_instruction: .word undefined_instruction
.globl _software_interrupt
_software_interrupt: .word software_interrupt
.globl _prefetch_abort
_prefetch_abort: .word prefetch_abort
.globl _data_abort
_data_abort: .word data_abort
.globl _not_used
_not_used: .word not_used
.globl _irq
_irq: .word irq
.globl _fiq
_fiq: .word fiq
_pad: .word 0x12345678 /* now 16*4=64 */
#endif /* CONFIG_SPL_BUILD */
.global _end_vect
_end_vect:
.balignl 16,0xdeadbeef
/*************************************************************************
*
* 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
*
*************************************************************************/
#ifdef CONFIG_USE_IRQ
/* IRQ stack memory (calculated at run-time) */
.globl IRQ_STACK_START
IRQ_STACK_START:
.word 0x0badc0de
/* IRQ stack memory (calculated at run-time) */
.globl FIQ_STACK_START
FIQ_STACK_START:
.word 0x0badc0de
#endif
/* IRQ stack memory (calculated at run-time) + 8 bytes */
.globl IRQ_STACK_START_IN
IRQ_STACK_START_IN:
.word 0x0badc0de
/*
* the actual reset code
*/
reset:
bl save_boot_params
/*
* 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 SCTRL register - for VBAR to point to vector */
mrc p15, 0, r0, c1, c0, 0 @ Read CP15 SCTRL Register
bic r0, #CR_V @ V = 0
mcr p15, 0, r0, c1, c0, 0 @ Write CP15 SCTRL 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
bl _main
这里首先就是#ifdef CONFIG_SPL_BUILD
这在哪里寻找呢?明显的是在spl目录下的makefile中:CONFIG_SPL_BUILD := y,在这里先说一下CONFIG_SPL_BUILD := y
是很重要的,因为SPL、uboot共用了很多代码,这个CONFIG_SPL_BUILD 就是用于区别这个文件是被编译为SPL还是uboot,后面分析还会用到
直接看reset:
bl save_boot_params: 如果没有重新定义save_boot_params,则使用<arch/arm/cpu/armv7/start.S>中的
save_boot_params。其不做任何事情,直接返回
#ifndef CONFIG_SKIP_LOWLEVEL_INIT为真,所以调用cpu_init_cp15、cpu_init_crit。
这两个函数都在arch/arm/cpu/armv7/start.S中,cpu_init_cp15设置I/D-Cache, MMU, TLBs。
#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
#ifndef CONFIG_SPL_BUILD
b
lowlevel_init:跳转到
<arch/arm/cpu/armv7/lowlevel_init.S>中的lowlevel_init
ENTRY(lowlevel_init)
/*
* Setup a temporary stack
*/
ldr sp, =CONFIG_SYS_INIT_SP_ADDR
bic sp, sp, #7 /* 8-byte alignment for ABI compliance */
#ifdef CONFIG_SPL_BUILD
ldr r9, =gdata
#else
sub sp, sp, #GD_SIZE
bic sp, sp, #7
mov r9, sp
#endif
/*
* Save the old lr(passed in ip) and the current lr to stack
*/
push {ip, lr}
/*
* go setup pll, mux, memory
*/
bl s_init
pop {ip, pc}
ENDPROC(lowlevel_init)
bl
s_init:跳转到<arch/arm/cpu/armv7/sunxi/board.c>中的s_init
/* do some early init */
void s_init(void)
{
#if !defined CONFIG_SPL_BUILD && defined CONFIG_SUN7I
/* Enable SMP mode for CPU0, by setting bit 6 of Auxiliary Ctl reg */
asm volatile(
"mrc p15, 0, r0, c1, c0, 1\n"
"orr r0, r0, #1 << 6\n"
"mcr p15, 0, r0, c1, c0, 1\n");
#endif
watchdog_init();
clock_init();
timer_init();
gpio_init();
#ifdef CONFIG_SPL_BUILD
gd = &gdata;
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();
#endif
}
由该文件的目录可知,s_init函数应该是由具体的arm厂商完成的,来完成具体的lowlevel_init即cpu_init_crit、lowlevel_init这两个函数中提到的go setup pll,mux,memory
这里转载几句话:我个人感觉, 新版本的uboot在CPUDIR下实现了一个lowlevel_init.S文件, 主要目标是初始化sp, 这样s_init就可以用C语言实现了. 而以前的老版本里面, s_init里面要做的事情都是用汇编做的.
(地址:http://blog.csdn.net/liuxinjohn/article/details/18512901)
完成之后返回到start.S
bl _main:跳转到<arch/arm/lib/crt0.S>中的_main,在这里注意一下在_main中就不用考虑函数的返回了
/*
* 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)
#endif
bic sp, sp, #7 /* 8-byte alignment for ABI compliance */
sub sp, sp, #GD_SIZE /* allocate one GD above SP */
bic sp, sp, #7 /* 8-byte alignment for ABI compliance */
mov r9, sp /* GD is above SP */
mov r0, #0
bl board_init_f
#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 */
bic sp, sp, #7 /* 8-byte alignment for ABI compliance */
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
ldr r0, [r9, #GD_RELOCADDR] /* r0 = gd->relocaddr */
b relocate_code
here:
/* Set up final (full) environment */
bl c_runtime_cpu_setup /* we still call old routine here */
ldr r0, =__bss_start /* this is auto-relocated! */
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 */
strlo r2, [r0] /* clear 32-bit BSS word */
addlo r0, r0, #4 /* move to next */
blo clbss_l
bl coloured_LED_init
bl red_led_on
/* 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 */
ldr pc, =board_init_r /* this is auto-relocated! */
/* we should not return here. */
#endif
ENDPROC(_main)
先看到#if ! defined(CONFIG_SPL_BUILD)可知_main只会执行到bl
board_init_f
bl board_init_f:跳转到board_init_f函数,搜索该函数由三个目标分别是
<arch/arm/lib/board.c>、<arch/arm/lib/spl.c>和<common/board_f.c>
分析<arch/arm/lib>下的makefile
ifndef CONFIG_SPL_BUILD
ifdef CONFIG_ARM64
obj-y += relocate_64.o
else
obj-y += relocate.o
endif
ifndef CONFIG_SYS_GENERIC_BOARD
obj-y += board.o
endif
obj-$(CONFIG_OF_LIBFDT) += bootm-fdt.o
obj-$(CONFIG_CMD_BOOTM) += bootm.o
obj-$(CONFIG_SYS_L2_PL310) += cache-pl310.o
obj-$(CONFIG_USE_ARCH_MEMSET) += memset.o
obj-$(CONFIG_USE_ARCH_MEMCPY) += memcpy.o
else
obj-$(CONFIG_SPL_FRAMEWORK) += spl.o
endif
ifndef CONFIG_SPL_BUILD为假,所以不会编译
arch/arm/lib/board.c,同理不会编译<common/board_f.c>
这里可以看出spl/目录下makefile中定义CONFIG_SPL_BUILD的用意、及其在分析SPL还是uboot的重要性
所以
bl
board_init_f:跳转到
<arch/arm/lib/spl.c>中的
board_init_f函数,还要注意不用考虑该函数返回,
/*
* In the context of SPL, board_init_f must ensure that any clocks/etc for
* DDR are enabled, ensure that the stack pointer is valid, clear the BSS
* and call board_init_f. We provide this version by default but mark it
* as __weak to allow for platforms to do this in their own way if needed.
*/
void __weak board_init_f(ulong dummy)
{
/* Clear the BSS. */
memset(__bss_start, 0, __bss_end - __bss_start);
/* Set global data pointer. */
gd = &gdata;
board_init_r(NULL, 0);
}
调用board_init_r(NULL,0);
分析Makefile可以看出, 该函数的实现是在<common/spl/spl.c>
void board_init_r(gd_t *dummy1, ulong dummy2)
{
u32 boot_device;
debug(">>spl:board_init_r()\n");
#ifdef CONFIG_SYS_SPL_MALLOC_START
mem_malloc_init(CONFIG_SYS_SPL_MALLOC_START,
CONFIG_SYS_SPL_MALLOC_SIZE);
#endif
#ifndef CONFIG_PPC
/*
* timer_init() does not exist on PPC systems. The timer is initialized
* and enabled (decrementer) in interrupt_init() here.
*/
timer_init();
#endif
#ifdef CONFIG_SPL_BOARD_INIT
spl_board_init();
#endif
boot_device = spl_boot_device();
debug("boot device - %d\n", boot_device);
switch (boot_device) {
#ifdef CONFIG_SPL_RAM_DEVICE
case BOOT_DEVICE_RAM:
spl_ram_load_image();
break;
#endif
#ifdef CONFIG_SPL_MMC_SUPPORT
case BOOT_DEVICE_MMC1:
case BOOT_DEVICE_MMC2:
case BOOT_DEVICE_MMC2_2:
spl_mmc_load_image();
break;
#endif
#ifdef CONFIG_SPL_NAND_SUPPORT
case BOOT_DEVICE_NAND:
spl_nand_load_image();
break;
#endif
#ifdef CONFIG_SPL_ONENAND_SUPPORT
case BOOT_DEVICE_ONENAND:
spl_onenand_load_image();
break;
#endif
#ifdef CONFIG_SPL_NOR_SUPPORT
case BOOT_DEVICE_NOR:
spl_nor_load_image();
break;
#endif
#ifdef CONFIG_SPL_YMODEM_SUPPORT
case BOOT_DEVICE_UART:
spl_ymodem_load_image();
break;
#endif
#ifdef CONFIG_SPL_SPI_SUPPORT
case BOOT_DEVICE_SPI:
spl_spi_load_image();
break;
#endif
#ifdef CONFIG_SPL_ETH_SUPPORT
case BOOT_DEVICE_CPGMAC:
#ifdef CONFIG_SPL_ETH_DEVICE
spl_net_load_image(CONFIG_SPL_ETH_DEVICE);
#else
spl_net_load_image(NULL);
#endif
break;
#endif
#ifdef CONFIG_SPL_USBETH_SUPPORT
case BOOT_DEVICE_USBETH:
spl_net_load_image("usb_ether");
break;
#endif
#ifdef CONFIG_SPL_USB_SUPPORT
case BOOT_DEVICE_USB:
spl_usb_load_image();
break;
#endif
#ifdef CONFIG_SPL_SATA_SUPPORT
case BOOT_DEVICE_SATA:
spl_sata_load_image();
break;
#endif
default:
debug("SPL: Un-supported Boot Device\n");
hang();
}
switch (spl_image.os) {
case IH_OS_U_BOOT:
debug("Jumping to U-Boot\n");
break;
#ifdef CONFIG_SPL_OS_BOOT
case IH_OS_LINUX:
debug("Jumping to Linux\n");
spl_board_prepare_for_linux();
jump_to_image_linux((void *)CONFIG_SYS_SPL_ARGS_ADDR);
#endif
default:
debug("Unsupported OS image.. Jumping nevertheless..\n");
}
jump_to_image_no_args(&spl_image);
}
boot_device = spl_boot_device();<arch/arm/cpu/armv7/sunxi/board.c>中
/* The sunxi internal brom will try to loader external bootloader
* from mmc0, nand flash, mmc2.
* Unfortunately we can't check how SPL was loaded so assume
* it's always the first SD/MMC controller
*/
u32 spl_boot_device(void)
{
return BOOT_DEVICE_MMC1;
}
(general code is good code)
jump_to_image_no_args(&spl_image);跳转到uboot或者linux
注意:boot_device = spl_boot_device();是厂商必须要实现的。
参考博文:http://blog.csdn.net/liuxinjohn/article/details/18512901