xref: /DragonStub/apps/helper.c (revision 3e6106c4d60a23aae3c0740979c5e6fb728b63c3)
1f412fd2aSLoGin #include "efidef.h"
2f412fd2aSLoGin #include <efi.h>
3f412fd2aSLoGin #include <efilib.h>
4f412fd2aSLoGin #include <lib.h>
5f412fd2aSLoGin #include <dragonstub/dragonstub.h>
6f412fd2aSLoGin 
778b790faSLoGin bool efi_nochunk;
878b790faSLoGin bool efi_nokaslr = true;
978b790faSLoGin // bool efi_nokaslr = !IS_ENABLED(CONFIG_RANDOMIZE_BASE);
1078b790faSLoGin bool efi_novamap;
1178b790faSLoGin 
1278b790faSLoGin static bool efi_noinitrd;
1378b790faSLoGin static bool efi_nosoftreserve;
1478b790faSLoGin static bool efi_disable_pci_dma = false;
1578b790faSLoGin // static bool efi_disable_pci_dma = IS_ENABLED(CONFIG_EFI_DISABLE_PCI_DMA);
1678b790faSLoGin 
17f412fd2aSLoGin enum efistub_event {
18f412fd2aSLoGin 	EFISTUB_EVT_INITRD,
19f412fd2aSLoGin 	EFISTUB_EVT_LOAD_OPTIONS,
20f412fd2aSLoGin 	EFISTUB_EVT_COUNT,
21f412fd2aSLoGin };
22f412fd2aSLoGin 
23f412fd2aSLoGin #define STR_WITH_SIZE(s) sizeof(s), s
24f412fd2aSLoGin 
25f412fd2aSLoGin static const struct {
26f412fd2aSLoGin 	u32 pcr_index;
27f412fd2aSLoGin 	u32 event_id;
28f412fd2aSLoGin 	u32 event_data_len;
29f412fd2aSLoGin 	u8 event_data[52];
30f412fd2aSLoGin } events[] = {
31f412fd2aSLoGin 	[EFISTUB_EVT_INITRD] = { 9, INITRD_EVENT_TAG_ID,
32f412fd2aSLoGin 				 STR_WITH_SIZE("Linux initrd") },
33f412fd2aSLoGin 	[EFISTUB_EVT_LOAD_OPTIONS] = { 9, LOAD_OPTIONS_EVENT_TAG_ID,
34f412fd2aSLoGin 				       STR_WITH_SIZE(
35f412fd2aSLoGin 					       "LOADED_IMAGE::LoadOptions") },
36f412fd2aSLoGin };
37f412fd2aSLoGin 
38f412fd2aSLoGin static efi_status_t efi_measure_tagged_event(unsigned long load_addr,
39f412fd2aSLoGin 					     unsigned long load_size,
40f412fd2aSLoGin 					     enum efistub_event event)
41f412fd2aSLoGin {
42f412fd2aSLoGin 	efi_guid_t tcg2_guid = EFI_TCG2_PROTOCOL_GUID;
43f412fd2aSLoGin 	efi_tcg2_protocol_t *tcg2 = NULL;
44f412fd2aSLoGin 	efi_status_t status;
45f412fd2aSLoGin 
46f412fd2aSLoGin 	efi_bs_call(LocateProtocol, &tcg2_guid, NULL, (void **)&tcg2);
47f412fd2aSLoGin 	if (tcg2) {
48f412fd2aSLoGin 		struct efi_measured_event {
49f412fd2aSLoGin 			efi_tcg2_event_t event_data;
50f412fd2aSLoGin 			efi_tcg2_tagged_event_t tagged_event;
51f412fd2aSLoGin 			u8 tagged_event_data[];
52f412fd2aSLoGin 		} * evt;
53f412fd2aSLoGin 		int size = sizeof(*evt) + events[event].event_data_len;
54f412fd2aSLoGin 
55f412fd2aSLoGin 		status = efi_bs_call(AllocatePool, EfiLoaderData, size,
56f412fd2aSLoGin 				     (void **)&evt);
57f412fd2aSLoGin 		if (status != EFI_SUCCESS)
58f412fd2aSLoGin 			goto fail;
59f412fd2aSLoGin 
60f412fd2aSLoGin 		evt->event_data = (struct efi_tcg2_event){
61f412fd2aSLoGin 			.event_size = size,
62f412fd2aSLoGin 			.event_header.header_size =
63f412fd2aSLoGin 				sizeof(evt->event_data.event_header),
64f412fd2aSLoGin 			.event_header.header_version =
65f412fd2aSLoGin 				EFI_TCG2_EVENT_HEADER_VERSION,
66f412fd2aSLoGin 			.event_header.pcr_index = events[event].pcr_index,
67f412fd2aSLoGin 			.event_header.event_type = EV_EVENT_TAG,
68f412fd2aSLoGin 		};
69f412fd2aSLoGin 
70f412fd2aSLoGin 		evt->tagged_event = (struct efi_tcg2_tagged_event){
71f412fd2aSLoGin 			.tagged_event_id = events[event].event_id,
72f412fd2aSLoGin 			.tagged_event_data_size = events[event].event_data_len,
73f412fd2aSLoGin 		};
74f412fd2aSLoGin 
75f412fd2aSLoGin 		memcpy(evt->tagged_event_data, events[event].event_data,
76f412fd2aSLoGin 		       events[event].event_data_len);
77f412fd2aSLoGin 
78f412fd2aSLoGin 		status = efi_call_proto(tcg2, hash_log_extend_event, 0,
79f412fd2aSLoGin 					load_addr, load_size, &evt->event_data);
80f412fd2aSLoGin 		efi_bs_call(FreePool, evt);
81f412fd2aSLoGin 
82f412fd2aSLoGin 		if (status != EFI_SUCCESS)
83f412fd2aSLoGin 			goto fail;
84f412fd2aSLoGin 		return EFI_SUCCESS;
85f412fd2aSLoGin 	}
86f412fd2aSLoGin 
87f412fd2aSLoGin 	return EFI_UNSUPPORTED;
88f412fd2aSLoGin fail:
89f412fd2aSLoGin 	efi_warn("Failed to measure data for event %d: 0x%lx\n", event, status);
90f412fd2aSLoGin 	return status;
91f412fd2aSLoGin }
92f412fd2aSLoGin 
93f412fd2aSLoGin /*
94f412fd2aSLoGin  * At least some versions of Dell firmware pass the entire contents of the
95f412fd2aSLoGin  * Boot#### variable, i.e. the EFI_LOAD_OPTION descriptor, rather than just the
96f412fd2aSLoGin  * OptionalData field.
97f412fd2aSLoGin  *
98f412fd2aSLoGin  * Detect this case and extract OptionalData.
99f412fd2aSLoGin  */
100f412fd2aSLoGin void efi_apply_loadoptions_quirk(const void **load_options,
101f412fd2aSLoGin 				 u32 *load_options_size)
102f412fd2aSLoGin {
103f412fd2aSLoGin #ifndef CONFIG_X86
104f412fd2aSLoGin 	return;
105f412fd2aSLoGin #else
106f412fd2aSLoGin 	const efi_load_option_t *load_option = *load_options;
107f412fd2aSLoGin 	efi_load_option_unpacked_t load_option_unpacked;
108f412fd2aSLoGin 	if (!load_option)
109f412fd2aSLoGin 		return;
110f412fd2aSLoGin 	if (*load_options_size < sizeof(*load_option))
111f412fd2aSLoGin 		return;
112f412fd2aSLoGin 	if ((load_option->attributes & ~EFI_LOAD_OPTION_BOOT_MASK) != 0)
113f412fd2aSLoGin 		return;
114f412fd2aSLoGin 
115f412fd2aSLoGin 	if (!efi_load_option_unpack(&load_option_unpacked, load_option,
116f412fd2aSLoGin 				    *load_options_size))
117f412fd2aSLoGin 		return;
118f412fd2aSLoGin 
119f412fd2aSLoGin 	efi_warn_once(FW_BUG "LoadOptions is an EFI_LOAD_OPTION descriptor\n");
120f412fd2aSLoGin 	efi_warn_once(FW_BUG "Using OptionalData as a workaround\n");
121f412fd2aSLoGin 
122f412fd2aSLoGin 	*load_options = load_option_unpacked.optional_data;
123f412fd2aSLoGin 	*load_options_size = load_option_unpacked.optional_data_size;
124f412fd2aSLoGin #endif
125f412fd2aSLoGin }
126f412fd2aSLoGin 
127f412fd2aSLoGin /*
128f412fd2aSLoGin  * Convert the unicode UEFI command line to ASCII to pass to kernel.
129f412fd2aSLoGin  * Size of memory allocated return in *cmd_line_len.
130f412fd2aSLoGin  * Returns NULL on error.
131f412fd2aSLoGin  */
132f412fd2aSLoGin char *efi_convert_cmdline(EFI_LOADED_IMAGE *image, int *cmd_line_len)
133f412fd2aSLoGin {
134f412fd2aSLoGin 	const efi_char16_t *options = efi_table_attr(image, LoadOptions);
135f412fd2aSLoGin 	u32 options_size = efi_table_attr(image, LoadOptionsSize);
136f412fd2aSLoGin 	int options_bytes = 0, safe_options_bytes = 0; /* UTF-8 bytes */
137f412fd2aSLoGin 	unsigned long cmdline_addr = 0;
138f412fd2aSLoGin 	const efi_char16_t *s2;
139f412fd2aSLoGin 	bool in_quote = false;
140f412fd2aSLoGin 	efi_status_t status;
141f412fd2aSLoGin 	u32 options_chars;
142f412fd2aSLoGin 
143f412fd2aSLoGin 	if (options_size > 0)
144f412fd2aSLoGin 		efi_measure_tagged_event((unsigned long)options, options_size,
145f412fd2aSLoGin 					 EFISTUB_EVT_LOAD_OPTIONS);
146f412fd2aSLoGin 
147f412fd2aSLoGin 	efi_apply_loadoptions_quirk((const void **)&options, &options_size);
148f412fd2aSLoGin 	options_chars = options_size / sizeof(efi_char16_t);
149f412fd2aSLoGin 
150f412fd2aSLoGin 	if (options) {
151f412fd2aSLoGin 		s2 = options;
152f412fd2aSLoGin 		while (options_bytes < COMMAND_LINE_SIZE && options_chars--) {
153f412fd2aSLoGin 			efi_char16_t c = *s2++;
154f412fd2aSLoGin 
155f412fd2aSLoGin 			if (c < 0x80) {
156f412fd2aSLoGin 				if (c == L'\0' || c == L'\n')
157f412fd2aSLoGin 					break;
158f412fd2aSLoGin 				if (c == L'"')
159f412fd2aSLoGin 					in_quote = !in_quote;
160f412fd2aSLoGin 				else if (!in_quote && isspace((char)c))
161f412fd2aSLoGin 					safe_options_bytes = options_bytes;
162f412fd2aSLoGin 
163f412fd2aSLoGin 				options_bytes++;
164f412fd2aSLoGin 				continue;
165f412fd2aSLoGin 			}
166f412fd2aSLoGin 
167f412fd2aSLoGin 			/*
168f412fd2aSLoGin 			 * Get the number of UTF-8 bytes corresponding to a
169f412fd2aSLoGin 			 * UTF-16 character.
170f412fd2aSLoGin 			 * The first part handles everything in the BMP.
171f412fd2aSLoGin 			 */
172f412fd2aSLoGin 			options_bytes += 2 + (c >= 0x800);
173f412fd2aSLoGin 			/*
174f412fd2aSLoGin 			 * Add one more byte for valid surrogate pairs. Invalid
175f412fd2aSLoGin 			 * surrogates will be replaced with 0xfffd and take up
176f412fd2aSLoGin 			 * only 3 bytes.
177f412fd2aSLoGin 			 */
178f412fd2aSLoGin 			if ((c & 0xfc00) == 0xd800) {
179f412fd2aSLoGin 				/*
180f412fd2aSLoGin 				 * If the very last word is a high surrogate,
181f412fd2aSLoGin 				 * we must ignore it since we can't access the
182f412fd2aSLoGin 				 * low surrogate.
183f412fd2aSLoGin 				 */
184f412fd2aSLoGin 				if (!options_chars) {
185f412fd2aSLoGin 					options_bytes -= 3;
186f412fd2aSLoGin 				} else if ((*s2 & 0xfc00) == 0xdc00) {
187f412fd2aSLoGin 					options_bytes++;
188f412fd2aSLoGin 					options_chars--;
189f412fd2aSLoGin 					s2++;
190f412fd2aSLoGin 				}
191f412fd2aSLoGin 			}
192f412fd2aSLoGin 		}
193f412fd2aSLoGin 		if (options_bytes >= COMMAND_LINE_SIZE) {
194f412fd2aSLoGin 			options_bytes = safe_options_bytes;
195f412fd2aSLoGin 			efi_err("Command line is too long: truncated to %d bytes\n",
196f412fd2aSLoGin 				options_bytes);
197f412fd2aSLoGin 		}
198f412fd2aSLoGin 	}
199f412fd2aSLoGin 
200f412fd2aSLoGin 	options_bytes++; /* NUL termination */
201f412fd2aSLoGin 
202f412fd2aSLoGin 	status = efi_bs_call(AllocatePool, EfiLoaderData, options_bytes,
203f412fd2aSLoGin 			     (void **)&cmdline_addr);
204f412fd2aSLoGin 	if (status != EFI_SUCCESS)
205f412fd2aSLoGin 		return NULL;
206f412fd2aSLoGin 
207f412fd2aSLoGin 	snprintf((char *)cmdline_addr, options_bytes, "%.*ls",
208f412fd2aSLoGin 		 options_bytes - 1, options);
209f412fd2aSLoGin 
210f412fd2aSLoGin 	*cmd_line_len = options_bytes;
211f412fd2aSLoGin 	return (char *)cmdline_addr;
212f412fd2aSLoGin }
21378b790faSLoGin 
21478b790faSLoGin /**
21578b790faSLoGin  *	parse_option_str - Parse a string and check an option is set or not
21678b790faSLoGin  *	@str: String to be parsed
21778b790faSLoGin  *	@option: option name
21878b790faSLoGin  *
21978b790faSLoGin  *	This function parses a string containing a comma-separated list of
22078b790faSLoGin  *	strings like a=b,c.
22178b790faSLoGin  *
22278b790faSLoGin  *	Return true if there's such option in the string, or return false.
22378b790faSLoGin  */
22478b790faSLoGin bool parse_option_str(const char *str, const char *option)
22578b790faSLoGin {
22678b790faSLoGin 	while (*str) {
22778b790faSLoGin 		if (!strncmp(str, option, strlen(option))) {
22878b790faSLoGin 			str += strlen(option);
22978b790faSLoGin 			if (!*str || *str == ',')
23078b790faSLoGin 				return true;
23178b790faSLoGin 		}
23278b790faSLoGin 
23378b790faSLoGin 		while (*str && *str != ',')
23478b790faSLoGin 			str++;
23578b790faSLoGin 
23678b790faSLoGin 		if (*str == ',')
23778b790faSLoGin 			str++;
23878b790faSLoGin 	}
23978b790faSLoGin 
24078b790faSLoGin 	return false;
24178b790faSLoGin }
24278b790faSLoGin 
24378b790faSLoGin /**
24478b790faSLoGin  * efi_parse_options() - Parse EFI command line options
24578b790faSLoGin  * @cmdline:	kernel command line
24678b790faSLoGin  *
24778b790faSLoGin  * Parse the ASCII string @cmdline for EFI options, denoted by the efi=
24878b790faSLoGin  * option, e.g. efi=nochunk.
24978b790faSLoGin  *
25078b790faSLoGin  * It should be noted that efi= is parsed in two very different
25178b790faSLoGin  * environments, first in the early boot environment of the EFI boot
25278b790faSLoGin  * stub, and subsequently during the kernel boot.
25378b790faSLoGin  *
25478b790faSLoGin  * Return:	status code
25578b790faSLoGin  */
25678b790faSLoGin efi_status_t efi_parse_options(char const *cmdline)
25778b790faSLoGin {
25878b790faSLoGin 	size_t len;
25978b790faSLoGin 	efi_status_t status;
26078b790faSLoGin 	char *str, *buf;
26178b790faSLoGin 
26278b790faSLoGin 	if (!cmdline)
26378b790faSLoGin 		return EFI_SUCCESS;
26478b790faSLoGin 
26578b790faSLoGin 	len = strnlen(cmdline, COMMAND_LINE_SIZE - 1) + 1;
26678b790faSLoGin 	status = efi_bs_call(AllocatePool, EfiLoaderData, len, (void **)&buf);
26778b790faSLoGin 	if (status != EFI_SUCCESS)
26878b790faSLoGin 		return status;
26978b790faSLoGin 
27078b790faSLoGin 	memcpy(buf, cmdline, len - 1);
27178b790faSLoGin 	buf[len - 1] = '\0';
27278b790faSLoGin 	str = skip_spaces(buf);
27378b790faSLoGin 
27478b790faSLoGin 	while (*str) {
27578b790faSLoGin 		char *param, *val;
27678b790faSLoGin 
27778b790faSLoGin 		str = next_arg(str, &param, &val);
27878b790faSLoGin 		if (!val && !strcmp(param, "--"))
27978b790faSLoGin 			break;
28078b790faSLoGin 
28178b790faSLoGin 		if (!strcmp(param, "nokaslr")) {
28278b790faSLoGin 			efi_nokaslr = true;
28378b790faSLoGin 		} else if (!strcmp(param, "quiet")) {
28478b790faSLoGin 			// efi_loglevel = CONSOLE_LOGLEVEL_QUIET;
28578b790faSLoGin 		} else if (!strcmp(param, "noinitrd")) {
28678b790faSLoGin 			efi_noinitrd = true;
28778b790faSLoGin 		}
28878b790faSLoGin #ifdef CONFIG_X86_64
28978b790faSLoGin 		else if (IS_ENABLED(CONFIG_X86_64) &&
29078b790faSLoGin 			 !strcmp(param, "no5lvl")) {
29178b790faSLoGin 			efi_no5lvl = true;
29278b790faSLoGin 		}
29378b790faSLoGin #endif
29478b790faSLoGin 		else if (!strcmp(param, "efi") && val) {
29578b790faSLoGin 			efi_nochunk = parse_option_str(val, "nochunk");
29678b790faSLoGin 			efi_novamap |= parse_option_str(val, "novamap");
29778b790faSLoGin 
29878b790faSLoGin 			// efi_nosoftreserve =
29978b790faSLoGin 			// 	IS_ENABLED(CONFIG_EFI_SOFT_RESERVE) &&
30078b790faSLoGin 			// 	parse_option_str(val, "nosoftreserve");
30178b790faSLoGin 			efi_nosoftreserve = false;
30278b790faSLoGin 
30378b790faSLoGin 			if (parse_option_str(val, "disable_early_pci_dma"))
30478b790faSLoGin 				efi_disable_pci_dma = true;
30578b790faSLoGin 			if (parse_option_str(val, "no_disable_early_pci_dma"))
30678b790faSLoGin 				efi_disable_pci_dma = false;
30778b790faSLoGin 			if (parse_option_str(val, "debug")) {
30878b790faSLoGin 				// efi_loglevel = CONSOLE_LOGLEVEL_DEBUG;
30978b790faSLoGin 			}
31078b790faSLoGin 		} else if (!strcmp(param, "video") && val &&
31178b790faSLoGin 			   strstarts(val, "efifb:")) {
31278b790faSLoGin 			// efi_parse_option_graphics(val + strlen("efifb:"));
31378b790faSLoGin 		}
31478b790faSLoGin 	}
31578b790faSLoGin 	efi_bs_call(FreePool, buf);
31678b790faSLoGin 	return EFI_SUCCESS;
31778b790faSLoGin }
318*3e6106c4SLoGin 
319*3e6106c4SLoGin /**
320*3e6106c4SLoGin  * get_efi_config_table() - retrieve UEFI configuration table
321*3e6106c4SLoGin  * @guid:	GUID of the configuration table to be retrieved
322*3e6106c4SLoGin  * Return:	pointer to the configuration table or NULL
323*3e6106c4SLoGin  */
324*3e6106c4SLoGin void *get_efi_config_table(efi_guid_t guid)
325*3e6106c4SLoGin {
326*3e6106c4SLoGin 	efi_config_table_t *tables = efi_table_attr(ST, ConfigurationTable);
327*3e6106c4SLoGin 	int nr_tables = efi_table_attr(ST, NumberOfTableEntries);
328*3e6106c4SLoGin 	int i;
329*3e6106c4SLoGin 
330*3e6106c4SLoGin 	for (i = 0; i < nr_tables; i++) {
331*3e6106c4SLoGin 		efi_config_table_t *t = (void *)tables;
332*3e6106c4SLoGin 		// print_efi_guid(&t->VendorGuid);
333*3e6106c4SLoGin 		if (efi_guidcmp(t->VendorGuid, guid) == 0)
334*3e6106c4SLoGin 			return efi_table_attr(t, VendorTable);
335*3e6106c4SLoGin 
336*3e6106c4SLoGin 		tables++;
337*3e6106c4SLoGin 	}
338*3e6106c4SLoGin 	return NULL;
339*3e6106c4SLoGin }