当前位置: 首页 > news >正文

【精读Uboot】SPL阶段的board_init_r详细分析

对于i.MX平台上的SPL来说,其不会直接跳转到Uboot,而是在SPL阶段借助BOOTROM跳转到ATF,然后再通过ATF跳转到Uboot。

board_init_f会初始化设备相关的硬件,最后进入board_init_r为镜像跳转做准备。下面是board_init_r调用的核心函数流程,接下来我们会对其中的函数进行详细分析。

spl_board_init //board/freescale/imx93_evk/spl.c
board_boot_order(spl_boot_list)//spl_boot_device获取SPL启动设备 arch/arm/mach-imx/imx8/cpu.c
spl_boot_device//arch/arm/mach-imx/spl.c->spl_board_boot_device//board/freescale/imx93_evk/spl.c
spl_return_to_bootrom
board_return_to_bootrom//arch/arm/mach-imx/spl_imx_romapi.c

1、spl_board_init

对于i.MX93芯片来说,spl_board_init启动ELE了引擎。这里不对ELE进行详细分析。

//board/freescale/imx93_evk/spl.c
void spl_board_init(void)
{int ret;puts("Normal Boot\n");ret = ahab_start_rng();if (ret)printf("Fail to start RNG: %d\n", ret);
}

2、board_boot_order

spl_boot_device根据boot配置获取当前的启动设备。对于使用SCU的芯片需要特殊处理BOOT_DEVICE_SPI类型的设备。

//arch/arm/mach-imx/imx8/cpu.c,适用于93
void board_boot_order(u32 *spl_boot_list)
{spl_boot_list[0] = spl_boot_device();if (spl_boot_list[0] == BOOT_DEVICE_SPI) {/* Check whether we own the flexspi0, if not, use NOR boot */if (!sc_rm_is_resource_owned(-1, SC_R_FSPI_0))spl_boot_list[0] = BOOT_DEVICE_NOR;}
}

spl_board_boot_device函数内容可以看出,i.MX8以及i.MX9系列芯片的SPL跳转皆是由BOOTROM辅助实现的,因为这里直接返回了BOOT_DEVICE_BOOTROM

//arch/arm/mach-imx/spl.c
u32 spl_boot_device(void)
{enum boot_device boot_device_spl = get_boot_device();return spl_board_boot_device(boot_device_spl);
}int spl_board_boot_device(enum boot_device boot_dev_spl)
{
#ifdef CONFIG_SPL_BOOTROM_SUPPORTreturn BOOT_DEVICE_BOOTROM;
#elseswitch (boot_dev_spl) {case SD1_BOOT:case MMC1_BOOT:return BOOT_DEVICE_MMC1;case SD2_BOOT:case MMC2_BOOT:return BOOT_DEVICE_MMC2;default:return BOOT_DEVICE_NONE;}
#endif
}

3、spl_return_to_bootrom

由于上面返回的boot设备是BOOT_DEVICE_BOOTROM,因此这里各家定义的board_return_to_bootrom用于辅助跳转。

通过ROM API查询当前的启动设备和启动阶段,启动阶段可以分为Primary bootSecondary bootRecovery bootUSB boot,打印对应的启动阶段信息,最后使用ROM API将Uboot搬运到DDR的对应位置。

int board_return_to_bootrom(struct spl_image_info *spl_image,struct spl_boot_device *bootdev)
{volatile gd_t *pgd = gd;int ret;u32 boot, bstage;ret = g_rom_api->query_boot_infor(QUERY_BT_DEV, &boot,((uintptr_t)&boot) ^ QUERY_BT_DEV);ret |= g_rom_api->query_boot_infor(QUERY_BT_STAGE, &bstage,((uintptr_t)&bstage) ^ QUERY_BT_STAGE);set_gd(pgd);if (ret != ROM_API_OKAY) {puts("ROMAPI: failure at query_boot_info\n");return -1;}printf("Boot Stage: ");switch (bstage) {case BT_STAGE_PRIMARY:printf("Primary boot\n");break;case BT_STAGE_SECONDARY:printf("Secondary boot\n");break;case BT_STAGE_RECOVERY:printf("Recovery boot\n");break;case BT_STAGE_USB:printf("USB boot\n");break;default:printf("Unknow (0x%x)\n", bstage);}//USB下载模式if (is_boot_from_stream_device(boot))return spl_romapi_load_image_stream(spl_image, bootdev);return spl_romapi_load_image_seekable(spl_image, bootdev, boot);
}

4、spl_romapi_load_image_seekable

