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