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/unaligned.h> 11 12 #include "efistub.h" 13 14 typedef void __noreturn (*jump_kernel_func)(unsigned long, unsigned long); 15 16 static unsigned long hartid; 17 18 static int get_boot_hartid_from_fdt(void) 19 { 20 const void *fdt; 21 int chosen_node, len; 22 const void *prop; 23 24 fdt = get_efi_config_table(DEVICE_TREE_GUID); 25 if (!fdt) 26 return -EINVAL; 27 28 chosen_node = fdt_path_offset(fdt, "/chosen"); 29 if (chosen_node < 0) 30 return -EINVAL; 31 32 prop = fdt_getprop((void *)fdt, chosen_node, "boot-hartid", &len); 33 if (!prop) 34 return -EINVAL; 35 36 if (len == sizeof(u32)) 37 hartid = (unsigned long) fdt32_to_cpu(*(fdt32_t *)prop); 38 else if (len == sizeof(u64)) 39 hartid = (unsigned long) fdt64_to_cpu(__get_unaligned_t(fdt64_t, prop)); 40 else 41 return -EINVAL; 42 43 return 0; 44 } 45 46 static efi_status_t get_boot_hartid_from_efi(void) 47 { 48 efi_guid_t boot_protocol_guid = RISCV_EFI_BOOT_PROTOCOL_GUID; 49 struct riscv_efi_boot_protocol *boot_protocol; 50 efi_status_t status; 51 52 status = efi_bs_call(locate_protocol, &boot_protocol_guid, NULL, 53 (void **)&boot_protocol); 54 if (status != EFI_SUCCESS) 55 return status; 56 return efi_call_proto(boot_protocol, get_boot_hartid, &hartid); 57 } 58 59 efi_status_t check_platform_features(void) 60 { 61 efi_status_t status; 62 int ret; 63 64 status = get_boot_hartid_from_efi(); 65 if (status != EFI_SUCCESS) { 66 ret = get_boot_hartid_from_fdt(); 67 if (ret) { 68 efi_err("Failed to get boot hartid!\n"); 69 return EFI_UNSUPPORTED; 70 } 71 } 72 return EFI_SUCCESS; 73 } 74 75 unsigned long __weak stext_offset(void) 76 { 77 /* 78 * This fallback definition is used by the EFI zboot stub, which loads 79 * the entire image so it can branch via the image header at offset #0. 80 */ 81 return 0; 82 } 83 84 void __noreturn efi_enter_kernel(unsigned long entrypoint, unsigned long fdt, 85 unsigned long fdt_size) 86 { 87 unsigned long kernel_entry = entrypoint + stext_offset(); 88 jump_kernel_func jump_kernel = (jump_kernel_func)kernel_entry; 89 90 /* 91 * Jump to real kernel here with following constraints. 92 * 1. MMU should be disabled. 93 * 2. a0 should contain hartid 94 * 3. a1 should DT address 95 */ 96 csr_write(CSR_SATP, 0); 97 jump_kernel(hartid, fdt); 98 } 99