[PATCH 00/15] riscv: Add EFI-application support
From: Simon Glass <simon.glass@canonical.com> Add support for building and running U-Boot as a 64-bit RISC-V EFI application on top of UEFI firmware such as EDK2. This includes build system changes (linker script, build flags, object exclusions), runtime fixes (FDT relocation skip, global data pointer handling, OF_BOARD_FIXUP disable), timer support via the firmware FDT, board files and defconfig It also provides QEMU integration in the build-efi script so it is easy to try out. Tested on QEMU riscv-virt with EDK2 firmware, boots to the U-Boot prompt. Simon Glass (15): board_f: Skip the FDT relocation when not relocating board_r: Skip set_gd() for RISC-V EFI applications riscv: Disable OF_BOARD_FIXUP for EFI applications riscv: timer: Read timebase frequency from the firmware FDT riscv: Add the EFI table field to arch_global_data riscv: Exclude -static -pie from LDFLAGS for EFI apps riscv: Add EFI application build flags riscv: efi: Exclude PLT relocations from the reloc loop riscv: Add EFI app linker script and build CRT objects riscv: Exclude start.o, mtrap.o, dram.o for EFI apps riscv: Skip prelink-riscv for EFI application builds riscv: Exclude unwanted startup code for EFI app builds efi: client: Build sync_dt for RISC-V EFI applications riscv: Add RISC-V EFI application board and defconfig scripts: build-efi: Add RISC-V architecture support Makefile | 2 + arch/riscv/Kconfig | 10 +- arch/riscv/Makefile | 2 + arch/riscv/config.mk | 19 +++ arch/riscv/cpu/Makefile | 7 +- arch/riscv/cpu/generic/Makefile | 2 + arch/riscv/dts/efi-riscv_app.dts | 49 ++++++++ arch/riscv/include/asm/global_data.h | 3 + arch/riscv/lib/Makefile | 2 + arch/riscv/lib/elf_riscv64_efi_app.lds | 136 ++++++++++++++++++++++ arch/riscv/lib/reloc_riscv_efi.c | 12 +- board/efi/Kconfig | 42 ++++--- board/efi/efi-riscv_app/Kconfig | 26 +++++ board/efi/efi-riscv_app/MAINTAINERS | 7 ++ board/efi/efi-riscv_app/Makefile | 5 + board/efi/efi-riscv_app/board.c | 51 ++++++++ board/efi/efi-riscv_app/config.mk | 6 + board/efi/efi-riscv_app/efi-riscv_app.env | 12 ++ common/board_f.c | 3 +- common/board_r.c | 3 +- configs/efi-riscv_app64_defconfig | 55 +++++++++ drivers/timer/riscv_timer.c | 13 +++ include/asm-generic/global_data.h | 8 ++ lib/efi_client/Kconfig | 6 +- lib/efi_client/Makefile | 2 + lib/efi_client/efi_app.c | 3 + scripts/build-efi | 25 +++- scripts/build_helper.py | 7 +- 28 files changed, 490 insertions(+), 28 deletions(-) create mode 100644 arch/riscv/dts/efi-riscv_app.dts create mode 100644 arch/riscv/lib/elf_riscv64_efi_app.lds create mode 100644 board/efi/efi-riscv_app/Kconfig create mode 100644 board/efi/efi-riscv_app/MAINTAINERS create mode 100644 board/efi/efi-riscv_app/Makefile create mode 100644 board/efi/efi-riscv_app/board.c create mode 100644 board/efi/efi-riscv_app/config.mk create mode 100644 board/efi/efi-riscv_app/efi-riscv_app.env create mode 100644 configs/efi-riscv_app64_defconfig -- 2.43.0 base-commit: b88a6ed6382861ea02ee538e22bff417f1d999ea branch: risva
From: Simon Glass <simon.glass@canonical.com> When GD_FLG_SKIP_RELOC is set (e.g. for EFI applications), U-Boot does not relocate itself, so the reserved FDT destination address is not valid. Attempting to memcpy the FDT to this address causes a page fault on RISC-V EFI apps. Add a GD_FLG_SKIP_RELOC check before the FDT copy in reloc_and_fix_fdt() so the FDT blob pointer is left unchanged when relocation is skipped. Co-developed-by: Claude Opus 4.6 <noreply@anthropic.com> Signed-off-by: Simon Glass <simon.glass@canonical.com> --- common/board_f.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/common/board_f.c b/common/board_f.c index ec0bdee7321..448cf2e319a 100644 --- a/common/board_f.c +++ b/common/board_f.c @@ -665,7 +665,8 @@ static int init_post(void) static int reloc_and_fix_fdt(void) { if (!IS_ENABLED(CONFIG_OF_EMBED)) { - if (gd->boardf->new_fdt) { + if (!(gd->flags & GD_FLG_SKIP_RELOC) && + gd->boardf->new_fdt) { memcpy(gd->boardf->new_fdt, gd->fdt_blob, fdt_totalsize(gd->fdt_blob)); gd->fdt_blob = gd->boardf->new_fdt; -- 2.43.0
From: Simon Glass <simon.glass@canonical.com> board_init_r() unconditionally calls set_gd(new_gd) for RISC-V, but the EFI application entry point passes NULL as the new_gd parameter. This zeroes the gp register (which holds the global data pointer), causing an immediate page fault when gd is next accessed. For EFI apps the global data pointer is already set correctly by efi_app.c before calling board_init_r(), so skip the set_gd() call. Co-developed-by: Claude Opus 4.6 <noreply@anthropic.com> Signed-off-by: Simon Glass <simon.glass@canonical.com> --- common/board_r.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/common/board_r.c b/common/board_r.c index c0f1ea9f003..18258b4fa0a 100644 --- a/common/board_r.c +++ b/common/board_r.c @@ -793,7 +793,8 @@ void board_init_r(gd_t *new_gd, ulong dest_addr) arch_setup_gd(new_gd); #if defined(CONFIG_RISCV) - set_gd(new_gd); + if (!IS_ENABLED(CONFIG_EFI_APP)) + set_gd(new_gd); #elif !defined(CONFIG_X86) && !defined(CONFIG_ARM) && !defined(CONFIG_ARM64) gd = new_gd; #endif -- 2.43.0
From: Simon Glass <simon.glass@canonical.com> The OF_BOARD_FIXUP event handler riscv_fdt_copy_resv_mem_node() tries to expand the FDT in place by 1 KiB to copy reserved-memory nodes from the firmware device tree. For EFI applications the FDT is embedded with no extra space, so fdt_open_into() fails and causes a crash. Disable OF_BOARD_FIXUP for EFI_APP builds. The EFI application does not need to copy firmware reserved-memory nodes since it runs on top of the UEFI firmware which manages its own memory map. Co-developed-by: Claude Opus 4.6 <noreply@anthropic.com> Signed-off-by: Simon Glass <simon.glass@canonical.com> --- arch/riscv/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/riscv/Kconfig b/arch/riscv/Kconfig index 3434dd0e169..378a974f06c 100644 --- a/arch/riscv/Kconfig +++ b/arch/riscv/Kconfig @@ -500,7 +500,7 @@ config STACK_SIZE_SHIFT default 14 config OF_BOARD_FIXUP - default y if OF_SEPARATE && RISCV_SMODE + default y if OF_SEPARATE && RISCV_SMODE && !EFI_APP menu "Use assembly optimized implementation of memory routines" -- 2.43.0
From: Simon Glass <simon.glass@canonical.com> The RISC-V timer driver reads the clock rate from /cpus/timebase-frequency in U-Boot's own device tree. For EFI applications this property may not be present, since the embedded DTB is minimal. Add a fallback that reads the timebase frequency from the firmware- provided FDT, which is available via the EFI configuration tables. To support this, scan for EFI_FDT_GUID in scan_tables() and store the address via a new gd_set_firmware_fdt_addr() accessor. Co-developed-by: Claude Opus 4.6 <noreply@anthropic.com> Signed-off-by: Simon Glass <simon.glass@canonical.com> --- drivers/timer/riscv_timer.c | 13 +++++++++++++ include/asm-generic/global_data.h | 8 ++++++++ lib/efi_client/efi_app.c | 3 +++ 3 files changed, 24 insertions(+) diff --git a/drivers/timer/riscv_timer.c b/drivers/timer/riscv_timer.c index 1f4980ceb38..ca28d1297d7 100644 --- a/drivers/timer/riscv_timer.c +++ b/drivers/timer/riscv_timer.c @@ -15,8 +15,12 @@ #include <dm.h> #include <errno.h> #include <fdt_support.h> +#include <mapmem.h> #include <timer.h> #include <asm/csr.h> +#include <asm/global_data.h> + +DECLARE_GLOBAL_DATA_PTR; static u64 notrace riscv_timer_get_count(struct udevice *dev) { @@ -89,6 +93,15 @@ static int riscv_timer_probe(struct udevice *dev) "/cpus", "timebase-frequency", 0); } + /* For EFI apps, try the firmware-provided FDT */ + if (!rate && IS_ENABLED(CONFIG_EFI_APP) && + gd_firmware_fdt_addr()) { + void *fw_fdt = map_sysmem(gd_firmware_fdt_addr(), 0); + + rate = fdt_getprop_u32_default(fw_fdt, "/cpus", + "timebase-frequency", 0); + } + uc_priv->clock_rate = rate; /* With rate==0, timer uclass post_probe might later fail with -EINVAL. diff --git a/include/asm-generic/global_data.h b/include/asm-generic/global_data.h index b17a2e4650b..86204e372aa 100644 --- a/include/asm-generic/global_data.h +++ b/include/asm-generic/global_data.h @@ -563,6 +563,14 @@ static_assert(sizeof(struct global_data) == GD_SIZE); #define gd_set_smbios_start(addr) #endif +#ifdef CONFIG_RISCV +#define gd_firmware_fdt_addr() gd->arch.firmware_fdt_addr +#define gd_set_firmware_fdt_addr(addr) gd->arch.firmware_fdt_addr = addr +#else +#define gd_firmware_fdt_addr() 0UL +#define gd_set_firmware_fdt_addr(addr) +#endif + #if CONFIG_IS_ENABLED(MULTI_DTB_FIT) #define gd_multi_dtb_fit() gd->multi_dtb_fit #define gd_set_multi_dtb_fit(_dtb) gd->multi_dtb_fit = _dtb diff --git a/lib/efi_client/efi_app.c b/lib/efi_client/efi_app.c index 178571d9970..d36ed959f95 100644 --- a/lib/efi_client/efi_app.c +++ b/lib/efi_client/efi_app.c @@ -172,6 +172,7 @@ static void scan_tables(struct efi_system_table *sys_table) efi_guid_t acpi = EFI_ACPI_TABLE_GUID; efi_guid_t smbios = SMBIOS_TABLE_GUID; efi_guid_t smbios3 = SMBIOS3_TABLE_GUID; + efi_guid_t fdt = EFI_FDT_GUID; uint i; for (i = 0; i < sys_table->nr_tables; i++) { @@ -182,6 +183,8 @@ static void scan_tables(struct efi_system_table *sys_table) else if (!memcmp(&tab->guid, &smbios, sizeof(efi_guid_t)) || !memcmp(&tab->guid, &smbios3, sizeof(efi_guid_t))) gd_set_smbios_start(map_to_sysmem(tab->table)); + else if (!memcmp(&tab->guid, &fdt, sizeof(efi_guid_t))) + gd_set_firmware_fdt_addr((ulong)tab->table); } } -- 2.43.0
From: Simon Glass <simon.glass@canonical.com> The EFI client code stores the EFI system table address in gd->arch.table. This field exists in the x86 and ARM arch_global_data but is missing for RISC-V, so add it. Co-developed-by: Claude Opus 4.6 <noreply@anthropic.com> Signed-off-by: Simon Glass <simon.glass@canonical.com> --- arch/riscv/include/asm/global_data.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/arch/riscv/include/asm/global_data.h b/arch/riscv/include/asm/global_data.h index 47b5e2cfc8f..1ef82f35fb0 100644 --- a/arch/riscv/include/asm/global_data.h +++ b/arch/riscv/include/asm/global_data.h @@ -42,6 +42,9 @@ struct arch_global_data { #endif #ifdef CONFIG_SMBIOS ulong smbios_start; /* Start address of SMBIOS table */ +#endif +#ifdef CONFIG_EFI_CLIENT + ulong table; /* holds the table address from previous EFI firmware */ #endif struct resume_data *resume; }; -- 2.43.0
From: Simon Glass <simon.glass@canonical.com> The RISC-V LDFLAGS_u-boot includes -static -pie, which conflicts with the -shared flag needed for building EFI applications as shared objects. Guard these flags with ifndef CONFIG_EFI_APP so that EFI app builds can use their own linker flags. Co-developed-by: Claude Opus 4.6 <noreply@anthropic.com> Signed-off-by: Simon Glass <simon.glass@canonical.com> --- arch/riscv/config.mk | 2 ++ 1 file changed, 2 insertions(+) diff --git a/arch/riscv/config.mk b/arch/riscv/config.mk index 9f16dda92a0..123b5d24cca 100644 --- a/arch/riscv/config.mk +++ b/arch/riscv/config.mk @@ -27,7 +27,9 @@ endif PLATFORM_CPPFLAGS += -ffixed-x3 -fpic PLATFORM_RELFLAGS += -fno-common -ffunction-sections -fdata-sections +ifndef CONFIG_EFI_APP LDFLAGS_u-boot += --gc-sections -static -pie +endif EFI_CRT0 := crt0_riscv_efi.o EFI_RELOC := reloc_riscv_efi.o -- 2.43.0
From: Simon Glass <simon.glass@canonical.com> Add the build machinery for RISC-V EFI applications in config.mk: - OBJCOPYFLAGS_EFI for the EFI stub binary sections - LDFLAGS_FINAL with -shared -Bsymbolic for the shared-object link - LDSCRIPT pointing to the EFI app linker script - Separate OBJCOPYFLAGS_EFI for the app, which uses a different set of sections since the linker script groups data contiguously Co-developed-by: Claude Opus 4.6 <noreply@anthropic.com> Signed-off-by: Simon Glass <simon.glass@canonical.com> --- arch/riscv/config.mk | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/arch/riscv/config.mk b/arch/riscv/config.mk index 123b5d24cca..1d98c015357 100644 --- a/arch/riscv/config.mk +++ b/arch/riscv/config.mk @@ -33,3 +33,20 @@ endif EFI_CRT0 := crt0_riscv_efi.o EFI_RELOC := reloc_riscv_efi.o + +OBJCOPYFLAGS_EFI += -j .text -j .rodata -j .data -j .sdata -j .dynamic \ + -j .dynsym -j .rela -j .reloc -j .got -j .got.plt \ + -j __u_boot_list -j .embedded_dtb -O binary + +ifeq ($(CONFIG_EFI_APP),y) + +LDFLAGS_FINAL += -znocombreloc -shared -Bsymbolic --gc-sections +LDSCRIPT := $(srctree)/arch/riscv/lib/elf_riscv64_efi_app.lds + +# The EFI app linker script places all data sections contiguously +# between _data and _edata so the PE .data section covers them. +OBJCOPYFLAGS_EFI := -j .text -j .rela.dyn \ + -j .dynsym -j .dynstr -j .embedded_dtb -j .data \ + -O binary + +endif -- 2.43.0
From: Simon Glass <simon.glass@canonical.com> GNU ld includes DT_PLTRELSZ in DT_RELASZ, so the EFI self-relocation code processes R_RISCV_JUMP_SLOT entries from .rela.plt alongside the expected R_RISCV_RELATIVE entries from .rela.dyn. JUMP_SLOT entries have a different format and cause incorrect fixups. Subtract DT_PLTRELSZ from the total relocation size so that only R_RISCV_RELATIVE entries are processed. Co-developed-by: Claude Opus 4.6 <noreply@anthropic.com> Signed-off-by: Simon Glass <simon.glass@canonical.com> --- arch/riscv/lib/reloc_riscv_efi.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/arch/riscv/lib/reloc_riscv_efi.c b/arch/riscv/lib/reloc_riscv_efi.c index c1039dd165d..da448bfdf96 100644 --- a/arch/riscv/lib/reloc_riscv_efi.c +++ b/arch/riscv/lib/reloc_riscv_efi.c @@ -52,7 +52,7 @@ efi_status_t EFIAPI _relocate(long ldbase, Elf_Dyn *dyn) { - long relsz = 0, relent = 0; + long relsz = 0, relent = 0, pltrelsz = 0; Elf_Rela *rel = 0; unsigned long *addr; int i; @@ -68,11 +68,21 @@ efi_status_t EFIAPI _relocate(long ldbase, Elf_Dyn *dyn) case DT_RELAENT: relent = dyn[i].d_un.d_val; break; + case DT_PLTRELSZ: + pltrelsz = dyn[i].d_un.d_val; + break; default: break; } } + /* + * GNU ld includes DT_PLTRELSZ in DT_RELASZ. Subtract it so + * we only process R_RISCV_RELATIVE entries from .rela.dyn and + * do not run into R_RISCV_JUMP_SLOT entries from .rela.plt. + */ + relsz -= pltrelsz; + if (!rel && relent == 0) return EFI_SUCCESS; -- 2.43.0
From: Simon Glass <simon.glass@canonical.com> Add a linker script for building U-Boot as a RISC-V EFI application. This defines the PE/COFF .text and .data sections, the .reloc section for PE base relocations and the .embedded_dtb section (16-byte aligned) for the devicetree blob. Also add the EFI CRT0 and relocation objects to the build for EFI_APP so they are linked into the final binary. Co-developed-by: Claude Opus 4.6 <noreply@anthropic.com> Signed-off-by: Simon Glass <simon.glass@canonical.com> --- arch/riscv/lib/Makefile | 2 + arch/riscv/lib/elf_riscv64_efi_app.lds | 136 +++++++++++++++++++++++++ 2 files changed, 138 insertions(+) create mode 100644 arch/riscv/lib/elf_riscv64_efi_app.lds diff --git a/arch/riscv/lib/Makefile b/arch/riscv/lib/Makefile index 36712cbb5c9..b0b1cabf903 100644 --- a/arch/riscv/lib/Makefile +++ b/arch/riscv/lib/Makefile @@ -38,6 +38,8 @@ CFLAGS_REMOVE_$(EFI_CRT0) := $(CFLAGS_NON_EFI) CFLAGS_$(EFI_RELOC) := $(CFLAGS_EFI) CFLAGS_REMOVE_$(EFI_RELOC) := $(CFLAGS_NON_EFI) +obj-$(CONFIG_EFI_APP) += $(EFI_CRT0) $(EFI_RELOC) + obj-$(CONFIG_$(PHASE_)USE_ARCH_MEMSET) += memset.o obj-$(CONFIG_$(PHASE_)USE_ARCH_MEMMOVE) += memmove.o obj-$(CONFIG_$(PHASE_)USE_ARCH_MEMCPY) += memcpy.o diff --git a/arch/riscv/lib/elf_riscv64_efi_app.lds b/arch/riscv/lib/elf_riscv64_efi_app.lds new file mode 100644 index 00000000000..6374433cdb3 --- /dev/null +++ b/arch/riscv/lib/elf_riscv64_efi_app.lds @@ -0,0 +1,136 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ +/* + * U-Boot riscv64 EFI application linker script + * + * Modified from lib/efi_loader/elf_efi.ldsi and + * arch/arm/lib/elf_aarch64_efi.lds + */ + +OUTPUT_FORMAT("elf64-littleriscv", "elf64-littleriscv", "elf64-littleriscv") +OUTPUT_ARCH(riscv) +ENTRY(_start) +SECTIONS +{ + /* + * The .text section must come first at VMA 0 so that the PE/COFF + * header in .text.head is at the start of the binary. The CRT0 + * defines image_base at the very beginning of .text.head. + */ + .text 0x0 : { + _text = .; + *(.text.head) + *(.text) + *(.text.*) + *(.gnu.linkonce.t.*) + *(.srodata) + *(.rodata*) + . = ALIGN(16); + *(.dynamic); + . = ALIGN(512); + } + . = ALIGN(4096); + _etext = .; + _text_size = . - _text; + + /* + * .hash and .gnu.hash are needed in the ELF for dynamic linking + * during the link step, but are not extracted into the PE binary. + */ + .hash : { *(.hash) } + .gnu.hash : { *(.gnu.hash) } + .eh_frame : { *(.eh_frame) } + + . = ALIGN(65536); + .reloc : { + KEEP (*(.reloc)) + } + + /* + * Everything from _data to _edata makes up the PE .data section. + * The CRT0 PE header uses _edata for both VirtualSize and + * SizeOfRawData, so BSS must be included (as zero-filled file + * data) rather than in a separate section. + */ + . = ALIGN(4096); + _data = .; + + /* + * Dynamic relocations in .rela.dyn so the linker sets DT_RELA + * to point here. All relocation input sections are gathered + * into this single output section. + */ + .rela.dyn : { + *(.rela.dyn) + *(.rela.text*) + *(.rela.data*) + *(.rela.got) + *(.rela.stab) + *(.rela.init_array*) + *(.rela.fini_array*) + *(.rela.ctors*) + *(.rela.dtors*) + *(.rela__u_boot_list*) + } + + /* Dynamic symbol table and strings */ + .dynsym : { *(.dynsym) } + .dynstr : { *(.dynstr) } + + /* Embedded device tree (must be separate for embeddtb command) */ + . = ALIGN(16); + .embedded_dtb : { + *(.embedded_dtb) + } + + /* + * Regular data and BSS live together in one output section. + * Mixing PROGBITS (.data) and NOBITS (.bss) input sections in + * the same output section forces the linker to emit file content + * for the entire section, so BSS gets zero-filled in the file. + * This avoids a VMA gap that would bloat the PE binary. + */ + .data : { + *(.sdata) + *(.data) + *(.data1) + *(.data.*) + *(.got.plt) + *(.got) + + /* U-Boot lists */ + . = ALIGN(8); + KEEP(*(SORT(__u_boot_list*))); + . = ALIGN(8); + + /* BSS merged into data to get file content */ + . = ALIGN(16); + _bss = .; + *(.sbss) + *(.scommon) + *(.dynbss) + *(.bss) + *(.bss.*) + *(COMMON) + . = ALIGN(512); + _bss_end = .; + _edata = .; + _end = .; + } + _data_size = _edata - _data; + + /* + * PLT relocations are placed after _edata so they do not fall + * within the DT_RELA range. _relocate() only handles + * R_RISCV_RELATIVE and would panic on R_RISCV_JUMP_SLOT entries. + */ + .rela.plt : { *(.rela.plt) } + + . = ALIGN(4096); + .note.gnu.build-id : { *(.note.gnu.build-id) } + /DISCARD/ : { + *(.rel.reloc) + *(.eh_frame) + *(.note.GNU-stack) + } + .comment 0 : { *(.comment) } +} -- 2.43.0
From: Simon Glass <simon.glass@canonical.com> EFI applications use their own entry point (CRT0) and run on top of UEFI firmware, so the bare-metal startup code (start.o), machine-mode trap handler (mtrap.o) and generic DRAM init (dram.o) are not needed and cause link conflicts. Skip these objects when CONFIG_EFI_APP is set. Co-developed-by: Claude Opus 4.6 <noreply@anthropic.com> Signed-off-by: Simon Glass <simon.glass@canonical.com> --- arch/riscv/cpu/Makefile | 7 ++++++- arch/riscv/cpu/generic/Makefile | 2 ++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/arch/riscv/cpu/Makefile b/arch/riscv/cpu/Makefile index 6bf6f911c67..331810b819d 100644 --- a/arch/riscv/cpu/Makefile +++ b/arch/riscv/cpu/Makefile @@ -2,6 +2,11 @@ # # Copyright (C) 2018, Bin Meng <bmeng.cn@gmail.com> +ifndef CONFIG_EFI_APP extra-y = start.o +endif -obj-y += cpu.o mtrap.o +obj-y += cpu.o +ifndef CONFIG_EFI_APP +obj-y += mtrap.o +endif diff --git a/arch/riscv/cpu/generic/Makefile b/arch/riscv/cpu/generic/Makefile index a9be44ec387..7eda97d2f55 100644 --- a/arch/riscv/cpu/generic/Makefile +++ b/arch/riscv/cpu/generic/Makefile @@ -2,4 +2,6 @@ # # Copyright (C) 2018, Bin Meng <bmeng.cn@gmail.com> +ifndef CONFIG_EFI_APP obj-y += dram.o +endif -- 2.43.0
From: Simon Glass <simon.glass@canonical.com> prelink-riscv processes static-PIE relocations in the U-Boot binary, but EFI applications are built as shared objects with dynamic relocations handled by the EFI CRT0. Running prelink-riscv on an EFI app binary fails. Skip the prelink-riscv step when CONFIG_EFI_APP is set. Co-developed-by: Claude Opus 4.6 <noreply@anthropic.com> Signed-off-by: Simon Glass <simon.glass@canonical.com> --- Makefile | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Makefile b/Makefile index ee5b54c4c51..a446e634a7f 100644 --- a/Makefile +++ b/Makefile @@ -1864,8 +1864,10 @@ ifeq ($(CONFIG_KALLSYMS),y) endif $(call cmd,llcheck,u-boot) ifeq ($(CONFIG_RISCV),y) +ifneq ($(CONFIG_EFI_APP),y) @tools/prelink-riscv $@ endif +endif # Common step: create archive and prepare modified object files quiet_cmd_ulib-objs = OBJS $@ -- 2.43.0
From: Simon Glass <simon.glass@canonical.com> The EFI application uses its own CRT0 entry point instead of the bare-metal start.o file. Exclude it so it is not linked as the entry point to the binary. Co-developed-by: Claude Opus 4.6 <noreply@anthropic.com> Signed-off-by: Simon Glass <simon.glass@canonical.com> --- arch/riscv/Makefile | 2 ++ 1 file changed, 2 insertions(+) diff --git a/arch/riscv/Makefile b/arch/riscv/Makefile index 6f80f4a7108..aaa5a02e17c 100644 --- a/arch/riscv/Makefile +++ b/arch/riscv/Makefile @@ -55,7 +55,9 @@ endif PLATFORM_CPPFLAGS += $(ARCH_FLAGS) CFLAGS_EFI += $(ARCH_FLAGS) +ifndef CONFIG_EFI_APP head-y := arch/riscv/cpu/start.o +endif libs-y += arch/riscv/cpu/ libs-y += arch/riscv/cpu/$(CPU)/ -- 2.43.0
From: Simon Glass <simon.glass@canonical.com> Theis file handles syncing the EFI memory map to the device tree reserved-memory nodes. It is needed for 64-bit architectures (ARM64 and RISC-V) but is only built for ARM64. Add RISC-V to the build condition. Co-developed-by: Claude Opus 4.6 <noreply@anthropic.com> Signed-off-by: Simon Glass <simon.glass@canonical.com> --- lib/efi_client/Makefile | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/efi_client/Makefile b/lib/efi_client/Makefile index 9d53ff3c924..685f0a5abfd 100644 --- a/lib/efi_client/Makefile +++ b/lib/efi_client/Makefile @@ -10,6 +10,8 @@ obj-$(CONFIG_EFI_STUB) += efi_info.o ifeq ($(CONFIG_ARM64),y) stub_obj := stub_arm64.o obj-$(CONFIG_EFI_APP) += sync_dt.o +else ifeq ($(CONFIG_RISCV),y) +obj-$(CONFIG_EFI_APP) += sync_dt.o else stub_obj := stub_x86.o -- 2.43.0
From: Simon Glass <simon.glass@canonical.com> Add support for building U-Boot as a 64-bit RISC-V EFI application. This allows U-Boot to run on top of UEFI firmware (e.g. EDK2) on RISC-V platforms. Follow the same pattern as the existing ARM and x86 EFI application boards. Merge the Kconfig choice block into a single block with per-entry arch dependencies, replacing the previous separate per-arch choice blocks. Include a timer node for the RISC-V architectural timer and a /cpus node with timebase-frequency for QEMU virt (10 MHz) in the device tree. Co-developed-by: Claude Opus 4.6 <noreply@anthropic.com> Signed-off-by: Simon Glass <simon.glass@canonical.com> --- arch/riscv/Kconfig | 8 ++++ arch/riscv/dts/efi-riscv_app.dts | 49 ++++++++++++++++++++ board/efi/Kconfig | 42 ++++++++++------- board/efi/efi-riscv_app/Kconfig | 26 +++++++++++ board/efi/efi-riscv_app/MAINTAINERS | 7 +++ board/efi/efi-riscv_app/Makefile | 5 +++ board/efi/efi-riscv_app/board.c | 51 +++++++++++++++++++++ board/efi/efi-riscv_app/config.mk | 6 +++ board/efi/efi-riscv_app/efi-riscv_app.env | 12 +++++ configs/efi-riscv_app64_defconfig | 55 +++++++++++++++++++++++ lib/efi_client/Kconfig | 6 +-- 11 files changed, 248 insertions(+), 19 deletions(-) create mode 100644 arch/riscv/dts/efi-riscv_app.dts create mode 100644 board/efi/efi-riscv_app/Kconfig create mode 100644 board/efi/efi-riscv_app/MAINTAINERS create mode 100644 board/efi/efi-riscv_app/Makefile create mode 100644 board/efi/efi-riscv_app/board.c create mode 100644 board/efi/efi-riscv_app/config.mk create mode 100644 board/efi/efi-riscv_app/efi-riscv_app.env create mode 100644 configs/efi-riscv_app64_defconfig diff --git a/arch/riscv/Kconfig b/arch/riscv/Kconfig index 378a974f06c..24b264933bb 100644 --- a/arch/riscv/Kconfig +++ b/arch/riscv/Kconfig @@ -55,6 +55,14 @@ config TARGET_XILINX_MBV config TARGET_ASPEED_AST2700_IBEX bool "Support Ibex RISC-V cores on Aspeed AST2700 SoC" +config ARCH_EFI_RISCV + bool "efi" + select ARCH_EFI + help + Indicates that this board uses EFI as its underlying base, i.e. + that it does not have bare-metal code and can only run as an EFI + application. + endchoice config SYS_ICACHE_OFF diff --git a/arch/riscv/dts/efi-riscv_app.dts b/arch/riscv/dts/efi-riscv_app.dts new file mode 100644 index 00000000000..092aec90087 --- /dev/null +++ b/arch/riscv/dts/efi-riscv_app.dts @@ -0,0 +1,49 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2026 Canonical Ltd + * Written by Simon Glass <simon.glass@canonical.com> + */ + +/dts-v1/; + +/ { + #address-cells = <2>; + #size-cells = <2>; + model = "EFI RISC-V Application"; + compatible = "efi,riscv-app"; + + chosen { + stdout-path = &serial; + }; + + serial: serial { + compatible = "efi,uart"; + }; + + reset { + compatible = "efi,reset"; + bootph-all; + }; + + efi-fb { + compatible = "efi-fb"; + bootph-some-ram; + }; + + keyboard { + compatible = "efi-keyboard"; + }; + + mouse { + compatible = "efi,mouse"; + }; + + cpus { + timebase-frequency = <10000000>; + }; + + timer { + compatible = "riscv,timer"; + }; + +}; diff --git a/board/efi/Kconfig b/board/efi/Kconfig index 5237f58093e..d27baa4ccbb 100644 --- a/board/efi/Kconfig +++ b/board/efi/Kconfig @@ -5,14 +5,13 @@ config ARCH_EFI if ARCH_EFI -if X86 - choice prompt "Mainboard model" optional config TARGET_EFI_X86_APP32 bool "32-bit efi application" + depends on X86 select EFI_APP help This target is used for running U-Boot on top of EFI. In @@ -22,6 +21,7 @@ config TARGET_EFI_X86_APP32 config TARGET_EFI_X86_APP64 bool "64-bit efi application" + depends on X86 select EFI_APP select X86_64 if X86 help @@ -32,27 +32,16 @@ config TARGET_EFI_X86_APP64 config TARGET_EFI_X86_PAYLOAD bool "efi payload" + depends on X86 help This target is used for running U-Boot on top of EFI. In this case EFI does the early initialisation, and U-Boot takes over once the RAM, video and CPU are fully running. U-Boot is loaded as a payload from EFI. -endchoice - -source "board/efi/efi-x86_app/Kconfig" -source "board/efi/efi-x86_payload/Kconfig" - -endif # X86 - -if ARM - -choice - prompt "Mainboard model" - optional - config TARGET_EFI_ARM_APP64 bool "64-bit efi application" + depends on ARM select EFI_APP select SYS_CUSTOM_LDSCRIPT select ARM64 @@ -62,10 +51,31 @@ config TARGET_EFI_ARM_APP64 starts once the RAM, video and CPU are fully running. U-Boot is loaded as an application from EFI. +config TARGET_EFI_RISCV_APP64 + bool "64-bit efi application" + depends on RISCV + select EFI_APP + select SYS_CUSTOM_LDSCRIPT + select ARCH_RV64I + help + This target is used for running U-Boot on top of EFI in 64-bit mode. + In this case EFI does the early initialisation, and U-Boot + starts once the RAM, video and CPU are fully running. + U-Boot is loaded as an application from EFI. + endchoice +if X86 +source "board/efi/efi-x86_app/Kconfig" +source "board/efi/efi-x86_payload/Kconfig" +endif + +if ARM source "board/efi/efi-arm_app/Kconfig" +endif -endif # ARM +if RISCV +source "board/efi/efi-riscv_app/Kconfig" +endif endif # ARCH_EFI diff --git a/board/efi/efi-riscv_app/Kconfig b/board/efi/efi-riscv_app/Kconfig new file mode 100644 index 00000000000..dba7789b7fa --- /dev/null +++ b/board/efi/efi-riscv_app/Kconfig @@ -0,0 +1,26 @@ +# Copyright 2026 Canonical Ltd +# Written by Simon Glass <simon.glass@canonical.com> + +if EFI_APP + +config SYS_BOARD + default "efi-riscv_app" + +config SYS_VENDOR + default "efi" + +config SYS_SOC + default "efi" + +config SYS_CPU + default "generic" + +config BOARD_SPECIFIC_OPTIONS # dummy + def_bool y + select GENERIC_RISCV + imply VIDEO_EFI + +config SYS_LDSCRIPT + default "arch/riscv/lib/elf_riscv64_efi_app.lds" + +endif diff --git a/board/efi/efi-riscv_app/MAINTAINERS b/board/efi/efi-riscv_app/MAINTAINERS new file mode 100644 index 00000000000..09babeeb811 --- /dev/null +++ b/board/efi/efi-riscv_app/MAINTAINERS @@ -0,0 +1,7 @@ +EFI-RISCV_APP64 BOARD +M: Simon Glass <simon.glass@canonical.com> +M: Simon Glass <sjg@chromium.org> +S: Maintained +F: board/efi/Kconfig +F: board/efi/efi-riscv_app/ +F: configs/efi-riscv_app64_defconfig diff --git a/board/efi/efi-riscv_app/Makefile b/board/efi/efi-riscv_app/Makefile new file mode 100644 index 00000000000..8864bcdd0cf --- /dev/null +++ b/board/efi/efi-riscv_app/Makefile @@ -0,0 +1,5 @@ +# SPDX-License-Identifier: GPL-2.0+ +# Copyright 2026 Canonical Ltd +# Written by Simon Glass <simon.glass@canonical.com> + +obj-y += board.o diff --git a/board/efi/efi-riscv_app/board.c b/board/efi/efi-riscv_app/board.c new file mode 100644 index 00000000000..9efed36c7d2 --- /dev/null +++ b/board/efi/efi-riscv_app/board.c @@ -0,0 +1,51 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2026 Canonical Ltd + * Written by Simon Glass <simon.glass@canonical.com> + */ + +#include <bootm.h> +#include <efi.h> +#include <event.h> +#include <init.h> + +int print_cpuinfo(void) +{ + return 0; +} + +int board_init(void) +{ + return 0; +} + +int board_exit_boot_services(void *ctx, struct event *evt) +{ + struct efi_priv *priv = efi_get_priv(); + struct efi_mem_desc *desc; + int desc_size; + uint version; + int size; + uint key; + int ret; + + if (evt->data.bootm_final.flags & BOOTM_FINAL_FAKE) { + printf("Not exiting EFI (fake go)\n"); + return 0; + } + printf("Exiting EFI\n"); + ret = efi_get_mmap(&desc, &size, &key, &desc_size, &version); + if (ret) { + printf("efi: Failed to get memory map\n"); + return -EFAULT; + } + + ret = efi_app_exit_boot_services(priv, key); + if (ret) + return ret; + + /* no console output after here as there are no EFI drivers! */ + + return 0; +} +EVENT_SPY_FULL(EVT_BOOTM_FINAL, board_exit_boot_services); diff --git a/board/efi/efi-riscv_app/config.mk b/board/efi/efi-riscv_app/config.mk new file mode 100644 index 00000000000..07262dad634 --- /dev/null +++ b/board/efi/efi-riscv_app/config.mk @@ -0,0 +1,6 @@ +# SPDX-License-Identifier: GPL-2.0+ +# Copyright 2026 Canonical Ltd +# Written by Simon Glass <simon.glass@canonical.com> + +BUILD_CFLAGS += -shared +PLATFORM_CPPFLAGS += $(CFLAGS_EFI) diff --git a/board/efi/efi-riscv_app/efi-riscv_app.env b/board/efi/efi-riscv_app/efi-riscv_app.env new file mode 100644 index 00000000000..57c05d6ee51 --- /dev/null +++ b/board/efi/efi-riscv_app/efi-riscv_app.env @@ -0,0 +1,12 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Environment file for RISC-V EFI app + * Copyright 2026 Canonical Ltd + * Written by Simon Glass <simon.glass@canonical.com> + */ + +/* common console settings */ +fdt_addr=40000000 +stdin=serial +stdout=serial,vidconsole +stderr=serial,vidconsole diff --git a/configs/efi-riscv_app64_defconfig b/configs/efi-riscv_app64_defconfig new file mode 100644 index 00000000000..f18bfb653ad --- /dev/null +++ b/configs/efi-riscv_app64_defconfig @@ -0,0 +1,55 @@ +CONFIG_RISCV=y +CONFIG_NR_DRAM_BANKS=8 +CONFIG_ENV_SIZE=0x1000 +CONFIG_DEFAULT_DEVICE_TREE="efi-riscv_app" +CONFIG_DEBUG_UART_BASE=0x0 +CONFIG_DEBUG_UART_CLOCK=0 +CONFIG_ARCH_EFI_RISCV=y +CONFIG_ARCH_RV64I=y +CONFIG_RISCV_SMODE=y +CONFIG_TARGET_EFI_RISCV_APP64=y +CONFIG_EFI_CLIENT=y +CONFIG_EFI_APP_64BIT=y +CONFIG_EFI_RAM_SIZE=0x20000000 +CONFIG_FIT=y +CONFIG_BOOTSTD_FULL=y +CONFIG_SHOW_BOOT_PROGRESS=y +CONFIG_OF_SYSTEM_SETUP=y +CONFIG_FDT_SIMPLEFB=y +CONFIG_USE_BOOTARGS=y +CONFIG_BOOTCOMMAND="bootflow scan -lbp" +CONFIG_SYS_PBSIZE=532 +CONFIG_SYS_CONSOLE_INFO_QUIET=y +CONFIG_LOG=y +CONFIG_LOGF_FUNC=y +CONFIG_DISPLAY_BOARDINFO_LATE=y +CONFIG_CYCLIC_MAX_CPU_TIME_US=50000 +CONFIG_BOARD_EARLY_INIT_R=y +CONFIG_CMD_SMBIOS=y +CONFIG_CMD_MEMINFO=y +CONFIG_CMD_MEMINFO_MAP=y +CONFIG_CMD_LSBLK=y +CONFIG_CMD_CAT=y +CONFIG_CMD_CACHE=y +CONFIG_CMD_TIME=y +CONFIG_CMD_KASLRSEED=y +CONFIG_CMD_HASH=y +CONFIG_CMD_EXT4_WRITE=y +CONFIG_MAC_PARTITION=y +CONFIG_OF_LIVE=y +CONFIG_ENV_OVERWRITE=y +CONFIG_SYS_RELOC_GD_ENV_ADDR=y +CONFIG_USE_BOOTFILE=y +CONFIG_BOOTFILE="Image" +CONFIG_NO_NET=y +CONFIG_REGMAP=y +CONFIG_SYSCON=y +CONFIG_DM_RNG=y +CONFIG_DEBUG_EFI_CONSOLE=y +CONFIG_SYSRESET=y +CONFIG_VIDEO=y +CONFIG_CONSOLE_TRUETYPE=y +CONFIG_SYS_WHITE_ON_BLACK=y +CONFIG_CONSOLE_SCROLL_LINES=5 +CONFIG_FAT_WRITE=y +CONFIG_CMD_DHRYSTONE=y diff --git a/lib/efi_client/Kconfig b/lib/efi_client/Kconfig index d1b4b3bfd8e..f00eea8e8ca 100644 --- a/lib/efi_client/Kconfig +++ b/lib/efi_client/Kconfig @@ -1,9 +1,9 @@ menu "U-Boot as UEFI application" - depends on X86 || ARM + depends on X86 || ARM || RISCV config EFI_CLIENT bool "Support running U-Boot from EFI" - depends on X86 || ARM + depends on X86 || ARM || RISCV imply DISPLAY_BOARDINFO imply X86_TSC_READ_BASE select EFI @@ -19,7 +19,7 @@ choice config EFI_APP bool "Support running as an EFI application" - depends on X86 || ARM + depends on X86 || ARM || RISCV select CHARSET select EVENT imply CONSOLE_MUX -- 2.43.0
From: Simon Glass <simon.glass@canonical.com> Add RISC-V as a supported architecture for the build-efi script. This configures QEMU with the riscv-virt machine, EDK2 firmware (RISCV_VIRT pflash images) and a virtio disk. The RISCV_VIRT_VARS.fd file is copied from the system directory on first use since QEMU needs it to be writable. Co-developed-by: Claude Opus 4.6 <noreply@anthropic.com> Signed-off-by: Simon Glass <simon.glass@canonical.com> --- scripts/build-efi | 25 +++++++++++++++++++++++-- scripts/build_helper.py | 7 +++++-- 2 files changed, 28 insertions(+), 4 deletions(-) diff --git a/scripts/build-efi b/scripts/build-efi index e23d81bd9b1..a6c8614562b 100755 --- a/scripts/build-efi +++ b/scripts/build-efi @@ -101,6 +101,22 @@ class BuildEfi: ] extra += ['-drive', f'if=virtio,file={self.img},format=raw,id=hd0'] + elif self.args.arch == 'riscv': + qemu_arch = 'riscv64' + extra += ['--machine', 'virt'] + bios = os.path.join(efi_dir, 'RISCV_VIRT_CODE.fd') + if not os.path.exists(bios): + bios = '/usr/share/qemu-efi-riscv64/RISCV_VIRT_CODE.fd' + var_store = os.path.join(efi_dir, 'RISCV_VIRT_VARS.fd') + if not os.path.exists(var_store): + sys_var = '/usr/share/qemu-efi-riscv64/RISCV_VIRT_VARS.fd' + shutil.copy(sys_var, var_store) + extra += [ + '-drive', f'if=pflash,format=raw,file={bios},readonly=on', + '-drive', f'if=pflash,format=raw,file={var_store}' + ] + extra += ['-drive', + f'if=virtio,file={self.img},format=raw,id=hd0'] else: # x86 if self.helper.bitness == 64: qemu_arch = 'x86_64' @@ -122,7 +138,7 @@ class BuildEfi: extra += ['-display', 'none', '-serial', 'mon:stdio'] serial_msg = ' (Ctrl-a x to quit)' else: - if self.args.arch == 'arm': + if self.args.arch in ('arm', 'riscv'): extra += ['-device', 'virtio-gpu-pci'] extra += ['-device', 'qemu-xhci', '-device', 'usb-kbd', '-device', 'usb-tablet'] @@ -239,7 +255,12 @@ class BuildEfi: def start(self): """This does all the work""" args = self.args - arch = 'arm' if self.args.arch == 'arm' else 'x86' + if self.args.arch == 'arm': + arch = 'arm' + elif self.args.arch == 'riscv': + arch = 'riscv' + else: + arch = 'x86' build_type = 'payload' if args.payload else 'app' build = f'efi-{arch}_{build_type}{self.helper.bitness}' diff --git a/scripts/build_helper.py b/scripts/build_helper.py index d3e951fc220..bb55daaf84b 100644 --- a/scripts/build_helper.py +++ b/scripts/build_helper.py @@ -45,6 +45,8 @@ class Helper: self.os_arch = 'arm64' else: self.os_arch = 'arm' + elif self.args.arch == 'riscv': + self.os_arch = 'riscv64' else: # x86 if self.bitness == 64: self.os_arch = 'amd64' @@ -288,8 +290,9 @@ def add_common_args(parser): Args: parser (argparse.ArgumentParser): Parser to modify """ - parser.add_argument('-a', '--arch', default='arm', choices=['arm', 'x86'], - help='Select architecture (arm, x86) Default: arm') + parser.add_argument('-a', '--arch', default='arm', + choices=['arm', 'riscv', 'x86'], + help='Select architecture (arm, riscv, x86) Default: arm') parser.add_argument('-b', '--bootcmd', type=str, help='U-Boot bootcmd to pass via fw_cfg') parser.add_argument('-B', '--no-build', action='store_true', -- 2.43.0
participants (1)
-
Simon Glass