1d7071743SAtish Patra // SPDX-License-Identifier: GPL-2.0
2d7071743SAtish Patra /*
3d7071743SAtish Patra  * Copyright (C) 2020 Western Digital Corporation or its affiliates.
4d7071743SAtish Patra  */
5d7071743SAtish Patra 
6d7071743SAtish Patra #include <linux/efi.h>
7d7071743SAtish Patra #include <linux/libfdt.h>
8d7071743SAtish Patra 
9d7071743SAtish Patra #include <asm/efi.h>
10d7071743SAtish Patra #include <asm/sections.h>
11d7071743SAtish Patra 
12d7071743SAtish Patra #include "efistub.h"
13d7071743SAtish Patra 
14d7071743SAtish Patra /*
15d7071743SAtish Patra  * RISC-V requires the kernel image to placed 2 MB aligned base for 64 bit and
16d7071743SAtish Patra  * 4MB for 32 bit.
17d7071743SAtish Patra  */
18d7071743SAtish Patra #ifdef CONFIG_64BIT
19d7071743SAtish Patra #define MIN_KIMG_ALIGN		SZ_2M
20d7071743SAtish Patra #else
21d7071743SAtish Patra #define MIN_KIMG_ALIGN		SZ_4M
22d7071743SAtish Patra #endif
23d7071743SAtish Patra 
24d7071743SAtish Patra typedef void __noreturn (*jump_kernel_func)(unsigned int, unsigned long);
25d7071743SAtish Patra 
26d7071743SAtish Patra static u32 hartid;
27d7071743SAtish Patra 
28dcf0c838SSunil V L static int get_boot_hartid_from_fdt(void)
29d7071743SAtish Patra {
30d7071743SAtish Patra 	const void *fdt;
31d7071743SAtish Patra 	int chosen_node, len;
32d7071743SAtish Patra 	const fdt32_t *prop;
33d7071743SAtish Patra 
34d7071743SAtish Patra 	fdt = get_efi_config_table(DEVICE_TREE_GUID);
35d7071743SAtish Patra 	if (!fdt)
36dcf0c838SSunil V L 		return -EINVAL;
37d7071743SAtish Patra 
38d7071743SAtish Patra 	chosen_node = fdt_path_offset(fdt, "/chosen");
39d7071743SAtish Patra 	if (chosen_node < 0)
40dcf0c838SSunil V L 		return -EINVAL;
41d7071743SAtish Patra 
42d7071743SAtish Patra 	prop = fdt_getprop((void *)fdt, chosen_node, "boot-hartid", &len);
43d7071743SAtish Patra 	if (!prop || len != sizeof(u32))
44dcf0c838SSunil V L 		return -EINVAL;
45d7071743SAtish Patra 
46dcf0c838SSunil V L 	hartid = fdt32_to_cpu(*prop);
47dcf0c838SSunil V L 	return 0;
48d7071743SAtish Patra }
49d7071743SAtish Patra 
50d7071743SAtish Patra efi_status_t check_platform_features(void)
51d7071743SAtish Patra {
52dcf0c838SSunil V L 	int ret;
53dcf0c838SSunil V L 
54dcf0c838SSunil V L 	ret = get_boot_hartid_from_fdt();
55dcf0c838SSunil V L 	if (ret) {
56d7071743SAtish Patra 		efi_err("/chosen/boot-hartid missing or invalid!\n");
57d7071743SAtish Patra 		return EFI_UNSUPPORTED;
58d7071743SAtish Patra 	}
59d7071743SAtish Patra 	return EFI_SUCCESS;
60d7071743SAtish Patra }
61d7071743SAtish Patra 
62d7071743SAtish Patra void __noreturn efi_enter_kernel(unsigned long entrypoint, unsigned long fdt,
63d7071743SAtish Patra 				 unsigned long fdt_size)
64d7071743SAtish Patra {
65d7071743SAtish Patra 	unsigned long stext_offset = _start_kernel - _start;
66d7071743SAtish Patra 	unsigned long kernel_entry = entrypoint + stext_offset;
67d7071743SAtish Patra 	jump_kernel_func jump_kernel = (jump_kernel_func)kernel_entry;
68d7071743SAtish Patra 
69d7071743SAtish Patra 	/*
70d7071743SAtish Patra 	 * Jump to real kernel here with following constraints.
71d7071743SAtish Patra 	 * 1. MMU should be disabled.
72d7071743SAtish Patra 	 * 2. a0 should contain hartid
73d7071743SAtish Patra 	 * 3. a1 should DT address
74d7071743SAtish Patra 	 */
75d7071743SAtish Patra 	csr_write(CSR_SATP, 0);
76d7071743SAtish Patra 	jump_kernel(hartid, fdt);
77d7071743SAtish Patra }
78d7071743SAtish Patra 
79d7071743SAtish Patra efi_status_t handle_kernel_image(unsigned long *image_addr,
80d7071743SAtish Patra 				 unsigned long *image_size,
81d7071743SAtish Patra 				 unsigned long *reserve_addr,
82d7071743SAtish Patra 				 unsigned long *reserve_size,
83*416a9f84SArd Biesheuvel 				 efi_loaded_image_t *image,
84*416a9f84SArd Biesheuvel 				 efi_handle_t image_handle)
85d7071743SAtish Patra {
86d7071743SAtish Patra 	unsigned long kernel_size = 0;
87d7071743SAtish Patra 	unsigned long preferred_addr;
88d7071743SAtish Patra 	efi_status_t status;
89d7071743SAtish Patra 
90d7071743SAtish Patra 	kernel_size = _edata - _start;
91d7071743SAtish Patra 	*image_addr = (unsigned long)_start;
92d7071743SAtish Patra 	*image_size = kernel_size + (_end - _edata);
93d7071743SAtish Patra 
94d7071743SAtish Patra 	/*
95d7071743SAtish Patra 	 * RISC-V kernel maps PAGE_OFFSET virtual address to the same physical
96d7071743SAtish Patra 	 * address where kernel is booted. That's why kernel should boot from
97d7071743SAtish Patra 	 * as low as possible to avoid wastage of memory. Currently, dram_base
98d7071743SAtish Patra 	 * is occupied by the firmware. So the preferred address for kernel to
99d7071743SAtish Patra 	 * boot is next aligned address. If preferred address is not available,
100d7071743SAtish Patra 	 * relocate_kernel will fall back to efi_low_alloc_above to allocate
101d7071743SAtish Patra 	 * lowest possible memory region as long as the address and size meets
102d7071743SAtish Patra 	 * the alignment constraints.
103d7071743SAtish Patra 	 */
104d7071743SAtish Patra 	preferred_addr = MIN_KIMG_ALIGN;
105d7071743SAtish Patra 	status = efi_relocate_kernel(image_addr, kernel_size, *image_size,
106d7071743SAtish Patra 				     preferred_addr, MIN_KIMG_ALIGN, 0x0);
107d7071743SAtish Patra 
108d7071743SAtish Patra 	if (status != EFI_SUCCESS) {
109d7071743SAtish Patra 		efi_err("Failed to relocate kernel\n");
110d7071743SAtish Patra 		*image_size = 0;
111d7071743SAtish Patra 	}
112d7071743SAtish Patra 	return status;
113d7071743SAtish Patra }
114