| // SPDX-License-Identifier: GPL-2.0+ | |
| /* | |
| * Copyright (C) 2019 Fraunhofer AISEC, | |
| * Lukas Auer <lukas.auer@aisec.fraunhofer.de> | |
| * | |
| * Based on common/spl/spl_atf.c | |
| */ | |
| #include <common.h> | |
| #include <cpu_func.h> | |
| #include <errno.h> | |
| #include <hang.h> | |
| #include <image.h> | |
| #include <spl.h> | |
| #include <asm/global_data.h> | |
| #include <asm/smp.h> | |
| #include <opensbi.h> | |
| #include <linux/libfdt.h> | |
| #include <linux/printk.h> | |
| DECLARE_GLOBAL_DATA_PTR; | |
| struct fw_dynamic_info opensbi_info; | |
| static int spl_opensbi_find_os_node(void *blob, int *uboot_node, int os_type) | |
| { | |
| int fit_images_node, node; | |
| const char *fit_os; | |
| fit_images_node = fdt_path_offset(blob, "/fit-images"); | |
| if (fit_images_node < 0) | |
| return -ENODEV; | |
| fdt_for_each_subnode(node, blob, fit_images_node) { | |
| fit_os = fdt_getprop(blob, node, FIT_OS_PROP, NULL); | |
| if (!fit_os) | |
| continue; | |
| if (genimg_get_os_id(fit_os) == os_type) { | |
| *uboot_node = node; | |
| return 0; | |
| } | |
| } | |
| return -ENODEV; | |
| } | |
| void __noreturn spl_invoke_opensbi(struct spl_image_info *spl_image) | |
| { | |
| int ret, os_node; | |
| ulong os_entry; | |
| int os_type; | |
| typedef void __noreturn (*opensbi_entry_t)(ulong hartid, ulong dtb, ulong info); | |
| opensbi_entry_t opensbi_entry; | |
| if (!spl_image->fdt_addr) { | |
| pr_err("No device tree specified in SPL image\n"); | |
| hang(); | |
| } | |
| /* | |
| * Find next os image in /fit-images | |
| * The next os image default is u-boot proper, once enable | |
| * OpenSBI OS boot mode, the OS image should be linux. | |
| */ | |
| if (CONFIG_IS_ENABLED(LOAD_FIT_OPENSBI_OS_BOOT)) | |
| os_type = IH_OS_LINUX; | |
| else | |
| os_type = IH_OS_U_BOOT; | |
| ret = spl_opensbi_find_os_node(spl_image->fdt_addr, &os_node, os_type); | |
| if (ret) { | |
| pr_err("Can't find %s node for opensbi, %d\n", | |
| genimg_get_os_name(os_type), ret); | |
| hang(); | |
| } | |
| /* Get U-Boot entry point */ | |
| ret = fit_image_get_entry(spl_image->fdt_addr, os_node, &os_entry); | |
| if (ret) | |
| ret = fit_image_get_load(spl_image->fdt_addr, os_node, &os_entry); | |
| /* Prepare opensbi_info object */ | |
| opensbi_info.magic = FW_DYNAMIC_INFO_MAGIC_VALUE; | |
| opensbi_info.version = FW_DYNAMIC_INFO_VERSION; | |
| opensbi_info.next_addr = os_entry; | |
| opensbi_info.next_mode = FW_DYNAMIC_INFO_NEXT_MODE_S; | |
| opensbi_info.options = CONFIG_SPL_OPENSBI_SCRATCH_OPTIONS; | |
| opensbi_info.boot_hart = gd->arch.boot_hart; | |
| opensbi_entry = (opensbi_entry_t)spl_image->entry_point; | |
| invalidate_icache_all(); | |
| #ifdef CONFIG_SPL_SMP | |
| /* | |
| * Start OpenSBI on all secondary harts and wait for acknowledgment. | |
| * | |
| * OpenSBI first relocates itself to its link address. This is done by | |
| * the main hart. To make sure no hart is still running U-Boot SPL | |
| * during relocation, we wait for all secondary harts to acknowledge | |
| * the call-function request before entering OpenSBI on the main hart. | |
| * Otherwise, code corruption can occur if the link address ranges of | |
| * U-Boot SPL and OpenSBI overlap. | |
| */ | |
| ret = smp_call_function((ulong)spl_image->entry_point, | |
| (ulong)spl_image->fdt_addr, | |
| (ulong)&opensbi_info, 1); | |
| if (ret) | |
| hang(); | |
| #endif | |
| opensbi_entry(gd->arch.boot_hart, (ulong)spl_image->fdt_addr, | |
| (ulong)&opensbi_info); | |
| } |