18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (C) 2020 Western Digital Corporation or its affiliates. 48c2ecf20Sopenharmony_ci */ 58c2ecf20Sopenharmony_ci 68c2ecf20Sopenharmony_ci#include <linux/efi.h> 78c2ecf20Sopenharmony_ci#include <linux/libfdt.h> 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci#include <asm/efi.h> 108c2ecf20Sopenharmony_ci#include <asm/sections.h> 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci#include "efistub.h" 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ci/* 158c2ecf20Sopenharmony_ci * RISC-V requires the kernel image to placed 2 MB aligned base for 64 bit and 168c2ecf20Sopenharmony_ci * 4MB for 32 bit. 178c2ecf20Sopenharmony_ci */ 188c2ecf20Sopenharmony_ci#ifdef CONFIG_64BIT 198c2ecf20Sopenharmony_ci#define MIN_KIMG_ALIGN SZ_2M 208c2ecf20Sopenharmony_ci#else 218c2ecf20Sopenharmony_ci#define MIN_KIMG_ALIGN SZ_4M 228c2ecf20Sopenharmony_ci#endif 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_citypedef void __noreturn (*jump_kernel_func)(unsigned int, unsigned long); 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_cistatic u32 hartid; 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_cistatic int get_boot_hartid_from_fdt(void) 298c2ecf20Sopenharmony_ci{ 308c2ecf20Sopenharmony_ci const void *fdt; 318c2ecf20Sopenharmony_ci int chosen_node, len; 328c2ecf20Sopenharmony_ci const fdt32_t *prop; 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci fdt = get_efi_config_table(DEVICE_TREE_GUID); 358c2ecf20Sopenharmony_ci if (!fdt) 368c2ecf20Sopenharmony_ci return -EINVAL; 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci chosen_node = fdt_path_offset(fdt, "/chosen"); 398c2ecf20Sopenharmony_ci if (chosen_node < 0) 408c2ecf20Sopenharmony_ci return -EINVAL; 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci prop = fdt_getprop((void *)fdt, chosen_node, "boot-hartid", &len); 438c2ecf20Sopenharmony_ci if (!prop || len != sizeof(u32)) 448c2ecf20Sopenharmony_ci return -EINVAL; 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci hartid = fdt32_to_cpu(*prop); 478c2ecf20Sopenharmony_ci return 0; 488c2ecf20Sopenharmony_ci} 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_ciefi_status_t check_platform_features(void) 518c2ecf20Sopenharmony_ci{ 528c2ecf20Sopenharmony_ci int ret; 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci ret = get_boot_hartid_from_fdt(); 558c2ecf20Sopenharmony_ci if (ret) { 568c2ecf20Sopenharmony_ci efi_err("/chosen/boot-hartid missing or invalid!\n"); 578c2ecf20Sopenharmony_ci return EFI_UNSUPPORTED; 588c2ecf20Sopenharmony_ci } 598c2ecf20Sopenharmony_ci return EFI_SUCCESS; 608c2ecf20Sopenharmony_ci} 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_civoid __noreturn efi_enter_kernel(unsigned long entrypoint, unsigned long fdt, 638c2ecf20Sopenharmony_ci unsigned long fdt_size) 648c2ecf20Sopenharmony_ci{ 658c2ecf20Sopenharmony_ci unsigned long stext_offset = _start_kernel - _start; 668c2ecf20Sopenharmony_ci unsigned long kernel_entry = entrypoint + stext_offset; 678c2ecf20Sopenharmony_ci jump_kernel_func jump_kernel = (jump_kernel_func)kernel_entry; 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ci /* 708c2ecf20Sopenharmony_ci * Jump to real kernel here with following constraints. 718c2ecf20Sopenharmony_ci * 1. MMU should be disabled. 728c2ecf20Sopenharmony_ci * 2. a0 should contain hartid 738c2ecf20Sopenharmony_ci * 3. a1 should DT address 748c2ecf20Sopenharmony_ci */ 758c2ecf20Sopenharmony_ci csr_write(CSR_SATP, 0); 768c2ecf20Sopenharmony_ci jump_kernel(hartid, fdt); 778c2ecf20Sopenharmony_ci} 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ciefi_status_t handle_kernel_image(unsigned long *image_addr, 808c2ecf20Sopenharmony_ci unsigned long *image_size, 818c2ecf20Sopenharmony_ci unsigned long *reserve_addr, 828c2ecf20Sopenharmony_ci unsigned long *reserve_size, 838c2ecf20Sopenharmony_ci efi_loaded_image_t *image) 848c2ecf20Sopenharmony_ci{ 858c2ecf20Sopenharmony_ci unsigned long kernel_size = 0; 868c2ecf20Sopenharmony_ci unsigned long preferred_addr; 878c2ecf20Sopenharmony_ci efi_status_t status; 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci kernel_size = _edata - _start; 908c2ecf20Sopenharmony_ci *image_addr = (unsigned long)_start; 918c2ecf20Sopenharmony_ci *image_size = kernel_size + (_end - _edata); 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci /* 948c2ecf20Sopenharmony_ci * RISC-V kernel maps PAGE_OFFSET virtual address to the same physical 958c2ecf20Sopenharmony_ci * address where kernel is booted. That's why kernel should boot from 968c2ecf20Sopenharmony_ci * as low as possible to avoid wastage of memory. Currently, dram_base 978c2ecf20Sopenharmony_ci * is occupied by the firmware. So the preferred address for kernel to 988c2ecf20Sopenharmony_ci * boot is next aligned address. If preferred address is not available, 998c2ecf20Sopenharmony_ci * relocate_kernel will fall back to efi_low_alloc_above to allocate 1008c2ecf20Sopenharmony_ci * lowest possible memory region as long as the address and size meets 1018c2ecf20Sopenharmony_ci * the alignment constraints. 1028c2ecf20Sopenharmony_ci */ 1038c2ecf20Sopenharmony_ci preferred_addr = MIN_KIMG_ALIGN; 1048c2ecf20Sopenharmony_ci status = efi_relocate_kernel(image_addr, kernel_size, *image_size, 1058c2ecf20Sopenharmony_ci preferred_addr, MIN_KIMG_ALIGN, 0x0); 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci if (status != EFI_SUCCESS) { 1088c2ecf20Sopenharmony_ci efi_err("Failed to relocate kernel\n"); 1098c2ecf20Sopenharmony_ci *image_size = 0; 1108c2ecf20Sopenharmony_ci } 1118c2ecf20Sopenharmony_ci return status; 1128c2ecf20Sopenharmony_ci} 113