×
嵌入式 > 嵌入式开发 > 详情

uboot中的SPL作用详解

发布时间:2021-05-14 发布时间:
|

  SPL

  SPL是uboot第一阶段执行的代码。 主要负责搬移uboot第二阶段的代码到内存中运行。 SPL是由固化在芯片内部的ROM引导的。 我们知道很多芯片厂商固化的ROM支持从nandflash, SDCARD等外部介质启动。

  所谓启动, 就是从这些外部介质中搬移一段固定大小(4K/8K/16K等)的代码到内部RAM中运行。 这里搬移的就是SPL. 在最新版本的uboot中, 可以看到SPL也支持nandflash, SDCARD等多种启动方式。 当SPL本身被搬移到内部RAM中运行时, 它会从nandflash, SDCARD等外部介质中搬移uboot第二阶段的代码到外部内存中。

  SPL的文件组成

  当我们在uboot下执行make命令的时候, 它最核心的功能是执行Makefile中的all目标编译出相应的文件。 我们来看看这个all目标

  [plain] view plaincopyall: $(ALL-y) $(SUBDIR_EXAMPLES)

  [plain] view plain copyall: $(ALL-y) $(SUBDIR_EXAMPLES)

  all依赖于$(ALL-y) 和 $(SUBDIR_EXAMPLES), 这里我只关注ALL-y, 如下:

  [plain] 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

  [plain] 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

  

  因为本节是讨论SPL, 所以我们只关注其中的一句ALL-$(CONFIG_SPL) += $(obj)spl/u-boot-spl.bin

  这句话表明

  必须定义CONFIG_SPL才能编译出spl的bin: 一般在“include/configs/${CONFIG_NAME}.h”中定义

  SPL的bin依赖于u-boot-spl.bin

  接着往下看

  [plain] view plaincopy$(obj)spl/u-boot-spl.bin: $(SUBDIR_TOOLS) depend

  $(MAKE) -C spl all

  [plain] view plain copy$(obj)spl/u-boot-spl.bin: $(SUBDIR_TOOLS) depend

  $(MAKE) -C spl all

  这里可以发现, u-boot-spl.bin依赖于 $(SUBDIR_TOOLS) depend

  $(SUBDIR_TOOLS) : 暂不分析

  depend: 参考附录中的depend

  进入spl目录, 执行make all

  接下来进入spl目录, 看看它的Makefile : 这里只分析与SPL相关的部分

  [plain] view plaincopyCONFIG_SPL_BUILD := y

  export CONFIG_SPL_BUILD

  [plain] 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这种条件编译来区分。 类似的还有其他一些文件。

  [plain] view plaincopyHAVE_VENDOR_COMMON_LIB = $(if $(wildcard $(SRCTREE)/board/$(VENDOR)/common/Makefile),y,n)

  [plain] 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

  [plain] view plaincopyifdef CONFIG_SPL_START_S_PATH

  START_PATH := $(subst “,,$(CONFIG_SPL_START_S_PATH))

  else

  START_PATH := $(CPUDIR)

  endif

  [plain] 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)

  [plain] view plaincopySTART := $(START_PATH)/start.o

  [plain] view plain copySTART := $(START_PATH)/start.o

  依赖start.o, 综合来看, 就是要把CPUDIR下的start.S编译进来。

  [plain] view plaincopyLIBS-y += arch/$(ARCH)/lib/lib$(ARCH).o

  [plain] view plain copyLIBS-y += arch/$(ARCH)/lib/lib$(ARCH).o

  依赖lib$(ARCH).o, 具体来看, 就是依赖arch/arm/lib/libarm.o

  [plain] view plaincopyLIBS-y += $(CPUDIR)/lib$(CPU).o

  [plain] 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

  [plain] view plain copyifdef SOC

  LIBS-y += $(CPUDIR)/$(SOC)/lib$(SOC).o

  endif

  如果定义了SOC, 则依赖lib$(SOC).o, 具体来看, 就是依赖arch/arm/cpu/s5pc1xx/libs5pc1xx.o

  [plain] view plaincopyLIBS-y += board/$(BOARDDIR)/lib$(BOARD).o

  [plain] view plain copyLIBS-y += board/$(BOARDDIR)/lib$(BOARD).o

  依赖lib$(BOARD).o, 具体来看, 就是依赖board/samsung/tiny210/libtiny210.o

  [plain] view plaincopyLIBS-$(HAVE_VENDOR_COMMON_LIB) += board/$(VENDOR)/common/lib$(VENDOR).o

  [plain] 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

  [plain] 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

  根据具体配置, 选择相应的依赖关系

  [plain] view plaincopyifeq ($(SOC),exynos)

  LIBS-y += $(CPUDIR)/s5p-common/libs5p-common.o

  endif

  [plain] view plain copyifeq ($(SOC),exynos)

  LIBS-y += $(CPUDIR)/s5p-common/libs5p-common.o

  endif

  如果SOC为exynos, 则依赖libs5p-common.o, 我们这里的SOC为s5pc1xx, 所以不依赖

  [plain] view plaincopySTART := $(addprefix $(SPLTREE)/,$(START))

  LIBS := $(addprefix $(SPLTREE)/,$(sort $(LIBS-y)))

  [plain] view plain copySTART := $(addprefix $(SPLTREE)/,$(START))

  LIBS := $(addprefix $(SPLTREE)/,$(sort $(LIBS-y)))

  给START和LIBS加上前缀, $(SPLTREE), 具体来看, 就是编译过程中生成的.o文件都会放到spl/目录下面

  [plain] 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

  [plain] 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

  [plain] 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

  [plain] 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目录下之后干啥事吗? 没错, 执行make all, 其实执行的就是这里的all目标。 它依赖$(ALL-y)。 具体的依赖关系就不赘述了, 顺藤摸瓜即可

  ifdef CONFIG_SAMSUNG: 这个是针对Samsung平台的特殊之处。

  利用tools/mk$(BOARD)spl这个工具, 将u-boot-spl.bin转为$(BOARD)-spl.bin

  转换的本质, 就是在u-boot-spl.bin前面加入head info.关于head info是什么, 可以参考文档S5PV210_iROM_ApplicationNote_Preliminary_20091126.pdf. 顺便可以理解一下samsung芯片的启动流程

  tools/mk$(BOARD)spl这个工具是在tools/Makefile里面生成的。

  OK, 分析结束, 接下来, 就基于我们上面的分析开始分析代码了。

  SPL代码分析

  u-boot-spl.lds: 它的位置在上文中我们分析了

  根据u-boot-spl.lds中的规则, 我们知道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

  [plain] 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)

  首先会跳转到reset处

  ldr pc, _xxx定义的是中断向量表

  [plain] 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之后, 一旦发生异常中断, 就会进入死循环。 所以我们的SPL里面不允许出发异常中断

  不过正常的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 : 跳转到《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

  [plain] 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

  [plain] 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 : 跳转到《arch/arm/cpu/armv7/lowlevel_init.S》中的lowlevel_init

  lowlevel_init.S

  lowlevel_init

  [plain] 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下面的板级文件夹下面实现的。 现在直接放到CPUDIR下面了, 那它做了什么事情呢

  对stack pointer赋值成CONFIG_SYS_INIT_SP_ADDR

  确保sp是8字节对齐

  将gdata的地址存入到r9寄存器中

  跳转到 s_init: 这个s_init就需要芯片厂商或者我们自己在板级文件里面实现了。 它主要做的事情

  setup pll, mux, memory

  我个人感觉, 新版本的uboot在CPUDIR下实现了一个lowlevel_init.S文件, 主要目标是初始化sp, 这样s_init就可以用C语言实现了。 而以前的老版本里面, s_init里面要做的事情都是用汇编做的。

  crt0.S

  _main

  [plain] 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

  [plain] 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里面的一个全局数据, 很多地方都会用到。 俗称 gd_t

  确认更新后的sp是8字对齐

  r9指向global_data, 后面别的地方想用global_data时候, 可以直接从r9里面获取地址。

  r0赋值0

  bl board_init_f: 跳转到board_init_f. 在编译SPL时, 分析Makefile可以看出, 该函数的实现是在《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);

  }

  [plain] 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: 表明该函数可以被重新定义

  对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)里面指定的规则存放到输出段。

  为什么会有这个赋值操作, 不太明白。。。

  board_init_r : 在编译SPL时, 分析Makefile可以看出, 该函数的实现是在《common/spl/spl.c》

  common/spl/spl.c

  board_init_r

  [plain] 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池初始化。 以后调用malloc就在这个池子里面分配内存

  [plain] 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》里面定义

  [plain] 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阶段, 如果还需要做什么初始化动作, 可以放在这里。 具体的实现可以在BOARDDIR下面。

  [plain] view plaincopyboot_device = spl_boot_device();

  debug(“boot device - %d\n”, boot_device);

  [plain] view plain copyboot_device = spl_boot_device();

  debug(“boot device - %d\n”, boot_device);

  必须实现spl_boot_device, 返回是从哪个外部设备启动的(NAND/SDCARD/NOR.。。)。 可以厂商或者自己在BOARDDIR下面实现

  [plain] 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\n”);

  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\n”);

  hang();

  }

  将image从具体的外部设备中load到ram中。 这里暂时先不分析具体的load过程。

  [plain] view plaincopyswitch (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);

  [plain] view plain copy 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);

  判断image的类型

  如果是u-boot,则直接break, 去运行u-boot

  如果是Linux,则启动Linux

  至此,SPL结束它的生命,控制权交于u-boot或Linux

  在接下来的一篇中, 我们会分析当控制权交给u-boot之后, uboot的运行流程

  总结

  SPL移植注意点

  s_init: C语言实现此函数, 必须的。 如果是厂商提供的, 一般在arch/arm/cpu/xxx下面。 如果厂商没有提供, 我们可以在BOARDDIR下面实现。 主要完成以下功能

  设置CPU的PLL, GPIO管脚复用, memory等

  spl_board_init: C语言实现, 可选的。 如果是厂商提供的, 一般在arch/arm/cpu/xxx下面。 如果厂商没有提供, 我们可以在BOARDDIR下面实现。 主要完成的功能自己决定

  spl_boot_device: C语言实现, 必须的。 如果是厂商提供的, 一般在arch/arm/cpu/xxx下面。 如果厂商没有提供, 我们可以在BOARDDIR下面实现。 主要完成的功能

  返回是从什么设备启动的(NAND/SDCARD/Nor 。。。)。 像Atmel, Samsung的芯片, 都是有办法做这个事情的.


『本文转载自网络,版权归原作者所有,如有侵权请联系删除』

热门文章 更多
一只老鸟的嵌入式ARM学习心得