在uboot代码的/doc/README.spl文件有简单的介绍:
To unify all existing implementations for a secondary program loader (SPL)
and to allow simply adding of new implementations this generic SPL framework
has been created. With this framework almost all source files for a board
can be reused. No code duplication or symlinking is necessary anymore.
可见,SPL被称作:二次程序加载器。UBOOT的SPL提供了一种框架,在这个框架内能够方便的实现一种SPL。该框架能复用一个单板几乎所有的UBOO和SPL的源码。
SPL模块的主要公共代码在common\spl目录。要使能SPL,要在configs/***_defconfig文件中配置相关SPL的编译宏。如下配置:
CONFIG_SPL=y
CONFIG_SPL_RAW_IMAGE_SUPPORT=y
CONFIG_SPL_LEGACY_IMAGE_SUPPORT=y
CONFIG_SPL_SYS_MALLOC_SIMPLE=y
CONFIG_SPL_CTC5236=y
CONFIG_SPL_YMODEM_SUPPORT=y
编译SPL过程中可使用CONFIG_SPL_BUILD编译宏。而对于arm的单板可用CONFIG_PRELOADER。
During the SPL build a variable named CONFIG_SPL_BUILD is exported
in the make environment and also appended to CPPFLAGS with -DCONFIG_SPL_BUILD.
Source files can therefore be compiled for SPL with different settings.
ARM-based boards have previously used the option CONFIG_PRELOADER for it.
SPL最后生成u-boot-spl, u-boot-spl.bin 和u-boot-spl.map.
SPL的链接脚本是u-boot-spl.lds
# 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)),)
$(error could not find linker script)
endif
对应以ARM V8为例,其具体的链接脚本在arch\arm\cpu\armv8如下:
MEMORY { .sram : ORIGIN = CONFIG_SPL_TEXT_BASE,
LENGTH = CONFIG_SPL_MAX_SIZE }
MEMORY { .sdram : ORIGIN = CONFIG_SPL_BSS_START_ADDR,
LENGTH = CONFIG_SPL_BSS_MAX_SIZE }
OUTPUT_FORMAT("elf64-littleaarch64", "elf64-littleaarch64", "elf64-littleaarch64")
OUTPUT_ARCH(aarch64)
ENTRY(_start)
SECTIONS
{
.text : {
. = ALIGN(8);
*(.__image_copy_start)
CPUDIR/start.o (.text*)
*(.text*)
} >.sram
.rodata : {
. = ALIGN(8);
*(SORT_BY_ALIGNMENT(SORT_BY_NAME(.rodata*)))
} >.sram
.data : {
. = ALIGN(8);
*(.data*)
} >.sram
.u_boot_list : {
. = ALIGN(8);
KEEP(*(SORT(.u_boot_list*)));
} >.sram
.image_copy_end : {
. = ALIGN(8);
*(.__image_copy_end)
} >.sram
.end : {
. = ALIGN(8);
*(.__end)
} >.sram
_image_binary_end = .;
.bss_start : {
. = ALIGN(8);
KEEP(*(.__bss_start));
} >.sdram
.bss : {
*(.bss*)
. = ALIGN(8);
} >.sdram
.bss_end : {
KEEP(*(.__bss_end));
} >.sdram
arch\arm\cpu\armv8\start.S,作为SPL的入口。
SPL的执行流程如下:
(reset) <arm\cpu\armv8\start.S中执行bllowlevel_init,
(b lowlevel_init: arch\arm\cpu\armv8\lowlevel_init.S) 中执行bllowlevel_init
(b _main) –> <arch/arm/lib/crt0.S>
(bl board_init_f <common\board_f.c>
后调用 <arch/arm/lib/spl.c> (board_init_r) 。主要调用
#ifdef CONFIG_SPL_BOARD_INIT
spl_board_init();
/*1 spl_board_init多数CPU会在对应的arch其自己cpu目录下进行针对其CPU的实现。
如centec芯片 spl.c (arch\arm\cpu\armv8\ctc5236)*/
#endif
/*board_boot_order common/spl/spl.c有弱引用定义。数CPU会在对应的arch其自己cpu目录下进行针对其CPU的实现。如arch\arm\cpu\armv8\ctc5236下有自己的实现*/
board_boot_order(spl_boot_list);
if (boot_from_devices(&spl_image, spl_boot_list,
ARRAY_SIZE(spl_boot_list))) {
puts("SPL: failed to boot from all boot devices\n");
hang();
}
boot_from_devices会去查找注册的boot接口。以ctc5236为例,会根据注册的flash和UART去引导uboot。
UART的引导入口在 spl_ymodem_ctc5236.c (common\spl)。主要代码如下:
static int ymodem_normal_boot_flow(struct spl_image_info *spl_image)
{
info.mode = xyzModem_ymodem;
res = xyzModem_stream_open(&info, &err);
NONZ_GOTO_END(res, end_stream);
/* skip spl image */
uboot_offset = CONFIG_SPL_PAD_TO;
while(uboot_offset>0)
{
res = xyzModem_stream_read(buf, BUF_SIZE, &err);
NOGT_GOTO_END(res, 0, end_stream);
uboot_offset -= BUF_SIZE;
}
/* get 64 bytes uboot image header and (BUF_SIZE-64) bytes data */
res = xyzModem_stream_read(buf, BUF_SIZE, &err);
NOGT_GOTO_END(res, 0, end_stream);
err = ctc5236_spl_parse_ih(spl_image, hdr);
NEGA_GOTO_END(err, end_stream);
……
}
(jump_to_image_no_args去启动u-boot) 到此SPL的生命周期结束。