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, &param, &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