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 * Loads a payload (U-Boot) within the EFI environment. This is built as an 9 * EFI application. It can be built either in 32-bit or 64-bit mode. 10 */ 11 12#include <debug_uart.h> 13#include <efi.h> 14#include <efi_api.h> 15#include <errno.h> 16#include <malloc.h> 17#include <ns16550.h> 18#include <asm/cpu.h> 19#include <asm/io.h> 20#include <linux/err.h> 21#include <linux/types.h> 22 23#ifndef CONFIG_X86 24/* 25 * Problem areas: 26 * - putc() uses the ns16550 address directly and assumed I/O access. Many 27 * platforms will use memory access 28 * get_codeseg32() is only meaningful on x86 29 */ 30#error "This file needs to be ported for use on architectures" 31#endif 32 33static bool use_uart; 34 35struct __packed desctab_info { 36 uint16_t limit; 37 uint64_t addr; 38 uint16_t pad; 39}; 40 41/* 42 * EFI uses Unicode and we don't. The easiest way to get a sensible output 43 * function is to use the U-Boot debug UART. We use EFI's console output 44 * function where available, and assume the built-in UART after that. We rely 45 * on EFI to set up the UART for us and just bring in the functions here. 46 * This last bit is a bit icky, but it's only for debugging anyway. We could 47 * build in ns16550.c with some effort, but this is a payload loader after 48 * all. 49 * 50 * Note: We avoid using printf() so we don't need to bring in lib/vsprintf.c. 51 * That would require some refactoring since we already build this for U-Boot. 52 * Building an EFI shared library version would have to be a separate stem. 53 * That might push us to using the SPL framework to build this stub. However 54 * that would involve a round of EFI-specific changes in SPL. Worth 55 * considering if we start needing more U-Boot functionality. Note that we 56 * could then move get_codeseg32() to arch/x86/cpu/cpu.c. 57 */ 58void _debug_uart_init(void) 59{ 60} 61 62void putc(const char ch) 63{ 64 struct efi_priv *priv = efi_get_priv(); 65 66 if (ch == '\n') 67 putc('\r'); 68 69 if (use_uart) { 70 struct ns16550 *com_port = (struct ns16550 *)0x3f8; 71 72 while ((inb((ulong)&com_port->lsr) & UART_LSR_THRE) == 0) 73 ; 74 outb(ch, (ulong)&com_port->thr); 75 } else { 76 efi_putc(priv, ch); 77 } 78} 79 80void puts(const char *str) 81{ 82 while (*str) 83 putc(*str++); 84} 85 86static void _debug_uart_putc(int ch) 87{ 88 putc(ch); 89} 90 91DEBUG_UART_FUNCS 92 93void *memcpy(void *dest, const void *src, size_t size) 94{ 95 unsigned char *dptr = dest; 96 const unsigned char *ptr = src; 97 const unsigned char *end = src + size; 98 99 while (ptr < end) 100 *dptr++ = *ptr++; 101 102 return dest; 103} 104 105void *memset(void *inptr, int ch, size_t size) 106{ 107 char *ptr = inptr; 108 char *end = ptr + size; 109 110 while (ptr < end) 111 *ptr++ = ch; 112 113 return ptr; 114} 115 116static void jump_to_uboot(ulong cs32, ulong addr, ulong info) 117{ 118#ifdef CONFIG_EFI_STUB_32BIT 119 /* 120 * U-Boot requires these parameters in registers, not on the stack. 121 * See _x86boot_start() for this code. 122 */ 123 typedef void (*func_t)(int bist, int unused, ulong info) 124 __attribute__((regparm(3))); 125 126 ((func_t)addr)(0, 0, info); 127#else 128 cpu_call32(cs32, CONFIG_TEXT_BASE, info); 129#endif 130} 131 132#ifdef CONFIG_EFI_STUB_64BIT 133static void get_gdt(struct desctab_info *info) 134{ 135 asm volatile ("sgdt %0" : : "m"(*info) : "memory"); 136} 137#endif 138 139static inline unsigned long read_cr3(void) 140{ 141 unsigned long val; 142 143 asm volatile("mov %%cr3,%0" : "=r" (val) : : "memory"); 144 return val; 145} 146 147/** 148 * get_codeseg32() - Find the code segment to use for 32-bit code 149 * 150 * U-Boot only works in 32-bit mode at present, so when booting from 64-bit 151 * EFI we must first change to 32-bit mode. To do this we need to find the 152 * correct code segment to use (an entry in the Global Descriptor Table). 153 * 154 * Return: code segment GDT offset, or 0 for 32-bit EFI, -ENOENT if not found 155 */ 156static int get_codeseg32(void) 157{ 158 int cs32 = 0; 159 160#ifdef CONFIG_EFI_STUB_64BIT 161 struct desctab_info gdt; 162 uint64_t *ptr; 163 int i; 164 165 get_gdt(&gdt); 166 for (ptr = (uint64_t *)(unsigned long)gdt.addr, i = 0; i < gdt.limit; 167 i += 8, ptr++) { 168 uint64_t desc = *ptr; 169 uint64_t base, limit; 170 171 /* 172 * Check that the target U-Boot jump address is within the 173 * selector and that the selector is of the right type. 174 */ 175 base = ((desc >> GDT_BASE_LOW_SHIFT) & GDT_BASE_LOW_MASK) | 176 ((desc >> GDT_BASE_HIGH_SHIFT) & GDT_BASE_HIGH_MASK) 177 << 16; 178 limit = ((desc >> GDT_LIMIT_LOW_SHIFT) & GDT_LIMIT_LOW_MASK) | 179 ((desc >> GDT_LIMIT_HIGH_SHIFT) & GDT_LIMIT_HIGH_MASK) 180 << 16; 181 base <<= 12; /* 4KB granularity */ 182 limit <<= 12; 183 if ((desc & GDT_PRESENT) && (desc & GDT_NOTSYS) && 184 !(desc & GDT_LONG) && (desc & GDT_4KB) && 185 (desc & GDT_32BIT) && (desc & GDT_CODE) && 186 CONFIG_TEXT_BASE > base && 187 CONFIG_TEXT_BASE + CONFIG_SYS_MONITOR_LEN < limit 188 ) { 189 cs32 = i; 190 break; 191 } 192 } 193 194#ifdef DEBUG 195 puts("\ngdt: "); 196 printhex8(gdt.limit); 197 puts(", addr: "); 198 printhex8(gdt.addr >> 32); 199 printhex8(gdt.addr); 200 for (i = 0; i < gdt.limit; i += 8) { 201 uint32_t *ptr = (uint32_t *)((unsigned long)gdt.addr + i); 202 203 puts("\n"); 204 printhex2(i); 205 puts(": "); 206 printhex8(ptr[1]); 207 puts(" "); 208 printhex8(ptr[0]); 209 } 210 puts("\n "); 211 puts("32-bit code segment: "); 212 printhex2(cs32); 213 puts("\n "); 214 215 puts("page_table: "); 216 printhex8(read_cr3()); 217 puts("\n "); 218#endif 219 if (!cs32) { 220 puts("Can't find 32-bit code segment\n"); 221 return -ENOENT; 222 } 223#endif 224 225 return cs32; 226} 227 228/** 229 * setup_info_table() - sets up a table containing information from EFI 230 * 231 * We must call exit_boot_services() before jumping out of the stub into U-Boot 232 * proper, so that U-Boot has full control of peripherals, memory, etc. 233 * 234 * Once we do this, we cannot call any boot-services functions so we must find 235 * out everything we need to before doing that. 236 * 237 * Set up a struct efi_info_hdr table which can hold various records (e.g. 238 * struct efi_entry_memmap) with information obtained from EFI. 239 * 240 * @priv: Pointer to our private information which contains the list 241 * @size: Size of the table to allocate 242 * Return: 0 if OK, non-zero on error 243 */ 244static int setup_info_table(struct efi_priv *priv, int size) 245{ 246 struct efi_info_hdr *info; 247 efi_status_t ret; 248 249 /* Get some memory for our info table */ 250 priv->info_size = size; 251 info = efi_malloc(priv, priv->info_size, &ret); 252 if (ret) { 253 printhex2(ret); 254 puts(" No memory for info table: "); 255 return ret; 256 } 257 258 memset(info, '\0', sizeof(*info)); 259 info->version = EFI_TABLE_VERSION; 260 info->hdr_size = sizeof(*info); 261 priv->info = info; 262 priv->next_hdr = (char *)info + info->hdr_size; 263 264 return 0; 265} 266 267/** 268 * add_entry_addr() - Add a new entry to the efi_info list 269 * 270 * This adds an entry, consisting of a tag and two lots of data. This avoids the 271 * caller having to coalesce the data first 272 * 273 * @priv: Pointer to our private information which contains the list 274 * @type: Type of the entry to add 275 * @ptr1: Pointer to first data block to add 276 * @size1: Size of first data block in bytes (can be 0) 277 * @ptr2: Pointer to second data block to add 278 * @size2: Size of second data block in bytes (can be 0) 279 */ 280static void add_entry_addr(struct efi_priv *priv, enum efi_entry_t type, 281 void *ptr1, int size1, void *ptr2, int size2) 282{ 283 struct efi_entry_hdr *hdr = priv->next_hdr; 284 285 hdr->type = type; 286 hdr->size = size1 + size2; 287 hdr->addr = 0; 288 hdr->link = ALIGN(sizeof(*hdr) + hdr->size, 16); 289 priv->next_hdr += hdr->link; 290 memcpy(hdr + 1, ptr1, size1); 291 memcpy((void *)(hdr + 1) + size1, ptr2, size2); 292 priv->info->total_size = (ulong)priv->next_hdr - (ulong)priv->info; 293} 294 295/** 296 * efi_main() - Start an EFI image 297 * 298 * This function is called by our EFI start-up code. It handles running 299 * U-Boot. If it returns, EFI will continue. 300 */ 301efi_status_t EFIAPI efi_main(efi_handle_t image, 302 struct efi_system_table *sys_table) 303{ 304 struct efi_priv local_priv, *priv = &local_priv; 305 struct efi_boot_services *boot = sys_table->boottime; 306 struct efi_entry_memmap map; 307 struct efi_gop *gop; 308 struct efi_entry_gopmode mode; 309 struct efi_entry_systable table; 310 efi_guid_t efi_gop_guid = EFI_GRAPHICS_OUTPUT_PROTOCOL_GUID; 311 efi_status_t ret; 312 int cs32; 313 314 ret = efi_init(priv, "Payload", image, sys_table); 315 if (ret) { 316 printhex2(ret); 317 puts(" efi_init() failed\n"); 318 return ret; 319 } 320 efi_set_priv(priv); 321 322 cs32 = get_codeseg32(); 323 if (cs32 < 0) 324 return EFI_UNSUPPORTED; 325 326 ret = efi_store_memory_map(priv); 327 if (ret) 328 return ret; 329 330 ret = setup_info_table(priv, priv->memmap_size + 128); 331 if (ret) 332 return ret; 333 334 ret = boot->locate_protocol(&efi_gop_guid, NULL, (void **)&gop); 335 if (ret) { 336 puts(" GOP unavailable\n"); 337 } else { 338 mode.fb_base = gop->mode->fb_base; 339 mode.fb_size = gop->mode->fb_size; 340 mode.info_size = gop->mode->info_size; 341 add_entry_addr(priv, EFIET_GOP_MODE, &mode, sizeof(mode), 342 gop->mode->info, 343 sizeof(struct efi_gop_mode_info)); 344 } 345 346 table.sys_table = (ulong)sys_table; 347 add_entry_addr(priv, EFIET_SYS_TABLE, &table, sizeof(table), NULL, 0); 348 349 ret = efi_call_exit_boot_services(); 350 if (ret) 351 return ret; 352 353 /* The EFI UART won't work now, switch to a debug one */ 354 use_uart = true; 355 356 map.version = priv->memmap_version; 357 map.desc_size = priv->memmap_desc_size; 358 add_entry_addr(priv, EFIET_MEMORY_MAP, &map, sizeof(map), 359 priv->memmap_desc, priv->memmap_size); 360 add_entry_addr(priv, EFIET_END, NULL, 0, 0, 0); 361 362 memcpy((void *)CONFIG_TEXT_BASE, _binary_u_boot_bin_start, 363 (ulong)_binary_u_boot_bin_end - 364 (ulong)_binary_u_boot_bin_start); 365 366#ifdef DEBUG 367 puts("EFI table at "); 368 printhex8((ulong)priv->info); 369 puts(" size "); 370 printhex8(priv->info->total_size); 371#endif 372 putc('\n'); 373 jump_to_uboot(cs32, CONFIG_TEXT_BASE, (ulong)priv->info); 374 375 return EFI_LOAD_ERROR; 376} 377