1476476e7SSimon Glass /* 2476476e7SSimon Glass * Copyright (c) 2015 Google, Inc 3476476e7SSimon Glass * 4476476e7SSimon Glass * SPDX-License-Identifier: GPL-2.0+ 5476476e7SSimon Glass * 6476476e7SSimon Glass * EFI information obtained here: 7476476e7SSimon Glass * http://wiki.phoenix.com/wiki/index.php/EFI_BOOT_SERVICES 8476476e7SSimon Glass * 996a8d409SSimon Glass * Loads a payload (U-Boot) within the EFI environment. This is built as an 1096a8d409SSimon Glass * EFI application. It can be built either in 32-bit or 64-bit mode. 11476476e7SSimon Glass */ 12476476e7SSimon Glass 13476476e7SSimon Glass #include <common.h> 14476476e7SSimon Glass #include <debug_uart.h> 15476476e7SSimon Glass #include <efi.h> 16476476e7SSimon Glass #include <efi_api.h> 17476476e7SSimon Glass #include <errno.h> 18476476e7SSimon Glass #include <ns16550.h> 19476476e7SSimon Glass #include <asm/cpu.h> 20476476e7SSimon Glass #include <asm/io.h> 21476476e7SSimon Glass #include <linux/err.h> 22476476e7SSimon Glass #include <linux/types.h> 23476476e7SSimon Glass 24476476e7SSimon Glass DECLARE_GLOBAL_DATA_PTR; 25476476e7SSimon Glass 26476476e7SSimon Glass #ifndef CONFIG_X86 27476476e7SSimon Glass /* 28476476e7SSimon Glass * Problem areas: 29476476e7SSimon Glass * - putc() uses the ns16550 address directly and assumed I/O access. Many 30476476e7SSimon Glass * platforms will use memory access 31476476e7SSimon Glass * get_codeseg32() is only meaningful on x86 32476476e7SSimon Glass */ 33476476e7SSimon Glass #error "This file needs to be ported for use on architectures" 34476476e7SSimon Glass #endif 35476476e7SSimon Glass 36476476e7SSimon Glass static struct efi_priv *global_priv; 37476476e7SSimon Glass static bool use_uart; 38476476e7SSimon Glass 39476476e7SSimon Glass struct __packed desctab_info { 40476476e7SSimon Glass uint16_t limit; 41476476e7SSimon Glass uint64_t addr; 42476476e7SSimon Glass uint16_t pad; 43476476e7SSimon Glass }; 44476476e7SSimon Glass 45476476e7SSimon Glass /* 46476476e7SSimon Glass * EFI uses Unicode and we don't. The easiest way to get a sensible output 47476476e7SSimon Glass * function is to use the U-Boot debug UART. We use EFI's console output 48476476e7SSimon Glass * function where available, and assume the built-in UART after that. We rely 49476476e7SSimon Glass * on EFI to set up the UART for us and just bring in the functions here. 50476476e7SSimon Glass * This last bit is a bit icky, but it's only for debugging anyway. We could 51476476e7SSimon Glass * build in ns16550.c with some effort, but this is a payload loader after 52476476e7SSimon Glass * all. 53476476e7SSimon Glass * 54476476e7SSimon Glass * Note: We avoid using printf() so we don't need to bring in lib/vsprintf.c. 55476476e7SSimon Glass * That would require some refactoring since we already build this for U-Boot. 56476476e7SSimon Glass * Building an EFI shared library version would have to be a separate stem. 57476476e7SSimon Glass * That might push us to using the SPL framework to build this stub. However 58476476e7SSimon Glass * that would involve a round of EFI-specific changes in SPL. Worth 59476476e7SSimon Glass * considering if we start needing more U-Boot functionality. Note that we 60476476e7SSimon Glass * could then move get_codeseg32() to arch/x86/cpu/cpu.c. 61476476e7SSimon Glass */ 6297b05973SSimon Glass void _debug_uart_init(void) 63476476e7SSimon Glass { 64476476e7SSimon Glass } 65476476e7SSimon Glass 66476476e7SSimon Glass void putc(const char ch) 67476476e7SSimon Glass { 68*075bb5c6SBin Meng if (ch == '\n') 69*075bb5c6SBin Meng putc('\r'); 70*075bb5c6SBin Meng 71476476e7SSimon Glass if (use_uart) { 72476476e7SSimon Glass NS16550_t com_port = (NS16550_t)0x3f8; 73476476e7SSimon Glass 74476476e7SSimon Glass while ((inb((ulong)&com_port->lsr) & UART_LSR_THRE) == 0) 75476476e7SSimon Glass ; 76476476e7SSimon Glass outb(ch, (ulong)&com_port->thr); 77476476e7SSimon Glass } else { 78476476e7SSimon Glass efi_putc(global_priv, ch); 79476476e7SSimon Glass } 80476476e7SSimon Glass } 81476476e7SSimon Glass 82476476e7SSimon Glass void puts(const char *str) 83476476e7SSimon Glass { 84476476e7SSimon Glass while (*str) 85476476e7SSimon Glass putc(*str++); 86476476e7SSimon Glass } 87476476e7SSimon Glass 88476476e7SSimon Glass static void _debug_uart_putc(int ch) 89476476e7SSimon Glass { 90476476e7SSimon Glass putc(ch); 91476476e7SSimon Glass } 92476476e7SSimon Glass 93476476e7SSimon Glass DEBUG_UART_FUNCS 94476476e7SSimon Glass 95476476e7SSimon Glass void *memcpy(void *dest, const void *src, size_t size) 96476476e7SSimon Glass { 97476476e7SSimon Glass unsigned char *dptr = dest; 98476476e7SSimon Glass const unsigned char *ptr = src; 99476476e7SSimon Glass const unsigned char *end = src + size; 100476476e7SSimon Glass 101476476e7SSimon Glass while (ptr < end) 102476476e7SSimon Glass *dptr++ = *ptr++; 103476476e7SSimon Glass 104476476e7SSimon Glass return dest; 105476476e7SSimon Glass } 106476476e7SSimon Glass 107476476e7SSimon Glass void *memset(void *inptr, int ch, size_t size) 108476476e7SSimon Glass { 109476476e7SSimon Glass char *ptr = inptr; 110476476e7SSimon Glass char *end = ptr + size; 111476476e7SSimon Glass 112476476e7SSimon Glass while (ptr < end) 113476476e7SSimon Glass *ptr++ = ch; 114476476e7SSimon Glass 115476476e7SSimon Glass return ptr; 116476476e7SSimon Glass } 117476476e7SSimon Glass 118476476e7SSimon Glass static void jump_to_uboot(ulong cs32, ulong addr, ulong info) 119476476e7SSimon Glass { 120476476e7SSimon Glass #ifdef CONFIG_EFI_STUB_32BIT 121476476e7SSimon Glass /* 122476476e7SSimon Glass * U-Boot requires these parameters in registers, not on the stack. 123476476e7SSimon Glass * See _x86boot_start() for this code. 124476476e7SSimon Glass */ 125476476e7SSimon Glass typedef void (*func_t)(int bist, int unused, ulong info) 126476476e7SSimon Glass __attribute__((regparm(3))); 127476476e7SSimon Glass 128476476e7SSimon Glass ((func_t)addr)(0, 0, info); 129476476e7SSimon Glass #else 13096a8d409SSimon Glass cpu_call32(cs32, CONFIG_SYS_TEXT_BASE, info); 131476476e7SSimon Glass #endif 132476476e7SSimon Glass } 133476476e7SSimon Glass 13496a8d409SSimon Glass #ifdef CONFIG_EFI_STUB_64BIT 135476476e7SSimon Glass static void get_gdt(struct desctab_info *info) 136476476e7SSimon Glass { 137476476e7SSimon Glass asm volatile ("sgdt %0" : : "m"(*info) : "memory"); 138476476e7SSimon Glass } 13996a8d409SSimon Glass #endif 140476476e7SSimon Glass 141476476e7SSimon Glass static inline unsigned long read_cr3(void) 142476476e7SSimon Glass { 143476476e7SSimon Glass unsigned long val; 144476476e7SSimon Glass 145476476e7SSimon Glass asm volatile("mov %%cr3,%0" : "=r" (val) : : "memory"); 146476476e7SSimon Glass return val; 147476476e7SSimon Glass } 148476476e7SSimon Glass 149476476e7SSimon Glass /** 150476476e7SSimon Glass * get_codeseg32() - Find the code segment to use for 32-bit code 151476476e7SSimon Glass * 152476476e7SSimon Glass * U-Boot only works in 32-bit mode at present, so when booting from 64-bit 153476476e7SSimon Glass * EFI we must first change to 32-bit mode. To do this we need to find the 154476476e7SSimon Glass * correct code segment to use (an entry in the Global Descriptor Table). 155476476e7SSimon Glass * 156476476e7SSimon Glass * @return code segment GDT offset, or 0 for 32-bit EFI, -ENOENT if not found 157476476e7SSimon Glass */ 158476476e7SSimon Glass static int get_codeseg32(void) 159476476e7SSimon Glass { 160476476e7SSimon Glass int cs32 = 0; 161476476e7SSimon Glass 16296a8d409SSimon Glass #ifdef CONFIG_EFI_STUB_64BIT 16396a8d409SSimon Glass struct desctab_info gdt; 16496a8d409SSimon Glass uint64_t *ptr; 16596a8d409SSimon Glass int i; 16696a8d409SSimon Glass 16796a8d409SSimon Glass get_gdt(&gdt); 16896a8d409SSimon Glass for (ptr = (uint64_t *)(unsigned long)gdt.addr, i = 0; i < gdt.limit; 16996a8d409SSimon Glass i += 8, ptr++) { 17096a8d409SSimon Glass uint64_t desc = *ptr; 17196a8d409SSimon Glass uint64_t base, limit; 17296a8d409SSimon Glass 17396a8d409SSimon Glass /* 17496a8d409SSimon Glass * Check that the target U-Boot jump address is within the 17596a8d409SSimon Glass * selector and that the selector is of the right type. 17696a8d409SSimon Glass */ 17796a8d409SSimon Glass base = ((desc >> GDT_BASE_LOW_SHIFT) & GDT_BASE_LOW_MASK) | 17896a8d409SSimon Glass ((desc >> GDT_BASE_HIGH_SHIFT) & GDT_BASE_HIGH_MASK) 17996a8d409SSimon Glass << 16; 18096a8d409SSimon Glass limit = ((desc >> GDT_LIMIT_LOW_SHIFT) & GDT_LIMIT_LOW_MASK) | 18196a8d409SSimon Glass ((desc >> GDT_LIMIT_HIGH_SHIFT) & GDT_LIMIT_HIGH_MASK) 18296a8d409SSimon Glass << 16; 18396a8d409SSimon Glass base <<= 12; /* 4KB granularity */ 18496a8d409SSimon Glass limit <<= 12; 18596a8d409SSimon Glass if ((desc & GDT_PRESENT) && (desc && GDT_NOTSYS) && 18696a8d409SSimon Glass !(desc & GDT_LONG) && (desc & GDT_4KB) && 18796a8d409SSimon Glass (desc & GDT_32BIT) && (desc & GDT_CODE) && 18896a8d409SSimon Glass CONFIG_SYS_TEXT_BASE > base && 18996a8d409SSimon Glass CONFIG_SYS_TEXT_BASE + CONFIG_SYS_MONITOR_LEN < limit 19096a8d409SSimon Glass ) { 19196a8d409SSimon Glass cs32 = i; 19296a8d409SSimon Glass break; 19396a8d409SSimon Glass } 19496a8d409SSimon Glass } 19596a8d409SSimon Glass 19696a8d409SSimon Glass #ifdef DEBUG 19796a8d409SSimon Glass puts("\ngdt: "); 19896a8d409SSimon Glass printhex8(gdt.limit); 19996a8d409SSimon Glass puts(", addr: "); 20096a8d409SSimon Glass printhex8(gdt.addr >> 32); 20196a8d409SSimon Glass printhex8(gdt.addr); 20296a8d409SSimon Glass for (i = 0; i < gdt.limit; i += 8) { 20396a8d409SSimon Glass uint32_t *ptr = (uint32_t *)((unsigned long)gdt.addr + i); 20496a8d409SSimon Glass 20596a8d409SSimon Glass puts("\n"); 20696a8d409SSimon Glass printhex2(i); 20796a8d409SSimon Glass puts(": "); 20896a8d409SSimon Glass printhex8(ptr[1]); 20996a8d409SSimon Glass puts(" "); 21096a8d409SSimon Glass printhex8(ptr[0]); 21196a8d409SSimon Glass } 21296a8d409SSimon Glass puts("\n "); 21396a8d409SSimon Glass puts("32-bit code segment: "); 21496a8d409SSimon Glass printhex2(cs32); 21596a8d409SSimon Glass puts("\n "); 21696a8d409SSimon Glass 21796a8d409SSimon Glass puts("page_table: "); 21896a8d409SSimon Glass printhex8(read_cr3()); 21996a8d409SSimon Glass puts("\n "); 22096a8d409SSimon Glass #endif 22196a8d409SSimon Glass if (!cs32) { 22296a8d409SSimon Glass puts("Can't find 32-bit code segment\n"); 22396a8d409SSimon Glass return -ENOENT; 22496a8d409SSimon Glass } 22596a8d409SSimon Glass #endif 22696a8d409SSimon Glass 227476476e7SSimon Glass return cs32; 228476476e7SSimon Glass } 229476476e7SSimon Glass 230476476e7SSimon Glass static int setup_info_table(struct efi_priv *priv, int size) 231476476e7SSimon Glass { 232476476e7SSimon Glass struct efi_info_hdr *info; 233476476e7SSimon Glass efi_status_t ret; 234476476e7SSimon Glass 235476476e7SSimon Glass /* Get some memory for our info table */ 236476476e7SSimon Glass priv->info_size = size; 237476476e7SSimon Glass info = efi_malloc(priv, priv->info_size, &ret); 238476476e7SSimon Glass if (ret) { 239476476e7SSimon Glass printhex2(ret); 240476476e7SSimon Glass puts(" No memory for info table: "); 241476476e7SSimon Glass return ret; 242476476e7SSimon Glass } 243476476e7SSimon Glass 244476476e7SSimon Glass memset(info, '\0', sizeof(*info)); 245476476e7SSimon Glass info->version = EFI_TABLE_VERSION; 246476476e7SSimon Glass info->hdr_size = sizeof(*info); 247476476e7SSimon Glass priv->info = info; 248476476e7SSimon Glass priv->next_hdr = (char *)info + info->hdr_size; 249476476e7SSimon Glass 250476476e7SSimon Glass return 0; 251476476e7SSimon Glass } 252476476e7SSimon Glass 253476476e7SSimon Glass static void add_entry_addr(struct efi_priv *priv, enum efi_entry_t type, 254476476e7SSimon Glass void *ptr1, int size1, void *ptr2, int size2) 255476476e7SSimon Glass { 256476476e7SSimon Glass struct efi_entry_hdr *hdr = priv->next_hdr; 257476476e7SSimon Glass 258476476e7SSimon Glass hdr->type = type; 259476476e7SSimon Glass hdr->size = size1 + size2; 260476476e7SSimon Glass hdr->addr = 0; 261476476e7SSimon Glass hdr->link = ALIGN(sizeof(*hdr) + hdr->size, 16); 262476476e7SSimon Glass priv->next_hdr += hdr->link; 263476476e7SSimon Glass memcpy(hdr + 1, ptr1, size1); 264476476e7SSimon Glass memcpy((void *)(hdr + 1) + size1, ptr2, size2); 265476476e7SSimon Glass priv->info->total_size = (ulong)priv->next_hdr - (ulong)priv->info; 266476476e7SSimon Glass } 267476476e7SSimon Glass 268476476e7SSimon Glass /** 269476476e7SSimon Glass * efi_main() - Start an EFI image 270476476e7SSimon Glass * 271476476e7SSimon Glass * This function is called by our EFI start-up code. It handles running 272476476e7SSimon Glass * U-Boot. If it returns, EFI will continue. 273476476e7SSimon Glass */ 274476476e7SSimon Glass efi_status_t efi_main(efi_handle_t image, struct efi_system_table *sys_table) 275476476e7SSimon Glass { 276476476e7SSimon Glass struct efi_priv local_priv, *priv = &local_priv; 277476476e7SSimon Glass struct efi_boot_services *boot = sys_table->boottime; 278476476e7SSimon Glass struct efi_mem_desc *desc; 279476476e7SSimon Glass struct efi_entry_memmap map; 280476476e7SSimon Glass ulong key, desc_size, size; 281476476e7SSimon Glass efi_status_t ret; 282476476e7SSimon Glass u32 version; 283476476e7SSimon Glass int cs32; 284476476e7SSimon Glass 285476476e7SSimon Glass ret = efi_init(priv, "Payload", image, sys_table); 286476476e7SSimon Glass if (ret) { 287476476e7SSimon Glass printhex2(ret); puts(" efi_init() failed\n"); 288476476e7SSimon Glass return ret; 289476476e7SSimon Glass } 290476476e7SSimon Glass global_priv = priv; 291476476e7SSimon Glass 292476476e7SSimon Glass cs32 = get_codeseg32(); 293476476e7SSimon Glass if (cs32 < 0) 294476476e7SSimon Glass return EFI_UNSUPPORTED; 295476476e7SSimon Glass 296476476e7SSimon Glass /* Get the memory map so we can switch off EFI */ 297476476e7SSimon Glass size = 0; 298476476e7SSimon Glass ret = boot->get_memory_map(&size, NULL, &key, &desc_size, &version); 299476476e7SSimon Glass if (ret != EFI_BUFFER_TOO_SMALL) { 300476476e7SSimon Glass printhex2(BITS_PER_LONG); 301476476e7SSimon Glass printhex2(ret); 302476476e7SSimon Glass puts(" No memory map\n"); 303476476e7SSimon Glass return ret; 304476476e7SSimon Glass } 305476476e7SSimon Glass size += 1024; /* Since doing a malloc() may change the memory map! */ 306476476e7SSimon Glass desc = efi_malloc(priv, size, &ret); 307476476e7SSimon Glass if (!desc) { 308476476e7SSimon Glass printhex2(ret); 309476476e7SSimon Glass puts(" No memory for memory descriptor: "); 310476476e7SSimon Glass return ret; 311476476e7SSimon Glass } 312476476e7SSimon Glass ret = setup_info_table(priv, size + 128); 313476476e7SSimon Glass if (ret) 314476476e7SSimon Glass return ret; 315476476e7SSimon Glass 316476476e7SSimon Glass ret = boot->get_memory_map(&size, desc, &key, &desc_size, &version); 317476476e7SSimon Glass if (ret) { 318476476e7SSimon Glass printhex2(ret); 319476476e7SSimon Glass puts(" Can't get memory map\n"); 320476476e7SSimon Glass return ret; 321476476e7SSimon Glass } 322476476e7SSimon Glass 323476476e7SSimon Glass ret = boot->exit_boot_services(image, key); 324476476e7SSimon Glass if (ret) { 325476476e7SSimon Glass /* 326476476e7SSimon Glass * Unfortunately it happens that we cannot exit boot services 327476476e7SSimon Glass * the first time. But the second time it work. I don't know 328476476e7SSimon Glass * why but this seems to be a repeatable problem. To get 329476476e7SSimon Glass * around it, just try again. 330476476e7SSimon Glass */ 331476476e7SSimon Glass printhex2(ret); 332476476e7SSimon Glass puts(" Can't exit boot services\n"); 333476476e7SSimon Glass size = sizeof(desc); 334476476e7SSimon Glass ret = boot->get_memory_map(&size, desc, &key, &desc_size, 335476476e7SSimon Glass &version); 336476476e7SSimon Glass if (ret) { 337476476e7SSimon Glass printhex2(ret); 338476476e7SSimon Glass puts(" Can't get memory map\n"); 339476476e7SSimon Glass return ret; 340476476e7SSimon Glass } 341476476e7SSimon Glass ret = boot->exit_boot_services(image, key); 342476476e7SSimon Glass if (ret) { 343476476e7SSimon Glass printhex2(ret); 344476476e7SSimon Glass puts(" Can't exit boot services 2\n"); 345476476e7SSimon Glass return ret; 346476476e7SSimon Glass } 347476476e7SSimon Glass } 348476476e7SSimon Glass 349476476e7SSimon Glass map.version = version; 350476476e7SSimon Glass map.desc_size = desc_size; 351476476e7SSimon Glass add_entry_addr(priv, EFIET_MEMORY_MAP, &map, sizeof(map), desc, size); 352476476e7SSimon Glass add_entry_addr(priv, EFIET_END, NULL, 0, 0, 0); 353476476e7SSimon Glass 354476476e7SSimon Glass /* The EFI UART won't work now, switch to a debug one */ 355476476e7SSimon Glass use_uart = true; 356476476e7SSimon Glass 357476476e7SSimon Glass memcpy((void *)CONFIG_SYS_TEXT_BASE, _binary_u_boot_dtb_bin_start, 358476476e7SSimon Glass (ulong)_binary_u_boot_dtb_bin_end - 359476476e7SSimon Glass (ulong)_binary_u_boot_dtb_bin_start); 360476476e7SSimon Glass 361476476e7SSimon Glass #ifdef DEBUG 362476476e7SSimon Glass puts("EFI table at "); 363476476e7SSimon Glass printhex8((ulong)priv->info); 364476476e7SSimon Glass puts(" size "); 365476476e7SSimon Glass printhex8(priv->info->total_size); 366476476e7SSimon Glass #endif 367476476e7SSimon Glass putc('\n'); 368476476e7SSimon Glass jump_to_uboot(cs32, CONFIG_SYS_TEXT_BASE, (ulong)priv->info); 369476476e7SSimon Glass 370476476e7SSimon Glass return EFI_LOAD_ERROR; 371476476e7SSimon Glass } 372