RISC-V: Add EFI stub support.

Add a RISC-V architecture specific stub code that actually copies the
actual kernel image to a valid address and jump to it after boot services
are terminated. Enable UEFI related kernel configs as well for RISC-V.

Signed-off-by: Atish Patra <atish.patra@wdc.com>
Link: https://lore.kernel.org/r/20200421033336.9663-4-atish.patra@wdc.com
[ardb: - move hartid fetch into check_platform_features()
- use image_size not reserve_size
- select ISA_C
- do not use dram_base]
Signed-off-by: Ard Biesheuvel <ardb@kernel.org>
Signed-off-by: Palmer Dabbelt <palmerdabbelt@google.com>

authored by Atish Patra and committed by Palmer Dabbelt d7071743 cb7d2dd5

+180 -1
+22
arch/riscv/Kconfig
··· 401 402 endchoice 403 404 endmenu 405 406 config BUILTIN_DTB ··· 433 source "kernel/power/Kconfig" 434 435 endmenu
··· 401 402 endchoice 403 404 + config EFI_STUB 405 + bool 406 + 407 + config EFI 408 + bool "UEFI runtime support" 409 + depends on OF 410 + select LIBFDT 411 + select UCS2_STRING 412 + select EFI_PARAMS_FROM_FDT 413 + select EFI_STUB 414 + select EFI_GENERIC_STUB 415 + select RISCV_ISA_C 416 + default y 417 + help 418 + This option provides support for runtime services provided 419 + by UEFI firmware (such as non-volatile variables, realtime 420 + clock, and platform reset). A UEFI stub is also provided to 421 + allow the kernel to be booted as an EFI application. This 422 + is only useful on systems that have UEFI firmware. 423 + 424 endmenu 425 426 config BUILTIN_DTB ··· 413 source "kernel/power/Kconfig" 414 415 endmenu 416 + 417 + source "drivers/firmware/Kconfig"
+1
arch/riscv/Makefile
··· 80 core-y += arch/riscv/ 81 82 libs-y += arch/riscv/lib/ 83 84 PHONY += vdso_install 85 vdso_install:
··· 80 core-y += arch/riscv/ 81 82 libs-y += arch/riscv/lib/ 83 + libs-$(CONFIG_EFI_STUB) += $(objtree)/drivers/firmware/efi/libstub/lib.a 84 85 PHONY += vdso_install 86 vdso_install:
+1
arch/riscv/configs/defconfig
··· 130 # CONFIG_RUNTIME_TESTING_MENU is not set 131 CONFIG_MEMTEST=y 132 # CONFIG_SYSFS_SYSCALL is not set
··· 130 # CONFIG_RUNTIME_TESTING_MENU is not set 131 CONFIG_MEMTEST=y 132 # CONFIG_SYSFS_SYSCALL is not set 133 + CONFIG_EFI=y
+35
arch/riscv/include/asm/efi.h
···
··· 1 + /* SPDX-License-Identifier: GPL-2.0 */ 2 + /* 3 + * Copyright (C) 2020 Western Digital Corporation or its affiliates. 4 + */ 5 + #ifndef _ASM_EFI_H 6 + #define _ASM_EFI_H 7 + 8 + #include <asm/io.h> 9 + #include <asm/mmu_context.h> 10 + #include <asm/ptrace.h> 11 + #include <asm/tlbflush.h> 12 + 13 + /* on RISC-V, the FDT may be located anywhere in system RAM */ 14 + static inline unsigned long efi_get_max_fdt_addr(unsigned long image_addr) 15 + { 16 + return ULONG_MAX; 17 + } 18 + 19 + /* Load initrd at enough distance from DRAM start */ 20 + static inline unsigned long efi_get_max_initrd_addr(unsigned long image_addr) 21 + { 22 + return image_addr + SZ_256M; 23 + } 24 + 25 + #define alloc_screen_info(x...) (&screen_info) 26 + 27 + static inline void free_screen_info(struct screen_info *si) 28 + { 29 + } 30 + 31 + static inline void efifb_setup_from_dmi(struct screen_info *si, const char *opt) 32 + { 33 + } 34 + 35 + #endif /* _ASM_EFI_H */
+2 -1
drivers/firmware/efi/Kconfig
··· 111 112 config EFI_ARMSTUB_DTB_LOADER 113 bool "Enable the DTB loader" 114 - depends on EFI_GENERIC_STUB 115 default y 116 help 117 Select this config option to add support for the dtb= command ··· 128 bool "Enable the command line initrd loader" if !X86 129 depends on EFI_STUB && (EFI_GENERIC_STUB || X86) 130 default y 131 help 132 Select this config option to add support for the initrd= command 133 line parameter, allowing an initrd that resides on the same volume
··· 111 112 config EFI_ARMSTUB_DTB_LOADER 113 bool "Enable the DTB loader" 114 + depends on EFI_GENERIC_STUB && !RISCV 115 default y 116 help 117 Select this config option to add support for the dtb= command ··· 128 bool "Enable the command line initrd loader" if !X86 129 depends on EFI_STUB && (EFI_GENERIC_STUB || X86) 130 default y 131 + depends on !RISCV 132 help 133 Select this config option to add support for the initrd= command 134 line parameter, allowing an initrd that resides on the same volume
+10
drivers/firmware/efi/libstub/Makefile
··· 22 cflags-$(CONFIG_ARM) := $(subst $(CC_FLAGS_FTRACE),,$(KBUILD_CFLAGS)) \ 23 -fno-builtin -fpic \ 24 $(call cc-option,-mno-single-pic-base) 25 26 cflags-$(CONFIG_EFI_GENERIC_STUB) += -I$(srctree)/scripts/dtc/libfdt 27 ··· 65 lib-$(CONFIG_ARM) += arm32-stub.o 66 lib-$(CONFIG_ARM64) += arm64-stub.o 67 lib-$(CONFIG_X86) += x86-stub.o 68 CFLAGS_arm32-stub.o := -DTEXT_OFFSET=$(TEXT_OFFSET) 69 CFLAGS_arm64-stub.o := -DTEXT_OFFSET=$(TEXT_OFFSET) 70 ··· 108 STUBCOPY_FLAGS-$(CONFIG_ARM64) += --prefix-alloc-sections=.init \ 109 --prefix-symbols=__efistub_ 110 STUBCOPY_RELOC-$(CONFIG_ARM64) := R_AARCH64_ABS 111 112 $(obj)/%.stub.o: $(obj)/%.o FORCE 113 $(call if_changed,stubcopy)
··· 22 cflags-$(CONFIG_ARM) := $(subst $(CC_FLAGS_FTRACE),,$(KBUILD_CFLAGS)) \ 23 -fno-builtin -fpic \ 24 $(call cc-option,-mno-single-pic-base) 25 + cflags-$(CONFIG_RISCV) := $(subst $(CC_FLAGS_FTRACE),,$(KBUILD_CFLAGS)) \ 26 + -fpic 27 28 cflags-$(CONFIG_EFI_GENERIC_STUB) += -I$(srctree)/scripts/dtc/libfdt 29 ··· 63 lib-$(CONFIG_ARM) += arm32-stub.o 64 lib-$(CONFIG_ARM64) += arm64-stub.o 65 lib-$(CONFIG_X86) += x86-stub.o 66 + lib-$(CONFIG_RISCV) += riscv-stub.o 67 CFLAGS_arm32-stub.o := -DTEXT_OFFSET=$(TEXT_OFFSET) 68 CFLAGS_arm64-stub.o := -DTEXT_OFFSET=$(TEXT_OFFSET) 69 ··· 105 STUBCOPY_FLAGS-$(CONFIG_ARM64) += --prefix-alloc-sections=.init \ 106 --prefix-symbols=__efistub_ 107 STUBCOPY_RELOC-$(CONFIG_ARM64) := R_AARCH64_ABS 108 + 109 + # For RISC-V, we don't need anything special other than arm64. Keep all the 110 + # symbols in .init section and make sure that no absolute symbols references 111 + # doesn't exist. 112 + STUBCOPY_FLAGS-$(CONFIG_RISCV) += --prefix-alloc-sections=.init \ 113 + --prefix-symbols=__efistub_ 114 + STUBCOPY_RELOC-$(CONFIG_RISCV) := R_RISCV_HI20 115 116 $(obj)/%.stub.o: $(obj)/%.o FORCE 117 $(call if_changed,stubcopy)
+109
drivers/firmware/efi/libstub/riscv-stub.c
···
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + /* 3 + * Copyright (C) 2020 Western Digital Corporation or its affiliates. 4 + */ 5 + 6 + #include <linux/efi.h> 7 + #include <linux/libfdt.h> 8 + 9 + #include <asm/efi.h> 10 + #include <asm/sections.h> 11 + 12 + #include "efistub.h" 13 + 14 + /* 15 + * RISC-V requires the kernel image to placed 2 MB aligned base for 64 bit and 16 + * 4MB for 32 bit. 17 + */ 18 + #ifdef CONFIG_64BIT 19 + #define MIN_KIMG_ALIGN SZ_2M 20 + #else 21 + #define MIN_KIMG_ALIGN SZ_4M 22 + #endif 23 + 24 + typedef void __noreturn (*jump_kernel_func)(unsigned int, unsigned long); 25 + 26 + static u32 hartid; 27 + 28 + static u32 get_boot_hartid_from_fdt(void) 29 + { 30 + const void *fdt; 31 + int chosen_node, len; 32 + const fdt32_t *prop; 33 + 34 + fdt = get_efi_config_table(DEVICE_TREE_GUID); 35 + if (!fdt) 36 + return U32_MAX; 37 + 38 + chosen_node = fdt_path_offset(fdt, "/chosen"); 39 + if (chosen_node < 0) 40 + return U32_MAX; 41 + 42 + prop = fdt_getprop((void *)fdt, chosen_node, "boot-hartid", &len); 43 + if (!prop || len != sizeof(u32)) 44 + return U32_MAX; 45 + 46 + return fdt32_to_cpu(*prop); 47 + } 48 + 49 + efi_status_t check_platform_features(void) 50 + { 51 + hartid = get_boot_hartid_from_fdt(); 52 + if (hartid == U32_MAX) { 53 + efi_err("/chosen/boot-hartid missing or invalid!\n"); 54 + return EFI_UNSUPPORTED; 55 + } 56 + return EFI_SUCCESS; 57 + } 58 + 59 + void __noreturn efi_enter_kernel(unsigned long entrypoint, unsigned long fdt, 60 + unsigned long fdt_size) 61 + { 62 + unsigned long stext_offset = _start_kernel - _start; 63 + unsigned long kernel_entry = entrypoint + stext_offset; 64 + jump_kernel_func jump_kernel = (jump_kernel_func)kernel_entry; 65 + 66 + /* 67 + * Jump to real kernel here with following constraints. 68 + * 1. MMU should be disabled. 69 + * 2. a0 should contain hartid 70 + * 3. a1 should DT address 71 + */ 72 + csr_write(CSR_SATP, 0); 73 + jump_kernel(hartid, fdt); 74 + } 75 + 76 + efi_status_t handle_kernel_image(unsigned long *image_addr, 77 + unsigned long *image_size, 78 + unsigned long *reserve_addr, 79 + unsigned long *reserve_size, 80 + efi_loaded_image_t *image) 81 + { 82 + unsigned long kernel_size = 0; 83 + unsigned long preferred_addr; 84 + efi_status_t status; 85 + 86 + kernel_size = _edata - _start; 87 + *image_addr = (unsigned long)_start; 88 + *image_size = kernel_size + (_end - _edata); 89 + 90 + /* 91 + * RISC-V kernel maps PAGE_OFFSET virtual address to the same physical 92 + * address where kernel is booted. That's why kernel should boot from 93 + * as low as possible to avoid wastage of memory. Currently, dram_base 94 + * is occupied by the firmware. So the preferred address for kernel to 95 + * boot is next aligned address. If preferred address is not available, 96 + * relocate_kernel will fall back to efi_low_alloc_above to allocate 97 + * lowest possible memory region as long as the address and size meets 98 + * the alignment constraints. 99 + */ 100 + preferred_addr = MIN_KIMG_ALIGN; 101 + status = efi_relocate_kernel(image_addr, kernel_size, *image_size, 102 + preferred_addr, MIN_KIMG_ALIGN, 0x0); 103 + 104 + if (status != EFI_SUCCESS) { 105 + efi_err("Failed to relocate kernel\n"); 106 + *image_size = 0; 107 + } 108 + return status; 109 + }