Cubietruck开发板SPL启动分析

来源:互联网 发布:域名历史记录查询 编辑:程序博客网 时间:2024/05/01 12:49

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: bresetldrpc, _undefined_instructionldrpc, _software_interruptldrpc, _prefetch_abortldrpc, _data_abortldrpc, _not_usedldrpc, _irqldrpc, _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_STARTIRQ_STACK_START:.word0x0badc0de/* IRQ stack memory (calculated at run-time) */.globl FIQ_STACK_STARTFIQ_STACK_START:.word 0x0badc0de#endif/* IRQ stack memory (calculated at run-time) + 8 bytes */.globl IRQ_STACK_START_INIRQ_STACK_START_IN:.word0x0badc0de/* * the actual reset code */reset:blsave_boot_params/* * disable interrupts (FIQ and IRQ), also set the cpu to SVC32 mode, * except if in HYP mode already */mrsr0, cpsrandr1, r0, #0x1f@ mask mode bitsteqr1, #0x1a@ test for HYP modebicner0, r0, #0x1f@ clear all mode bitsorrner0, r0, #0x13@ set SVC modeorrr0, r0, #0xc0@ disable FIQ and IRQmsrcpsr,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 */mrcp15, 0, r0, c1, c0, 0@ Read CP15 SCTRL Registerbicr0, #CR_V@ V = 0mcrp15, 0, r0, c1, c0, 0@ Write CP15 SCTRL Register/* Set vector address in CP15 VBAR register */ldrr0, =_startmcrp15, 0, r0, c12, c0, 0@Set VBAR#endif/* the mask ROM code should have PLL and others stable */#ifndef CONFIG_SKIP_LOWLEVEL_INITblcpu_init_cp15blcpu_init_crit#endifbl_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. */blowlevel_init@ go setup pll,mux,memoryENDPROC(cpu_init_crit)#endif#ifndef CONFIG_SPL_BUILD
lowlevel_init:跳转到<arch/arm/cpu/armv7/lowlevel_init.S>中的lowlevel_init
ENTRY(lowlevel_init)/* * Setup a temporary stack */ldrsp, =CONFIG_SYS_INIT_SP_ADDRbicsp, sp, #7 /* 8-byte alignment for ABI compliance */#ifdef CONFIG_SPL_BUILDldrr9, =gdata#elsesubsp, sp, #GD_SIZEbicsp, sp, #7movr9, sp#endif/* * Save the old lr(passed in ip) and the current lr to stack */push{ip, lr}/* * go setup pll, mux, memory */bls_initpop{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");#endifwatchdog_init();clock_init();timer_init();gpio_init();#ifdef CONFIG_SPL_BUILDgd = &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);#endifsunxi_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)ldrsp, =(CONFIG_SPL_STACK)#elseldrsp, =(CONFIG_SYS_INIT_SP_ADDR)#endifbicsp, sp, #7/* 8-byte alignment for ABI compliance */subsp, sp, #GD_SIZE/* allocate one GD above SP */bicsp, sp, #7/* 8-byte alignment for ABI compliance */movr9, sp/* GD is above SP */movr0, #0blboard_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. */ldrsp, [r9, #GD_START_ADDR_SP]/* sp = gd->start_addr_sp */bicsp, sp, #7/* 8-byte alignment for ABI compliance */ldrr9, [r9, #GD_BD]/* r9 = gd->bd */subr9, r9, #GD_SIZE/* new GD is below bd */adrlr, hereldrr0, [r9, #GD_RELOC_OFF]/* r0 = gd->reloc_off */addlr, lr, r0ldrr0, [r9, #GD_RELOCADDR]/* r0 = gd->relocaddr */brelocate_codehere:/* Set up final (full) environment */blc_runtime_cpu_setup/* we still call old routine here */ldrr0, =__bss_start/* this is auto-relocated! */ldrr1, =__bss_end/* this is auto-relocated! */movr2, #0x00000000/* prepare zero to clear BSS */clbss_l:cmpr0, r1/* while not at end of BSS */strlor2, [r0]/* clear 32-bit BSS word */addlor0, r0, #4/* move to next */bloclbss_lbl coloured_LED_initbl red_led_on/* call board_init_r(gd_t *id, ulong dest_addr) */mov     r0, r9                  /* gd_t */ldrr1, [r9, #GD_RELOCADDR]/* dest_addr *//* call board_init_r */ldrpc, =board_init_r/* this is auto-relocated! *//* we should not return here. */#endifENDPROC(_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_BUILDifdef CONFIG_ARM64obj-y+= relocate_64.oelseobj-y+= relocate.oendififndef CONFIG_SYS_GENERIC_BOARDobj-y+= board.oendifobj-$(CONFIG_OF_LIBFDT) += bootm-fdt.oobj-$(CONFIG_CMD_BOOTM) += bootm.oobj-$(CONFIG_SYS_L2_PL310) += cache-pl310.oobj-$(CONFIG_USE_ARCH_MEMSET) += memset.oobj-$(CONFIG_USE_ARCH_MEMCPY) += memcpy.oelseobj-$(CONFIG_SPL_FRAMEWORK) += spl.oendif
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_STARTmem_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_INITspl_board_init();#endifboot_device = spl_boot_device();debug("boot device - %d\n", boot_device);switch (boot_device) {#ifdef CONFIG_SPL_RAM_DEVICEcase BOOT_DEVICE_RAM:spl_ram_load_image();break;#endif#ifdef CONFIG_SPL_MMC_SUPPORTcase BOOT_DEVICE_MMC1:case BOOT_DEVICE_MMC2:case BOOT_DEVICE_MMC2_2:spl_mmc_load_image();break;#endif#ifdef CONFIG_SPL_NAND_SUPPORTcase BOOT_DEVICE_NAND:spl_nand_load_image();break;#endif#ifdef CONFIG_SPL_ONENAND_SUPPORTcase BOOT_DEVICE_ONENAND:spl_onenand_load_image();break;#endif#ifdef CONFIG_SPL_NOR_SUPPORTcase BOOT_DEVICE_NOR:spl_nor_load_image();break;#endif#ifdef CONFIG_SPL_YMODEM_SUPPORTcase BOOT_DEVICE_UART:spl_ymodem_load_image();break;#endif#ifdef CONFIG_SPL_SPI_SUPPORTcase BOOT_DEVICE_SPI:spl_spi_load_image();break;#endif#ifdef CONFIG_SPL_ETH_SUPPORTcase BOOT_DEVICE_CPGMAC:#ifdef CONFIG_SPL_ETH_DEVICEspl_net_load_image(CONFIG_SPL_ETH_DEVICE);#elsespl_net_load_image(NULL);#endifbreak;#endif#ifdef CONFIG_SPL_USBETH_SUPPORTcase BOOT_DEVICE_USBETH:spl_net_load_image("usb_ether");break;#endif#ifdef CONFIG_SPL_USB_SUPPORTcase BOOT_DEVICE_USB:spl_usb_load_image();break;#endif#ifdef CONFIG_SPL_SATA_SUPPORTcase BOOT_DEVICE_SATA:spl_sata_load_image();break;#endifdefault: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_BOOTcase IH_OS_LINUX:debug("Jumping to Linux\n");spl_board_prepare_for_linux();jump_to_image_linux((void *)CONFIG_SYS_SPL_ARGS_ADDR);#endifdefault: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
0 0
原创粉丝点击