Loading...
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 | // SPDX-License-Identifier: GPL-2.0+ /* * Copyright (c) 2015 Google, Inc * * EFI information obtained here: * http://wiki.phoenix.com/wiki/index.php/EFI_BOOT_SERVICES * * Provides helper functions for use with the stub */ #define LOG_CATEGORY LOGC_EFI #include <debug_uart.h> #include <efi.h> #include <efi_api.h> #include <efi_stub.h> /* * true if we must use the hardware UART directory (EFI not available). This * is normally false, meaning that character output is sent to the efi_putc() * routine. Once exit-boot-services is called, we must either not use character * output at all, or use a hardware UART directly, if there is a driver * available. */ bool use_hw_uart; int efi_stub_exit_boot_services(void) { struct efi_priv *priv = efi_get_priv(); const struct efi_boot_services *boot = priv->boot; efi_uintn_t size; u32 version; efi_status_t ret; size = priv->memmap_alloc; ret = boot->get_memory_map(&size, priv->memmap_desc, &priv->memmap_key, &priv->memmap_desc_size, &version); if (ret) { printhex2(ret); puts(" Can't get memory map\n"); return ret; } ret = boot->exit_boot_services(priv->parent_image, priv->memmap_key); if (ret) return ret; return 0; } void *memcpy(void *dest, const void *src, size_t size) { unsigned char *dptr = dest; const unsigned char *ptr = src; const unsigned char *end = src + size; while (ptr < end) *dptr++ = *ptr++; return dest; } void *memset(void *inptr, int ch, size_t size) { char *ptr = inptr; char *end = ptr + size; while (ptr < end) *ptr++ = ch; return ptr; } int setup_info_table(struct efi_priv *priv, int size) { struct efi_info_hdr *info; efi_status_t ret; /* Get some memory for our info table */ priv->info_size = size; info = efi_malloc(priv, priv->info_size, &ret); if (ret) { printhex2(ret); puts(" No memory for info table: "); return ret; } memset(info, '\0', sizeof(*info)); info->version = EFI_TABLE_VERSION; info->hdr_size = sizeof(*info); priv->info = info; priv->next_hdr = (char *)info + info->hdr_size; return 0; } /** * add_entry_addr() - Add a new entry to the efi_info list * * This adds an entry, consisting of a tag and two lots of data. This avoids the * caller having to coalesce the data first * * @priv: Pointer to our private information which contains the list * @type: Type of the entry to add * @ptr1: Pointer to first data block to add * @size1: Size of first data block in bytes (can be 0) * @ptr2: Pointer to second data block to add * @size2: Size of second data block in bytes (can be 0) */ void add_entry_addr(struct efi_priv *priv, enum efi_entry_t type, void *ptr1, int size1, void *ptr2, int size2) { struct efi_entry_hdr *hdr = priv->next_hdr; hdr->type = type; hdr->size = size1 + size2; hdr->addr = 0; hdr->link = ALIGN(sizeof(*hdr) + hdr->size, 16); priv->next_hdr += hdr->link; memcpy(hdr + 1, ptr1, size1); memcpy((void *)(hdr + 1) + size1, ptr2, size2); priv->info->total_size = (ulong)priv->next_hdr - (ulong)priv->info; } static void efi_copy_code(struct efi_priv *priv) { memcpy((void *)priv->jump_addr, _binary_u_boot_bin_start, (ulong)_binary_u_boot_bin_end - (ulong)_binary_u_boot_bin_start); } /** * efi_store_memory_map() - Collect the memory-map info from EFI * * Collect the memory info and store it for later use, e.g. in calling * exit_boot_services() * * @priv: Pointer to private EFI structure * Returns: 0 if OK, non-zero on error */ static int efi_store_memory_map(struct efi_priv *priv) { struct efi_boot_services *boot = priv->sys_table->boottime; efi_uintn_t size, desc_size; efi_status_t ret; /* Get the memory map so we can switch off EFI */ size = 0; ret = boot->get_memory_map(&size, NULL, &priv->memmap_key, &priv->memmap_desc_size, &priv->memmap_version); if (ret != EFI_BUFFER_TOO_SMALL) { /* * Note this function avoids using printf() since it is not * available in the stub */ printhex2(EFI_BITS_PER_LONG); putc(' '); printhex2(ret); puts(" No memory map\n"); return ret; } /* * Since doing a malloc() may change the memory map and also we want to * be able to read the memory map in efi_call_exit_boot_services() * below, after more changes have happened */ priv->memmap_alloc = size + 1024; priv->memmap_size = priv->memmap_alloc; priv->memmap_desc = efi_malloc(priv, size, &ret); if (!priv->memmap_desc) { printhex2(ret); puts(" No memory for memory descriptor\n"); return ret; } ret = boot->get_memory_map(&priv->memmap_size, priv->memmap_desc, &priv->memmap_key, &desc_size, &priv->memmap_version); if (ret) { printhex2(ret); puts(" Can't get memory map\n"); return ret; } return 0; } /** * efi_main() - Start an EFI image * * This function is called by our EFI start-up code. It handles running * U-Boot. If it returns, EFI will continue. */ efi_status_t EFIAPI efi_main(efi_handle_t image, struct efi_system_table *sys_table) { struct efi_priv local_priv, *priv = &local_priv; struct efi_boot_services *boot = sys_table->boottime; struct efi_entry_memmap map; struct efi_gop *gop; struct efi_entry_gopmode mode; struct efi_entry_systable table; efi_guid_t efi_gop_guid = EFI_GRAPHICS_OUTPUT_PROTOCOL_GUID; efi_status_t ret; /* initially we can use the EFI UART */ use_hw_uart = false; ret = efi_init(priv, "Payload", image, sys_table, true); if (ret) { printhex2(ret); puts(" efi_init() failed\n"); return ret; } efi_set_priv(priv); ret = arch_efi_main_init(priv, boot); if (ret) return ret; ret = efi_store_memory_map(priv); if (ret) return ret; ret = setup_info_table(priv, priv->memmap_size + 128); if (ret) return ret; ret = boot->locate_protocol(&efi_gop_guid, NULL, (void **)&gop); if (ret) { puts(" GOP unavailable\n"); } else { mode.fb_base = gop->mode->fb_base; mode.fb_size = gop->mode->fb_size; mode.info_size = gop->mode->info_size; add_entry_addr(priv, EFIET_GOP_MODE, &mode, sizeof(mode), gop->mode->info, sizeof(struct efi_gop_mode_info)); } table.sys_table = (ulong)sys_table; add_entry_addr(priv, EFIET_SYS_TABLE, &table, sizeof(table), NULL, 0); ret = efi_stub_exit_boot_services(); if (ret) return ret; /* The EFI UART won't work now, switch to a debug one */ use_hw_uart = true; map.version = priv->memmap_version; map.desc_size = priv->memmap_desc_size; add_entry_addr(priv, EFIET_MEMORY_MAP, &map, sizeof(map), priv->memmap_desc, priv->memmap_size); add_entry_addr(priv, EFIET_END, NULL, 0, 0, 0); efi_copy_code(priv); /* This will only work if you patched your own debug uart into this file. */ #ifdef DEBUG puts("EFI table at "); printhex8((ulong)priv->info); puts(" size "); printhex8(priv->info->total_size); putc('\n'); #endif arch_efi_jump_to_payload(priv); return EFI_LOAD_ERROR; } |