下面我们将分析ROM API是如何将Uboot搬运到指定位置的。

  1. 通过query_boot_infor查询IVT的偏移量、pagesize和image_offset。

  2. 获取header的位置

    header = (struct image_header *)(CONFIG_SPL_IMX_ROMAPI_LOADADDR);//0x48000000 内存地址
    
  3. 获取Uboot在MMC介质中的偏移量,将其存储在offset(0x41400)中。

    offset = spl_romapi_get_uboot_base(image_offset, rom_bt_dev);ulong spl_romapi_get_uboot_base(u32 image_offset, u32 rom_bt_dev)
    {ulong end;image_offset = spl_arch_boot_image_offset(image_offset, rom_bt_dev);end = get_imageset_end((void *)(ulong)image_offset, ROM_API_DEV);end = ROUND(end, SZ_1K);printf("Load image from 0x%lx by ROM_API\n", end);return end;
    }
    
  4. 使用download_image函数从MMC中的0x41400处下载header信息到DDR中的0x48000000处,后续需要对header里的信息进行判断(image_get_magic(header) == FDT_MAGIC)。这个header由mkimage_imx8.c写入。

    g_rom_api->download_image((u8 *)header, offset, size,((uintptr_t)header) ^ offset ^ size);
    
  5. 设置其他固件的信息,对于93/8ulp来说调用的是spl_load_imx_container函数,其余芯片为spl_load_simple_fit函数。spl_load_simple_fit这个函数会解析itb文件,获取里面的配置信息,填充spl_image_infospl_load_info中的信息,加载ATF做好跳转之前的准备。

5、spl_load_simple_fit

在进入之前,设置了load.readspl_romapi_read_seekable,然后spl_simple_fit_read会调用传入的read函数和上一节而最后读取到内存的header读取整个fit固件。这个是后续读取固件的核心函数。

if (IS_ENABLED(CONFIG_SPL_LOAD_FIT) && image_get_magic(header) == FDT_MAGIC) {struct spl_load_info load;memset(&load, 0, sizeof(load));load.bl_len = pagesize;load.read = spl_romapi_read_seekable;load.priv = &pagesize;return spl_load_simple_fit(spl_image, &load, offset / pagesize, header);
}

spl_load_simple_fit_fix_load使用ROM API读取itb到内存中,然后解析/configurations节点,其中的default配置名称和images的偏移量。

5.1、解析uboot

解析默认config(config-1)下面的firmware所指向的名称,这里解析出"uboot-1",然后返回出这个uboot节点在itb文件中的偏移量。一个config只有一个firmware,可以是uboot也可以是kernel,其余均为external数据。

spl_load_fit_image根据解析出的加载地址,将u-boot-nodtb.bin放到加载地址处。然后填充spl_image_info中的load_addr等信息。

#define FIT_FIRMWARE_PROP	"firmware"
node = spl_fit_get_image_node(&ctx, FIT_FIRMWARE_PROP, 0);ret = spl_load_fit_image(info, sector, &ctx, node, spl_image);

5.2、解析fdt

spl_fit_append_fdt首先也是解析出its中关于设备树的相关信息,然后使用fdt_overlay_apply_verbose->fdt_overlay_apply将its中需要overlay的部分覆盖进原始dtb中。

if (os_takes_devicetree(spl_image->os)) {ret = spl_fit_append_fdt(spl_image, info, sector, &ctx);
}

overlay dtb格式1:

/dts-v1/;
/plugin/;/ {fragment@0 {target-path = "/";__overlay__ {/*在此添加要插入的节点*/.......};};fragment@1 {target = <&XXXXX>;__overlay__ {/*在此添加要插入的节点*/.......};};.......};

overlay dtb格式2:

/dts-v1/;
/plugin/;&{/} {/*此处在根节点"/"下,添加要插入的节点或者属性*/
};&XXXXX {/*此处在节点"XXXXX"下,添加要插入的节点或者属性*/
};

5.3、解析loadables

和解析加载uboot类似,先找到its节点中的信息,然后根据这些信息将atf和tee放到指定位置。

for (; ; index++) {uint8_t os_type = IH_OS_INVALID;node = spl_fit_get_image_node(&ctx, "loadables", index);image_info.load_addr = 0;ret = spl_load_fit_image(info, sector, &ctx, node, &image_info);/* Record our loadables into the FDT */if (spl_image->fdt_addr)spl_fit_record_loadable(&ctx, index,spl_image->fdt_addr,&image_info);}

如果firmware属性中未定义entry值,那么将第一个loadables的entry作为跳转入口(spl_image->entry_point)。从its中我们可以知道,第一个loadables就是atf。

6、跳转至ATF

直接跳转进spl_image->entry_point所定义的地址,也就是进入ATF中。

