xref: /DragonStub/apps/helper.c (revision 78b790fa8bc8298f8bdfd5bd1945781650dbf361)
1f412fd2aSLoGin #include "efidef.h"
2f412fd2aSLoGin #include <efi.h>
3f412fd2aSLoGin #include <efilib.h>
4f412fd2aSLoGin #include <lib.h>
5f412fd2aSLoGin #include <dragonstub/dragonstub.h>
6f412fd2aSLoGin 
7*78b790faSLoGin bool efi_nochunk;
8*78b790faSLoGin bool efi_nokaslr = true;
9*78b790faSLoGin // bool efi_nokaslr = !IS_ENABLED(CONFIG_RANDOMIZE_BASE);
10*78b790faSLoGin bool efi_novamap;
11*78b790faSLoGin 
12*78b790faSLoGin static bool efi_noinitrd;
13*78b790faSLoGin static bool efi_nosoftreserve;
14*78b790faSLoGin static bool efi_disable_pci_dma = false;
15*78b790faSLoGin // static bool efi_disable_pci_dma = IS_ENABLED(CONFIG_EFI_DISABLE_PCI_DMA);
16*78b790faSLoGin 
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 }
213*78b790faSLoGin 
214*78b790faSLoGin /**
215*78b790faSLoGin  *	parse_option_str - Parse a string and check an option is set or not
216*78b790faSLoGin  *	@str: String to be parsed
217*78b790faSLoGin  *	@option: option name
218*78b790faSLoGin  *
219*78b790faSLoGin  *	This function parses a string containing a comma-separated list of
220*78b790faSLoGin  *	strings like a=b,c.
221*78b790faSLoGin  *
222*78b790faSLoGin  *	Return true if there's such option in the string, or return false.
223*78b790faSLoGin  */
224*78b790faSLoGin bool parse_option_str(const char *str, const char *option)
225*78b790faSLoGin {
226*78b790faSLoGin 	while (*str) {
227*78b790faSLoGin 		if (!strncmp(str, option, strlen(option))) {
228*78b790faSLoGin 			str += strlen(option);
229*78b790faSLoGin 			if (!*str || *str == ',')
230*78b790faSLoGin 				return true;
231*78b790faSLoGin 		}
232*78b790faSLoGin 
233*78b790faSLoGin 		while (*str && *str != ',')
234*78b790faSLoGin 			str++;
235*78b790faSLoGin 
236*78b790faSLoGin 		if (*str == ',')
237*78b790faSLoGin 			str++;
238*78b790faSLoGin 	}
239*78b790faSLoGin 
240*78b790faSLoGin 	return false;
241*78b790faSLoGin }
242*78b790faSLoGin 
243*78b790faSLoGin /**
244*78b790faSLoGin  * efi_parse_options() - Parse EFI command line options
245*78b790faSLoGin  * @cmdline:	kernel command line
246*78b790faSLoGin  *
247*78b790faSLoGin  * Parse the ASCII string @cmdline for EFI options, denoted by the efi=
248*78b790faSLoGin  * option, e.g. efi=nochunk.
249*78b790faSLoGin  *
250*78b790faSLoGin  * It should be noted that efi= is parsed in two very different
251*78b790faSLoGin  * environments, first in the early boot environment of the EFI boot
252*78b790faSLoGin  * stub, and subsequently during the kernel boot.
253*78b790faSLoGin  *
254*78b790faSLoGin  * Return:	status code
255*78b790faSLoGin  */
256*78b790faSLoGin efi_status_t efi_parse_options(char const *cmdline)
257*78b790faSLoGin {
258*78b790faSLoGin 	size_t len;
259*78b790faSLoGin 	efi_status_t status;
260*78b790faSLoGin 	char *str, *buf;
261*78b790faSLoGin 
262*78b790faSLoGin 	if (!cmdline)
263*78b790faSLoGin 		return EFI_SUCCESS;
264*78b790faSLoGin 
265*78b790faSLoGin 	len = strnlen(cmdline, COMMAND_LINE_SIZE - 1) + 1;
266*78b790faSLoGin 	status = efi_bs_call(AllocatePool, EfiLoaderData, len, (void **)&buf);
267*78b790faSLoGin 	if (status != EFI_SUCCESS)
268*78b790faSLoGin 		return status;
269*78b790faSLoGin 
270*78b790faSLoGin 	memcpy(buf, cmdline, len - 1);
271*78b790faSLoGin 	buf[len - 1] = '\0';
272*78b790faSLoGin 	str = skip_spaces(buf);
273*78b790faSLoGin 
274*78b790faSLoGin 	while (*str) {
275*78b790faSLoGin 		char *param, *val;
276*78b790faSLoGin 
277*78b790faSLoGin 		str = next_arg(str, &param, &val);
278*78b790faSLoGin 		if (!val && !strcmp(param, "--"))
279*78b790faSLoGin 			break;
280*78b790faSLoGin 
281*78b790faSLoGin 		if (!strcmp(param, "nokaslr")) {
282*78b790faSLoGin 			efi_nokaslr = true;
283*78b790faSLoGin 		} else if (!strcmp(param, "quiet")) {
284*78b790faSLoGin 			// efi_loglevel = CONSOLE_LOGLEVEL_QUIET;
285*78b790faSLoGin 		} else if (!strcmp(param, "noinitrd")) {
286*78b790faSLoGin 			efi_noinitrd = true;
287*78b790faSLoGin 		}
288*78b790faSLoGin #ifdef CONFIG_X86_64
289*78b790faSLoGin 		else if (IS_ENABLED(CONFIG_X86_64) &&
290*78b790faSLoGin 			 !strcmp(param, "no5lvl")) {
291*78b790faSLoGin 			efi_no5lvl = true;
292*78b790faSLoGin 		}
293*78b790faSLoGin #endif
294*78b790faSLoGin 		else if (!strcmp(param, "efi") && val) {
295*78b790faSLoGin 			efi_nochunk = parse_option_str(val, "nochunk");
296*78b790faSLoGin 			efi_novamap |= parse_option_str(val, "novamap");
297*78b790faSLoGin 
298*78b790faSLoGin 			// efi_nosoftreserve =
299*78b790faSLoGin 			// 	IS_ENABLED(CONFIG_EFI_SOFT_RESERVE) &&
300*78b790faSLoGin 			// 	parse_option_str(val, "nosoftreserve");
301*78b790faSLoGin 			efi_nosoftreserve = false;
302*78b790faSLoGin 
303*78b790faSLoGin 			if (parse_option_str(val, "disable_early_pci_dma"))
304*78b790faSLoGin 				efi_disable_pci_dma = true;
305*78b790faSLoGin 			if (parse_option_str(val, "no_disable_early_pci_dma"))
306*78b790faSLoGin 				efi_disable_pci_dma = false;
307*78b790faSLoGin 			if (parse_option_str(val, "debug")) {
308*78b790faSLoGin 				// efi_loglevel = CONSOLE_LOGLEVEL_DEBUG;
309*78b790faSLoGin 			}
310*78b790faSLoGin 		} else if (!strcmp(param, "video") && val &&
311*78b790faSLoGin 			   strstarts(val, "efifb:")) {
312*78b790faSLoGin 			// efi_parse_option_graphics(val + strlen("efifb:"));
313*78b790faSLoGin 		}
314*78b790faSLoGin 	}
315*78b790faSLoGin 	efi_bs_call(FreePool, buf);
316*78b790faSLoGin 	return EFI_SUCCESS;
317*78b790faSLoGin }
318