1// SPDX-License-Identifier: GPL-2.0+
2/*
3 * Copyright (c) 2015 Google, Inc
4 *
5 * EFI information obtained here:
6 * http://wiki.phoenix.com/wiki/index.php/EFI_BOOT_SERVICES
7 *
8 * This file implements U-Boot running as an EFI application.
9 */
10
11#include <cpu_func.h>
12#include <debug_uart.h>
13#include <dm.h>
14#include <efi.h>
15#include <efi_api.h>
16#include <errno.h>
17#include <init.h>
18#include <malloc.h>
19#include <sysreset.h>
20#include <uuid.h>
21#include <asm/global_data.h>
22#include <linux/err.h>
23#include <linux/types.h>
24#include <asm/global_data.h>
25#include <dm/device-internal.h>
26#include <dm/lists.h>
27#include <dm/root.h>
28#include <mapmem.h>
29
30DECLARE_GLOBAL_DATA_PTR;
31
32int efi_info_get(enum efi_entry_t type, void **datap, int *sizep)
33{
34	return -ENOSYS;
35}
36
37int efi_get_mmap(struct efi_mem_desc **descp, int *sizep, uint *keyp,
38		 int *desc_sizep, uint *versionp)
39{
40	struct efi_priv *priv = efi_get_priv();
41	struct efi_boot_services *boot = priv->sys_table->boottime;
42	efi_uintn_t size, desc_size, key;
43	struct efi_mem_desc *desc;
44	efi_status_t ret;
45	u32 version;
46
47	/* Get the memory map so we can switch off EFI */
48	size = 0;
49	ret = boot->get_memory_map(&size, NULL, &key, &desc_size, &version);
50	if (ret != EFI_BUFFER_TOO_SMALL)
51		return log_msg_ret("get", -ENOMEM);
52
53	desc = malloc(size);
54	if (!desc)
55		return log_msg_ret("mem", -ENOMEM);
56
57	ret = boot->get_memory_map(&size, desc, &key, &desc_size, &version);
58	if (ret)
59		return log_msg_ret("get", -EINVAL);
60
61	*descp = desc;
62	*sizep = size;
63	*desc_sizep = desc_size;
64	*versionp = version;
65	*keyp = key;
66
67	return 0;
68}
69
70static efi_status_t setup_memory(struct efi_priv *priv)
71{
72	struct efi_boot_services *boot = priv->boot;
73	efi_physical_addr_t addr;
74	efi_status_t ret;
75	int pages;
76
77	/*
78	 * Use global_data_ptr instead of gd since it is an assignment. There
79	 * are very few assignments to global_data in U-Boot and this makes
80	 * it easier to find them.
81	 */
82	global_data_ptr = efi_malloc(priv, sizeof(struct global_data), &ret);
83	if (!global_data_ptr)
84		return ret;
85	memset(gd, '\0', sizeof(*gd));
86
87	gd->malloc_base = (ulong)efi_malloc(priv, CONFIG_VAL(SYS_MALLOC_F_LEN),
88					    &ret);
89	if (!gd->malloc_base)
90		return ret;
91	pages = CONFIG_EFI_RAM_SIZE >> 12;
92
93	/*
94	 * Don't allocate any memory above 4GB. U-Boot is a 32-bit application
95	 * so we want it to load below 4GB.
96	 */
97	addr = 1ULL << 32;
98	ret = boot->allocate_pages(EFI_ALLOCATE_MAX_ADDRESS,
99				   priv->image_data_type, pages, &addr);
100	if (ret) {
101		log_info("(using pool %lx) ", ret);
102		priv->ram_base = (ulong)efi_malloc(priv, CONFIG_EFI_RAM_SIZE,
103						   &ret);
104		if (!priv->ram_base)
105			return ret;
106		priv->use_pool_for_malloc = true;
107	} else {
108		log_info("(using allocated RAM address %lx) ", (ulong)addr);
109		priv->ram_base = addr;
110	}
111	gd->ram_size = pages << 12;
112
113	return 0;
114}
115
116/**
117 * free_memory() - Free memory used by the U-Boot app
118 *
119 * This frees memory allocated in setup_memory(), in preparation for returning
120 * to UEFI. It also zeroes the global_data pointer.
121 *
122 * @priv: Private EFI data
123 */
124static void free_memory(struct efi_priv *priv)
125{
126	struct efi_boot_services *boot = priv->boot;
127
128	if (priv->use_pool_for_malloc)
129		efi_free(priv, (void *)priv->ram_base);
130	else
131		boot->free_pages(priv->ram_base, gd->ram_size >> 12);
132
133	efi_free(priv, (void *)gd->malloc_base);
134	efi_free(priv, gd);
135	global_data_ptr = NULL;
136}
137
138static void scan_tables(struct efi_system_table *sys_table)
139{
140	efi_guid_t acpi = EFI_ACPI_TABLE_GUID;
141	uint i;
142
143	for (i = 0; i < sys_table->nr_tables; i++) {
144		struct efi_configuration_table *tab = &sys_table->tables[i];
145
146		if (!memcmp(&tab->guid, &acpi, sizeof(efi_guid_t)))
147			gd_set_acpi_start(map_to_sysmem(tab->table));
148	}
149}
150
151/**
152 * efi_main() - Start an EFI image
153 *
154 * This function is called by our EFI start-up code. It handles running
155 * U-Boot. If it returns, EFI will continue. Another way to get back to EFI
156 * is via reset_cpu().
157 */
158efi_status_t EFIAPI efi_main(efi_handle_t image,
159			     struct efi_system_table *sys_table)
160{
161	struct efi_priv local_priv, *priv = &local_priv;
162	efi_status_t ret;
163
164	/* Set up access to EFI data structures */
165	ret = efi_init(priv, "App", image, sys_table);
166	if (ret) {
167		printf("Failed to set up U-Boot: err=%lx\n", ret);
168		return ret;
169	}
170	efi_set_priv(priv);
171
172	/*
173	 * Set up the EFI debug UART so that printf() works. This is
174	 * implemented in the EFI serial driver, serial_efi.c. The application
175	 * can use printf() freely.
176	 */
177	debug_uart_init();
178
179	ret = setup_memory(priv);
180	if (ret) {
181		printf("Failed to set up memory: ret=%lx\n", ret);
182		return ret;
183	}
184
185	scan_tables(priv->sys_table);
186
187	/*
188	 * We could store the EFI memory map here, but it changes all the time,
189	 * so this is only useful for debugging.
190	 *
191	 * ret = efi_store_memory_map(priv);
192	 * if (ret)
193	 *	return ret;
194	 */
195
196	printf("starting\n");
197
198	board_init_f(GD_FLG_SKIP_RELOC);
199	board_init_r(NULL, 0);
200	free_memory(priv);
201
202	return EFI_SUCCESS;
203}
204
205static void efi_exit(void)
206{
207	struct efi_priv *priv = efi_get_priv();
208
209	free_memory(priv);
210	printf("U-Boot EFI exiting\n");
211	priv->boot->exit(priv->parent_image, EFI_SUCCESS, 0, NULL);
212}
213
214static int efi_sysreset_request(struct udevice *dev, enum sysreset_t type)
215{
216	efi_exit();
217
218	return -EINPROGRESS;
219}
220
221static const struct udevice_id efi_sysreset_ids[] = {
222	{ .compatible = "efi,reset" },
223	{ }
224};
225
226static struct sysreset_ops efi_sysreset_ops = {
227	.request = efi_sysreset_request,
228};
229
230U_BOOT_DRIVER(efi_sysreset) = {
231	.name = "efi-sysreset",
232	.id = UCLASS_SYSRESET,
233	.of_match = efi_sysreset_ids,
234	.ops = &efi_sysreset_ops,
235};
236