SPL-TPL 链式启动:spl.c 与CONFIG_TPL 三阶段加载

复杂 SoC(如 TI K3、NXP i.MX8)ROM 只能载入极小第一级 loader,U-Boot 用 TPL → SPL → Proper 三级镜像逐级初始化 DRAM 与外设。common/spl/spl.c 是 SPL 核心:spl_early_init()、board_init_r()、spl_load_image() 按 spl_image_loader 注册表加载下一级,jump_to_image() 跳转到 U-Boot 或 Linux。与单级 SPL 相比,链式启动把 DDR training、PMIC、时钟树拆到 TPL,减轻 SPL 体积限制。

源码锚点

路径 关键符号 作用
common/spl/spl.c board_init_r() SPL 主逻辑
common/spl/spl.c spl_load_image() 加载下一级镜像
common/spl/spl.c jump_to_image() 跳转 Proper/内核
common/spl/spl.c spl_parse_image_header() 解析 mkimage
include/spl.h struct spl_image_info 加载结果
common/spl/spl_fat.c spl_load_image_fat() 从 FAT 加载
tpl/arch/... TPL 入口 第一级 C 代码

SPL board_init_r 骨架:

c 复制代码
/* common/spl/spl.c */
void board_init_r(gd_t *dummy1, ulong dummy2)
{
	u32 spl_boot_list[] = {
		BOOT_DEVICE_MMC1,
		BOOT_DEVICE_MMC2,
		BOOT_DEVICE_UART,
		...
	};

	if (IS_ENABLED(CONFIG_SPL_SYS_MALLOC_SIMPLE))
		mem_malloc_init(SPL_MALLOC_BASE, SPL_MALLOC_SIZE);

	if (IS_ENABLED(CONFIG_SPL_DM))
		spl_dm_init();

	if (CONFIG_IS_ENABLED(LOAD_FIT)) {
		ret = spl_load_image(spl_boot_list, count, &spl_image);
	} else {
		ret = spl_load_image(spl_boot_list, count, &spl_image);
	}

	switch (spl_image.os) {
	case IH_OS_U_BOOT:
		jump_to_image(&spl_image);
		break;
	case IH_OS_LINUX:
		spl_board_prepare_for_boot();
		jump_to_image(&spl_image);
		break;
	default:
		hang();
	}
}

加载器查找:

c 复制代码
/* common/spl/spl.c */
static struct spl_image_loader *spl_ll_find_loader(uint boot_device)
{
	struct spl_image_loader *drv =
		ll_entry_start(struct spl_image_loader, spl_image_loader);
	const int n_ents = ll_entry_count(struct spl_image_loader,
					  spl_image_loader);

	for (; drv != drv + n_ents; drv++) {
		if (drv->boot_device == boot_device)
			return drv;
	}
	return NULL;
}

jump_to_image:

c 复制代码
/* common/spl/spl.c */
__weak void __noreturn jump_to_image(struct spl_image_info *spl_image)
{
	typedef void __noreturn (*image_entry_t)(ulong, ulong, ulong);
	image_entry_t entry = (image_entry_t)spl_image->entry_point;

	debug("Jumping to image at %lx\n", spl_image->entry_point);
	cleanup_before_linux();
	entry(0, 0, 0);
}

TPL 跳 SPL(概念流程):

c 复制代码
/* tpl 完成 DDR 后 */
spl_image.entry_point = CONFIG_SPL_TEXT_BASE;
memcpy((void *)load_addr, u_boot_spl, spl_size);
/* 跳转 SPL reset vector */

调用链

复制代码
BootROM
  └── 加载 TPL @ SRAM
        └── board_init_f (TPL)
              └── 初始化最小时钟 + DDR
                    └── spl_load_image → 加载 SPL 到 SRAM/DDR
                          └── jump_to_image → SPL

SPL board_init_r
  ├── spl_early_init / spl_board_init
  ├── spl_load_image(spl_boot_list)
  │     ├── spl_mmc_load() / spl_fat_load() / spl_spi_load()
  │     └── spl_parse_image_header → spl_image.entry_point
  └── jump_to_image
        └── U-Boot Proper board_init_f
              或 Falcon: spl_direct_jump_linux()

重点知识

CONFIG_TPL vs CONFIG_SPL :Kconfig 独立开关;TPL 镜像更小、无完整 DM;SPL 可含 CONFIG_SPL_LIBCOMMON_SUPPORT、FAT、USB。三者 defconfig 各生成 u-boot-tpl.bin、u-boot-spl.bin、u-boot.bin。

spl_boot_list 顺序 :板级 spl_boot_device() 或 eFUSE/strap 决定优先介质;失败则尝试下一项。量产常固定 eMMC,开发加 UART/YMODEM。

SPL_FIT / SPL_LEGACY :CONFIG_SPL_LOAD_FIT 从 FIT 读 u-boot 子镜像;Legacy 裸二进制需 CONFIG_SPL_PAYLOAD 指定链接地址。

SPL size 限制 :SRAM 128KB 典型;用 CONFIG_SPL_MAX_SIZE 与 u-boot-spl.map 检查。TPL 更小,DDR 代码必须进 SPL。

SPL_OS_BOOT :SPL 直接加载内核(Falcon)跳过 Proper;与完整 U-Boot 的 booti 能力互斥配置。

Handoff 数据 :spl_handoff 或 ATF BL31 传递 DRAM 参数;K3 等平台 SPL 先起 ATF 再起 U-Boot。

Checklist

  • 确认 ROM 支持的镜像格式与加载地址(Reference Manual)
  • TPL/SPL/Proper 各自 TEXT_BASE 不重叠
  • spl_boot_list 与硬件 boot pin 一致
  • FIT 中 u-boot、spl 节点 load/entry 正确
  • CONFIG_SPL_STACK 足够 DDR init 调用栈
  • 串口 log 区分 TPL/SPL 阶段 banner
  • 测量各阶段耗时(bootstage 在 SPL 可用性)
  • OTA 更新需同时升级 tpl/spl/proper 还是仅 spl+proper
相关推荐
努力实干4 分钟前
第二阶段 · 第 7 课:C++ 多线程与线程安全
开发语言·c++·安全
Navigator_Z4 分钟前
LeetCode //C - 1309. Decrypt String from Alphabet to Integer Mapping
c语言·算法·leetcode
爱聊灵光的维C27 分钟前
C语言初阶:关于函数设计的详细讲解和注意事项
c语言·开发语言·青少年编程·蓝桥杯·学习方法
熊猫钓鱼>_>34 分钟前
上架审核常见驳回与隐私合规清单:过审前先自查这 8 条【鸿蒙心迹】
开发语言·前端·华为·华为云·harmonyos·openharmony·ai制作
SEO_juper43 分钟前
Bash 交叉验证 robots.txt 和 sitemap:两个文件自相矛盾,页面永远收录不了
开发语言·python·node.js·json·bash
geovindu1 小时前
rust: Read-Write Lock Pattern
开发语言·后端·设计模式·rust·读写锁模式·同步型模式
怪我冷i1 小时前
Rust智能指针Rc<T> 使用总结
开发语言·后端·rust
全栈陈序员1 小时前
【Python】基础语法入门(三十三)—— 日志系统详解:用 logging 记录程序运行
java·开发语言·python
ESDWAN1 小时前
SD-WAN 是什么?工作原理、适用场景与选型要点
开发语言·网络·数据库·架构·php
iCxhust1 小时前
C#双时区时钟设计
开发语言·c#