14febfb8dSArd Biesheuvel // SPDX-License-Identifier: GPL-2.0
2f4f75ad5SArd Biesheuvel /*
3f4f75ad5SArd Biesheuvel  * Helper functions used by the EFI stub on multiple
4f4f75ad5SArd Biesheuvel  * architectures. This should be #included by the EFI stub
5f4f75ad5SArd Biesheuvel  * implementation files.
6f4f75ad5SArd Biesheuvel  *
7f4f75ad5SArd Biesheuvel  * Copyright 2011 Intel Corporation; author Matt Fleming
8f4f75ad5SArd Biesheuvel  */
9f4f75ad5SArd Biesheuvel 
10c0891ac1SAlexey Dobriyan #include <linux/stdarg.h>
112c7d1e30SArvind Sankar 
1280b1bfe1SArvind Sankar #include <linux/ctype.h>
13f4f75ad5SArd Biesheuvel #include <linux/efi.h>
14fd0528a2SArvind Sankar #include <linux/kernel.h>
1523d5b73fSArvind Sankar #include <linux/printk.h> /* For CONSOLE_LOGLEVEL_* */
16f4f75ad5SArd Biesheuvel #include <asm/efi.h>
1780b1bfe1SArvind Sankar #include <asm/setup.h>
18f4f75ad5SArd Biesheuvel 
19f4f75ad5SArd Biesheuvel #include "efistub.h"
20f4f75ad5SArd Biesheuvel 
21980771f6SArd Biesheuvel bool efi_nochunk;
227c116db2SWill Deacon bool efi_nokaslr = !IS_ENABLED(CONFIG_RANDOMIZE_BASE);
2323d5b73fSArvind Sankar int efi_loglevel = CONSOLE_LOGLEVEL_DEFAULT;
24980771f6SArd Biesheuvel bool efi_novamap;
25980771f6SArd Biesheuvel 
2620287d56SArd Biesheuvel static bool efi_noinitrd;
2754439370SArvind Sankar static bool efi_nosoftreserve;
2854439370SArvind Sankar static bool efi_disable_pci_dma = IS_ENABLED(CONFIG_EFI_DISABLE_PCI_DMA);
2960f38de7SArd Biesheuvel 
30b617c526SDan Williams bool __pure __efi_soft_reserve_enabled(void)
31b617c526SDan Williams {
32b617c526SDan Williams 	return !efi_nosoftreserve;
33b617c526SDan Williams }
3460f38de7SArd Biesheuvel 
358c0a839cSHeinrich Schuchardt /**
368c0a839cSHeinrich Schuchardt  * efi_char16_puts() - Write a UCS-2 encoded string to the console
378c0a839cSHeinrich Schuchardt  * @str:	UCS-2 encoded string
388c0a839cSHeinrich Schuchardt  */
39cb8c90a0SArvind Sankar void efi_char16_puts(efi_char16_t *str)
40cb8c90a0SArvind Sankar {
41cb8c90a0SArvind Sankar 	efi_call_proto(efi_table_attr(efi_system_table, con_out),
42cb8c90a0SArvind Sankar 		       output_string, str);
43cb8c90a0SArvind Sankar }
44cb8c90a0SArvind Sankar 
454b75bd36SArvind Sankar static
464b75bd36SArvind Sankar u32 utf8_to_utf32(const u8 **s8)
474b75bd36SArvind Sankar {
484b75bd36SArvind Sankar 	u32 c32;
494b75bd36SArvind Sankar 	u8 c0, cx;
504b75bd36SArvind Sankar 	size_t clen, i;
514b75bd36SArvind Sankar 
524b75bd36SArvind Sankar 	c0 = cx = *(*s8)++;
534b75bd36SArvind Sankar 	/*
544b75bd36SArvind Sankar 	 * The position of the most-significant 0 bit gives us the length of
554b75bd36SArvind Sankar 	 * a multi-octet encoding.
564b75bd36SArvind Sankar 	 */
574b75bd36SArvind Sankar 	for (clen = 0; cx & 0x80; ++clen)
584b75bd36SArvind Sankar 		cx <<= 1;
594b75bd36SArvind Sankar 	/*
604b75bd36SArvind Sankar 	 * If the 0 bit is in position 8, this is a valid single-octet
614b75bd36SArvind Sankar 	 * encoding. If the 0 bit is in position 7 or positions 1-3, the
624b75bd36SArvind Sankar 	 * encoding is invalid.
634b75bd36SArvind Sankar 	 * In either case, we just return the first octet.
644b75bd36SArvind Sankar 	 */
654b75bd36SArvind Sankar 	if (clen < 2 || clen > 4)
664b75bd36SArvind Sankar 		return c0;
674b75bd36SArvind Sankar 	/* Get the bits from the first octet. */
684b75bd36SArvind Sankar 	c32 = cx >> clen--;
694b75bd36SArvind Sankar 	for (i = 0; i < clen; ++i) {
704b75bd36SArvind Sankar 		/* Trailing octets must have 10 in most significant bits. */
714b75bd36SArvind Sankar 		cx = (*s8)[i] ^ 0x80;
724b75bd36SArvind Sankar 		if (cx & 0xc0)
734b75bd36SArvind Sankar 			return c0;
744b75bd36SArvind Sankar 		c32 = (c32 << 6) | cx;
754b75bd36SArvind Sankar 	}
764b75bd36SArvind Sankar 	/*
774b75bd36SArvind Sankar 	 * Check for validity:
784b75bd36SArvind Sankar 	 * - The character must be in the Unicode range.
794b75bd36SArvind Sankar 	 * - It must not be a surrogate.
804b75bd36SArvind Sankar 	 * - It must be encoded using the correct number of octets.
814b75bd36SArvind Sankar 	 */
824b75bd36SArvind Sankar 	if (c32 > 0x10ffff ||
834b75bd36SArvind Sankar 	    (c32 & 0xf800) == 0xd800 ||
844b75bd36SArvind Sankar 	    clen != (c32 >= 0x80) + (c32 >= 0x800) + (c32 >= 0x10000))
854b75bd36SArvind Sankar 		return c0;
864b75bd36SArvind Sankar 	*s8 += clen;
874b75bd36SArvind Sankar 	return c32;
884b75bd36SArvind Sankar }
894b75bd36SArvind Sankar 
908c0a839cSHeinrich Schuchardt /**
918c0a839cSHeinrich Schuchardt  * efi_puts() - Write a UTF-8 encoded string to the console
928c0a839cSHeinrich Schuchardt  * @str:	UTF-8 encoded string
938c0a839cSHeinrich Schuchardt  */
94cb8c90a0SArvind Sankar void efi_puts(const char *str)
95f4f75ad5SArd Biesheuvel {
96fd0528a2SArvind Sankar 	efi_char16_t buf[128];
97fd0528a2SArvind Sankar 	size_t pos = 0, lim = ARRAY_SIZE(buf);
984b75bd36SArvind Sankar 	const u8 *s8 = (const u8 *)str;
994b75bd36SArvind Sankar 	u32 c32;
100f4f75ad5SArd Biesheuvel 
1014b75bd36SArvind Sankar 	while (*s8) {
1024b75bd36SArvind Sankar 		if (*s8 == '\n')
103fd0528a2SArvind Sankar 			buf[pos++] = L'\r';
1044b75bd36SArvind Sankar 		c32 = utf8_to_utf32(&s8);
1054b75bd36SArvind Sankar 		if (c32 < 0x10000) {
1064b75bd36SArvind Sankar 			/* Characters in plane 0 use a single word. */
1074b75bd36SArvind Sankar 			buf[pos++] = c32;
1084b75bd36SArvind Sankar 		} else {
1094b75bd36SArvind Sankar 			/*
1104b75bd36SArvind Sankar 			 * Characters in other planes encode into a surrogate
1114b75bd36SArvind Sankar 			 * pair.
1124b75bd36SArvind Sankar 			 */
1134b75bd36SArvind Sankar 			buf[pos++] = (0xd800 - (0x10000 >> 10)) + (c32 >> 10);
1144b75bd36SArvind Sankar 			buf[pos++] = 0xdc00 + (c32 & 0x3ff);
1154b75bd36SArvind Sankar 		}
1164b75bd36SArvind Sankar 		if (*s8 == '\0' || pos >= lim - 2) {
117fd0528a2SArvind Sankar 			buf[pos] = L'\0';
118fd0528a2SArvind Sankar 			efi_char16_puts(buf);
119fd0528a2SArvind Sankar 			pos = 0;
120fd0528a2SArvind Sankar 		}
121f4f75ad5SArd Biesheuvel 	}
122f4f75ad5SArd Biesheuvel }
123f4f75ad5SArd Biesheuvel 
1248c0a839cSHeinrich Schuchardt /**
1258c0a839cSHeinrich Schuchardt  * efi_printk() - Print a kernel message
1268c0a839cSHeinrich Schuchardt  * @fmt:	format string
1278c0a839cSHeinrich Schuchardt  *
1288c0a839cSHeinrich Schuchardt  * The first letter of the format string is used to determine the logging level
1298c0a839cSHeinrich Schuchardt  * of the message. If the level is less then the current EFI logging level, the
1308c0a839cSHeinrich Schuchardt  * message is suppressed. The message will be truncated to 255 bytes.
1318c0a839cSHeinrich Schuchardt  *
1328c0a839cSHeinrich Schuchardt  * Return:	number of printed characters
1338c0a839cSHeinrich Schuchardt  */
1342c7d1e30SArvind Sankar int efi_printk(const char *fmt, ...)
1352c7d1e30SArvind Sankar {
1362c7d1e30SArvind Sankar 	char printf_buf[256];
1372c7d1e30SArvind Sankar 	va_list args;
1382c7d1e30SArvind Sankar 	int printed;
13923d5b73fSArvind Sankar 	int loglevel = printk_get_level(fmt);
14023d5b73fSArvind Sankar 
14123d5b73fSArvind Sankar 	switch (loglevel) {
14223d5b73fSArvind Sankar 	case '0' ... '9':
14323d5b73fSArvind Sankar 		loglevel -= '0';
14423d5b73fSArvind Sankar 		break;
14523d5b73fSArvind Sankar 	default:
14623d5b73fSArvind Sankar 		/*
14723d5b73fSArvind Sankar 		 * Use loglevel -1 for cases where we just want to print to
14823d5b73fSArvind Sankar 		 * the screen.
14923d5b73fSArvind Sankar 		 */
15023d5b73fSArvind Sankar 		loglevel = -1;
15123d5b73fSArvind Sankar 		break;
15223d5b73fSArvind Sankar 	}
15323d5b73fSArvind Sankar 
15423d5b73fSArvind Sankar 	if (loglevel >= efi_loglevel)
15523d5b73fSArvind Sankar 		return 0;
15623d5b73fSArvind Sankar 
15723d5b73fSArvind Sankar 	if (loglevel >= 0)
15823d5b73fSArvind Sankar 		efi_puts("EFI stub: ");
15923d5b73fSArvind Sankar 
16023d5b73fSArvind Sankar 	fmt = printk_skip_level(fmt);
1612c7d1e30SArvind Sankar 
1622c7d1e30SArvind Sankar 	va_start(args, fmt);
1638fb331e1SArvind Sankar 	printed = vsnprintf(printf_buf, sizeof(printf_buf), fmt, args);
1642c7d1e30SArvind Sankar 	va_end(args);
1652c7d1e30SArvind Sankar 
1662c7d1e30SArvind Sankar 	efi_puts(printf_buf);
1678fb331e1SArvind Sankar 	if (printed >= sizeof(printf_buf)) {
1688fb331e1SArvind Sankar 		efi_puts("[Message truncated]\n");
1698fb331e1SArvind Sankar 		return -1;
1708fb331e1SArvind Sankar 	}
1712c7d1e30SArvind Sankar 
1722c7d1e30SArvind Sankar 	return printed;
1732c7d1e30SArvind Sankar }
1742c7d1e30SArvind Sankar 
1758c0a839cSHeinrich Schuchardt /**
1768c0a839cSHeinrich Schuchardt  * efi_parse_options() - Parse EFI command line options
1778c0a839cSHeinrich Schuchardt  * @cmdline:	kernel command line
1788c0a839cSHeinrich Schuchardt  *
1798c0a839cSHeinrich Schuchardt  * Parse the ASCII string @cmdline for EFI options, denoted by the efi=
1805a17dae4SMatt Fleming  * option, e.g. efi=nochunk.
1815a17dae4SMatt Fleming  *
1825a17dae4SMatt Fleming  * It should be noted that efi= is parsed in two very different
1835a17dae4SMatt Fleming  * environments, first in the early boot environment of the EFI boot
1845a17dae4SMatt Fleming  * stub, and subsequently during the kernel boot.
1858c0a839cSHeinrich Schuchardt  *
1868c0a839cSHeinrich Schuchardt  * Return:	status code
1875a17dae4SMatt Fleming  */
18860f38de7SArd Biesheuvel efi_status_t efi_parse_options(char const *cmdline)
1895a17dae4SMatt Fleming {
190a37ca6a2SArvind Sankar 	size_t len;
19191d150c0SArd Biesheuvel 	efi_status_t status;
19291d150c0SArd Biesheuvel 	char *str, *buf;
1935a17dae4SMatt Fleming 
194a37ca6a2SArvind Sankar 	if (!cmdline)
195a37ca6a2SArvind Sankar 		return EFI_SUCCESS;
196a37ca6a2SArvind Sankar 
1978a8a3237SArvind Sankar 	len = strnlen(cmdline, COMMAND_LINE_SIZE - 1) + 1;
19891d150c0SArd Biesheuvel 	status = efi_bs_call(allocate_pool, EFI_LOADER_DATA, len, (void **)&buf);
19991d150c0SArd Biesheuvel 	if (status != EFI_SUCCESS)
20091d150c0SArd Biesheuvel 		return status;
20191d150c0SArd Biesheuvel 
2028a8a3237SArvind Sankar 	memcpy(buf, cmdline, len - 1);
2038a8a3237SArvind Sankar 	buf[len - 1] = '\0';
2048a8a3237SArvind Sankar 	str = skip_spaces(buf);
20591d150c0SArd Biesheuvel 
20691d150c0SArd Biesheuvel 	while (*str) {
20791d150c0SArd Biesheuvel 		char *param, *val;
20891d150c0SArd Biesheuvel 
20991d150c0SArd Biesheuvel 		str = next_arg(str, &param, &val);
2101fd9717dSArvind Sankar 		if (!val && !strcmp(param, "--"))
2111fd9717dSArvind Sankar 			break;
21291d150c0SArd Biesheuvel 
21391d150c0SArd Biesheuvel 		if (!strcmp(param, "nokaslr")) {
2147d4e323dSArd Biesheuvel 			efi_nokaslr = true;
21591d150c0SArd Biesheuvel 		} else if (!strcmp(param, "quiet")) {
21623d5b73fSArvind Sankar 			efi_loglevel = CONSOLE_LOGLEVEL_QUIET;
21779d3219dSArd Biesheuvel 		} else if (!strcmp(param, "noinitrd")) {
21879d3219dSArd Biesheuvel 			efi_noinitrd = true;
21991d150c0SArd Biesheuvel 		} else if (!strcmp(param, "efi") && val) {
22091d150c0SArd Biesheuvel 			efi_nochunk = parse_option_str(val, "nochunk");
22191d150c0SArd Biesheuvel 			efi_novamap = parse_option_str(val, "novamap");
222eeff7d63SArd Biesheuvel 
22391d150c0SArd Biesheuvel 			efi_nosoftreserve = IS_ENABLED(CONFIG_EFI_SOFT_RESERVE) &&
22491d150c0SArd Biesheuvel 					    parse_option_str(val, "nosoftreserve");
2255a17dae4SMatt Fleming 
22691d150c0SArd Biesheuvel 			if (parse_option_str(val, "disable_early_pci_dma"))
2274444f854SMatthew Garrett 				efi_disable_pci_dma = true;
22891d150c0SArd Biesheuvel 			if (parse_option_str(val, "no_disable_early_pci_dma"))
2294444f854SMatthew Garrett 				efi_disable_pci_dma = false;
23023d5b73fSArvind Sankar 			if (parse_option_str(val, "debug"))
23123d5b73fSArvind Sankar 				efi_loglevel = CONSOLE_LOGLEVEL_DEBUG;
232fffb6804SArvind Sankar 		} else if (!strcmp(param, "video") &&
233fffb6804SArvind Sankar 			   val && strstarts(val, "efifb:")) {
234fffb6804SArvind Sankar 			efi_parse_option_graphics(val + strlen("efifb:"));
2354444f854SMatthew Garrett 		}
2365a17dae4SMatt Fleming 	}
23791d150c0SArd Biesheuvel 	efi_bs_call(free_pool, buf);
2385a17dae4SMatt Fleming 	return EFI_SUCCESS;
2395a17dae4SMatt Fleming }
240f4f75ad5SArd Biesheuvel 
241f4f75ad5SArd Biesheuvel /*
2424a568ce2SArvind Sankar  * The EFI_LOAD_OPTION descriptor has the following layout:
2434a568ce2SArvind Sankar  *	u32 Attributes;
2444a568ce2SArvind Sankar  *	u16 FilePathListLength;
2454a568ce2SArvind Sankar  *	u16 Description[];
2464a568ce2SArvind Sankar  *	efi_device_path_protocol_t FilePathList[];
2474a568ce2SArvind Sankar  *	u8 OptionalData[];
2484a568ce2SArvind Sankar  *
2494a568ce2SArvind Sankar  * This function validates and unpacks the variable-size data fields.
2504a568ce2SArvind Sankar  */
2514a568ce2SArvind Sankar static
2524a568ce2SArvind Sankar bool efi_load_option_unpack(efi_load_option_unpacked_t *dest,
2534a568ce2SArvind Sankar 			    const efi_load_option_t *src, size_t size)
2544a568ce2SArvind Sankar {
2554a568ce2SArvind Sankar 	const void *pos;
2564a568ce2SArvind Sankar 	u16 c;
2574a568ce2SArvind Sankar 	efi_device_path_protocol_t header;
2584a568ce2SArvind Sankar 	const efi_char16_t *description;
2594a568ce2SArvind Sankar 	const efi_device_path_protocol_t *file_path_list;
2604a568ce2SArvind Sankar 
2614a568ce2SArvind Sankar 	if (size < offsetof(efi_load_option_t, variable_data))
2624a568ce2SArvind Sankar 		return false;
2634a568ce2SArvind Sankar 	pos = src->variable_data;
2644a568ce2SArvind Sankar 	size -= offsetof(efi_load_option_t, variable_data);
2654a568ce2SArvind Sankar 
2664a568ce2SArvind Sankar 	if ((src->attributes & ~EFI_LOAD_OPTION_MASK) != 0)
2674a568ce2SArvind Sankar 		return false;
2684a568ce2SArvind Sankar 
2694a568ce2SArvind Sankar 	/* Scan description. */
2704a568ce2SArvind Sankar 	description = pos;
2714a568ce2SArvind Sankar 	do {
2724a568ce2SArvind Sankar 		if (size < sizeof(c))
2734a568ce2SArvind Sankar 			return false;
2744a568ce2SArvind Sankar 		c = *(const u16 *)pos;
2754a568ce2SArvind Sankar 		pos += sizeof(c);
2764a568ce2SArvind Sankar 		size -= sizeof(c);
2774a568ce2SArvind Sankar 	} while (c != L'\0');
2784a568ce2SArvind Sankar 
2794a568ce2SArvind Sankar 	/* Scan file_path_list. */
2804a568ce2SArvind Sankar 	file_path_list = pos;
2814a568ce2SArvind Sankar 	do {
2824a568ce2SArvind Sankar 		if (size < sizeof(header))
2834a568ce2SArvind Sankar 			return false;
2844a568ce2SArvind Sankar 		header = *(const efi_device_path_protocol_t *)pos;
2854a568ce2SArvind Sankar 		if (header.length < sizeof(header))
2864a568ce2SArvind Sankar 			return false;
2874a568ce2SArvind Sankar 		if (size < header.length)
2884a568ce2SArvind Sankar 			return false;
2894a568ce2SArvind Sankar 		pos += header.length;
2904a568ce2SArvind Sankar 		size -= header.length;
2914a568ce2SArvind Sankar 	} while ((header.type != EFI_DEV_END_PATH && header.type != EFI_DEV_END_PATH2) ||
2924a568ce2SArvind Sankar 		 (header.sub_type != EFI_DEV_END_ENTIRE));
2934a568ce2SArvind Sankar 	if (pos != (const void *)file_path_list + src->file_path_list_length)
2944a568ce2SArvind Sankar 		return false;
2954a568ce2SArvind Sankar 
2964a568ce2SArvind Sankar 	dest->attributes = src->attributes;
2974a568ce2SArvind Sankar 	dest->file_path_list_length = src->file_path_list_length;
2984a568ce2SArvind Sankar 	dest->description = description;
2994a568ce2SArvind Sankar 	dest->file_path_list = file_path_list;
3004a568ce2SArvind Sankar 	dest->optional_data_size = size;
3014a568ce2SArvind Sankar 	dest->optional_data = size ? pos : NULL;
3024a568ce2SArvind Sankar 
3034a568ce2SArvind Sankar 	return true;
3044a568ce2SArvind Sankar }
3054a568ce2SArvind Sankar 
3064a568ce2SArvind Sankar /*
3074a568ce2SArvind Sankar  * At least some versions of Dell firmware pass the entire contents of the
3084a568ce2SArvind Sankar  * Boot#### variable, i.e. the EFI_LOAD_OPTION descriptor, rather than just the
3094a568ce2SArvind Sankar  * OptionalData field.
3104a568ce2SArvind Sankar  *
3114a568ce2SArvind Sankar  * Detect this case and extract OptionalData.
3124a568ce2SArvind Sankar  */
313a241d94bSArd Biesheuvel void efi_apply_loadoptions_quirk(const void **load_options, u32 *load_options_size)
3144a568ce2SArvind Sankar {
3154a568ce2SArvind Sankar 	const efi_load_option_t *load_option = *load_options;
3164a568ce2SArvind Sankar 	efi_load_option_unpacked_t load_option_unpacked;
3174a568ce2SArvind Sankar 
3184a568ce2SArvind Sankar 	if (!IS_ENABLED(CONFIG_X86))
3194a568ce2SArvind Sankar 		return;
3204a568ce2SArvind Sankar 	if (!load_option)
3214a568ce2SArvind Sankar 		return;
3224a568ce2SArvind Sankar 	if (*load_options_size < sizeof(*load_option))
3234a568ce2SArvind Sankar 		return;
3244a568ce2SArvind Sankar 	if ((load_option->attributes & ~EFI_LOAD_OPTION_BOOT_MASK) != 0)
3254a568ce2SArvind Sankar 		return;
3264a568ce2SArvind Sankar 
3274a568ce2SArvind Sankar 	if (!efi_load_option_unpack(&load_option_unpacked, load_option, *load_options_size))
3284a568ce2SArvind Sankar 		return;
3294a568ce2SArvind Sankar 
3304a568ce2SArvind Sankar 	efi_warn_once(FW_BUG "LoadOptions is an EFI_LOAD_OPTION descriptor\n");
3314a568ce2SArvind Sankar 	efi_warn_once(FW_BUG "Using OptionalData as a workaround\n");
3324a568ce2SArvind Sankar 
3334a568ce2SArvind Sankar 	*load_options = load_option_unpacked.optional_data;
3344a568ce2SArvind Sankar 	*load_options_size = load_option_unpacked.optional_data_size;
3354a568ce2SArvind Sankar }
3364a568ce2SArvind Sankar 
337*56633169SIlias Apalodimas enum efistub_event {
338*56633169SIlias Apalodimas 	EFISTUB_EVT_INITRD,
339*56633169SIlias Apalodimas 	EFISTUB_EVT_COUNT,
340*56633169SIlias Apalodimas };
341*56633169SIlias Apalodimas 
342*56633169SIlias Apalodimas #define STR_WITH_SIZE(s)	sizeof(s), s
343*56633169SIlias Apalodimas 
344*56633169SIlias Apalodimas static const struct {
345*56633169SIlias Apalodimas 	u32		pcr_index;
346*56633169SIlias Apalodimas 	u32		event_id;
347*56633169SIlias Apalodimas 	u32		event_data_len;
348*56633169SIlias Apalodimas 	u8		event_data[52];
349*56633169SIlias Apalodimas } events[] = {
350*56633169SIlias Apalodimas 	[EFISTUB_EVT_INITRD] = {
351*56633169SIlias Apalodimas 		9,
352*56633169SIlias Apalodimas 		INITRD_EVENT_TAG_ID,
353*56633169SIlias Apalodimas 		STR_WITH_SIZE("Linux initrd")
354*56633169SIlias Apalodimas 	},
355*56633169SIlias Apalodimas };
356*56633169SIlias Apalodimas 
357*56633169SIlias Apalodimas static efi_status_t efi_measure_tagged_event(unsigned long load_addr,
358*56633169SIlias Apalodimas 					     unsigned long load_size,
359*56633169SIlias Apalodimas 					     enum efistub_event event)
360*56633169SIlias Apalodimas {
361*56633169SIlias Apalodimas 	efi_guid_t tcg2_guid = EFI_TCG2_PROTOCOL_GUID;
362*56633169SIlias Apalodimas 	efi_tcg2_protocol_t *tcg2 = NULL;
363*56633169SIlias Apalodimas 	efi_status_t status;
364*56633169SIlias Apalodimas 
365*56633169SIlias Apalodimas 	efi_bs_call(locate_protocol, &tcg2_guid, NULL, (void **)&tcg2);
366*56633169SIlias Apalodimas 	if (tcg2) {
367*56633169SIlias Apalodimas 		struct efi_measured_event {
368*56633169SIlias Apalodimas 			efi_tcg2_event_t	event_data;
369*56633169SIlias Apalodimas 			efi_tcg2_tagged_event_t tagged_event;
370*56633169SIlias Apalodimas 			u8			tagged_event_data[];
371*56633169SIlias Apalodimas 		} *evt;
372*56633169SIlias Apalodimas 		int size = sizeof(*evt) + events[event].event_data_len;
373*56633169SIlias Apalodimas 
374*56633169SIlias Apalodimas 		status = efi_bs_call(allocate_pool, EFI_LOADER_DATA, size,
375*56633169SIlias Apalodimas 				     (void **)&evt);
376*56633169SIlias Apalodimas 		if (status != EFI_SUCCESS)
377*56633169SIlias Apalodimas 			goto fail;
378*56633169SIlias Apalodimas 
379*56633169SIlias Apalodimas 		evt->event_data = (struct efi_tcg2_event){
380*56633169SIlias Apalodimas 			.event_size			= size,
381*56633169SIlias Apalodimas 			.event_header.header_size	= sizeof(evt->event_data.event_header),
382*56633169SIlias Apalodimas 			.event_header.header_version	= EFI_TCG2_EVENT_HEADER_VERSION,
383*56633169SIlias Apalodimas 			.event_header.pcr_index		= events[event].pcr_index,
384*56633169SIlias Apalodimas 			.event_header.event_type	= EV_EVENT_TAG,
385*56633169SIlias Apalodimas 		};
386*56633169SIlias Apalodimas 
387*56633169SIlias Apalodimas 		evt->tagged_event = (struct efi_tcg2_tagged_event){
388*56633169SIlias Apalodimas 			.tagged_event_id		= events[event].event_id,
389*56633169SIlias Apalodimas 			.tagged_event_data_size		= events[event].event_data_len,
390*56633169SIlias Apalodimas 		};
391*56633169SIlias Apalodimas 
392*56633169SIlias Apalodimas 		memcpy(evt->tagged_event_data, events[event].event_data,
393*56633169SIlias Apalodimas 		       events[event].event_data_len);
394*56633169SIlias Apalodimas 
395*56633169SIlias Apalodimas 		status = efi_call_proto(tcg2, hash_log_extend_event, 0,
396*56633169SIlias Apalodimas 					load_addr, load_size, &evt->event_data);
397*56633169SIlias Apalodimas 		efi_bs_call(free_pool, evt);
398*56633169SIlias Apalodimas 
399*56633169SIlias Apalodimas 		if (status != EFI_SUCCESS)
400*56633169SIlias Apalodimas 			goto fail;
401*56633169SIlias Apalodimas 		return EFI_SUCCESS;
402*56633169SIlias Apalodimas 	}
403*56633169SIlias Apalodimas 
404*56633169SIlias Apalodimas 	return EFI_UNSUPPORTED;
405*56633169SIlias Apalodimas fail:
406*56633169SIlias Apalodimas 	efi_warn("Failed to measure data for event %d: 0x%lx\n", event, status);
407*56633169SIlias Apalodimas 	return status;
408*56633169SIlias Apalodimas }
409*56633169SIlias Apalodimas 
4104a568ce2SArvind Sankar /*
411f4f75ad5SArd Biesheuvel  * Convert the unicode UEFI command line to ASCII to pass to kernel.
412f4f75ad5SArd Biesheuvel  * Size of memory allocated return in *cmd_line_len.
413f4f75ad5SArd Biesheuvel  * Returns NULL on error.
414f4f75ad5SArd Biesheuvel  */
41527cd5511SArd Biesheuvel char *efi_convert_cmdline(efi_loaded_image_t *image, int *cmd_line_len)
416f4f75ad5SArd Biesheuvel {
417a241d94bSArd Biesheuvel 	const efi_char16_t *options = efi_table_attr(image, load_options);
418a241d94bSArd Biesheuvel 	u32 options_size = efi_table_attr(image, load_options_size);
41980b1bfe1SArvind Sankar 	int options_bytes = 0, safe_options_bytes = 0;  /* UTF-8 bytes */
420a241d94bSArd Biesheuvel 	unsigned long cmdline_addr = 0;
421a241d94bSArd Biesheuvel 	const efi_char16_t *s2;
42280b1bfe1SArvind Sankar 	bool in_quote = false;
423f4f75ad5SArd Biesheuvel 	efi_status_t status;
424a241d94bSArd Biesheuvel 	u32 options_chars;
425f4f75ad5SArd Biesheuvel 
426a241d94bSArd Biesheuvel 	efi_apply_loadoptions_quirk((const void **)&options, &options_size);
427a241d94bSArd Biesheuvel 	options_chars = options_size / sizeof(efi_char16_t);
4284a568ce2SArvind Sankar 
429f4f75ad5SArd Biesheuvel 	if (options) {
430f4f75ad5SArd Biesheuvel 		s2 = options;
43180b1bfe1SArvind Sankar 		while (options_bytes < COMMAND_LINE_SIZE && options_chars--) {
432a241d94bSArd Biesheuvel 			efi_char16_t c = *s2++;
43315c316bcSArvind Sankar 
43480b1bfe1SArvind Sankar 			if (c < 0x80) {
43515c316bcSArvind Sankar 				if (c == L'\0' || c == L'\n')
43615c316bcSArvind Sankar 					break;
43780b1bfe1SArvind Sankar 				if (c == L'"')
43880b1bfe1SArvind Sankar 					in_quote = !in_quote;
43980b1bfe1SArvind Sankar 				else if (!in_quote && isspace((char)c))
44080b1bfe1SArvind Sankar 					safe_options_bytes = options_bytes;
44180b1bfe1SArvind Sankar 
44280b1bfe1SArvind Sankar 				options_bytes++;
44380b1bfe1SArvind Sankar 				continue;
44480b1bfe1SArvind Sankar 			}
44580b1bfe1SArvind Sankar 
44615c316bcSArvind Sankar 			/*
44715c316bcSArvind Sankar 			 * Get the number of UTF-8 bytes corresponding to a
44815c316bcSArvind Sankar 			 * UTF-16 character.
44915c316bcSArvind Sankar 			 * The first part handles everything in the BMP.
45015c316bcSArvind Sankar 			 */
45180b1bfe1SArvind Sankar 			options_bytes += 2 + (c >= 0x800);
45215c316bcSArvind Sankar 			/*
45315c316bcSArvind Sankar 			 * Add one more byte for valid surrogate pairs. Invalid
45415c316bcSArvind Sankar 			 * surrogates will be replaced with 0xfffd and take up
45515c316bcSArvind Sankar 			 * only 3 bytes.
45615c316bcSArvind Sankar 			 */
45715c316bcSArvind Sankar 			if ((c & 0xfc00) == 0xd800) {
45815c316bcSArvind Sankar 				/*
45915c316bcSArvind Sankar 				 * If the very last word is a high surrogate,
46015c316bcSArvind Sankar 				 * we must ignore it since we can't access the
46115c316bcSArvind Sankar 				 * low surrogate.
46215c316bcSArvind Sankar 				 */
46304b24409SArvind Sankar 				if (!options_chars) {
46415c316bcSArvind Sankar 					options_bytes -= 3;
46515c316bcSArvind Sankar 				} else if ((*s2 & 0xfc00) == 0xdc00) {
46615c316bcSArvind Sankar 					options_bytes++;
46704b24409SArvind Sankar 					options_chars--;
46815c316bcSArvind Sankar 					s2++;
46915c316bcSArvind Sankar 				}
47015c316bcSArvind Sankar 			}
471f4f75ad5SArd Biesheuvel 		}
47280b1bfe1SArvind Sankar 		if (options_bytes >= COMMAND_LINE_SIZE) {
47380b1bfe1SArvind Sankar 			options_bytes = safe_options_bytes;
47480b1bfe1SArvind Sankar 			efi_err("Command line is too long: truncated to %d bytes\n",
47580b1bfe1SArvind Sankar 				options_bytes);
47680b1bfe1SArvind Sankar 		}
477f4f75ad5SArd Biesheuvel 	}
478f4f75ad5SArd Biesheuvel 
479f4f75ad5SArd Biesheuvel 	options_bytes++;	/* NUL termination */
480f4f75ad5SArd Biesheuvel 
48127cd5511SArd Biesheuvel 	status = efi_bs_call(allocate_pool, EFI_LOADER_DATA, options_bytes,
48227cd5511SArd Biesheuvel 			     (void **)&cmdline_addr);
483f4f75ad5SArd Biesheuvel 	if (status != EFI_SUCCESS)
484f4f75ad5SArd Biesheuvel 		return NULL;
485f4f75ad5SArd Biesheuvel 
48604b24409SArvind Sankar 	snprintf((char *)cmdline_addr, options_bytes, "%.*ls",
48704b24409SArvind Sankar 		 options_bytes - 1, options);
488f4f75ad5SArd Biesheuvel 
489f4f75ad5SArd Biesheuvel 	*cmd_line_len = options_bytes;
490f4f75ad5SArd Biesheuvel 	return (char *)cmdline_addr;
491f4f75ad5SArd Biesheuvel }
492fc07716bSJeffrey Hugo 
4938c0a839cSHeinrich Schuchardt /**
4948c0a839cSHeinrich Schuchardt  * efi_exit_boot_services() - Exit boot services
4958c0a839cSHeinrich Schuchardt  * @handle:	handle of the exiting image
4968c0a839cSHeinrich Schuchardt  * @priv:	argument to be passed to @priv_func
4978c0a839cSHeinrich Schuchardt  * @priv_func:	function to process the memory map before exiting boot services
4988c0a839cSHeinrich Schuchardt  *
499fc07716bSJeffrey Hugo  * Handle calling ExitBootServices according to the requirements set out by the
500fc07716bSJeffrey Hugo  * spec.  Obtains the current memory map, and returns that info after calling
501fc07716bSJeffrey Hugo  * ExitBootServices.  The client must specify a function to perform any
502fc07716bSJeffrey Hugo  * processing of the memory map data prior to ExitBootServices.  A client
503fc07716bSJeffrey Hugo  * specific structure may be passed to the function via priv.  The client
504fc07716bSJeffrey Hugo  * function may be called multiple times.
5058c0a839cSHeinrich Schuchardt  *
5068c0a839cSHeinrich Schuchardt  * Return:	status code
507fc07716bSJeffrey Hugo  */
508eab31265SArd Biesheuvel efi_status_t efi_exit_boot_services(void *handle, void *priv,
509fc07716bSJeffrey Hugo 				    efi_exit_boot_map_processing priv_func)
510fc07716bSJeffrey Hugo {
511eab31265SArd Biesheuvel 	struct efi_boot_memmap *map;
512fc07716bSJeffrey Hugo 	efi_status_t status;
513fc07716bSJeffrey Hugo 
514171539f5SArd Biesheuvel 	status = efi_get_memory_map(&map, true);
515fc07716bSJeffrey Hugo 	if (status != EFI_SUCCESS)
516a12b78b5SArd Biesheuvel 		return status;
517fc07716bSJeffrey Hugo 
518cd33a5c1SArd Biesheuvel 	status = priv_func(map, priv);
519a12b78b5SArd Biesheuvel 	if (status != EFI_SUCCESS) {
520a12b78b5SArd Biesheuvel 		efi_bs_call(free_pool, map);
521a12b78b5SArd Biesheuvel 		return status;
522a12b78b5SArd Biesheuvel 	}
523fc07716bSJeffrey Hugo 
5244444f854SMatthew Garrett 	if (efi_disable_pci_dma)
5254444f854SMatthew Garrett 		efi_pci_disable_bridge_busmaster();
5264444f854SMatthew Garrett 
527eab31265SArd Biesheuvel 	status = efi_bs_call(exit_boot_services, handle, map->map_key);
528fc07716bSJeffrey Hugo 
529fc07716bSJeffrey Hugo 	if (status == EFI_INVALID_PARAMETER) {
530fc07716bSJeffrey Hugo 		/*
531fc07716bSJeffrey Hugo 		 * The memory map changed between efi_get_memory_map() and
532fc07716bSJeffrey Hugo 		 * exit_boot_services().  Per the UEFI Spec v2.6, Section 6.4:
533fc07716bSJeffrey Hugo 		 * EFI_BOOT_SERVICES.ExitBootServices we need to get the
534fc07716bSJeffrey Hugo 		 * updated map, and try again.  The spec implies one retry
535fc07716bSJeffrey Hugo 		 * should be sufficent, which is confirmed against the EDK2
536fc07716bSJeffrey Hugo 		 * implementation.  Per the spec, we can only invoke
537fc07716bSJeffrey Hugo 		 * get_memory_map() and exit_boot_services() - we cannot alloc
538fc07716bSJeffrey Hugo 		 * so efi_get_memory_map() cannot be used, and we must reuse
539fc07716bSJeffrey Hugo 		 * the buffer.  For all practical purposes, the headroom in the
540fc07716bSJeffrey Hugo 		 * buffer should account for any changes in the map so the call
541fc07716bSJeffrey Hugo 		 * to get_memory_map() is expected to succeed here.
542fc07716bSJeffrey Hugo 		 */
543eab31265SArd Biesheuvel 		map->map_size = map->buff_size;
544966291f6SArd Biesheuvel 		status = efi_bs_call(get_memory_map,
545eab31265SArd Biesheuvel 				     &map->map_size,
546eab31265SArd Biesheuvel 				     &map->map,
547eab31265SArd Biesheuvel 				     &map->map_key,
548eab31265SArd Biesheuvel 				     &map->desc_size,
549eab31265SArd Biesheuvel 				     &map->desc_ver);
550fc07716bSJeffrey Hugo 
551fc07716bSJeffrey Hugo 		/* exit_boot_services() was called, thus cannot free */
552fc07716bSJeffrey Hugo 		if (status != EFI_SUCCESS)
553a12b78b5SArd Biesheuvel 			return status;
554fc07716bSJeffrey Hugo 
555cd33a5c1SArd Biesheuvel 		status = priv_func(map, priv);
556fc07716bSJeffrey Hugo 		/* exit_boot_services() was called, thus cannot free */
557fc07716bSJeffrey Hugo 		if (status != EFI_SUCCESS)
558a12b78b5SArd Biesheuvel 			return status;
559fc07716bSJeffrey Hugo 
560eab31265SArd Biesheuvel 		status = efi_bs_call(exit_boot_services, handle, map->map_key);
561fc07716bSJeffrey Hugo 	}
562fc07716bSJeffrey Hugo 
563fc07716bSJeffrey Hugo 	return status;
564fc07716bSJeffrey Hugo }
56582d736acSMatthew Garrett 
5668c0a839cSHeinrich Schuchardt /**
5678c0a839cSHeinrich Schuchardt  * get_efi_config_table() - retrieve UEFI configuration table
5688c0a839cSHeinrich Schuchardt  * @guid:	GUID of the configuration table to be retrieved
5698c0a839cSHeinrich Schuchardt  * Return:	pointer to the configuration table or NULL
5708c0a839cSHeinrich Schuchardt  */
571cd33a5c1SArd Biesheuvel void *get_efi_config_table(efi_guid_t guid)
57282d736acSMatthew Garrett {
573ccc27ae7SArd Biesheuvel 	unsigned long tables = efi_table_attr(efi_system_table, tables);
574ccc27ae7SArd Biesheuvel 	int nr_tables = efi_table_attr(efi_system_table, nr_tables);
575f958efe9SArd Biesheuvel 	int i;
576f958efe9SArd Biesheuvel 
577f958efe9SArd Biesheuvel 	for (i = 0; i < nr_tables; i++) {
578f958efe9SArd Biesheuvel 		efi_config_table_t *t = (void *)tables;
579f958efe9SArd Biesheuvel 
580f958efe9SArd Biesheuvel 		if (efi_guidcmp(t->guid, guid) == 0)
58199ea8b1dSArd Biesheuvel 			return efi_table_attr(t, table);
582f958efe9SArd Biesheuvel 
583f958efe9SArd Biesheuvel 		tables += efi_is_native() ? sizeof(efi_config_table_t)
584f958efe9SArd Biesheuvel 					  : sizeof(efi_config_table_32_t);
585f958efe9SArd Biesheuvel 	}
586f958efe9SArd Biesheuvel 	return NULL;
58782d736acSMatthew Garrett }
588dc29da14SArd Biesheuvel 
589ec93fc37SArd Biesheuvel /*
590ec93fc37SArd Biesheuvel  * The LINUX_EFI_INITRD_MEDIA_GUID vendor media device path below provides a way
591ec93fc37SArd Biesheuvel  * for the firmware or bootloader to expose the initrd data directly to the stub
592ec93fc37SArd Biesheuvel  * via the trivial LoadFile2 protocol, which is defined in the UEFI spec, and is
593ec93fc37SArd Biesheuvel  * very easy to implement. It is a simple Linux initrd specific conduit between
594ec93fc37SArd Biesheuvel  * kernel and firmware, allowing us to put the EFI stub (being part of the
595ec93fc37SArd Biesheuvel  * kernel) in charge of where and when to load the initrd, while leaving it up
596ec93fc37SArd Biesheuvel  * to the firmware to decide whether it needs to expose its filesystem hierarchy
597ec93fc37SArd Biesheuvel  * via EFI protocols.
598ec93fc37SArd Biesheuvel  */
599ec93fc37SArd Biesheuvel static const struct {
600ec93fc37SArd Biesheuvel 	struct efi_vendor_dev_path	vendor;
601ec93fc37SArd Biesheuvel 	struct efi_generic_dev_path	end;
602ec93fc37SArd Biesheuvel } __packed initrd_dev_path = {
603ec93fc37SArd Biesheuvel 	{
604ec93fc37SArd Biesheuvel 		{
605ec93fc37SArd Biesheuvel 			EFI_DEV_MEDIA,
606ec93fc37SArd Biesheuvel 			EFI_DEV_MEDIA_VENDOR,
607ec93fc37SArd Biesheuvel 			sizeof(struct efi_vendor_dev_path),
608ec93fc37SArd Biesheuvel 		},
609ec93fc37SArd Biesheuvel 		LINUX_EFI_INITRD_MEDIA_GUID
610ec93fc37SArd Biesheuvel 	}, {
611ec93fc37SArd Biesheuvel 		EFI_DEV_END_PATH,
612ec93fc37SArd Biesheuvel 		EFI_DEV_END_ENTIRE,
613ec93fc37SArd Biesheuvel 		sizeof(struct efi_generic_dev_path)
614ec93fc37SArd Biesheuvel 	}
615ec93fc37SArd Biesheuvel };
616ec93fc37SArd Biesheuvel 
617ec93fc37SArd Biesheuvel /**
6188c0a839cSHeinrich Schuchardt  * efi_load_initrd_dev_path() - load the initrd from the Linux initrd device path
619ec93fc37SArd Biesheuvel  * @load_addr:	pointer to store the address where the initrd was loaded
620ec93fc37SArd Biesheuvel  * @load_size:	pointer to store the size of the loaded initrd
621ec93fc37SArd Biesheuvel  * @max:	upper limit for the initrd memory allocation
6228c0a839cSHeinrich Schuchardt  *
6238c0a839cSHeinrich Schuchardt  * Return:
6248c0a839cSHeinrich Schuchardt  * * %EFI_SUCCESS if the initrd was loaded successfully, in which
625ec93fc37SArd Biesheuvel  *   case @load_addr and @load_size are assigned accordingly
6268c0a839cSHeinrich Schuchardt  * * %EFI_NOT_FOUND if no LoadFile2 protocol exists on the initrd device path
6278c0a839cSHeinrich Schuchardt  * * %EFI_OUT_OF_RESOURCES if memory allocation failed
6288c0a839cSHeinrich Schuchardt  * * %EFI_LOAD_ERROR in all other cases
629ec93fc37SArd Biesheuvel  */
630f61900fdSArvind Sankar static
631f4dc7fffSArd Biesheuvel efi_status_t efi_load_initrd_dev_path(struct linux_efi_initrd *initrd,
632ec93fc37SArd Biesheuvel 				      unsigned long max)
633ec93fc37SArd Biesheuvel {
634ec93fc37SArd Biesheuvel 	efi_guid_t lf2_proto_guid = EFI_LOAD_FILE2_PROTOCOL_GUID;
635ec93fc37SArd Biesheuvel 	efi_device_path_protocol_t *dp;
636ec93fc37SArd Biesheuvel 	efi_load_file2_protocol_t *lf2;
637ec93fc37SArd Biesheuvel 	efi_handle_t handle;
638ec93fc37SArd Biesheuvel 	efi_status_t status;
639ec93fc37SArd Biesheuvel 
640ec93fc37SArd Biesheuvel 	dp = (efi_device_path_protocol_t *)&initrd_dev_path;
641ec93fc37SArd Biesheuvel 	status = efi_bs_call(locate_device_path, &lf2_proto_guid, &dp, &handle);
642ec93fc37SArd Biesheuvel 	if (status != EFI_SUCCESS)
643ec93fc37SArd Biesheuvel 		return status;
644ec93fc37SArd Biesheuvel 
645ec93fc37SArd Biesheuvel 	status = efi_bs_call(handle_protocol, handle, &lf2_proto_guid,
646ec93fc37SArd Biesheuvel 			     (void **)&lf2);
647ec93fc37SArd Biesheuvel 	if (status != EFI_SUCCESS)
648ec93fc37SArd Biesheuvel 		return status;
649ec93fc37SArd Biesheuvel 
650f4dc7fffSArd Biesheuvel 	initrd->size = 0;
651f4dc7fffSArd Biesheuvel 	status = efi_call_proto(lf2, load_file, dp, false, &initrd->size, NULL);
652ec93fc37SArd Biesheuvel 	if (status != EFI_BUFFER_TOO_SMALL)
653ec93fc37SArd Biesheuvel 		return EFI_LOAD_ERROR;
654ec93fc37SArd Biesheuvel 
655f4dc7fffSArd Biesheuvel 	status = efi_allocate_pages(initrd->size, &initrd->base, max);
656ec93fc37SArd Biesheuvel 	if (status != EFI_SUCCESS)
657ec93fc37SArd Biesheuvel 		return status;
658ec93fc37SArd Biesheuvel 
659f4dc7fffSArd Biesheuvel 	status = efi_call_proto(lf2, load_file, dp, false, &initrd->size,
660f4dc7fffSArd Biesheuvel 				(void *)initrd->base);
661ec93fc37SArd Biesheuvel 	if (status != EFI_SUCCESS) {
662f4dc7fffSArd Biesheuvel 		efi_free(initrd->size, initrd->base);
663ec93fc37SArd Biesheuvel 		return EFI_LOAD_ERROR;
664ec93fc37SArd Biesheuvel 	}
665ec93fc37SArd Biesheuvel 	return EFI_SUCCESS;
666ec93fc37SArd Biesheuvel }
667f61900fdSArvind Sankar 
668f61900fdSArvind Sankar static
669f61900fdSArvind Sankar efi_status_t efi_load_initrd_cmdline(efi_loaded_image_t *image,
670f4dc7fffSArd Biesheuvel 				     struct linux_efi_initrd *initrd,
671f61900fdSArvind Sankar 				     unsigned long soft_limit,
672f61900fdSArvind Sankar 				     unsigned long hard_limit)
673f61900fdSArvind Sankar {
674f61900fdSArvind Sankar 	if (!IS_ENABLED(CONFIG_EFI_GENERIC_STUB_INITRD_CMDLINE_LOADER) ||
675f4dc7fffSArd Biesheuvel 	    (IS_ENABLED(CONFIG_X86) && (!efi_is_native() || image == NULL)))
676f4dc7fffSArd Biesheuvel 		return EFI_UNSUPPORTED;
677f61900fdSArvind Sankar 
678f61900fdSArvind Sankar 	return handle_cmdline_files(image, L"initrd=", sizeof(L"initrd=") - 2,
679f61900fdSArvind Sankar 				    soft_limit, hard_limit,
680f4dc7fffSArd Biesheuvel 				    &initrd->base, &initrd->size);
681f61900fdSArvind Sankar }
682f61900fdSArvind Sankar 
6838c0a839cSHeinrich Schuchardt /**
6848c0a839cSHeinrich Schuchardt  * efi_load_initrd() - Load initial RAM disk
6858c0a839cSHeinrich Schuchardt  * @image:	EFI loaded image protocol
686947228cbSAtish Patra  * @soft_limit:	preferred address for loading the initrd
687947228cbSAtish Patra  * @hard_limit:	upper limit address for loading the initrd
6888c0a839cSHeinrich Schuchardt  *
6898c0a839cSHeinrich Schuchardt  * Return:	status code
6908c0a839cSHeinrich Schuchardt  */
691f61900fdSArvind Sankar efi_status_t efi_load_initrd(efi_loaded_image_t *image,
692f61900fdSArvind Sankar 			     unsigned long soft_limit,
693f4dc7fffSArd Biesheuvel 			     unsigned long hard_limit,
694f4dc7fffSArd Biesheuvel 			     const struct linux_efi_initrd **out)
695f61900fdSArvind Sankar {
696f4dc7fffSArd Biesheuvel 	efi_guid_t tbl_guid = LINUX_EFI_INITRD_MEDIA_GUID;
697f4dc7fffSArd Biesheuvel 	efi_status_t status = EFI_SUCCESS;
698f4dc7fffSArd Biesheuvel 	struct linux_efi_initrd initrd, *tbl;
699f61900fdSArvind Sankar 
700f4dc7fffSArd Biesheuvel 	if (!IS_ENABLED(CONFIG_BLK_DEV_INITRD) || efi_noinitrd)
701f4dc7fffSArd Biesheuvel 		return EFI_SUCCESS;
702f4dc7fffSArd Biesheuvel 
703f4dc7fffSArd Biesheuvel 	status = efi_load_initrd_dev_path(&initrd, hard_limit);
704f61900fdSArvind Sankar 	if (status == EFI_SUCCESS) {
705f61900fdSArvind Sankar 		efi_info("Loaded initrd from LINUX_EFI_INITRD_MEDIA_GUID device path\n");
706*56633169SIlias Apalodimas 		if (initrd.size > 0 &&
707*56633169SIlias Apalodimas 		    efi_measure_tagged_event(initrd.base, initrd.size,
708*56633169SIlias Apalodimas 					     EFISTUB_EVT_INITRD) == EFI_SUCCESS)
709*56633169SIlias Apalodimas 			efi_info("Measured initrd data into PCR 9\n");
710f61900fdSArvind Sankar 	} else if (status == EFI_NOT_FOUND) {
711f4dc7fffSArd Biesheuvel 		status = efi_load_initrd_cmdline(image, &initrd, soft_limit,
712f4dc7fffSArd Biesheuvel 						 hard_limit);
713f4dc7fffSArd Biesheuvel 		/* command line loader disabled or no initrd= passed? */
714f4dc7fffSArd Biesheuvel 		if (status == EFI_UNSUPPORTED || status == EFI_NOT_READY)
715f4dc7fffSArd Biesheuvel 			return EFI_SUCCESS;
716f4dc7fffSArd Biesheuvel 		if (status == EFI_SUCCESS)
717f61900fdSArvind Sankar 			efi_info("Loaded initrd from command line option\n");
718f61900fdSArvind Sankar 	}
719f4dc7fffSArd Biesheuvel 	if (status != EFI_SUCCESS)
720f4dc7fffSArd Biesheuvel 		goto failed;
721f046fff8SIlias Apalodimas 
722f4dc7fffSArd Biesheuvel 	status = efi_bs_call(allocate_pool, EFI_LOADER_DATA, sizeof(initrd),
723f4dc7fffSArd Biesheuvel 			     (void **)&tbl);
724f4dc7fffSArd Biesheuvel 	if (status != EFI_SUCCESS)
725f4dc7fffSArd Biesheuvel 		goto free_initrd;
726f4dc7fffSArd Biesheuvel 
727f4dc7fffSArd Biesheuvel 	*tbl = initrd;
728f4dc7fffSArd Biesheuvel 	status = efi_bs_call(install_configuration_table, &tbl_guid, tbl);
729f4dc7fffSArd Biesheuvel 	if (status != EFI_SUCCESS)
730f4dc7fffSArd Biesheuvel 		goto free_tbl;
731f4dc7fffSArd Biesheuvel 
732f4dc7fffSArd Biesheuvel 	if (out)
733f4dc7fffSArd Biesheuvel 		*out = tbl;
734f4dc7fffSArd Biesheuvel 	return EFI_SUCCESS;
735f4dc7fffSArd Biesheuvel 
736f4dc7fffSArd Biesheuvel free_tbl:
737f4dc7fffSArd Biesheuvel 	efi_bs_call(free_pool, tbl);
738f4dc7fffSArd Biesheuvel free_initrd:
739f4dc7fffSArd Biesheuvel 	efi_free(initrd.size, initrd.base);
740f4dc7fffSArd Biesheuvel failed:
741f4dc7fffSArd Biesheuvel 	efi_err("Failed to load initrd: 0x%lx\n", status);
742f61900fdSArvind Sankar 	return status;
743f61900fdSArvind Sankar }
74414c574f3SArvind Sankar 
7458c0a839cSHeinrich Schuchardt /**
7468c0a839cSHeinrich Schuchardt  * efi_wait_for_key() - Wait for key stroke
7478c0a839cSHeinrich Schuchardt  * @usec:	number of microseconds to wait for key stroke
7488c0a839cSHeinrich Schuchardt  * @key:	key entered
7498c0a839cSHeinrich Schuchardt  *
7508c0a839cSHeinrich Schuchardt  * Wait for up to @usec microseconds for a key stroke.
7518c0a839cSHeinrich Schuchardt  *
7528c0a839cSHeinrich Schuchardt  * Return:	status code, EFI_SUCCESS if key received
7538c0a839cSHeinrich Schuchardt  */
75414c574f3SArvind Sankar efi_status_t efi_wait_for_key(unsigned long usec, efi_input_key_t *key)
75514c574f3SArvind Sankar {
75614c574f3SArvind Sankar 	efi_event_t events[2], timer;
75714c574f3SArvind Sankar 	unsigned long index;
75814c574f3SArvind Sankar 	efi_simple_text_input_protocol_t *con_in;
75914c574f3SArvind Sankar 	efi_status_t status;
76014c574f3SArvind Sankar 
76114c574f3SArvind Sankar 	con_in = efi_table_attr(efi_system_table, con_in);
76214c574f3SArvind Sankar 	if (!con_in)
76314c574f3SArvind Sankar 		return EFI_UNSUPPORTED;
76414c574f3SArvind Sankar 	efi_set_event_at(events, 0, efi_table_attr(con_in, wait_for_key));
76514c574f3SArvind Sankar 
76614c574f3SArvind Sankar 	status = efi_bs_call(create_event, EFI_EVT_TIMER, 0, NULL, NULL, &timer);
76714c574f3SArvind Sankar 	if (status != EFI_SUCCESS)
76814c574f3SArvind Sankar 		return status;
76914c574f3SArvind Sankar 
77014c574f3SArvind Sankar 	status = efi_bs_call(set_timer, timer, EfiTimerRelative,
77114c574f3SArvind Sankar 			     EFI_100NSEC_PER_USEC * usec);
77214c574f3SArvind Sankar 	if (status != EFI_SUCCESS)
77314c574f3SArvind Sankar 		return status;
77414c574f3SArvind Sankar 	efi_set_event_at(events, 1, timer);
77514c574f3SArvind Sankar 
77614c574f3SArvind Sankar 	status = efi_bs_call(wait_for_event, 2, events, &index);
77714c574f3SArvind Sankar 	if (status == EFI_SUCCESS) {
77814c574f3SArvind Sankar 		if (index == 0)
77914c574f3SArvind Sankar 			status = efi_call_proto(con_in, read_keystroke, key);
78014c574f3SArvind Sankar 		else
78114c574f3SArvind Sankar 			status = EFI_TIMEOUT;
78214c574f3SArvind Sankar 	}
78314c574f3SArvind Sankar 
78414c574f3SArvind Sankar 	efi_bs_call(close_event, timer);
78514c574f3SArvind Sankar 
78614c574f3SArvind Sankar 	return status;
78714c574f3SArvind Sankar }
788