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”, boot_device);
[plain] view plain copyboot_device = spl_boot_device();
debug(“boot device - %d”, 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”);
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从具体的外部设备中load到ram中。 这里暂时先不分析具体的load过程。
[plain] 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);
[plain] 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结束它的生命,控制权交于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的芯片, 都是有办法做这个事情的.
!销售/回收/维修HP66309B电源HP66309B 小兵
三星S8什么时候上市?三星S8下周发布,不知道三星这次能否像国足一样给力
Single Voice Core关键技术有何种优势
!销售/收购/维修HP53131A频率计HP53131A小兵
汽车电气化如何发展电压电源板网
uboot中的SPL作用详解
如何破解JS加密?
IPA蒸汽干燥硅晶片中的水分实验研究
IPv6下的移动多媒体通信系统
华为Mate 40即将面世,它将带动移动摄影的新高度
直流电动机与永磁无刷直流电动机的结构与特点介绍
使用DragonBoard 410c快速开发基于Windows10 IoT应用
手持式气象仪(BNL-GPRS系列)的功能特点是什么
判断电感饱和的几个小窍门
虚拟局域网技术详解
全新水上机器人亮相 可解决生物污损问题
SAN网络存储的类型
十铨科技发布新一代火神Z SATA SSD 打造快速启动游戏全新升级体验
FPGA学习系列:5.阻塞赋值与非阻塞赋值
AR在移动领域有着无限的潜力