18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Helper functions used by the EFI stub on multiple 48c2ecf20Sopenharmony_ci * architectures. This should be #included by the EFI stub 58c2ecf20Sopenharmony_ci * implementation files. 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Copyright 2011 Intel Corporation; author Matt Fleming 88c2ecf20Sopenharmony_ci */ 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#include <stdarg.h> 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci#include <linux/ctype.h> 138c2ecf20Sopenharmony_ci#include <linux/efi.h> 148c2ecf20Sopenharmony_ci#include <linux/kernel.h> 158c2ecf20Sopenharmony_ci#include <linux/printk.h> /* For CONSOLE_LOGLEVEL_* */ 168c2ecf20Sopenharmony_ci#include <asm/efi.h> 178c2ecf20Sopenharmony_ci#include <asm/setup.h> 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_ci#include "efistub.h" 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_cibool efi_nochunk; 228c2ecf20Sopenharmony_cibool efi_nokaslr = !IS_ENABLED(CONFIG_RANDOMIZE_BASE); 238c2ecf20Sopenharmony_cibool efi_noinitrd; 248c2ecf20Sopenharmony_ciint efi_loglevel = CONSOLE_LOGLEVEL_DEFAULT; 258c2ecf20Sopenharmony_cibool efi_novamap; 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_cistatic bool efi_nosoftreserve; 288c2ecf20Sopenharmony_cistatic bool efi_disable_pci_dma = IS_ENABLED(CONFIG_EFI_DISABLE_PCI_DMA); 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_cibool __pure __efi_soft_reserve_enabled(void) 318c2ecf20Sopenharmony_ci{ 328c2ecf20Sopenharmony_ci return !efi_nosoftreserve; 338c2ecf20Sopenharmony_ci} 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ci/** 368c2ecf20Sopenharmony_ci * efi_char16_puts() - Write a UCS-2 encoded string to the console 378c2ecf20Sopenharmony_ci * @str: UCS-2 encoded string 388c2ecf20Sopenharmony_ci */ 398c2ecf20Sopenharmony_civoid efi_char16_puts(efi_char16_t *str) 408c2ecf20Sopenharmony_ci{ 418c2ecf20Sopenharmony_ci efi_call_proto(efi_table_attr(efi_system_table, con_out), 428c2ecf20Sopenharmony_ci output_string, str); 438c2ecf20Sopenharmony_ci} 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_cistatic 468c2ecf20Sopenharmony_ciu32 utf8_to_utf32(const u8 **s8) 478c2ecf20Sopenharmony_ci{ 488c2ecf20Sopenharmony_ci u32 c32; 498c2ecf20Sopenharmony_ci u8 c0, cx; 508c2ecf20Sopenharmony_ci size_t clen, i; 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ci c0 = cx = *(*s8)++; 538c2ecf20Sopenharmony_ci /* 548c2ecf20Sopenharmony_ci * The position of the most-significant 0 bit gives us the length of 558c2ecf20Sopenharmony_ci * a multi-octet encoding. 568c2ecf20Sopenharmony_ci */ 578c2ecf20Sopenharmony_ci for (clen = 0; cx & 0x80; ++clen) 588c2ecf20Sopenharmony_ci cx <<= 1; 598c2ecf20Sopenharmony_ci /* 608c2ecf20Sopenharmony_ci * If the 0 bit is in position 8, this is a valid single-octet 618c2ecf20Sopenharmony_ci * encoding. If the 0 bit is in position 7 or positions 1-3, the 628c2ecf20Sopenharmony_ci * encoding is invalid. 638c2ecf20Sopenharmony_ci * In either case, we just return the first octet. 648c2ecf20Sopenharmony_ci */ 658c2ecf20Sopenharmony_ci if (clen < 2 || clen > 4) 668c2ecf20Sopenharmony_ci return c0; 678c2ecf20Sopenharmony_ci /* Get the bits from the first octet. */ 688c2ecf20Sopenharmony_ci c32 = cx >> clen--; 698c2ecf20Sopenharmony_ci for (i = 0; i < clen; ++i) { 708c2ecf20Sopenharmony_ci /* Trailing octets must have 10 in most significant bits. */ 718c2ecf20Sopenharmony_ci cx = (*s8)[i] ^ 0x80; 728c2ecf20Sopenharmony_ci if (cx & 0xc0) 738c2ecf20Sopenharmony_ci return c0; 748c2ecf20Sopenharmony_ci c32 = (c32 << 6) | cx; 758c2ecf20Sopenharmony_ci } 768c2ecf20Sopenharmony_ci /* 778c2ecf20Sopenharmony_ci * Check for validity: 788c2ecf20Sopenharmony_ci * - The character must be in the Unicode range. 798c2ecf20Sopenharmony_ci * - It must not be a surrogate. 808c2ecf20Sopenharmony_ci * - It must be encoded using the correct number of octets. 818c2ecf20Sopenharmony_ci */ 828c2ecf20Sopenharmony_ci if (c32 > 0x10ffff || 838c2ecf20Sopenharmony_ci (c32 & 0xf800) == 0xd800 || 848c2ecf20Sopenharmony_ci clen != (c32 >= 0x80) + (c32 >= 0x800) + (c32 >= 0x10000)) 858c2ecf20Sopenharmony_ci return c0; 868c2ecf20Sopenharmony_ci *s8 += clen; 878c2ecf20Sopenharmony_ci return c32; 888c2ecf20Sopenharmony_ci} 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci/** 918c2ecf20Sopenharmony_ci * efi_puts() - Write a UTF-8 encoded string to the console 928c2ecf20Sopenharmony_ci * @str: UTF-8 encoded string 938c2ecf20Sopenharmony_ci */ 948c2ecf20Sopenharmony_civoid efi_puts(const char *str) 958c2ecf20Sopenharmony_ci{ 968c2ecf20Sopenharmony_ci efi_char16_t buf[128]; 978c2ecf20Sopenharmony_ci size_t pos = 0, lim = ARRAY_SIZE(buf); 988c2ecf20Sopenharmony_ci const u8 *s8 = (const u8 *)str; 998c2ecf20Sopenharmony_ci u32 c32; 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci while (*s8) { 1028c2ecf20Sopenharmony_ci if (*s8 == '\n') 1038c2ecf20Sopenharmony_ci buf[pos++] = L'\r'; 1048c2ecf20Sopenharmony_ci c32 = utf8_to_utf32(&s8); 1058c2ecf20Sopenharmony_ci if (c32 < 0x10000) { 1068c2ecf20Sopenharmony_ci /* Characters in plane 0 use a single word. */ 1078c2ecf20Sopenharmony_ci buf[pos++] = c32; 1088c2ecf20Sopenharmony_ci } else { 1098c2ecf20Sopenharmony_ci /* 1108c2ecf20Sopenharmony_ci * Characters in other planes encode into a surrogate 1118c2ecf20Sopenharmony_ci * pair. 1128c2ecf20Sopenharmony_ci */ 1138c2ecf20Sopenharmony_ci buf[pos++] = (0xd800 - (0x10000 >> 10)) + (c32 >> 10); 1148c2ecf20Sopenharmony_ci buf[pos++] = 0xdc00 + (c32 & 0x3ff); 1158c2ecf20Sopenharmony_ci } 1168c2ecf20Sopenharmony_ci if (*s8 == '\0' || pos >= lim - 2) { 1178c2ecf20Sopenharmony_ci buf[pos] = L'\0'; 1188c2ecf20Sopenharmony_ci efi_char16_puts(buf); 1198c2ecf20Sopenharmony_ci pos = 0; 1208c2ecf20Sopenharmony_ci } 1218c2ecf20Sopenharmony_ci } 1228c2ecf20Sopenharmony_ci} 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci/** 1258c2ecf20Sopenharmony_ci * efi_printk() - Print a kernel message 1268c2ecf20Sopenharmony_ci * @fmt: format string 1278c2ecf20Sopenharmony_ci * 1288c2ecf20Sopenharmony_ci * The first letter of the format string is used to determine the logging level 1298c2ecf20Sopenharmony_ci * of the message. If the level is less then the current EFI logging level, the 1308c2ecf20Sopenharmony_ci * message is suppressed. The message will be truncated to 255 bytes. 1318c2ecf20Sopenharmony_ci * 1328c2ecf20Sopenharmony_ci * Return: number of printed characters 1338c2ecf20Sopenharmony_ci */ 1348c2ecf20Sopenharmony_ciint efi_printk(const char *fmt, ...) 1358c2ecf20Sopenharmony_ci{ 1368c2ecf20Sopenharmony_ci char printf_buf[256]; 1378c2ecf20Sopenharmony_ci va_list args; 1388c2ecf20Sopenharmony_ci int printed; 1398c2ecf20Sopenharmony_ci int loglevel = printk_get_level(fmt); 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci switch (loglevel) { 1428c2ecf20Sopenharmony_ci case '0' ... '9': 1438c2ecf20Sopenharmony_ci loglevel -= '0'; 1448c2ecf20Sopenharmony_ci break; 1458c2ecf20Sopenharmony_ci default: 1468c2ecf20Sopenharmony_ci /* 1478c2ecf20Sopenharmony_ci * Use loglevel -1 for cases where we just want to print to 1488c2ecf20Sopenharmony_ci * the screen. 1498c2ecf20Sopenharmony_ci */ 1508c2ecf20Sopenharmony_ci loglevel = -1; 1518c2ecf20Sopenharmony_ci break; 1528c2ecf20Sopenharmony_ci } 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci if (loglevel >= efi_loglevel) 1558c2ecf20Sopenharmony_ci return 0; 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci if (loglevel >= 0) 1588c2ecf20Sopenharmony_ci efi_puts("EFI stub: "); 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci fmt = printk_skip_level(fmt); 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci va_start(args, fmt); 1638c2ecf20Sopenharmony_ci printed = vsnprintf(printf_buf, sizeof(printf_buf), fmt, args); 1648c2ecf20Sopenharmony_ci va_end(args); 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ci efi_puts(printf_buf); 1678c2ecf20Sopenharmony_ci if (printed >= sizeof(printf_buf)) { 1688c2ecf20Sopenharmony_ci efi_puts("[Message truncated]\n"); 1698c2ecf20Sopenharmony_ci return -1; 1708c2ecf20Sopenharmony_ci } 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ci return printed; 1738c2ecf20Sopenharmony_ci} 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci/** 1768c2ecf20Sopenharmony_ci * efi_parse_options() - Parse EFI command line options 1778c2ecf20Sopenharmony_ci * @cmdline: kernel command line 1788c2ecf20Sopenharmony_ci * 1798c2ecf20Sopenharmony_ci * Parse the ASCII string @cmdline for EFI options, denoted by the efi= 1808c2ecf20Sopenharmony_ci * option, e.g. efi=nochunk. 1818c2ecf20Sopenharmony_ci * 1828c2ecf20Sopenharmony_ci * It should be noted that efi= is parsed in two very different 1838c2ecf20Sopenharmony_ci * environments, first in the early boot environment of the EFI boot 1848c2ecf20Sopenharmony_ci * stub, and subsequently during the kernel boot. 1858c2ecf20Sopenharmony_ci * 1868c2ecf20Sopenharmony_ci * Return: status code 1878c2ecf20Sopenharmony_ci */ 1888c2ecf20Sopenharmony_ciefi_status_t efi_parse_options(char const *cmdline) 1898c2ecf20Sopenharmony_ci{ 1908c2ecf20Sopenharmony_ci size_t len; 1918c2ecf20Sopenharmony_ci efi_status_t status; 1928c2ecf20Sopenharmony_ci char *str, *buf; 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci if (!cmdline) 1958c2ecf20Sopenharmony_ci return EFI_SUCCESS; 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci len = strnlen(cmdline, COMMAND_LINE_SIZE - 1) + 1; 1988c2ecf20Sopenharmony_ci status = efi_bs_call(allocate_pool, EFI_LOADER_DATA, len, (void **)&buf); 1998c2ecf20Sopenharmony_ci if (status != EFI_SUCCESS) 2008c2ecf20Sopenharmony_ci return status; 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci memcpy(buf, cmdline, len - 1); 2038c2ecf20Sopenharmony_ci buf[len - 1] = '\0'; 2048c2ecf20Sopenharmony_ci str = skip_spaces(buf); 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci while (*str) { 2078c2ecf20Sopenharmony_ci char *param, *val; 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci str = next_arg(str, ¶m, &val); 2108c2ecf20Sopenharmony_ci if (!val && !strcmp(param, "--")) 2118c2ecf20Sopenharmony_ci break; 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_ci if (!strcmp(param, "nokaslr")) { 2148c2ecf20Sopenharmony_ci efi_nokaslr = true; 2158c2ecf20Sopenharmony_ci } else if (!strcmp(param, "quiet")) { 2168c2ecf20Sopenharmony_ci efi_loglevel = CONSOLE_LOGLEVEL_QUIET; 2178c2ecf20Sopenharmony_ci } else if (!strcmp(param, "noinitrd")) { 2188c2ecf20Sopenharmony_ci efi_noinitrd = true; 2198c2ecf20Sopenharmony_ci } else if (!strcmp(param, "efi") && val) { 2208c2ecf20Sopenharmony_ci efi_nochunk = parse_option_str(val, "nochunk"); 2218c2ecf20Sopenharmony_ci efi_novamap = parse_option_str(val, "novamap"); 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_ci efi_nosoftreserve = IS_ENABLED(CONFIG_EFI_SOFT_RESERVE) && 2248c2ecf20Sopenharmony_ci parse_option_str(val, "nosoftreserve"); 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_ci if (parse_option_str(val, "disable_early_pci_dma")) 2278c2ecf20Sopenharmony_ci efi_disable_pci_dma = true; 2288c2ecf20Sopenharmony_ci if (parse_option_str(val, "no_disable_early_pci_dma")) 2298c2ecf20Sopenharmony_ci efi_disable_pci_dma = false; 2308c2ecf20Sopenharmony_ci if (parse_option_str(val, "debug")) 2318c2ecf20Sopenharmony_ci efi_loglevel = CONSOLE_LOGLEVEL_DEBUG; 2328c2ecf20Sopenharmony_ci } else if (!strcmp(param, "video") && 2338c2ecf20Sopenharmony_ci val && strstarts(val, "efifb:")) { 2348c2ecf20Sopenharmony_ci efi_parse_option_graphics(val + strlen("efifb:")); 2358c2ecf20Sopenharmony_ci } 2368c2ecf20Sopenharmony_ci } 2378c2ecf20Sopenharmony_ci efi_bs_call(free_pool, buf); 2388c2ecf20Sopenharmony_ci return EFI_SUCCESS; 2398c2ecf20Sopenharmony_ci} 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_ci/* 2428c2ecf20Sopenharmony_ci * The EFI_LOAD_OPTION descriptor has the following layout: 2438c2ecf20Sopenharmony_ci * u32 Attributes; 2448c2ecf20Sopenharmony_ci * u16 FilePathListLength; 2458c2ecf20Sopenharmony_ci * u16 Description[]; 2468c2ecf20Sopenharmony_ci * efi_device_path_protocol_t FilePathList[]; 2478c2ecf20Sopenharmony_ci * u8 OptionalData[]; 2488c2ecf20Sopenharmony_ci * 2498c2ecf20Sopenharmony_ci * This function validates and unpacks the variable-size data fields. 2508c2ecf20Sopenharmony_ci */ 2518c2ecf20Sopenharmony_cistatic 2528c2ecf20Sopenharmony_cibool efi_load_option_unpack(efi_load_option_unpacked_t *dest, 2538c2ecf20Sopenharmony_ci const efi_load_option_t *src, size_t size) 2548c2ecf20Sopenharmony_ci{ 2558c2ecf20Sopenharmony_ci const void *pos; 2568c2ecf20Sopenharmony_ci u16 c; 2578c2ecf20Sopenharmony_ci efi_device_path_protocol_t header; 2588c2ecf20Sopenharmony_ci const efi_char16_t *description; 2598c2ecf20Sopenharmony_ci const efi_device_path_protocol_t *file_path_list; 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_ci if (size < offsetof(efi_load_option_t, variable_data)) 2628c2ecf20Sopenharmony_ci return false; 2638c2ecf20Sopenharmony_ci pos = src->variable_data; 2648c2ecf20Sopenharmony_ci size -= offsetof(efi_load_option_t, variable_data); 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_ci if ((src->attributes & ~EFI_LOAD_OPTION_MASK) != 0) 2678c2ecf20Sopenharmony_ci return false; 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_ci /* Scan description. */ 2708c2ecf20Sopenharmony_ci description = pos; 2718c2ecf20Sopenharmony_ci do { 2728c2ecf20Sopenharmony_ci if (size < sizeof(c)) 2738c2ecf20Sopenharmony_ci return false; 2748c2ecf20Sopenharmony_ci c = *(const u16 *)pos; 2758c2ecf20Sopenharmony_ci pos += sizeof(c); 2768c2ecf20Sopenharmony_ci size -= sizeof(c); 2778c2ecf20Sopenharmony_ci } while (c != L'\0'); 2788c2ecf20Sopenharmony_ci 2798c2ecf20Sopenharmony_ci /* Scan file_path_list. */ 2808c2ecf20Sopenharmony_ci file_path_list = pos; 2818c2ecf20Sopenharmony_ci do { 2828c2ecf20Sopenharmony_ci if (size < sizeof(header)) 2838c2ecf20Sopenharmony_ci return false; 2848c2ecf20Sopenharmony_ci header = *(const efi_device_path_protocol_t *)pos; 2858c2ecf20Sopenharmony_ci if (header.length < sizeof(header)) 2868c2ecf20Sopenharmony_ci return false; 2878c2ecf20Sopenharmony_ci if (size < header.length) 2888c2ecf20Sopenharmony_ci return false; 2898c2ecf20Sopenharmony_ci pos += header.length; 2908c2ecf20Sopenharmony_ci size -= header.length; 2918c2ecf20Sopenharmony_ci } while ((header.type != EFI_DEV_END_PATH && header.type != EFI_DEV_END_PATH2) || 2928c2ecf20Sopenharmony_ci (header.sub_type != EFI_DEV_END_ENTIRE)); 2938c2ecf20Sopenharmony_ci if (pos != (const void *)file_path_list + src->file_path_list_length) 2948c2ecf20Sopenharmony_ci return false; 2958c2ecf20Sopenharmony_ci 2968c2ecf20Sopenharmony_ci dest->attributes = src->attributes; 2978c2ecf20Sopenharmony_ci dest->file_path_list_length = src->file_path_list_length; 2988c2ecf20Sopenharmony_ci dest->description = description; 2998c2ecf20Sopenharmony_ci dest->file_path_list = file_path_list; 3008c2ecf20Sopenharmony_ci dest->optional_data_size = size; 3018c2ecf20Sopenharmony_ci dest->optional_data = size ? pos : NULL; 3028c2ecf20Sopenharmony_ci 3038c2ecf20Sopenharmony_ci return true; 3048c2ecf20Sopenharmony_ci} 3058c2ecf20Sopenharmony_ci 3068c2ecf20Sopenharmony_ci/* 3078c2ecf20Sopenharmony_ci * At least some versions of Dell firmware pass the entire contents of the 3088c2ecf20Sopenharmony_ci * Boot#### variable, i.e. the EFI_LOAD_OPTION descriptor, rather than just the 3098c2ecf20Sopenharmony_ci * OptionalData field. 3108c2ecf20Sopenharmony_ci * 3118c2ecf20Sopenharmony_ci * Detect this case and extract OptionalData. 3128c2ecf20Sopenharmony_ci */ 3138c2ecf20Sopenharmony_civoid efi_apply_loadoptions_quirk(const void **load_options, int *load_options_size) 3148c2ecf20Sopenharmony_ci{ 3158c2ecf20Sopenharmony_ci const efi_load_option_t *load_option = *load_options; 3168c2ecf20Sopenharmony_ci efi_load_option_unpacked_t load_option_unpacked; 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_ci if (!IS_ENABLED(CONFIG_X86)) 3198c2ecf20Sopenharmony_ci return; 3208c2ecf20Sopenharmony_ci if (!load_option) 3218c2ecf20Sopenharmony_ci return; 3228c2ecf20Sopenharmony_ci if (*load_options_size < sizeof(*load_option)) 3238c2ecf20Sopenharmony_ci return; 3248c2ecf20Sopenharmony_ci if ((load_option->attributes & ~EFI_LOAD_OPTION_BOOT_MASK) != 0) 3258c2ecf20Sopenharmony_ci return; 3268c2ecf20Sopenharmony_ci 3278c2ecf20Sopenharmony_ci if (!efi_load_option_unpack(&load_option_unpacked, load_option, *load_options_size)) 3288c2ecf20Sopenharmony_ci return; 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_ci efi_warn_once(FW_BUG "LoadOptions is an EFI_LOAD_OPTION descriptor\n"); 3318c2ecf20Sopenharmony_ci efi_warn_once(FW_BUG "Using OptionalData as a workaround\n"); 3328c2ecf20Sopenharmony_ci 3338c2ecf20Sopenharmony_ci *load_options = load_option_unpacked.optional_data; 3348c2ecf20Sopenharmony_ci *load_options_size = load_option_unpacked.optional_data_size; 3358c2ecf20Sopenharmony_ci} 3368c2ecf20Sopenharmony_ci 3378c2ecf20Sopenharmony_ci/* 3388c2ecf20Sopenharmony_ci * Convert the unicode UEFI command line to ASCII to pass to kernel. 3398c2ecf20Sopenharmony_ci * Size of memory allocated return in *cmd_line_len. 3408c2ecf20Sopenharmony_ci * Returns NULL on error. 3418c2ecf20Sopenharmony_ci */ 3428c2ecf20Sopenharmony_cichar *efi_convert_cmdline(efi_loaded_image_t *image, int *cmd_line_len) 3438c2ecf20Sopenharmony_ci{ 3448c2ecf20Sopenharmony_ci const u16 *s2; 3458c2ecf20Sopenharmony_ci unsigned long cmdline_addr = 0; 3468c2ecf20Sopenharmony_ci int options_chars = efi_table_attr(image, load_options_size); 3478c2ecf20Sopenharmony_ci const u16 *options = efi_table_attr(image, load_options); 3488c2ecf20Sopenharmony_ci int options_bytes = 0, safe_options_bytes = 0; /* UTF-8 bytes */ 3498c2ecf20Sopenharmony_ci bool in_quote = false; 3508c2ecf20Sopenharmony_ci efi_status_t status; 3518c2ecf20Sopenharmony_ci 3528c2ecf20Sopenharmony_ci efi_apply_loadoptions_quirk((const void **)&options, &options_chars); 3538c2ecf20Sopenharmony_ci options_chars /= sizeof(*options); 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_ci if (options) { 3568c2ecf20Sopenharmony_ci s2 = options; 3578c2ecf20Sopenharmony_ci while (options_bytes < COMMAND_LINE_SIZE && options_chars--) { 3588c2ecf20Sopenharmony_ci u16 c = *s2++; 3598c2ecf20Sopenharmony_ci 3608c2ecf20Sopenharmony_ci if (c < 0x80) { 3618c2ecf20Sopenharmony_ci if (c == L'\0' || c == L'\n') 3628c2ecf20Sopenharmony_ci break; 3638c2ecf20Sopenharmony_ci if (c == L'"') 3648c2ecf20Sopenharmony_ci in_quote = !in_quote; 3658c2ecf20Sopenharmony_ci else if (!in_quote && isspace((char)c)) 3668c2ecf20Sopenharmony_ci safe_options_bytes = options_bytes; 3678c2ecf20Sopenharmony_ci 3688c2ecf20Sopenharmony_ci options_bytes++; 3698c2ecf20Sopenharmony_ci continue; 3708c2ecf20Sopenharmony_ci } 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_ci /* 3738c2ecf20Sopenharmony_ci * Get the number of UTF-8 bytes corresponding to a 3748c2ecf20Sopenharmony_ci * UTF-16 character. 3758c2ecf20Sopenharmony_ci * The first part handles everything in the BMP. 3768c2ecf20Sopenharmony_ci */ 3778c2ecf20Sopenharmony_ci options_bytes += 2 + (c >= 0x800); 3788c2ecf20Sopenharmony_ci /* 3798c2ecf20Sopenharmony_ci * Add one more byte for valid surrogate pairs. Invalid 3808c2ecf20Sopenharmony_ci * surrogates will be replaced with 0xfffd and take up 3818c2ecf20Sopenharmony_ci * only 3 bytes. 3828c2ecf20Sopenharmony_ci */ 3838c2ecf20Sopenharmony_ci if ((c & 0xfc00) == 0xd800) { 3848c2ecf20Sopenharmony_ci /* 3858c2ecf20Sopenharmony_ci * If the very last word is a high surrogate, 3868c2ecf20Sopenharmony_ci * we must ignore it since we can't access the 3878c2ecf20Sopenharmony_ci * low surrogate. 3888c2ecf20Sopenharmony_ci */ 3898c2ecf20Sopenharmony_ci if (!options_chars) { 3908c2ecf20Sopenharmony_ci options_bytes -= 3; 3918c2ecf20Sopenharmony_ci } else if ((*s2 & 0xfc00) == 0xdc00) { 3928c2ecf20Sopenharmony_ci options_bytes++; 3938c2ecf20Sopenharmony_ci options_chars--; 3948c2ecf20Sopenharmony_ci s2++; 3958c2ecf20Sopenharmony_ci } 3968c2ecf20Sopenharmony_ci } 3978c2ecf20Sopenharmony_ci } 3988c2ecf20Sopenharmony_ci if (options_bytes >= COMMAND_LINE_SIZE) { 3998c2ecf20Sopenharmony_ci options_bytes = safe_options_bytes; 4008c2ecf20Sopenharmony_ci efi_err("Command line is too long: truncated to %d bytes\n", 4018c2ecf20Sopenharmony_ci options_bytes); 4028c2ecf20Sopenharmony_ci } 4038c2ecf20Sopenharmony_ci } 4048c2ecf20Sopenharmony_ci 4058c2ecf20Sopenharmony_ci options_bytes++; /* NUL termination */ 4068c2ecf20Sopenharmony_ci 4078c2ecf20Sopenharmony_ci status = efi_bs_call(allocate_pool, EFI_LOADER_DATA, options_bytes, 4088c2ecf20Sopenharmony_ci (void **)&cmdline_addr); 4098c2ecf20Sopenharmony_ci if (status != EFI_SUCCESS) 4108c2ecf20Sopenharmony_ci return NULL; 4118c2ecf20Sopenharmony_ci 4128c2ecf20Sopenharmony_ci snprintf((char *)cmdline_addr, options_bytes, "%.*ls", 4138c2ecf20Sopenharmony_ci options_bytes - 1, options); 4148c2ecf20Sopenharmony_ci 4158c2ecf20Sopenharmony_ci *cmd_line_len = options_bytes; 4168c2ecf20Sopenharmony_ci return (char *)cmdline_addr; 4178c2ecf20Sopenharmony_ci} 4188c2ecf20Sopenharmony_ci 4198c2ecf20Sopenharmony_ci/** 4208c2ecf20Sopenharmony_ci * efi_exit_boot_services() - Exit boot services 4218c2ecf20Sopenharmony_ci * @handle: handle of the exiting image 4228c2ecf20Sopenharmony_ci * @priv: argument to be passed to @priv_func 4238c2ecf20Sopenharmony_ci * @priv_func: function to process the memory map before exiting boot services 4248c2ecf20Sopenharmony_ci * 4258c2ecf20Sopenharmony_ci * Handle calling ExitBootServices according to the requirements set out by the 4268c2ecf20Sopenharmony_ci * spec. Obtains the current memory map, and returns that info after calling 4278c2ecf20Sopenharmony_ci * ExitBootServices. The client must specify a function to perform any 4288c2ecf20Sopenharmony_ci * processing of the memory map data prior to ExitBootServices. A client 4298c2ecf20Sopenharmony_ci * specific structure may be passed to the function via priv. The client 4308c2ecf20Sopenharmony_ci * function may be called multiple times. 4318c2ecf20Sopenharmony_ci * 4328c2ecf20Sopenharmony_ci * Return: status code 4338c2ecf20Sopenharmony_ci */ 4348c2ecf20Sopenharmony_ciefi_status_t efi_exit_boot_services(void *handle, void *priv, 4358c2ecf20Sopenharmony_ci efi_exit_boot_map_processing priv_func) 4368c2ecf20Sopenharmony_ci{ 4378c2ecf20Sopenharmony_ci struct efi_boot_memmap *map; 4388c2ecf20Sopenharmony_ci efi_status_t status; 4398c2ecf20Sopenharmony_ci 4408c2ecf20Sopenharmony_ci status = efi_get_memory_map(&map, true); 4418c2ecf20Sopenharmony_ci if (status != EFI_SUCCESS) 4428c2ecf20Sopenharmony_ci return status; 4438c2ecf20Sopenharmony_ci 4448c2ecf20Sopenharmony_ci status = priv_func(map, priv); 4458c2ecf20Sopenharmony_ci if (status != EFI_SUCCESS) { 4468c2ecf20Sopenharmony_ci efi_bs_call(free_pool, map); 4478c2ecf20Sopenharmony_ci return status; 4488c2ecf20Sopenharmony_ci } 4498c2ecf20Sopenharmony_ci 4508c2ecf20Sopenharmony_ci if (efi_disable_pci_dma) 4518c2ecf20Sopenharmony_ci efi_pci_disable_bridge_busmaster(); 4528c2ecf20Sopenharmony_ci 4538c2ecf20Sopenharmony_ci status = efi_bs_call(exit_boot_services, handle, map->map_key); 4548c2ecf20Sopenharmony_ci 4558c2ecf20Sopenharmony_ci if (status == EFI_INVALID_PARAMETER) { 4568c2ecf20Sopenharmony_ci /* 4578c2ecf20Sopenharmony_ci * The memory map changed between efi_get_memory_map() and 4588c2ecf20Sopenharmony_ci * exit_boot_services(). Per the UEFI Spec v2.6, Section 6.4: 4598c2ecf20Sopenharmony_ci * EFI_BOOT_SERVICES.ExitBootServices we need to get the 4608c2ecf20Sopenharmony_ci * updated map, and try again. The spec implies one retry 4618c2ecf20Sopenharmony_ci * should be sufficent, which is confirmed against the EDK2 4628c2ecf20Sopenharmony_ci * implementation. Per the spec, we can only invoke 4638c2ecf20Sopenharmony_ci * get_memory_map() and exit_boot_services() - we cannot alloc 4648c2ecf20Sopenharmony_ci * so efi_get_memory_map() cannot be used, and we must reuse 4658c2ecf20Sopenharmony_ci * the buffer. For all practical purposes, the headroom in the 4668c2ecf20Sopenharmony_ci * buffer should account for any changes in the map so the call 4678c2ecf20Sopenharmony_ci * to get_memory_map() is expected to succeed here. 4688c2ecf20Sopenharmony_ci */ 4698c2ecf20Sopenharmony_ci map->map_size = map->buff_size; 4708c2ecf20Sopenharmony_ci status = efi_bs_call(get_memory_map, 4718c2ecf20Sopenharmony_ci &map->map_size, 4728c2ecf20Sopenharmony_ci &map->map, 4738c2ecf20Sopenharmony_ci &map->map_key, 4748c2ecf20Sopenharmony_ci &map->desc_size, 4758c2ecf20Sopenharmony_ci &map->desc_ver); 4768c2ecf20Sopenharmony_ci 4778c2ecf20Sopenharmony_ci /* exit_boot_services() was called, thus cannot free */ 4788c2ecf20Sopenharmony_ci if (status != EFI_SUCCESS) 4798c2ecf20Sopenharmony_ci return status; 4808c2ecf20Sopenharmony_ci 4818c2ecf20Sopenharmony_ci status = priv_func(map, priv); 4828c2ecf20Sopenharmony_ci /* exit_boot_services() was called, thus cannot free */ 4838c2ecf20Sopenharmony_ci if (status != EFI_SUCCESS) 4848c2ecf20Sopenharmony_ci return status; 4858c2ecf20Sopenharmony_ci 4868c2ecf20Sopenharmony_ci status = efi_bs_call(exit_boot_services, handle, map->map_key); 4878c2ecf20Sopenharmony_ci } 4888c2ecf20Sopenharmony_ci 4898c2ecf20Sopenharmony_ci return status; 4908c2ecf20Sopenharmony_ci} 4918c2ecf20Sopenharmony_ci 4928c2ecf20Sopenharmony_ci/** 4938c2ecf20Sopenharmony_ci * get_efi_config_table() - retrieve UEFI configuration table 4948c2ecf20Sopenharmony_ci * @guid: GUID of the configuration table to be retrieved 4958c2ecf20Sopenharmony_ci * Return: pointer to the configuration table or NULL 4968c2ecf20Sopenharmony_ci */ 4978c2ecf20Sopenharmony_civoid *get_efi_config_table(efi_guid_t guid) 4988c2ecf20Sopenharmony_ci{ 4998c2ecf20Sopenharmony_ci unsigned long tables = efi_table_attr(efi_system_table, tables); 5008c2ecf20Sopenharmony_ci int nr_tables = efi_table_attr(efi_system_table, nr_tables); 5018c2ecf20Sopenharmony_ci int i; 5028c2ecf20Sopenharmony_ci 5038c2ecf20Sopenharmony_ci for (i = 0; i < nr_tables; i++) { 5048c2ecf20Sopenharmony_ci efi_config_table_t *t = (void *)tables; 5058c2ecf20Sopenharmony_ci 5068c2ecf20Sopenharmony_ci if (efi_guidcmp(t->guid, guid) == 0) 5078c2ecf20Sopenharmony_ci return efi_table_attr(t, table); 5088c2ecf20Sopenharmony_ci 5098c2ecf20Sopenharmony_ci tables += efi_is_native() ? sizeof(efi_config_table_t) 5108c2ecf20Sopenharmony_ci : sizeof(efi_config_table_32_t); 5118c2ecf20Sopenharmony_ci } 5128c2ecf20Sopenharmony_ci return NULL; 5138c2ecf20Sopenharmony_ci} 5148c2ecf20Sopenharmony_ci 5158c2ecf20Sopenharmony_ci/* 5168c2ecf20Sopenharmony_ci * The LINUX_EFI_INITRD_MEDIA_GUID vendor media device path below provides a way 5178c2ecf20Sopenharmony_ci * for the firmware or bootloader to expose the initrd data directly to the stub 5188c2ecf20Sopenharmony_ci * via the trivial LoadFile2 protocol, which is defined in the UEFI spec, and is 5198c2ecf20Sopenharmony_ci * very easy to implement. It is a simple Linux initrd specific conduit between 5208c2ecf20Sopenharmony_ci * kernel and firmware, allowing us to put the EFI stub (being part of the 5218c2ecf20Sopenharmony_ci * kernel) in charge of where and when to load the initrd, while leaving it up 5228c2ecf20Sopenharmony_ci * to the firmware to decide whether it needs to expose its filesystem hierarchy 5238c2ecf20Sopenharmony_ci * via EFI protocols. 5248c2ecf20Sopenharmony_ci */ 5258c2ecf20Sopenharmony_cistatic const struct { 5268c2ecf20Sopenharmony_ci struct efi_vendor_dev_path vendor; 5278c2ecf20Sopenharmony_ci struct efi_generic_dev_path end; 5288c2ecf20Sopenharmony_ci} __packed initrd_dev_path = { 5298c2ecf20Sopenharmony_ci { 5308c2ecf20Sopenharmony_ci { 5318c2ecf20Sopenharmony_ci EFI_DEV_MEDIA, 5328c2ecf20Sopenharmony_ci EFI_DEV_MEDIA_VENDOR, 5338c2ecf20Sopenharmony_ci sizeof(struct efi_vendor_dev_path), 5348c2ecf20Sopenharmony_ci }, 5358c2ecf20Sopenharmony_ci LINUX_EFI_INITRD_MEDIA_GUID 5368c2ecf20Sopenharmony_ci }, { 5378c2ecf20Sopenharmony_ci EFI_DEV_END_PATH, 5388c2ecf20Sopenharmony_ci EFI_DEV_END_ENTIRE, 5398c2ecf20Sopenharmony_ci sizeof(struct efi_generic_dev_path) 5408c2ecf20Sopenharmony_ci } 5418c2ecf20Sopenharmony_ci}; 5428c2ecf20Sopenharmony_ci 5438c2ecf20Sopenharmony_ci/** 5448c2ecf20Sopenharmony_ci * efi_load_initrd_dev_path() - load the initrd from the Linux initrd device path 5458c2ecf20Sopenharmony_ci * @load_addr: pointer to store the address where the initrd was loaded 5468c2ecf20Sopenharmony_ci * @load_size: pointer to store the size of the loaded initrd 5478c2ecf20Sopenharmony_ci * @max: upper limit for the initrd memory allocation 5488c2ecf20Sopenharmony_ci * 5498c2ecf20Sopenharmony_ci * Return: 5508c2ecf20Sopenharmony_ci * * %EFI_SUCCESS if the initrd was loaded successfully, in which 5518c2ecf20Sopenharmony_ci * case @load_addr and @load_size are assigned accordingly 5528c2ecf20Sopenharmony_ci * * %EFI_NOT_FOUND if no LoadFile2 protocol exists on the initrd device path 5538c2ecf20Sopenharmony_ci * * %EFI_OUT_OF_RESOURCES if memory allocation failed 5548c2ecf20Sopenharmony_ci * * %EFI_LOAD_ERROR in all other cases 5558c2ecf20Sopenharmony_ci */ 5568c2ecf20Sopenharmony_cistatic 5578c2ecf20Sopenharmony_ciefi_status_t efi_load_initrd_dev_path(struct linux_efi_initrd *initrd, 5588c2ecf20Sopenharmony_ci unsigned long max) 5598c2ecf20Sopenharmony_ci{ 5608c2ecf20Sopenharmony_ci efi_guid_t lf2_proto_guid = EFI_LOAD_FILE2_PROTOCOL_GUID; 5618c2ecf20Sopenharmony_ci efi_device_path_protocol_t *dp; 5628c2ecf20Sopenharmony_ci efi_load_file2_protocol_t *lf2; 5638c2ecf20Sopenharmony_ci efi_handle_t handle; 5648c2ecf20Sopenharmony_ci efi_status_t status; 5658c2ecf20Sopenharmony_ci 5668c2ecf20Sopenharmony_ci dp = (efi_device_path_protocol_t *)&initrd_dev_path; 5678c2ecf20Sopenharmony_ci status = efi_bs_call(locate_device_path, &lf2_proto_guid, &dp, &handle); 5688c2ecf20Sopenharmony_ci if (status != EFI_SUCCESS) 5698c2ecf20Sopenharmony_ci return status; 5708c2ecf20Sopenharmony_ci 5718c2ecf20Sopenharmony_ci status = efi_bs_call(handle_protocol, handle, &lf2_proto_guid, 5728c2ecf20Sopenharmony_ci (void **)&lf2); 5738c2ecf20Sopenharmony_ci if (status != EFI_SUCCESS) 5748c2ecf20Sopenharmony_ci return status; 5758c2ecf20Sopenharmony_ci 5768c2ecf20Sopenharmony_ci initrd->size = 0; 5778c2ecf20Sopenharmony_ci status = efi_call_proto(lf2, load_file, dp, false, &initrd->size, NULL); 5788c2ecf20Sopenharmony_ci if (status != EFI_BUFFER_TOO_SMALL) 5798c2ecf20Sopenharmony_ci return EFI_LOAD_ERROR; 5808c2ecf20Sopenharmony_ci 5818c2ecf20Sopenharmony_ci status = efi_allocate_pages(initrd->size, &initrd->base, max); 5828c2ecf20Sopenharmony_ci if (status != EFI_SUCCESS) 5838c2ecf20Sopenharmony_ci return status; 5848c2ecf20Sopenharmony_ci 5858c2ecf20Sopenharmony_ci status = efi_call_proto(lf2, load_file, dp, false, &initrd->size, 5868c2ecf20Sopenharmony_ci (void *)initrd->base); 5878c2ecf20Sopenharmony_ci if (status != EFI_SUCCESS) { 5888c2ecf20Sopenharmony_ci efi_free(initrd->size, initrd->base); 5898c2ecf20Sopenharmony_ci return EFI_LOAD_ERROR; 5908c2ecf20Sopenharmony_ci } 5918c2ecf20Sopenharmony_ci return EFI_SUCCESS; 5928c2ecf20Sopenharmony_ci} 5938c2ecf20Sopenharmony_ci 5948c2ecf20Sopenharmony_cistatic 5958c2ecf20Sopenharmony_ciefi_status_t efi_load_initrd_cmdline(efi_loaded_image_t *image, 5968c2ecf20Sopenharmony_ci struct linux_efi_initrd *initrd, 5978c2ecf20Sopenharmony_ci unsigned long soft_limit, 5988c2ecf20Sopenharmony_ci unsigned long hard_limit) 5998c2ecf20Sopenharmony_ci{ 6008c2ecf20Sopenharmony_ci if (!IS_ENABLED(CONFIG_EFI_GENERIC_STUB_INITRD_CMDLINE_LOADER) || 6018c2ecf20Sopenharmony_ci (IS_ENABLED(CONFIG_X86) && (!efi_is_native() || image == NULL))) 6028c2ecf20Sopenharmony_ci return EFI_UNSUPPORTED; 6038c2ecf20Sopenharmony_ci 6048c2ecf20Sopenharmony_ci return handle_cmdline_files(image, L"initrd=", sizeof(L"initrd=") - 2, 6058c2ecf20Sopenharmony_ci soft_limit, hard_limit, 6068c2ecf20Sopenharmony_ci &initrd->base, &initrd->size); 6078c2ecf20Sopenharmony_ci} 6088c2ecf20Sopenharmony_ci 6098c2ecf20Sopenharmony_ci/** 6108c2ecf20Sopenharmony_ci * efi_load_initrd() - Load initial RAM disk 6118c2ecf20Sopenharmony_ci * @image: EFI loaded image protocol 6128c2ecf20Sopenharmony_ci * @soft_limit: preferred size of allocated memory for loading the initrd 6138c2ecf20Sopenharmony_ci * @hard_limit: minimum size of allocated memory 6148c2ecf20Sopenharmony_ci * 6158c2ecf20Sopenharmony_ci * Return: status code 6168c2ecf20Sopenharmony_ci */ 6178c2ecf20Sopenharmony_ciefi_status_t efi_load_initrd(efi_loaded_image_t *image, 6188c2ecf20Sopenharmony_ci unsigned long soft_limit, 6198c2ecf20Sopenharmony_ci unsigned long hard_limit, 6208c2ecf20Sopenharmony_ci const struct linux_efi_initrd **out) 6218c2ecf20Sopenharmony_ci{ 6228c2ecf20Sopenharmony_ci efi_guid_t tbl_guid = LINUX_EFI_INITRD_MEDIA_GUID; 6238c2ecf20Sopenharmony_ci efi_status_t status = EFI_SUCCESS; 6248c2ecf20Sopenharmony_ci struct linux_efi_initrd initrd, *tbl; 6258c2ecf20Sopenharmony_ci 6268c2ecf20Sopenharmony_ci if (!IS_ENABLED(CONFIG_BLK_DEV_INITRD) || efi_noinitrd) 6278c2ecf20Sopenharmony_ci return EFI_SUCCESS; 6288c2ecf20Sopenharmony_ci 6298c2ecf20Sopenharmony_ci status = efi_load_initrd_dev_path(&initrd, hard_limit); 6308c2ecf20Sopenharmony_ci if (status == EFI_SUCCESS) { 6318c2ecf20Sopenharmony_ci efi_info("Loaded initrd from LINUX_EFI_INITRD_MEDIA_GUID device path\n"); 6328c2ecf20Sopenharmony_ci } else if (status == EFI_NOT_FOUND) { 6338c2ecf20Sopenharmony_ci status = efi_load_initrd_cmdline(image, &initrd, soft_limit, 6348c2ecf20Sopenharmony_ci hard_limit); 6358c2ecf20Sopenharmony_ci /* command line loader disabled or no initrd= passed? */ 6368c2ecf20Sopenharmony_ci if (status == EFI_UNSUPPORTED || status == EFI_NOT_READY) 6378c2ecf20Sopenharmony_ci return EFI_SUCCESS; 6388c2ecf20Sopenharmony_ci if (status == EFI_SUCCESS) 6398c2ecf20Sopenharmony_ci efi_info("Loaded initrd from command line option\n"); 6408c2ecf20Sopenharmony_ci } 6418c2ecf20Sopenharmony_ci if (status != EFI_SUCCESS) 6428c2ecf20Sopenharmony_ci goto failed; 6438c2ecf20Sopenharmony_ci 6448c2ecf20Sopenharmony_ci status = efi_bs_call(allocate_pool, EFI_LOADER_DATA, sizeof(initrd), 6458c2ecf20Sopenharmony_ci (void **)&tbl); 6468c2ecf20Sopenharmony_ci if (status != EFI_SUCCESS) 6478c2ecf20Sopenharmony_ci goto free_initrd; 6488c2ecf20Sopenharmony_ci 6498c2ecf20Sopenharmony_ci *tbl = initrd; 6508c2ecf20Sopenharmony_ci status = efi_bs_call(install_configuration_table, &tbl_guid, tbl); 6518c2ecf20Sopenharmony_ci if (status != EFI_SUCCESS) 6528c2ecf20Sopenharmony_ci goto free_tbl; 6538c2ecf20Sopenharmony_ci 6548c2ecf20Sopenharmony_ci if (out) 6558c2ecf20Sopenharmony_ci *out = tbl; 6568c2ecf20Sopenharmony_ci return EFI_SUCCESS; 6578c2ecf20Sopenharmony_ci 6588c2ecf20Sopenharmony_cifree_tbl: 6598c2ecf20Sopenharmony_ci efi_bs_call(free_pool, tbl); 6608c2ecf20Sopenharmony_cifree_initrd: 6618c2ecf20Sopenharmony_ci efi_free(initrd.size, initrd.base); 6628c2ecf20Sopenharmony_cifailed: 6638c2ecf20Sopenharmony_ci efi_err("Failed to load initrd: 0x%lx\n", status); 6648c2ecf20Sopenharmony_ci return status; 6658c2ecf20Sopenharmony_ci} 6668c2ecf20Sopenharmony_ci 6678c2ecf20Sopenharmony_ci/** 6688c2ecf20Sopenharmony_ci * efi_wait_for_key() - Wait for key stroke 6698c2ecf20Sopenharmony_ci * @usec: number of microseconds to wait for key stroke 6708c2ecf20Sopenharmony_ci * @key: key entered 6718c2ecf20Sopenharmony_ci * 6728c2ecf20Sopenharmony_ci * Wait for up to @usec microseconds for a key stroke. 6738c2ecf20Sopenharmony_ci * 6748c2ecf20Sopenharmony_ci * Return: status code, EFI_SUCCESS if key received 6758c2ecf20Sopenharmony_ci */ 6768c2ecf20Sopenharmony_ciefi_status_t efi_wait_for_key(unsigned long usec, efi_input_key_t *key) 6778c2ecf20Sopenharmony_ci{ 6788c2ecf20Sopenharmony_ci efi_event_t events[2], timer; 6798c2ecf20Sopenharmony_ci unsigned long index; 6808c2ecf20Sopenharmony_ci efi_simple_text_input_protocol_t *con_in; 6818c2ecf20Sopenharmony_ci efi_status_t status; 6828c2ecf20Sopenharmony_ci 6838c2ecf20Sopenharmony_ci con_in = efi_table_attr(efi_system_table, con_in); 6848c2ecf20Sopenharmony_ci if (!con_in) 6858c2ecf20Sopenharmony_ci return EFI_UNSUPPORTED; 6868c2ecf20Sopenharmony_ci efi_set_event_at(events, 0, efi_table_attr(con_in, wait_for_key)); 6878c2ecf20Sopenharmony_ci 6888c2ecf20Sopenharmony_ci status = efi_bs_call(create_event, EFI_EVT_TIMER, 0, NULL, NULL, &timer); 6898c2ecf20Sopenharmony_ci if (status != EFI_SUCCESS) 6908c2ecf20Sopenharmony_ci return status; 6918c2ecf20Sopenharmony_ci 6928c2ecf20Sopenharmony_ci status = efi_bs_call(set_timer, timer, EfiTimerRelative, 6938c2ecf20Sopenharmony_ci EFI_100NSEC_PER_USEC * usec); 6948c2ecf20Sopenharmony_ci if (status != EFI_SUCCESS) 6958c2ecf20Sopenharmony_ci return status; 6968c2ecf20Sopenharmony_ci efi_set_event_at(events, 1, timer); 6978c2ecf20Sopenharmony_ci 6988c2ecf20Sopenharmony_ci status = efi_bs_call(wait_for_event, 2, events, &index); 6998c2ecf20Sopenharmony_ci if (status == EFI_SUCCESS) { 7008c2ecf20Sopenharmony_ci if (index == 0) 7018c2ecf20Sopenharmony_ci status = efi_call_proto(con_in, read_keystroke, key); 7028c2ecf20Sopenharmony_ci else 7038c2ecf20Sopenharmony_ci status = EFI_TIMEOUT; 7048c2ecf20Sopenharmony_ci } 7058c2ecf20Sopenharmony_ci 7068c2ecf20Sopenharmony_ci efi_bs_call(close_event, timer); 7078c2ecf20Sopenharmony_ci 7088c2ecf20Sopenharmony_ci return status; 7098c2ecf20Sopenharmony_ci} 710