arch/arm/mach-imx/spl.c
/** +------------+  0x0 (DDR_UIMAGE_START) -* |   Header   |                          |* +------------+  0x40                    |* |            |                          |* |            |                          |* |            |                          |* |            |                          |* | Image Data |                          |* .            |                          |* .            |                           > Stuff to be authenticated ----+* .            |                          |                                |* |            |                          |                                |* |            |                          |                                |* +------------+                          |                                |* |            |                          |                                |* | Fill Data  |                          |                                |* |            |                          |                                |* +------------+ Align to ALIGN_SIZE      |                                |* |    IVT     |                          |                                |* +------------+ + IVT_SIZE              -                                 |* |            |                                                           |* |  CSF DATA  | <---------------------------------------------------------+* |            |* +------------+* |            |* | Fill Data  |* |            |* +------------+ + CSF_PAD_SIZE*/__weak void __noreturn jump_to_image_no_args(struct spl_image_info *spl_image)
{typedef void __noreturn (*image_entry_noargs_t)(void);uint32_t offset;image_entry_noargs_t image_entry =(image_entry_noargs_t)(unsigned long)spl_image->entry_point;debug("image entry point: 0x%lX\n", spl_image->entry_point);if (spl_image->flags & SPL_FIT_FOUND) {image_entry();} else {/** HAB looks for the CSF at the end of the authenticated* data therefore, we need to subtract the size of the* CSF from the actual filesize*/offset = spl_image->size - CONFIG_CSF_SIZE;if (!imx_hab_authenticate_image(spl_image->load_addr,offset + IVT_SIZE +CSF_PAD_SIZE, offset)) {image_entry();} else {panic("spl: ERROR:  image authentication fail\n");}}
}

附录1:its表

/dts-v1/;/ {description = "Configuration to load ATF before U-Boot";#address-cells = <1>;images {uboot-1 {description = "U-Boot (64-bit)";data = /incbin/("u-boot-nodtb.bin");type = "standalone";arch = "arm64";compression = "none";load = <0x40200000>;};fdt-1 {description = "evk";data = /incbin/("evk.dtb");type = "flat_dt";compression = "none";};atf-1 {description = "ARM Trusted Firmware";data = /incbin/("bl31.bin");type = "firmware";arch = "arm64";compression = "none";load = <0x00970000>;entry = <0x00970000>;};tee-1 {description = "TEE firmware";data = /incbin/("tee.bin");type = "firmware";arch = "arm64";compression = "none";load = <0x56000000>;entry = <0x56000000>;};};configurations {default = "config-1";config-1 {description = "evk";firmware = "uboot-1";loadables = "atf-1", "tee-1";fdt = "fdt-1";};};
};

附录2:启动log

i.MX93

U-Boot SPL 2022.04-lf_v2022.04+g1734965341 (Jun 30 2023 - 10:23:49 +0000)
SOC: 0xa0009300
LC: 0x40010
M33 prepare ok
>>SPL: board_init_r()
spl_init
Normal Boot
Trying to boot from BOOTROM
Boot Stage: Primary boot
image offset 0x0, pagesize 0x200, ivt offset 0x0 
Load image from 0x41400 by ROM_API 
Unsupported OS image.. Jumping nevertheless..
image entry point: 0x204e0000

i.MX8MP

U-Boot SPL 2023.04-lf_v2023.04+gaf7d004eaf (Aug 14 2023 - 03:48:45 +0000)
DDRINFO: start DRAM init
DDRINFO: DRAM rate 4000MTS
DDRINFO:ddrphy calibration done
DDRINFO: ddrmix config done
>>SPL: board_init_r()
spl_init
SEC0:  RNG instantiated
Normal Boot
Trying to boot from BOOTROM
Boot Stage: Primary boot
image offset 0x0, pagesize 0x200, ivt offset 0x0
Jumping to U-Boot...
image entry point: 0x970000
http://www.lryc.cn/news/159679.html

相关文章:

  • canvas绘制渐变色三角形金字塔
  • 企业电子招标采购系统源码Spring Boot + Mybatis + Redis + Layui + 前后端分离 构建企业电子招采平台之立项流程图
  • Debain JDK8 安装
  • Python序列操作指南:列表、字符串和元组的基本用法和操作
  • 【已更新代码图表】2023数学建模国赛E题python代码--黄河水沙监测数据分析
  • 【前端】CSS-Grid网格布局
  • 计算机竞赛 基于深度学习的动物识别 - 卷积神经网络 机器视觉 图像识别
  • 2023-9-8 求组合数(二)
  • k8s service的一些特性
  • C++中std::enable_if和SFINAE介绍
  • 华为OD机考算法题:数字加减游戏
  • WPF命令
  • Unity中Shader的屏幕抓取 GrabPass
  • 手撕 队列
  • 【autodl/linux配环境心得:conda/本地配cuda,cudnn及pytorch心得】-未完成
  • macOS Ventura 13.5.2(22G91)发布,附黑/白苹果镜像下载地址
  • vue 子组件向父组件传递参数 子传父
  • 自然语言处理学习笔记(八)———— 准确率
  • Matlab 如何选择窗函数和 FFT 的长度
  • node.js下载安装环境配置以及快速使用
  • 使用栈检查括号的合法性 C 实现
  • 小白备战大厂算法笔试(四)——哈希表
  • 云原生Kubernetes:pod基础
  • Ansys Zemax | 手机镜头设计 - 第 3 部分:使用 STAR 模块和 ZOS-API 进行 STOP 分析
  • CSP-J初赛复习大题整理笔记
  • 面试题 ⑤
  • 硅谷课堂1
  • 第6节-PhotoShop基础课程-认识选区
  • SQLServer如何获取客户端IP
  • 爬虫数据清洗可视化实战-就业形势分析