xref: /DragonStub/apps/helper.c (revision 8515606674058ca81cd1c0b99453e326875c5c0d) !
1 #include "dragonstub/printk.h"
2 #include "efidef.h"
3 #include <efi.h>
4 #include <efilib.h>
5 #include <lib.h>
6 #include <dragonstub/dragonstub.h>
7 
8 bool efi_nochunk;
9 bool efi_nokaslr = true;
10 // bool efi_nokaslr = !IS_ENABLED(CONFIG_RANDOMIZE_BASE);
11 bool efi_novamap = false;
12 
13 static bool efi_noinitrd;
14 static bool efi_nosoftreserve;
15 static bool efi_disable_pci_dma = false;
16 // static bool efi_disable_pci_dma = IS_ENABLED(CONFIG_EFI_DISABLE_PCI_DMA);
17 
18 enum efistub_event {
19 	EFISTUB_EVT_INITRD,
20 	EFISTUB_EVT_LOAD_OPTIONS,
21 	EFISTUB_EVT_COUNT,
22 };
23 
24 #define STR_WITH_SIZE(s) sizeof(s), s
25 
26 static const struct {
27 	u32 pcr_index;
28 	u32 event_id;
29 	u32 event_data_len;
30 	u8 event_data[52];
31 } events[] = {
32 	[EFISTUB_EVT_INITRD] = { 9, INITRD_EVENT_TAG_ID,
33 				 STR_WITH_SIZE("Linux initrd") },
34 	[EFISTUB_EVT_LOAD_OPTIONS] = { 9, LOAD_OPTIONS_EVENT_TAG_ID,
35 				       STR_WITH_SIZE(
36 					       "LOADED_IMAGE::LoadOptions") },
37 };
38 
efi_measure_tagged_event(unsigned long load_addr,unsigned long load_size,enum efistub_event event)39 static efi_status_t efi_measure_tagged_event(unsigned long load_addr,
40 					     unsigned long load_size,
41 					     enum efistub_event event)
42 {
43 	efi_guid_t tcg2_guid = EFI_TCG2_PROTOCOL_GUID;
44 	efi_tcg2_protocol_t *tcg2 = NULL;
45 	efi_status_t status;
46 
47 	efi_bs_call(LocateProtocol, &tcg2_guid, NULL, (void **)&tcg2);
48 	if (tcg2) {
49 		struct efi_measured_event {
50 			efi_tcg2_event_t event_data;
51 			efi_tcg2_tagged_event_t tagged_event;
52 			u8 tagged_event_data[];
53 		} * evt;
54 		int size = sizeof(*evt) + events[event].event_data_len;
55 
56 		status = efi_bs_call(AllocatePool, EfiLoaderData, size,
57 				     (void **)&evt);
58 		if (status != EFI_SUCCESS)
59 			goto fail;
60 
61 		evt->event_data = (struct efi_tcg2_event){
62 			.event_size = size,
63 			.event_header.header_size =
64 				sizeof(evt->event_data.event_header),
65 			.event_header.header_version =
66 				EFI_TCG2_EVENT_HEADER_VERSION,
67 			.event_header.pcr_index = events[event].pcr_index,
68 			.event_header.event_type = EV_EVENT_TAG,
69 		};
70 
71 		evt->tagged_event = (struct efi_tcg2_tagged_event){
72 			.tagged_event_id = events[event].event_id,
73 			.tagged_event_data_size = events[event].event_data_len,
74 		};
75 
76 		memcpy(evt->tagged_event_data, events[event].event_data,
77 		       events[event].event_data_len);
78 
79 		status = efi_call_proto(tcg2, hash_log_extend_event, 0,
80 					load_addr, load_size, &evt->event_data);
81 		efi_bs_call(FreePool, evt);
82 
83 		if (status != EFI_SUCCESS)
84 			goto fail;
85 		return EFI_SUCCESS;
86 	}
87 
88 	return EFI_UNSUPPORTED;
89 fail:
90 	efi_warn("Failed to measure data for event %d: 0x%lx\n", event, status);
91 	return status;
92 }
93 
94 /*
95  * At least some versions of Dell firmware pass the entire contents of the
96  * Boot#### variable, i.e. the EFI_LOAD_OPTION descriptor, rather than just the
97  * OptionalData field.
98  *
99  * Detect this case and extract OptionalData.
100  */
efi_apply_loadoptions_quirk(const void ** load_options,u32 * load_options_size)101 void efi_apply_loadoptions_quirk(const void **load_options,
102 				 u32 *load_options_size)
103 {
104 #ifndef CONFIG_X86
105 	return;
106 #else
107 	const efi_load_option_t *load_option = *load_options;
108 	efi_load_option_unpacked_t load_option_unpacked;
109 	if (!load_option)
110 		return;
111 	if (*load_options_size < sizeof(*load_option))
112 		return;
113 	if ((load_option->attributes & ~EFI_LOAD_OPTION_BOOT_MASK) != 0)
114 		return;
115 
116 	if (!efi_load_option_unpack(&load_option_unpacked, load_option,
117 				    *load_options_size))
118 		return;
119 
120 	efi_warn_once(FW_BUG "LoadOptions is an EFI_LOAD_OPTION descriptor\n");
121 	efi_warn_once(FW_BUG "Using OptionalData as a workaround\n");
122 
123 	*load_options = load_option_unpacked.optional_data;
124 	*load_options_size = load_option_unpacked.optional_data_size;
125 #endif
126 }
127 
128 /*
129  * Convert the unicode UEFI command line to ASCII to pass to kernel.
130  * Size of memory allocated return in *cmd_line_len.
131  * Returns NULL on error.
132  */
efi_convert_cmdline(EFI_LOADED_IMAGE * image,int * cmd_line_len)133 char *efi_convert_cmdline(EFI_LOADED_IMAGE *image, int *cmd_line_len)
134 {
135 	const efi_char16_t *options = efi_table_attr(image, LoadOptions);
136 	u32 options_size = efi_table_attr(image, LoadOptionsSize);
137 	int options_bytes = 0, safe_options_bytes = 0; /* UTF-8 bytes */
138 	unsigned long cmdline_addr = 0;
139 	const efi_char16_t *s2;
140 	bool in_quote = false;
141 	efi_status_t status;
142 	u32 options_chars;
143 
144 	if (options_size > 0)
145 		efi_measure_tagged_event((unsigned long)options, options_size,
146 					 EFISTUB_EVT_LOAD_OPTIONS);
147 
148 	efi_apply_loadoptions_quirk((const void **)&options, &options_size);
149 	options_chars = options_size / sizeof(efi_char16_t);
150 
151 	if (options) {
152 		s2 = options;
153 		while (options_bytes < COMMAND_LINE_SIZE && options_chars--) {
154 			efi_char16_t c = *s2++;
155 
156 			if (c < 0x80) {
157 				if (c == L'\0' || c == L'\n')
158 					break;
159 				if (c == L'"')
160 					in_quote = !in_quote;
161 				else if (!in_quote && isspace((char)c))
162 					safe_options_bytes = options_bytes;
163 
164 				options_bytes++;
165 				continue;
166 			}
167 
168 			/*
169 			 * Get the number of UTF-8 bytes corresponding to a
170 			 * UTF-16 character.
171 			 * The first part handles everything in the BMP.
172 			 */
173 			options_bytes += 2 + (c >= 0x800);
174 			/*
175 			 * Add one more byte for valid surrogate pairs. Invalid
176 			 * surrogates will be replaced with 0xfffd and take up
177 			 * only 3 bytes.
178 			 */
179 			if ((c & 0xfc00) == 0xd800) {
180 				/*
181 				 * If the very last word is a high surrogate,
182 				 * we must ignore it since we can't access the
183 				 * low surrogate.
184 				 */
185 				if (!options_chars) {
186 					options_bytes -= 3;
187 				} else if ((*s2 & 0xfc00) == 0xdc00) {
188 					options_bytes++;
189 					options_chars--;
190 					s2++;
191 				}
192 			}
193 		}
194 		if (options_bytes >= COMMAND_LINE_SIZE) {
195 			options_bytes = safe_options_bytes;
196 			efi_err("Command line is too long: truncated to %d bytes\n",
197 				options_bytes);
198 		}
199 	}
200 
201 	options_bytes++; /* NUL termination */
202 
203 	status = efi_bs_call(AllocatePool, EfiLoaderData, options_bytes,
204 			     (void **)&cmdline_addr);
205 	if (status != EFI_SUCCESS)
206 		return NULL;
207 
208 	snprintf((char *)cmdline_addr, options_bytes, "%.*ls",
209 		 options_bytes - 1, options);
210 
211 	*cmd_line_len = options_bytes;
212 	return (char *)cmdline_addr;
213 }
214 
215 /**
216  *	parse_option_str - Parse a string and check an option is set or not
217  *	@str: String to be parsed
218  *	@option: option name
219  *
220  *	This function parses a string containing a comma-separated list of
221  *	strings like a=b,c.
222  *
223  *	Return true if there's such option in the string, or return false.
224  */
parse_option_str(const char * str,const char * option)225 bool parse_option_str(const char *str, const char *option)
226 {
227 	while (*str) {
228 		if (!strncmp(str, option, strlen(option))) {
229 			str += strlen(option);
230 			if (!*str || *str == ',')
231 				return true;
232 		}
233 
234 		while (*str && *str != ',')
235 			str++;
236 
237 		if (*str == ',')
238 			str++;
239 	}
240 
241 	return false;
242 }
243 
244 /**
245  * efi_parse_options() - Parse EFI command line options
246  * @cmdline:	kernel command line
247  *
248  * Parse the ASCII string @cmdline for EFI options, denoted by the efi=
249  * option, e.g. efi=nochunk.
250  *
251  * It should be noted that efi= is parsed in two very different
252  * environments, first in the early boot environment of the EFI boot
253  * stub, and subsequently during the kernel boot.
254  *
255  * Return:	status code
256  */
efi_parse_options(char const * cmdline)257 efi_status_t efi_parse_options(char const *cmdline)
258 {
259 	size_t len;
260 	efi_status_t status;
261 	char *str, *buf;
262 
263 	if (!cmdline)
264 		return EFI_SUCCESS;
265 
266 	len = strnlen(cmdline, COMMAND_LINE_SIZE - 1) + 1;
267 	status = efi_bs_call(AllocatePool, EfiLoaderData, len, (void **)&buf);
268 	if (status != EFI_SUCCESS)
269 		return status;
270 
271 	memcpy(buf, cmdline, len - 1);
272 	buf[len - 1] = '\0';
273 	str = skip_spaces(buf);
274 
275 	while (*str) {
276 		char *param, *val;
277 
278 		str = next_arg(str, &param, &val);
279 		if (!val && !strcmp(param, "--"))
280 			break;
281 
282 		if (!strcmp(param, "nokaslr")) {
283 			efi_nokaslr = true;
284 		} else if (!strcmp(param, "quiet")) {
285 			// efi_loglevel = CONSOLE_LOGLEVEL_QUIET;
286 		} else if (!strcmp(param, "noinitrd")) {
287 			efi_noinitrd = true;
288 		}
289 #ifdef CONFIG_X86_64
290 		else if (IS_ENABLED(CONFIG_X86_64) &&
291 			 !strcmp(param, "no5lvl")) {
292 			efi_no5lvl = true;
293 		}
294 #endif
295 		else if (!strcmp(param, "efi") && val) {
296 			efi_nochunk = parse_option_str(val, "nochunk");
297 			efi_novamap |= parse_option_str(val, "novamap");
298 
299 			// efi_nosoftreserve =
300 			// 	IS_ENABLED(CONFIG_EFI_SOFT_RESERVE) &&
301 			// 	parse_option_str(val, "nosoftreserve");
302 			efi_nosoftreserve = false;
303 
304 			if (parse_option_str(val, "disable_early_pci_dma"))
305 				efi_disable_pci_dma = true;
306 			if (parse_option_str(val, "no_disable_early_pci_dma"))
307 				efi_disable_pci_dma = false;
308 			if (parse_option_str(val, "debug")) {
309 				// efi_loglevel = CONSOLE_LOGLEVEL_DEBUG;
310 			}
311 		} else if (!strcmp(param, "video") && val &&
312 			   strstarts(val, "efifb:")) {
313 			// efi_parse_option_graphics(val + strlen("efifb:"));
314 		}
315 	}
316 	efi_bs_call(FreePool, buf);
317 	return EFI_SUCCESS;
318 }
319 
320 /**
321  * get_efi_config_table() - retrieve UEFI configuration table
322  * @guid:	GUID of the configuration table to be retrieved
323  * Return:	pointer to the configuration table or NULL
324  */
get_efi_config_table(efi_guid_t guid)325 void *get_efi_config_table(efi_guid_t guid)
326 {
327 	efi_config_table_t *tables = efi_table_attr(ST, ConfigurationTable);
328 	int nr_tables = efi_table_attr(ST, NumberOfTableEntries);
329 	int i;
330 
331 	for (i = 0; i < nr_tables; i++) {
332 		efi_config_table_t *t = (void *)tables;
333 		// print_efi_guid(&t->VendorGuid);
334 		if (efi_guidcmp(t->VendorGuid, guid) == 0)
335 			return efi_table_attr(t, VendorTable);
336 
337 		tables++;
338 	}
339 	return NULL;
340 }
341 
342 /**
343  * efi_exit_boot_services() - Exit boot services
344  * @handle:	handle of the exiting image
345  * @priv:	argument to be passed to @priv_func
346  * @priv_func:	function to process the memory map before exiting boot services
347  *
348  * Handle calling ExitBootServices according to the requirements set out by the
349  * spec.  Obtains the current memory map, and returns that info after calling
350  * ExitBootServices.  The client must specify a function to perform any
351  * processing of the memory map data prior to ExitBootServices.  A client
352  * specific structure may be passed to the function via priv.  The client
353  * function may be called multiple times.
354  *
355  * Return:	status code
356  */
efi_exit_boot_services(void * handle,void * priv,efi_exit_boot_map_processing priv_func)357 efi_status_t efi_exit_boot_services(void *handle, void *priv,
358 				    efi_exit_boot_map_processing priv_func)
359 {
360 	struct efi_boot_memmap *map;
361 	efi_status_t status;
362 
363 	if (efi_disable_pci_dma) {
364 		efi_todo("efi_exit_boot_services:: efi_disable_pci_dma: efi_pci_disable_bridge_busmaster");
365 		// efi_pci_disable_bridge_busmaster();
366 	}
367 
368 	status = efi_get_memory_map(&map, true);
369 	if (status != EFI_SUCCESS)
370 		return status;
371 	efi_debug("before priv_func\n");
372 	status = priv_func(map, priv);
373 	if (status != EFI_SUCCESS) {
374 
375 		efi_bs_call(FreePool, map);
376 		return status;
377 	}
378 
379 	efi_debug("before ExitBootServices, handle=%p, map_key=%p\n", handle, map->map_key);
380 	efi_debug("BS->ExitBootServices=%p\n", BS->ExitBootServices);
381 	efi_debug("ST->BS->ExitBootServices=%p\n", ST->BootServices->ExitBootServices);
382 	status = efi_bs_call(ExitBootServices, handle, map->map_key);
383 	// exit之后不能再使用打印函数,否则会出现错误
384 
385 	if (status == EFI_INVALID_PARAMETER) {
386 		/*
387 		 * The memory map changed between efi_get_memory_map() and
388 		 * exit_boot_services().  Per the UEFI Spec v2.6, Section 6.4:
389 		 * EFI_BOOT_SERVICES.ExitBootServices we need to get the
390 		 * updated map, and try again.  The spec implies one retry
391 		 * should be sufficent, which is confirmed against the EDK2
392 		 * implementation.  Per the spec, we can only invoke
393 		 * get_memory_map() and exit_boot_services() - we cannot alloc
394 		 * so efi_get_memory_map() cannot be used, and we must reuse
395 		 * the buffer.  For all practical purposes, the headroom in the
396 		 * buffer should account for any changes in the map so the call
397 		 * to get_memory_map() is expected to succeed here.
398 		 */
399 		map->map_size = map->buff_size;
400 		status = efi_bs_call(GetMemoryMap, &map->map_size, &map->map,
401 				     &map->map_key, &map->desc_size,
402 				     &map->desc_ver);
403 
404 		/* exit_boot_services() was called, thus cannot free */
405 		if (status != EFI_SUCCESS)
406 			return status;
407 
408 		status = priv_func(map, priv);
409 		/* exit_boot_services() was called, thus cannot free */
410 		if (status != EFI_SUCCESS)
411 			return status;
412 
413 		status = efi_bs_call(ExitBootServices, handle, map->map_key);
414 	}
415 
416 	return status;
417 }