RISC-V spl/OpenSBI/u-boot/Linux启动流程交互节点
OpenSBI给出的fw_dynamic镜像启动建议流程:
实际详细流程如下:
1 SPL到OpenSBI
spl启动流程如下:
- 从外设加载镜像到DDR中。
- 解析镜像格式,比如FIT。
- 解析FIT中OpenSBI镜像地址和入口地址;uboot镜像地址,并附着到fdt中;解析获取fdt地址。
- 准备好HART ID、fdt地址、struct fw_dynamic_info结构体三个参数后,跳转到OpenSBI镜像开始执行。
_start
wait_for_gd_init
board_init_f
spl_early_init
spl_stack_gd_setup
spl_call_board_init_r
board_init_r
spl_invoke_opensbi--如果镜像类型是IH_OS_OPENSBI则启动OpenSBI。
spl_opensbi_find_uboot_node--在设备树中查找U-Boot节点,包含了U-Boot镜像的配置信息。这些信息从FIT镜像中解析,append到fdt_addr中。
fit_image_get_entry--获取U-Boot的entry地址,如果没有找到则使用load地址。
fit_image_get_load--获取U-Boot的load地址。
--填充opensbi_info结构体,这个结构体包含了传递给OpenSBI的信息,如魔术数字、版本、下一阶段地址、模式、选项和首选启动HART ID。
invalidate_icache_all--使缓存无效,确保调用的是最新的代码。
--通过转换spl_image->entry_point为函数指针并调用它,将控制权交给 OpenSBI。
opensbi_entry(gd->arch.boot_hart, (ulong)spl_image->fdt_addr,(ulong)&opensbi_info);--第一个参数HART ID,第二个参数FDT地址,第三个参数struct fw_dynamic_info。
struct spl_image_info 结构体用于存储有关引导过程中使用的图像(通常是内核或引导程序的一部分)的信息。这个结构体在 U-Boot 的 SPL(Second Program Loader,第二阶段引导加载程序)中定义,用于传递图像的加载和入口信息。
struct spl_image_info { const char *name;--表示镜像的名称。 u8 os; uintptr_t load_addr;--OpenSBI加载到内存中的地址。 uintptr_t entry_point;--OpenSBI的入口点地址,通常是代码的第一条指令的地址。 #if CONFIG_IS_ENABLED(LOAD_FIT) || CONFIG_IS_ENABLED(LOAD_FIT_FULL) void *fdt_addr;--用于FDT的地址。 #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 };
struct fw_dynamic_info 结构体是 OpenSBI 规范中定义的一个结构体,用于在引导加载程序(如 U-Boot)和 OpenSBI 之间传递动态信息。这个结构体通常在系统启动的早期阶段被填充,并由引导加载程序传递给 OpenSBI。
struct fw_dynamic_info { unsigned long magic;--用于标识数据结构的版本和兼容性。这有助于确保 OpenSBI 能够识别和处理传入的信息。 unsigned long version;--结构体的版本号,用于确保 OpenSBI 能够理解结构体中包含的信息。 unsigned long next_addr;--下一阶段引导程序或操作系统的入口地址。这个地址指示 OpenSBI 在初始化完成后跳转到的位置。 unsigned long next_mode;--指示下一阶段引导程序或操作系统的运行模式。比如OpenSBI之后是u-boot,运行于S Mode。 unsigned long options;--提供给OpenSBI库的选项。 unsigned long boot_hart;--首选的引导HART(硬件线程)ID。在多HART系统中,这个字段指定了哪一个HART应该跳转到OpenSBI。 } __packed;
OpenSBI启动时a0存放启动HART ID、a1存放fdt地址、a2存放struct fw_dynamic_info结构体地址:
__start
fw_boot_hart--从a2寄存器指向的struct fw_dynamic_info中获取boot_hart。
fw_save_info--将next_addr、next_mode、options、boot_hart分别保存到_dynamic_next_addr、_dynamic_next_mode、_dynamic_options、_dynamic_boot_hard中。
fw_platform_init--共5个参数:a0-boot HART, a1-FDT地址。
_scratch_init--获取fw_save_info保存的变量,填入struct sbi_scratch的next_addr、next_mode、options。并将FDT地址填入next_arg1中。
2 OpenSBI到u-boot
结合《OpenSBI主要启动流程解析》,在sbi_hart_switch_mode时跳转到u-boot:
sbi_hsm_hart_start_finish--hardid作为第一参数,从struct sbi_scratch中获取next_arg1、next_addr、next_mode作为第2-4参数启动下一级镜像。
sbi_hart_switch_mode
--判断下一级镜像Mode,仅支持S和U。
--配置next_mode到CSR_MSTATUS、next_addr配置到CSR_STVEC中。
--调用mret指令,hardid和next_arg1作为第1-2参数启动u-boot。
在u-boot起始阶段,将hartid和next_arg1保存到gd_t中:
_start mv tp, a0 mv s1, a1 SREG s1, GD_FIRMWARE_FDT_ADDR(gp) /* save the boot hart id to global_data */ SREG tp, GD_BOOT_HART(gp)
3 u-boot到Linux
uboot启动Linux:
bo_bootm_linux
boot_jump_linux
kernel--使用images->ep地址,以gd->arch.boot_hart和images->ft_addr作为第1-2参数。
RISC-V kernel启动时保存a0/a1寄存器到s0/s1:
_start
_start_kernel
mv s0, a0
mv s1, a1
setup_vm--以DTB物理地址为参数,配置页表映射。
联系方式:arnoldlu@qq.com