首先下载U-Boot源码,然后查看有的配置文件,我们这里使用configs/imx6ul_isiot_nand_defconfig文件作为配置文件使用,然后使用如下命令进行编译,
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- distclean
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- imx6ul_isiot_emmc_defconfig
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- -j8
编译完成后u-boot-with-spl.imx即为我们的文件
然后通过烧录工具mfgtools进行烧录,完蛋,发现NAND版本的根本就无法启动。EMMC正常启动到U-Boot。 然后打开debug打印,打开方式如下。
diff --git a/u-boot/include/log.h b/u-boot/include/log.h
index e0e12ce19..8c6f98af7 100644
--- a/u-boot/include/log.h
+++ b/u-boot/include/log.h
@@ -240,7 +240,7 @@ int _log_buffer(enum log_category_t cat, enum log_level_t level,
_log_nop((enum log_category_t)(_cat), _l, __FILE__, __LINE__, \
__func__, pr_fmt(_fmt),
})
-
+
重新运行,可以看到,应该是没有正确读取到对应的U-Boot镜像,对比EMMC启动,相差点打印信息。
oobsize=64
chipsize=d
in func nand_spl_load_image : offset:0x00200000 len:64 page:400
else start spl_parse_image_header
mkimage signature not found - ih_magic = ffffffff
in last do nand_spl_load_image
in func nand_spl_load_image : offset:0x00200000 len:204800 page:400
in func is_badblock: offs=0x00220000 block:17 page:1088
zxy Jumping to U-Boot...
in func board_init_r do start spl_board_prepare_for_boot
in __weak void spl_board_prepare_for_boot
zxy image entry point: 0x87800000
然后就开始追踪看下代码处理过程,主要执行的函数如下所示,在文件common/spl/spl.c中执行。
void board_init_r(gd_t *dummy1, ulong dummy2)
{
u32 spl_boot_list[] = {
BOOT_DEVICE_NONE,
BOOT_DEVICE_NONE,
BOOT_DEVICE_NONE,
BOOT_DEVICE_NONE,
BOOT_DEVICE_NONE,
};
struct spl_image_info spl_image;
int ret;
debug("zxy >>" SPL_TPL_PROMPT "board_init_r()\n");
spl_set_bd();
mem_malloc_init(CONFIG_SYS_SPL_MALLOC_START,
CONFIG_SYS_SPL_MALLOC_SIZE);
gd->flags |= GD_FLG_FULL_MALLOC_INIT;
if (!(gd->flags & GD_FLG_SPL_INIT)) {
if (spl_init())
hang();
}
/*
* timer_init() does not exist on PPC systems. The timer is initialized
* and enabled (decrementer) in interrupt_init() here.
*/
timer_init();
if (CONFIG_IS_ENABLED(BLOBLIST)) {
ret = bloblist_init();
if (ret) {
debug("%s: Failed to set up bloblist: ret=%d\n",
__func__, ret);
puts(SPL_TPL_PROMPT "Cannot set up bloblist\n");
hang();
}
}
if (CONFIG_IS_ENABLED(HANDOFF)) {
int ret;
ret = setup_spl_handoff();
if (ret) {
puts(SPL_TPL_PROMPT "Cannot set up SPL handoff\n");
hang();
}
}
spl_board_init();
initr_watchdog();
if (IS_ENABLED(CONFIG_SPL_OS_BOOT) || CONFIG_IS_ENABLED(HANDOFF) ||
IS_ENABLED(CONFIG_SPL_ATF))
dram_init_banksize();
bootcount_inc();
memset(&spl_image, '\0', sizeof(spl_image));
spl_image.arg = (void *)CONFIG_SYS_SPL_ARGS_ADDR;
printf("spl_image.arg = 0x%08x",spl_image.arg);
spl_image.boot_device = BOOT_DEVICE_NONE;
board_boot_order(spl_boot_list);
ret = boot_from_devices(&spl_image, spl_boot_list,
ARRAY_SIZE(spl_boot_list));
if (ret) {
if (CONFIG_IS_ENABLED(SHOW_ERRORS) &&
CONFIG_IS_ENABLED(LIBCOMMON_SUPPORT))
printf(SPL_TPL_PROMPT "failed to boot from all boot devices (err=%d)\n",
ret);
else
puts(SPL_TPL_PROMPT "failed to boot from all boot devices\n");
hang();
}
spl_perform_fixups(&spl_image);
if (CONFIG_IS_ENABLED(HANDOFF)) {
ret = write_spl_handoff();
if (ret)
printf(SPL_TPL_PROMPT
"SPL hand-off write failed (err=%d)\n", ret);
}
if (CONFIG_IS_ENABLED(BLOBLIST)) {
ret = bloblist_finish();
if (ret)
printf("Warning: Failed to finish bloblist (ret=%d)\n",
ret);
}
spl_image.entry_point |= 0x1;
switch (spl_image.os) {
case IH_OS_U_BOOT:
debug("zxy Jumping to %s...\n", spl_phase_name(spl_next_phase()));
break;
case IH_OS_ARM_TRUSTED_FIRMWARE:
debug("Jumping to U-Boot via ARM Trusted Firmware\n");
spl_fixup_fdt(spl_image.fdt_addr);
spl_invoke_atf(&spl_image);
break;
case IH_OS_TEE:
debug("Jumping to U-Boot via OP-TEE\n");
spl_board_prepare_for_optee(spl_image.fdt_addr);
jump_to_image_optee(&spl_image);
break;
case IH_OS_OPENSBI:
debug("Jumping to U-Boot via RISC-V OpenSBI\n");
spl_invoke_opensbi(&spl_image);
break;
case IH_OS_LINUX:
debug("Jumping to Linux\n");
spl_fixup_fdt((void *)CONFIG_SYS_SPL_ARGS_ADDR);
spl_board_prepare_for_linux();
jump_to_image_linux(&spl_image);
default:
debug("Unsupported OS image.. Jumping nevertheless..\n");
}
debug("SPL malloc() used 0x%lx bytes (%ld KB)\n", gd->malloc_ptr,
gd->malloc_ptr / 1024);
bootstage_mark_name(get_bootstage_id(false), "end phase");
ret = bootstage_stash((void *)CONFIG_BOOTSTAGE_STASH_ADDR,
CONFIG_BOOTSTAGE_STASH_SIZE);
if (ret)
debug("Failed to stash bootstage: err=%d\n", ret);
printf("in func %s do start spl_board_prepare_for_boot\n",__func__);
spl_board_prepare_for_boot();
jump_to_image_no_args(&spl_image);
}
针对以上,梳理下执行流程,因为我们是无法跳转到U-Boot执行,所以我们重点是去看SPL阶段,是如何去加载NAND中的U-Boot镜像文件的。 最重要的数据就是结构体变量struct spl_image_info spl_image;,所以我们只需要追踪下这个数据的来源,以及赋值即可。
结构体定义如下
struct spl_image_info {
const char *name;
u8 os;
uintptr_t load_addr;
uintptr_t entry_point;
#if CONFIG_IS_ENABLED(LOAD_FIT) || CONFIG_IS_ENABLED(LOAD_FIT_FULL)
void *fdt_addr;
#endif
u32 boot_device;
u32 offset;
u32 size;
u32 flags;
void *arg;
#ifdef CONFIG_SPL_LEGACY_IMAGE_CRC_CHECK
ulong dcrc_data;
ulong dcrc_length;
ulong dcrc;
#endif
};
memset(&spl_image, '\0', sizeof(spl_image));
spl_image.boot_device = BOOT_DEVICE_NONE;
board_boot_order(spl_boot_list);
__weak void board_boot_order(u32 *spl_boot_list)
{
spl_boot_list[0] = spl_boot_device();
}
ret = boot_from_devices(&spl_image, spl_boot_list,
ARRAY_SIZE(spl_boot_list));
static int boot_from_devices(struct spl_image_info *spl_image,
u32 spl_boot_list[], int count)
{
int ret = -ENODEV;
int i;
for (i = 0; i < count && spl_boot_list[i] != BOOT_DEVICE_NONE; i++) {
struct spl_image_loader *loader;
int bootdev = spl_boot_list[i];
loader = spl_ll_find_loader(bootdev);
if (CONFIG_IS_ENABLED(SERIAL) &&
CONFIG_IS_ENABLED(LIBCOMMON_SUPPORT) &&
!IS_ENABLED(CONFIG_SILENT_CONSOLE)) {
if (loader)
printf("Trying to boot from %s\n",
spl_loader_name(loader));
else if (CONFIG_IS_ENABLED(SHOW_ERRORS))
printf(SPL_TPL_PROMPT
"Unsupported Boot Device %d\n", bootdev);
else
puts(SPL_TPL_PROMPT "Unsupported Boot Device!\n");
}
if (loader && !spl_load_image(spl_image, loader)) {
spl_image->boot_device = bootdev;
return 0;
}
}
return ret;
}
我们接着看一下函数spl_ll_find_loader
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);
struct spl_image_loader *entry;
for (entry = drv; entry != drv + n_ents; entry++) {
if (boot_device == entry->boot_device)
return entry;
}
return NULL;
}
static char start[0] __aligned(CONFIG_LINKER_LIST_ALIGN)__attribute__((unused))__section(".u_boot_list_2_spl_image_loader_1");
(spl_image_loader *)&start;
可以通过查看u-boot-spl.map文件查找到对应的段内容
其实就是找到两个值的变化值,
#define ll_entry_count(_type, _list) \
({ \
_type *start = ll_entry_start(_type, _list); \
_type *end = ll_entry_end(_type, _list); \
unsigned int _ll_result = end - start; \
_ll_result; \
})
所以再来看如下的函数,其实就好分析了。
#ifdef CONFIG_SPL_LIBCOMMON_SUPPORT
#define SPL_LOAD_IMAGE_METHOD(_name, _priority, _boot_device, _method) \
SPL_LOAD_IMAGE(_boot_device ## _priority ## _method) = { \
.name = _name, \
.boot_device = _boot_device, \
.load_image = _method, \
}
那么我们在common/spl/spl_nand.c中定义的宏SPL_LOAD_IMAGE_METHOD("NAND", 1, BOOT_DEVICE_NAND, spl_nand_load_image);,展开后就是
#define SPL_LOAD_IMAGE(__name) \
ll_entry_declare(struct spl_image_loader, __name, spl_image_loader)
#define ll_entry_declare(_type, _name, _list) \
_type _u_boot_list_2_##_list##_2_##_name __aligned(4) \
__attribute__((unused)) \
__section(".u_boot_list_2_"#_list"_2_"#_name)
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);
struct spl_image_loader *entry;
for (entry = drv; entry != drv + n_ents; entry++) {
if (boot_device == entry->boot_device)
return entry;
}
SPL_LOAD_IMAGE_METHOD("NAND", 1, BOOT_DEVICE_NAND, spl_nand_load_image);
entry->name = "NAND"
entry->boot_device = BOOT_DEVICE_NAND
entry->load_image = spl_nand_load_image;后面就是操作这个函数。
return NULL;
}
struct spl_image_loader {
#ifdef CONFIG_SPL_LIBCOMMON_SUPPORT
const char *name;
#endif
uint boot_device;
int (*load_image)(struct spl_image_info *spl_image,
struct spl_boot_device *bootdev);
};
然后执行函数
static inline const char *spl_loader_name(const struct spl_image_loader *loader)
{
#ifdef CONFIG_SPL_LIBCOMMON_SUPPORT
return loader->name;
#else
return NULL;
#endif
}
其实就是返回一个字符串
|