定制 SoC 的 U-Boot SPL image type 类型
开发板 SoC 系统上电启动时,从 boot rom
开始运行、并加载后续程序;被加载的通常是 U-Boot SPL
,它运行并加载其他后续的程序。按先后顺序,一般是这样的:boot rom --> U-Boot SPL --> sbi --> U-Boot --> Linux Kernel --> ..
其中,U-Boot SPL
是 Secondary Program Loader 的缩写,这个名字很明确地说明了它的启动时机。当然,除了最开始的 boot rom
是固化的,从第二步开始,只要符合 SoC 的设计约定,可以替换成其他程序。
是否需要 SPL 自定义类型
对于新研发的 SoC 来说,需要实现 SPL 对板子的支持。
如果通用的代码基本能满足 SoC 板子的要求,那么对于为数不多的额外设置,可以在 spl_board_init_f
等处添加适当的回调,满足 boot rom 以及其他的要求即可。
但如果板子相关要求较多,配置复杂,或者其他一些实际的需求,比如 boot rom 需要验证后续程序是否符合要求,等等,那还是需要自定义打包类型,从而把相关功能以及实现代码归拢在一起,保持 U-Boot 代码结构,方便项目的开发和维护。以下记录了为 SoC 定制 SPL 镜像类型的过程。U-Boot 源码基于 2021-10 版本。
Image Types
镜像程序的类型定义。除了多种标准类型外,还包括了很多厂商自定义的类型。所以需要这样修改,大概是这样的:
文件:include/image.h
IH_TYPE_SOCFPGAIMAGE_V1, /* Altera SOCFPGA A10 Preloader */
IH_TYPE_MTKIMAGE, /* MediaTek BootROM loadable Image */
IH_TYPE_IMX8MIMAGE, /* Freescale IMX8MBoot Image */
IH_TYPE_IMX8IMAGE, /* Freescale IMX8Boot Image */
IH_TYPE_COPRO, /* Coprocessor Image for remoteproc*/
IH_TYPE_SUNXI_EGON, /* Allwinner eGON Boot Image */
+ IH_TYPE_XXX, /* XXX Boot Image */
IH_TYPE_COUNT, /* Number of image types */
};
/*
* Compression Types
表格里类型相关的入口
类型相关的名字定义,用于查找和调试打印。是这样定义的:
/*
* Translation table for entries of a specific type; used by
* get_table_entry_id() and get_table_entry_name().
*/
typedef struct table_entry {
int id;
char *sname; /* short (input) name to find table entry */
char *lname; /* long (output) name to print for messages */
} table_entry_t;
所以大概这样修改:
文件:common/image.c
{ IH_TYPE_FIRMWARE_IVT, "firmware_ivt", "Firmware with HABv4 IVT" },
{ IH_TYPE_PMMC, "pmmc", "TI Power Management Micro-Controller Firmware",},
{ IH_TYPE_STM32IMAGE, "stm32image", "STMicroelectronics STM32 Image" },
{ IH_TYPE_MTKIMAGE, "mtk_image", "MediaTek BootROM loadable Image" },
{ IH_TYPE_COPRO, "copro", "Coprocessor Image"},
{ IH_TYPE_SUNXI_EGON, "sunxi_egon", "Allwinner eGON Boot Image" },
+ { IH_TYPE_XXX, "xxx", "XXX Boot Image" },
{ -1, "", "", },
};
static const table_entry_t uimage_comp[] = {
{ IH_COMP_NONE, "none", "uncompressed", },
{ IH_COMP_BZIP2, "bzip2", "bzip2 compressed", },
打包镜像文件的实现
镜像文件的打包格式需要按照 SoC 里 boot rom 的要求来实现,包括各种参数的设置、验证,等等。
按照 U-Boot 的开发格式,代码文件放在这里:tools/xxx.c。代码就不贴了,这里说一下大概的实现要求和过程:
定义文件头格式:对于自定义的镜像格式,定义文件头是有必要的。其中需要包括版本信息、实际文件大小、CRC 校验值、加载目标地址、硬件相关的设置参数,等等。当然,还有一些本身的特殊标志值。这些都是 boot rom
在加载时所需要知道的。
定义一些回调函数:
- 检查文件头是否完整:
xxx_verify_header
; - 打印文件头以及镜像文件相关信息:
xxx_print_header
; - 设置文件头:
xxx_set_header
。包括计算并设置每一个字段。有一个有说特别说一下,Boot rom 在跳转过来以后,需要知道从哪里开始运行。考虑到文件头在镜像文件起始的位置,一种做法是文件头里指明运行起始地址;也可以不指明,从默认起始地址开始,这时此处需要硬编码一条跳转指令,跳过文件头,打包程序需要实现这个逻辑。 - 检查文件头类型:
xxx_check_image_type
,确保加载镜像文件符合规范; - 其他 ……
把这些信息统一注册(具体的结构格式,可以进一步参考 struct image_type_params
的定义,在文件 tools/imagetool.h
里):
U_BOOT_IMAGE_TYPE(
XXX,
"XXXBoot Image",
sizeof(struct xxx_file_head),
NULL,
xxx_check_params,
xxx_verify_header,
xxx_print_header,
xxx_set_header,
NULL,
xxx_check_image_type,
NULL,
xxx_vrec_header
);
并修改文件 tools/Makefile,加入编译:
# common objs for dumpimage and mkimage
dumpimage-mkimage-objs := aisimage.o \
...
...
$(ROCKCHIP_OBS) \
socfpgaimage.o \
sunxi_egon.o \
+ xxx.o \
lib/crc16.o \
lib/hash-checksum.o \
lib/sha1.o \
生成镜像文件
有了打包工具,接下来是使用。
修改文件:scripts/Makefile.spl
ifdef CONFIG_TARGET_XXX
INPUTS-y += $(obj)/u-boot-spl-xxx.bin
endif
MKIMAGEFLAGS_u-boot-spl-xxx.bin = -T xxx -e $(CONFIG_SPL_TEXT_BASE)
$(obj)/u-boot-spl-xxx.bin: $(obj)/u-boot-spl.bin FORCE
$(call if_changed,mkimage)
如果把这里的依赖关系展开来看,实际调用了 ./tools/mkimage
打包工具,通过 -T
选项指定新实现的类型,以及 -e
选项指定代码入口。这样,当启用 CONFIG_TARGET_XXX
时,在编译生成默认的 u-boot-spl.bin
后,会进一步打包并生成成 u-boot-spl-xxx.bin
。
这两个文件的关系: file head
+ u-boot-spl.bin
=> u-boot-spl-xxx.bin
,即:
+-------------------+ -
| file head ... ... | \
+-------------------+ \
| | \
| | >>> u-boot-spl-xxx.bin
| u-boot-spl.bin | /
| | /
| | /
+-------------------+ -