SPL
SPL是uboot第一階段執(zhí)行的代碼。 主要負責搬移uboot第二階段的代碼到內(nèi)存中運行。 SPL是由固化在芯片內(nèi)部的ROM引導的。 我們知道很多芯片廠商固化的ROM支持從nandflash, SDCARD等外部介質(zhì)啟動。
所謂啟動, 就是從這些外部介質(zhì)中搬移一段固定大?。?K/8K/16K等)的代碼到內(nèi)部RAM中運行。 這里搬移的就是SPL. 在最新版本的uboot中, 可以看到SPL也支持nandflash, SDCARD等多種啟動方式。 當SPL本身被搬移到內(nèi)部RAM中運行時, 它會從nandflash, SDCARD等外部介質(zhì)中搬移uboot第二階段的代碼到外部內(nèi)存中。
SPL的文件組成
當我們在uboot下執(zhí)行make命令的時候, 它最核心的功能是執(zhí)行Makefile中的all目標編譯出相應的文件。 我們來看看這個all目標
?。踦lain] view plaincopyall: $(ALL-y) $(SUBDIR_EXAMPLES)
?。踦lain] view plain copyall: $(ALL-y) $(SUBDIR_EXAMPLES)
all依賴于$(ALL-y) 和 $(SUBDIR_EXAMPLES), 這里我只關(guān)注ALL-y, 如下:
?。踦lain] view plaincopy# Always append ALL so that arch config.mk‘s can add custom ones
ALL-y += $(obj)u-boot.srec $(obj)u-boot.bin $(obj)System.map
ALL-$(CONFIG_NAND_U_BOOT) += $(obj)u-boot-nand.bin
ALL-$(CONFIG_ONENAND_U_BOOT) += $(obj)u-boot-onenand.bin
ALL-$(CONFIG_SPL) += $(obj)spl/u-boot-spl.bin
ALL-$(CONFIG_SPL_FRAMEWORK) += $(obj)u-boot.img
ALL-$(CONFIG_TPL) += $(obj)tpl/u-boot-tpl.bin
ALL-$(CONFIG_OF_SEPARATE) += $(obj)u-boot.dtb $(obj)u-boot-dtb.bin
ifneq ($(CONFIG_SPL_TARGET),)
ALL-$(CONFIG_SPL) += $(obj)$(subst “,,$(CONFIG_SPL_TARGET))
endif
# enable combined SPL/u-boot/dtb rules for tegra
ifneq ($(CONFIG_TEGRA),)
ifeq ($(CONFIG_OF_SEPARATE),y)
ALL-y += $(obj)u-boot-dtb-tegra.bin
else
ALL-y += $(obj)u-boot-nodtb-tegra.bin
endif
endif
?。踦lain] view plain copy# Always append ALL so that arch config.mk’s can add custom ones
ALL-y += $(obj)u-boot.srec $(obj)u-boot.bin $(obj)System.map
ALL-$(CONFIG_NAND_U_BOOT) += $(obj)u-boot-nand.bin
ALL-$(CONFIG_ONENAND_U_BOOT) += $(obj)u-boot-onenand.bin
ALL-$(CONFIG_SPL) += $(obj)spl/u-boot-spl.bin
ALL-$(CONFIG_SPL_FRAMEWORK) += $(obj)u-boot.img
ALL-$(CONFIG_TPL) += $(obj)tpl/u-boot-tpl.bin
ALL-$(CONFIG_OF_SEPARATE) += $(obj)u-boot.dtb $(obj)u-boot-dtb.bin
ifneq ($(CONFIG_SPL_TARGET),)
ALL-$(CONFIG_SPL) += $(obj)$(subst ”,,$(CONFIG_SPL_TARGET))
endif
# enable combined SPL/u-boot/dtb rules for tegra
ifneq ($(CONFIG_TEGRA),)
ifeq ($(CONFIG_OF_SEPARATE),y)
ALL-y += $(obj)u-boot-dtb-tegra.bin
else
ALL-y += $(obj)u-boot-nodtb-tegra.bin
endif
endif
因為本節(jié)是討論SPL, 所以我們只關(guān)注其中的一句ALL-$(CONFIG_SPL) += $(obj)spl/u-boot-spl.bin
這句話表明
必須定義CONFIG_SPL才能編譯出spl的bin: 一般在“include/configs/${CONFIG_NAME}.h”中定義
SPL的bin依賴于u-boot-spl.bin
接著往下看
?。踦lain] view plaincopy$(obj)spl/u-boot-spl.bin: $(SUBDIR_TOOLS) depend
$(MAKE) -C spl all
?。踦lain] view plain copy$(obj)spl/u-boot-spl.bin: $(SUBDIR_TOOLS) depend
$(MAKE) -C spl all
這里可以發(fā)現(xiàn), u-boot-spl.bin依賴于 $(SUBDIR_TOOLS) depend
$(SUBDIR_TOOLS) : 暫不分析
depend: 參考附錄中的depend
進入spl目錄, 執(zhí)行make all
接下來進入spl目錄, 看看它的Makefile : 這里只分析與SPL相關(guān)的部分
?。踦lain] view plaincopyCONFIG_SPL_BUILD := y
export CONFIG_SPL_BUILD
?。踦lain] view plain copyCONFIG_SPL_BUILD := y
export CONFIG_SPL_BUILD
export CONFIG_SPL_BUILD: 在接下來的編譯中, 這個變量為y. 從后面的分析中可以看到, uboot的stage1, stage2階段的代碼用的是同一個Start.S, 只不過在Start.S中用#ifdef CONFIG_SPL_BUILD這種條件編譯來區(qū)分。 類似的還有其他一些文件。
[plain] view plaincopyHAVE_VENDOR_COMMON_LIB = $(if $(wildcard $(SRCTREE)/board/$(VENDOR)/common/Makefile),y,n)
?。踦lain] view plain copyHAVE_VENDOR_COMMON_LIB = $(if $(wildcard $(SRCTREE)/board/$(VENDOR)/common/Makefile),y,n)
[cpp] view plaincopy如果board/$(VENDOR)/common目錄中有Makefile文件,則HAVE_VENDOR_COMMON_LIB為y否則為n
[cpp] view plain copy如果board/$(VENDOR)/common目錄中有Makefile文件,則HAVE_VENDOR_COMMON_LIB為y否則為n
?。踦lain] view plaincopyifdef CONFIG_SPL_START_S_PATH
START_PATH := $(subst “,,$(CONFIG_SPL_START_S_PATH))
else
START_PATH := $(CPUDIR)
endif
?。踦lain] view plain copyifdef CONFIG_SPL_START_S_PATH
START_PATH := $(subst ”,,$(CONFIG_SPL_START_S_PATH))
else
START_PATH := $(CPUDIR)
endif
我們這里沒有定義CONFIG_SPL_START_S_PATH, 所以START_PATH := $(CPUDIR)
?。踦lain] view plaincopySTART := $(START_PATH)/start.o
[plain] view plain copySTART := $(START_PATH)/start.o
依賴start.o, 綜合來看, 就是要把CPUDIR下的start.S編譯進來。
?。踦lain] view plaincopyLIBS-y += arch/$(ARCH)/lib/lib$(ARCH).o
?。踦lain] view plain copyLIBS-y += arch/$(ARCH)/lib/lib$(ARCH).o
依賴lib$(ARCH).o, 具體來看, 就是依賴arch/arm/lib/libarm.o
?。踦lain] view plaincopyLIBS-y += $(CPUDIR)/lib$(CPU).o
?。踦lain] view plain copyLIBS-y += $(CPUDIR)/lib$(CPU).o
依賴lib$(CPU).o, 具體來看, 就是依賴arch/arm/cpu/armv7/libarmv7.o
[plain] view plaincopyifdef SOC
LIBS-y += $(CPUDIR)/$(SOC)/lib$(SOC).o
endif
?。踦lain] view plain copyifdef SOC
LIBS-y += $(CPUDIR)/$(SOC)/lib$(SOC).o
endif
如果定義了SOC, 則依賴lib$(SOC).o, 具體來看, 就是依賴arch/arm/cpu/s5pc1xx/libs5pc1xx.o
?。踦lain] view plaincopyLIBS-y += board/$(BOARDDIR)/lib$(BOARD).o
?。踦lain] view plain copyLIBS-y += board/$(BOARDDIR)/lib$(BOARD).o
依賴lib$(BOARD).o, 具體來看, 就是依賴board/samsung/tiny210/libtiny210.o
?。踦lain] view plaincopyLIBS-$(HAVE_VENDOR_COMMON_LIB) += board/$(VENDOR)/common/lib$(VENDOR).o
?。踦lain] view plain copyLIBS-$(HAVE_VENDOR_COMMON_LIB) += board/$(VENDOR)/common/lib$(VENDOR).o
如果HAVE_VENDOR_COMMON_LIB為y, 則依賴lib$(VENDOR).o, 具體來看, 就是依賴board/samsung/common/libsamsung.o
?。踦lain] view plaincopyLIBS-$(CONFIG_SPL_FRAMEWORK) += common/spl/libspl.o
LIBS-$(CONFIG_SPL_LIBCOMMON_SUPPORT) += common/libcommon.o
LIBS-$(CONFIG_SPL_LIBDISK_SUPPORT) += disk/libdisk.o
LIBS-$(CONFIG_SPL_I2C_SUPPORT) += drivers/i2c/libi2c.o
LIBS-$(CONFIG_SPL_GPIO_SUPPORT) += drivers/gpio/libgpio.o
LIBS-$(CONFIG_SPL_MMC_SUPPORT) += drivers/mmc/libmmc.o
LIBS-$(CONFIG_SPL_SERIAL_SUPPORT) += drivers/serial/libserial.o
LIBS-$(CONFIG_SPL_SPI_FLASH_SUPPORT) += drivers/mtd/spi/libspi_flash.o
LIBS-$(CONFIG_SPL_SPI_SUPPORT) += drivers/spi/libspi.o
LIBS-$(CONFIG_SPL_FAT_SUPPORT) += fs/fat/libfat.o
LIBS-$(CONFIG_SPL_LIBGENERIC_SUPPORT) += lib/libgeneric.o
LIBS-$(CONFIG_SPL_POWER_SUPPORT) += drivers/power/libpower.o
drivers/power/pmic/libpmic.o
LIBS-$(CONFIG_SPL_NAND_SUPPORT) += drivers/mtd/nand/libnand.o
LIBS-$(CONFIG_SPL_ONENAND_SUPPORT) += drivers/mtd/onenand/libonenand.o
LIBS-$(CONFIG_SPL_DMA_SUPPORT) += drivers/dma/libdma.o
LIBS-$(CONFIG_SPL_POST_MEM_SUPPORT) += post/drivers/memory.o
LIBS-$(CONFIG_SPL_NET_SUPPORT) += net/libnet.o
LIBS-$(CONFIG_SPL_ETH_SUPPORT) += drivers/net/libnet.o
LIBS-$(CONFIG_SPL_ETH_SUPPORT) += drivers/net/phy/libphy.o
LIBS-$(CONFIG_SPL_USBETH_SUPPORT) += drivers/net/phy/libphy.o
LIBS-$(CONFIG_SPL_MUSB_NEW_SUPPORT) += drivers/usb/musb-new/libusb_musb-new.o
LIBS-$(CONFIG_SPL_USBETH_SUPPORT) += drivers/usb/gadget/libusb_gadget.o
LIBS-$(CONFIG_SPL_WATCHDOG_SUPPORT) += drivers/watchdog/libwatchdog.o
[plain] view plain copyLIBS-$(CONFIG_SPL_FRAMEWORK) += common/spl/libspl.o
LIBS-$(CONFIG_SPL_LIBCOMMON_SUPPORT) += common/libcommon.o
LIBS-$(CONFIG_SPL_LIBDISK_SUPPORT) += disk/libdisk.o
LIBS-$(CONFIG_SPL_I2C_SUPPORT) += drivers/i2c/libi2c.o
LIBS-$(CONFIG_SPL_GPIO_SUPPORT) += drivers/gpio/libgpio.o
LIBS-$(CONFIG_SPL_MMC_SUPPORT) += drivers/mmc/libmmc.o
LIBS-$(CONFIG_SPL_SERIAL_SUPPORT) += drivers/serial/libserial.o
LIBS-$(CONFIG_SPL_SPI_FLASH_SUPPORT) += drivers/mtd/spi/libspi_flash.o
LIBS-$(CONFIG_SPL_SPI_SUPPORT) += drivers/spi/libspi.o
LIBS-$(CONFIG_SPL_FAT_SUPPORT) += fs/fat/libfat.o
LIBS-$(CONFIG_SPL_LIBGENERIC_SUPPORT) += lib/libgeneric.o
LIBS-$(CONFIG_SPL_POWER_SUPPORT) += drivers/power/libpower.o
drivers/power/pmic/libpmic.o
LIBS-$(CONFIG_SPL_NAND_SUPPORT) += drivers/mtd/nand/libnand.o
LIBS-$(CONFIG_SPL_ONENAND_SUPPORT) += drivers/mtd/onenand/libonenand.o
LIBS-$(CONFIG_SPL_DMA_SUPPORT) += drivers/dma/libdma.o
LIBS-$(CONFIG_SPL_POST_MEM_SUPPORT) += post/drivers/memory.o
LIBS-$(CONFIG_SPL_NET_SUPPORT) += net/libnet.o
LIBS-$(CONFIG_SPL_ETH_SUPPORT) += drivers/net/libnet.o
LIBS-$(CONFIG_SPL_ETH_SUPPORT) += drivers/net/phy/libphy.o
LIBS-$(CONFIG_SPL_USBETH_SUPPORT) += drivers/net/phy/libphy.o
LIBS-$(CONFIG_SPL_MUSB_NEW_SUPPORT) += drivers/usb/musb-new/libusb_musb-new.o
LIBS-$(CONFIG_SPL_USBETH_SUPPORT) += drivers/usb/gadget/libusb_gadget.o
LIBS-$(CONFIG_SPL_WATCHDOG_SUPPORT) += drivers/watchdog/libwatchdog.o
根據(jù)具體配置, 選擇相應的依賴關(guān)系
[plain] view plaincopyifeq ($(SOC),exynos)
LIBS-y += $(CPUDIR)/s5p-common/libs5p-common.o
endif
?。踦lain] view plain copyifeq ($(SOC),exynos)
LIBS-y += $(CPUDIR)/s5p-common/libs5p-common.o
endif
如果SOC為exynos, 則依賴libs5p-common.o, 我們這里的SOC為s5pc1xx, 所以不依賴
?。踦lain] view plaincopySTART := $(addprefix $(SPLTREE)/,$(START))
LIBS := $(addprefix $(SPLTREE)/,$(sort $(LIBS-y)))
?。踦lain] view plain copySTART := $(addprefix $(SPLTREE)/,$(START))
LIBS := $(addprefix $(SPLTREE)/,$(sort $(LIBS-y)))
給START和LIBS加上前綴, $(SPLTREE), 具體來看, 就是編譯過程中生成的.o文件都會放到spl/目錄下面
?。踦lain] view plaincopy# Linker Script
ifdef CONFIG_SPL_LDSCRIPT
# need to strip off double quotes
LDSCRIPT := $(addprefix $(SRCTREE)/,$(subst “,,$(CONFIG_SPL_LDSCRIPT)))
endif
ifeq ($(wildcard $(LDSCRIPT)),)
LDSCRIPT := $(TOPDIR)/board/$(BOARDDIR)/u-boot-spl.lds
endif
ifeq ($(wildcard $(LDSCRIPT)),)
LDSCRIPT := $(TOPDIR)/$(CPUDIR)/u-boot-spl.lds
endif
ifeq ($(wildcard $(LDSCRIPT)),)
LDSCRIPT := $(TOPDIR)/arch/$(ARCH)/cpu/u-boot-spl.lds
endif
ifeq ($(wildcard $(LDSCRIPT)),)
$(error could not find linker script)
endif
?。踦lain] view plain copy# Linker Script
ifdef CONFIG_SPL_LDSCRIPT
# need to strip off double quotes
LDSCRIPT := $(addprefix $(SRCTREE)/,$(subst ”,,$(CONFIG_SPL_LDSCRIPT)))
endif
ifeq ($(wildcard $(LDSCRIPT)),)
LDSCRIPT := $(TOPDIR)/board/$(BOARDDIR)/u-boot-spl.lds
endif
ifeq ($(wildcard $(LDSCRIPT)),)
LDSCRIPT := $(TOPDIR)/$(CPUDIR)/u-boot-spl.lds
endif
ifeq ($(wildcard $(LDSCRIPT)),)
LDSCRIPT := $(TOPDIR)/arch/$(ARCH)/cpu/u-boot-spl.lds
endif
ifeq ($(wildcard $(LDSCRIPT)),)
$(error could not find linker script)
endif
找到spl的鏈接配置文件, 具體來看, 用的是arch/arm/cpu下的u-boot-spl.lds
?。踦lain] view plaincopyALL-y += $(obj)$(SPL_BIN).bin
ifdef CONFIG_SAMSUNG
ALL-y += $(obj)$(BOARD)-spl.bin
endif
all: $(ALL-y)
ifdef CONFIG_SAMSUNG
$(obj)$(BOARD)-spl.bin: $(obj)u-boot-spl.bin
$(OBJTREE)/tools/mk$(BOARD)spl
$(obj)u-boot-spl.bin $(obj)$(BOARD)-spl.bin
endif
$(obj)$(SPL_BIN).bin: $(obj)$(SPL_BIN)
$(OBJCOPY) $(OBJCFLAGS) -O binary $《 $@
GEN_UBOOT =
cd $(obj) && $(LD) $(LDFLAGS) $(LDFLAGS_$(@F)) $(__START)
--start-group $(__LIBS) --end-group $(PLATFORM_LIBS)
-Map $(SPL_BIN).map -o $(SPL_BIN)
$(obj)$(SPL_BIN): depend $(START) $(LIBS) $(obj)u-boot-spl.lds
$(GEN_UBOOT)
$(START): depend
$(MAKE) -C $(SRCTREE)/$(START_PATH) $@
$(LIBS): depend
$(MAKE) -C $(SRCTREE)$(dir $(subst $(SPLTREE),,$@))
$(obj)u-boot-spl.lds: $(LDSCRIPT) depend
$(CPP) $(CPPFLAGS) $(LDPPFLAGS) -I$(obj)。 -ansi -D__ASSEMBLY__ -P - 《 $《 》 $@
depend: $(obj).depend
.PHONY: depend
?。踦lain] view plain copyALL-y += $(obj)$(SPL_BIN).bin
ifdef CONFIG_SAMSUNG
ALL-y += $(obj)$(BOARD)-spl.bin
endif
all: $(ALL-y)
ifdef CONFIG_SAMSUNG
$(obj)$(BOARD)-spl.bin: $(obj)u-boot-spl.bin
$(OBJTREE)/tools/mk$(BOARD)spl
$(obj)u-boot-spl.bin $(obj)$(BOARD)-spl.bin
endif
$(obj)$(SPL_BIN).bin: $(obj)$(SPL_BIN)
$(OBJCOPY) $(OBJCFLAGS) -O binary $《 $@
GEN_UBOOT =
cd $(obj) && $(LD) $(LDFLAGS) $(LDFLAGS_$(@F)) $(__START)
--start-group $(__LIBS) --end-group $(PLATFORM_LIBS)
-Map $(SPL_BIN).map -o $(SPL_BIN)
$(obj)$(SPL_BIN): depend $(START) $(LIBS) $(obj)u-boot-spl.lds
$(GEN_UBOOT)
$(START): depend
$(MAKE) -C $(SRCTREE)/$(START_PATH) $@
$(LIBS): depend
$(MAKE) -C $(SRCTREE)$(dir $(subst $(SPLTREE),,$@))
$(obj)u-boot-spl.lds: $(LDSCRIPT) depend
$(CPP) $(CPPFLAGS) $(LDPPFLAGS) -I$(obj)。 -ansi -D__ASSEMBLY__ -P - 《 $《 》 $@
depend: $(obj).depend
.PHONY: depend
all: $(ALL-y), 還記得本篇上面說的, 進入到spl目錄下之后干啥事嗎? 沒錯, 執(zhí)行make all, 其實執(zhí)行的就是這里的all目標。 它依賴$(ALL-y)。 具體的依賴關(guān)系就不贅述了, 順藤摸瓜即可
ifdef CONFIG_SAMSUNG: 這個是針對Samsung平臺的特殊之處。
利用tools/mk$(BOARD)spl這個工具, 將u-boot-spl.bin轉(zhuǎn)為$(BOARD)-spl.bin
轉(zhuǎn)換的本質(zhì), 就是在u-boot-spl.bin前面加入head info.關(guān)于head info是什么, 可以參考文檔S5PV210_iROM_ApplicationNote_Preliminary_20091126.pdf. 順便可以理解一下samsung芯片的啟動流程
tools/mk$(BOARD)spl這個工具是在tools/Makefile里面生成的。
OK, 分析結(jié)束, 接下來, 就基于我們上面的分析開始分析代碼了。
SPL代碼分析
u-boot-spl.lds: 它的位置在上文中我們分析了
根據(jù)u-boot-spl.lds中的規(guī)則, 我們知道CPUDIR/start.o被放在了最前面。 它所對應的文件就是arch/arm/cpu/armv7/start.S
start.S
下面我們看看start.S
[plain] view plaincopy.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
?。踦lain] view plain copy.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
_start是我們在lds里面指定的ENTRY(_start)
首先會跳轉(zhuǎn)到reset處
ldr pc, _xxx定義的是中斷向量表
?。踦lain] view plaincopy#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
_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 */
#endif /* CONFIG_SPL_BUILD */
[plain] view plain copy#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
_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 */
#endif /* CONFIG_SPL_BUILD */
當CONFIG了SPL_BUILD之后, 一旦發(fā)生異常中斷, 就會進入死循環(huán)。 所以我們的SPL里面不允許出發(fā)異常中斷
不過正常的uboot(即stage2階段)還是可以處理異常中斷的。
reset
[plain] view plaincopy/*
* 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
/* 。。。。。。。。 */
/* 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
[plain] view plain copy/*
* 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
/* 。。。。。。。。 */
/* 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
當初次上電或者復位時, Uboot最新運行的就是這里的代碼
bl save_boot_params: 如果沒有重新定義save_boot_params,則使用《arch/arm/cpu/armv7/start.S》中的save_boot_params。其不做任何事情,直接返回
禁止FIQ, IRQ; 設置CPU工作在SVC32模式
bl cpu_init_cp15: (I/D-Cache, MMU, TLBs),具體見下面代碼中注釋
bl cpu_init_crit : 主要是設置CPU的PLL, GPIO管腳復用, memory等。 具體見下面
bl _main : 跳轉(zhuǎn)到《arch/arm/lib/crt0.S》中的_main. 具體見下面
cpu_init_cp15
[plain] view plaincopy/*************************************************************************
*
* 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
#ifdef CONFIG_ARM_ERRATA_716044
mrc p15, 0, r0, c1, c0, 0 @ read system control register
orr r0, r0, #1 《《 11 @ set bit #11
mcr p15, 0, r0, c1, c0, 0 @ write system control register
#endif
#ifdef CONFIG_ARM_ERRATA_742230
mrc p15, 0, r0, c15, c0, 1 @ read diagnostic register
orr r0, r0, #1 《《 4 @ set bit #4
mcr p15, 0, r0, c15, c0, 1 @ write diagnostic register
#endif
#ifdef CONFIG_ARM_ERRATA_743622
mrc p15, 0, r0, c15, c0, 1 @ read diagnostic register
orr r0, r0, #1 《《 6 @ set bit #6
mcr p15, 0, r0, c15, c0, 1 @ write diagnostic register
#endif
#ifdef CONFIG_ARM_ERRATA_751472
mrc p15, 0, r0, c15, c0, 1 @ read diagnostic register
orr r0, r0, #1 《《 11 @ set bit #11
mcr p15, 0, r0, c15, c0, 1 @ write diagnostic register
#endif
mov pc, lr @ back to my caller
ENDPROC(cpu_init_cp15)
[plain] view plain copy/*************************************************************************
*
* 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
#ifdef CONFIG_ARM_ERRATA_716044
mrc p15, 0, r0, c1, c0, 0 @ read system control register
orr r0, r0, #1 《《 11 @ set bit #11
mcr p15, 0, r0, c1, c0, 0 @ write system control register
#endif
#ifdef CONFIG_ARM_ERRATA_742230
mrc p15, 0, r0, c15, c0, 1 @ read diagnostic register
orr r0, r0, #1 《《 4 @ set bit #4
mcr p15, 0, r0, c15, c0, 1 @ write diagnostic register
#endif
#ifdef CONFIG_ARM_ERRATA_743622
mrc p15, 0, r0, c15, c0, 1 @ read diagnostic register
orr r0, r0, #1 《《 6 @ set bit #6
mcr p15, 0, r0, c15, c0, 1 @ write diagnostic register
#endif
#ifdef CONFIG_ARM_ERRATA_751472
mrc p15, 0, r0, c15, c0, 1 @ read diagnostic register
orr r0, r0, #1 《《 11 @ set bit #11
mcr p15, 0, r0, c15, c0, 1 @ write diagnostic register
#endif
mov pc, lr @ back to my caller
ENDPROC(cpu_init_cp15)
cpu_init_crit
?。踦lain] view plaincopy#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
?。踦lain] view plain copy#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
b lowlevel_init : 跳轉(zhuǎn)到《arch/arm/cpu/armv7/lowlevel_init.S》中的lowlevel_init
lowlevel_init.S
lowlevel_init
?。踦lain] view plaincopy#include 《asm-offsets.h》
#include 《config.h》
#include 《linux/linkage.h》
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, #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}
[plain] view plain copy#include 《asm-offsets.h》
#include 《config.h》
#include 《linux/linkage.h》
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, #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}
以前老版本的uboot, lowlevel_init一般都是在board/xxx下面的板級文件夾下面實現(xiàn)的。 現(xiàn)在直接放到CPUDIR下面了, 那它做了什么事情呢
對stack pointer賦值成CONFIG_SYS_INIT_SP_ADDR
確保sp是8字節(jié)對齊
將gdata的地址存入到r9寄存器中
跳轉(zhuǎn)到 s_init: 這個s_init就需要芯片廠商或者我們自己在板級文件里面實現(xiàn)了。 它主要做的事情
setup pll, mux, memory
我個人感覺, 新版本的uboot在CPUDIR下實現(xiàn)了一個lowlevel_init.S文件, 主要目標是初始化sp, 這樣s_init就可以用C語言實現(xiàn)了。 而以前的老版本里面, s_init里面要做的事情都是用匯編做的。
crt0.S
_main
?。踦lain] view plaincopyENTRY(_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, #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
?。踦lain] view plain copyENTRY(_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, #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
重新對SP賦值, 確認sp是8字對齊
在棧頂保留一個global_data的大小, 這個global_data是uboot里面的一個全局數(shù)據(jù), 很多地方都會用到。 俗稱 gd_t
確認更新后的sp是8字對齊
r9指向global_data, 后面別的地方想用global_data時候, 可以直接從r9里面獲取地址。
r0賦值0
bl board_init_f: 跳轉(zhuǎn)到board_init_f. 在編譯SPL時, 分析Makefile可以看出, 該函數(shù)的實現(xiàn)是在《arch/arm/lib/spl.c》。
arch/arm/lib/spl.c
board_init_f
[plain] view plaincopy/*
* 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);
}
?。踦lain] view plain copy/*
* 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);
}
__weak: 表明該函數(shù)可以被重新定義
對BSS段進行清零操作
gd = &gdata;
gd的定義在DECLARE_GLOBAL_DATA_PTR 《arch/arm/include/asm/global_data.h》
#define DECLARE_GLOBAL_DATA_PTR register volatile gd_t *gd asm (“r9”)
還記得r9這個寄存器嗎, 在上面初始化過了
gdata的定義在本文件中: gd_t gdata __attribute__ ((section(“.data”)));
它是一個 gd_t 也就是global_data類型的變量
__attribute__表示這個變量會被放到“.data”這個輸入段中。 連接器會把輸入段按照鏈接腳本(u-boot-spl.lds)里面指定的規(guī)則存放到輸出段。
為什么會有這個賦值操作, 不太明白。。。
board_init_r : 在編譯SPL時, 分析Makefile可以看出, 該函數(shù)的實現(xiàn)是在《common/spl/spl.c》
common/spl/spl.c
board_init_r
?。踦lain] view plaincopy#ifdef CONFIG_SYS_SPL_MALLOC_START
mem_malloc_init(CONFIG_SYS_SPL_MALLOC_START,
CONFIG_SYS_SPL_MALLOC_SIZE);
#endif
[plain] view plain copy#ifdef CONFIG_SYS_SPL_MALLOC_START
mem_malloc_init(CONFIG_SYS_SPL_MALLOC_START,
CONFIG_SYS_SPL_MALLOC_SIZE);
#endif
如果定義了:CONFIG_SYS_SPL_MALLOC_START, 則進行memory的malloc池初始化。 以后調(diào)用malloc就在這個池子里面分配內(nèi)存
?。踦lain] view plaincopy#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
[plain] view plain copy#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
如果沒有定義:CONFIG_PPC, 則進行timer的初始化。 《arch/arm/cpu/armv7/s5p-common/timer.c》里面定義
?。踦lain] view plaincopy#ifdef CONFIG_SPL_BOARD_INIT
spl_board_init();
#endif
[plain] view plain copy#ifdef CONFIG_SPL_BOARD_INIT
spl_board_init();
#endif
SPL階段, 如果還需要做什么初始化動作, 可以放在這里。 具體的實現(xiàn)可以在BOARDDIR下面。
?。踦lain] view plaincopyboot_device = spl_boot_device();
debug(“boot device - %d ”, boot_device);
?。踦lain] view plain copyboot_device = spl_boot_device();
debug(“boot device - %d ”, boot_device);
必須實現(xiàn)spl_boot_device, 返回是從哪個外部設備啟動的(NAND/SDCARD/NOR.。。)。 可以廠商或者自己在BOARDDIR下面實現(xiàn)
?。踦lain] view plaincopyswitch (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
default:
debug(“SPL: Un-supported Boot Device ”);
hang();
}
[plain] view plain copy 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
default:
debug(“SPL: Un-supported Boot Device ”);
hang();
}
將image從具體的外部設備中l(wèi)oad到ram中。 這里暫時先不分析具體的load過程。
?。踦lain] view plaincopyswitch (spl_image.os) {
case IH_OS_U_BOOT:
debug(“Jumping to U-Boot ”);
break;
#ifdef CONFIG_SPL_OS_BOOT
case IH_OS_LINUX:
debug(“Jumping to Linux ”);
spl_board_prepare_for_linux();
jump_to_image_linux((void *)CONFIG_SYS_SPL_ARGS_ADDR);
#endif
default:
debug(“Unsupported OS image.。 Jumping nevertheless.。 ”);
}
jump_to_image_no_args(&spl_image);
?。踦lain] view plain copy switch (spl_image.os) {
case IH_OS_U_BOOT:
debug(“Jumping to U-Boot ”);
break;
#ifdef CONFIG_SPL_OS_BOOT
case IH_OS_LINUX:
debug(“Jumping to Linux ”);
spl_board_prepare_for_linux();
jump_to_image_linux((void *)CONFIG_SYS_SPL_ARGS_ADDR);
#endif
default:
debug(“Unsupported OS image.。 Jumping nevertheless.。 ”);
}
jump_to_image_no_args(&spl_image);
判斷image的類型
如果是u-boot,則直接break, 去運行u-boot
如果是Linux,則啟動Linux
至此,SPL結(jié)束它的生命,控制權(quán)交于u-boot或Linux
在接下來的一篇中, 我們會分析當控制權(quán)交給u-boot之后, uboot的運行流程
總結(jié)
SPL移植注意點
s_init: C語言實現(xiàn)此函數(shù), 必須的。 如果是廠商提供的, 一般在arch/arm/cpu/xxx下面。 如果廠商沒有提供, 我們可以在BOARDDIR下面實現(xiàn)。 主要完成以下功能
設置CPU的PLL, GPIO管腳復用, memory等
spl_board_init: C語言實現(xiàn), 可選的。 如果是廠商提供的, 一般在arch/arm/cpu/xxx下面。 如果廠商沒有提供, 我們可以在BOARDDIR下面實現(xiàn)。 主要完成的功能自己決定
spl_boot_device: C語言實現(xiàn), 必須的。 如果是廠商提供的, 一般在arch/arm/cpu/xxx下面。 如果廠商沒有提供, 我們可以在BOARDDIR下面實現(xiàn)。 主要完成的功能
返回是從什么設備啟動的(NAND/SDCARD/Nor 。。。)。 像Atmel, Samsung的芯片, 都是有辦法做這個事情的.
評論
查看更多