1// SPDX-License-Identifier: GPL-2.0
2/*
3 * Author: Yun Liu <liuyun@loongson.cn>
4 *         Huacai Chen <chenhuacai@loongson.cn>
5 * Copyright (C) 2020-2022 Loongson Technology Corporation Limited
6 */
7
8#include <asm/efi.h>
9#include <asm/addrspace.h>
10#include "efistub.h"
11
12#define BOOT_HEAP_SIZE 0x400000
13
14typedef void __noreturn (*kernel_entry_t)(bool efi, unsigned long cmdline,
15					  unsigned long systab, long kdump_reloc_offset);
16
17extern long kernel_entaddr;
18extern void decompress_kernel(unsigned long boot_heap_start, long kdump_reloc_offset);
19
20static unsigned char efi_heap[BOOT_HEAP_SIZE];
21static efi_guid_t screen_info_guid = LINUX_EFI_ARM_SCREEN_INFO_TABLE_GUID;
22static kernel_entry_t kernel_entry;
23
24struct screen_info *alloc_screen_info(void)
25{
26	efi_status_t status;
27	struct screen_info *si;
28
29	status = efi_bs_call(allocate_pool,
30			EFI_RUNTIME_SERVICES_DATA, sizeof(*si), (void **)&si);
31	if (status != EFI_SUCCESS)
32		return NULL;
33
34	memset(si, 0, sizeof(*si));
35
36	status = efi_bs_call(install_configuration_table, &screen_info_guid, si);
37	if (status == EFI_SUCCESS)
38		return si;
39
40	efi_bs_call(free_pool, si);
41
42	return NULL;
43}
44
45void free_screen_info(struct screen_info *si)
46{
47	if (!si)
48		return;
49
50	efi_bs_call(install_configuration_table, &screen_info_guid, NULL);
51	efi_bs_call(free_pool, si);
52}
53
54efi_status_t check_platform_features(void)
55{
56	return EFI_SUCCESS;
57}
58
59efi_status_t handle_kernel_image(unsigned long *image_addr,
60				 unsigned long *image_size,
61				 unsigned long *reserve_addr,
62				 unsigned long *reserve_size,
63				 efi_loaded_image_t *image)
64{
65	/* Config Direct Mapping */
66	csr_write64(CSR_DMW0_INIT, LOONGARCH_CSR_DMWIN0);
67	csr_write64(CSR_DMW1_INIT, LOONGARCH_CSR_DMWIN1);
68
69	decompress_kernel((unsigned long)efi_heap, 0);
70	kernel_entry = (kernel_entry_t)kernel_entaddr;
71
72	return EFI_SUCCESS;
73}
74
75struct exit_boot_struct {
76	efi_memory_desc_t	*runtime_map;
77	int			runtime_entry_count;
78};
79
80static efi_status_t exit_boot_func(struct efi_boot_memmap *map, void *priv)
81{
82	struct exit_boot_struct *p = priv;
83
84	/*
85	 * Update the memory map with virtual addresses. The function will also
86	 * populate @runtime_map with copies of just the EFI_MEMORY_RUNTIME
87	 * entries so that we can pass it straight to SetVirtualAddressMap()
88	 */
89	efi_get_virtmap(map->map, map->map_size, map->desc_size,
90			p->runtime_map, &p->runtime_entry_count);
91
92	return EFI_SUCCESS;
93}
94
95efi_status_t efi_boot_kernel(void *handle, efi_loaded_image_t *image,
96			     unsigned long kernel_addr, char *cmdline_ptr)
97{
98	struct exit_boot_struct priv;
99	unsigned long desc_size;
100	efi_status_t status;
101	u32 desc_ver;
102
103	status = efi_alloc_virtmap(&priv.runtime_map, &desc_size, &desc_ver);
104	if (status != EFI_SUCCESS) {
105		efi_err("Unable to retrieve UEFI memory map.\n");
106		return status;
107	}
108
109	efi_info("Exiting boot services\n");
110
111	efi_novamap = false;
112	status = efi_exit_boot_services(handle, &priv, exit_boot_func);
113	if (status != EFI_SUCCESS)
114		return status;
115
116	/* Install the new virtual address map */
117	efi_rt_call(set_virtual_address_map,
118		    priv.runtime_entry_count * desc_size, desc_size,
119		    desc_ver, priv.runtime_map);
120
121	kernel_entry(true, (unsigned long)cmdline_ptr, (unsigned long)efi_system_table, 0);
122}
123