Commit | Line | Data |
---|---|---|
4febfb8d | 1 | // SPDX-License-Identifier: GPL-2.0 |
7721da4c RF |
2 | /* |
3 | * Helper functions used by the EFI stub on multiple | |
4 | * architectures. This should be #included by the EFI stub | |
5 | * implementation files. | |
6 | * | |
7 | * Copyright 2011 Intel Corporation; author Matt Fleming | |
7721da4c | 8 | */ |
7721da4c | 9 | |
c0891ac1 | 10 | #include <linux/stdarg.h> |
2c7d1e30 | 11 | |
bd669475 | 12 | #include <linux/efi.h> |
fd0528a2 | 13 | #include <linux/kernel.h> |
3e0b0f88 | 14 | #include <linux/overflow.h> |
bd669475 | 15 | #include <asm/efi.h> |
80b1bfe1 | 16 | #include <asm/setup.h> |
bd669475 AB |
17 | |
18 | #include "efistub.h" | |
9bb40191 | 19 | |
980771f6 | 20 | bool efi_nochunk; |
7c116db2 | 21 | bool efi_nokaslr = !IS_ENABLED(CONFIG_RANDOMIZE_BASE); |
980771f6 AB |
22 | bool efi_novamap; |
23 | ||
20287d56 | 24 | static bool efi_noinitrd; |
54439370 AS |
25 | static bool efi_nosoftreserve; |
26 | static bool efi_disable_pci_dma = IS_ENABLED(CONFIG_EFI_DISABLE_PCI_DMA); | |
60f38de7 | 27 | |
7205f06e AB |
28 | int efi_mem_encrypt; |
29 | ||
b617c526 DW |
30 | bool __pure __efi_soft_reserve_enabled(void) |
31 | { | |
32 | return !efi_nosoftreserve; | |
33 | } | |
60f38de7 | 34 | |
8c0a839c HS |
35 | /** |
36 | * efi_parse_options() - Parse EFI command line options | |
37 | * @cmdline: kernel command line | |
38 | * | |
39 | * Parse the ASCII string @cmdline for EFI options, denoted by the efi= | |
5a17dae4 MF |
40 | * option, e.g. efi=nochunk. |
41 | * | |
42 | * It should be noted that efi= is parsed in two very different | |
43 | * environments, first in the early boot environment of the EFI boot | |
44 | * stub, and subsequently during the kernel boot. | |
8c0a839c HS |
45 | * |
46 | * Return: status code | |
5a17dae4 | 47 | */ |
60f38de7 | 48 | efi_status_t efi_parse_options(char const *cmdline) |
5a17dae4 | 49 | { |
4e23c96b | 50 | char *buf __free(efi_pool) = NULL; |
91d150c0 | 51 | efi_status_t status; |
4e23c96b AB |
52 | size_t len; |
53 | char *str; | |
5a17dae4 | 54 | |
a37ca6a2 AS |
55 | if (!cmdline) |
56 | return EFI_SUCCESS; | |
57 | ||
8a8a3237 | 58 | len = strnlen(cmdline, COMMAND_LINE_SIZE - 1) + 1; |
91d150c0 AB |
59 | status = efi_bs_call(allocate_pool, EFI_LOADER_DATA, len, (void **)&buf); |
60 | if (status != EFI_SUCCESS) | |
61 | return status; | |
4e46c2a9 | 62 | |
8a8a3237 AS |
63 | memcpy(buf, cmdline, len - 1); |
64 | buf[len - 1] = '\0'; | |
65 | str = skip_spaces(buf); | |
b617c526 | 66 | |
91d150c0 AB |
67 | while (*str) { |
68 | char *param, *val; | |
4444f854 | 69 | |
91d150c0 | 70 | str = next_arg(str, ¶m, &val); |
1fd9717d AS |
71 | if (!val && !strcmp(param, "--")) |
72 | break; | |
4444f854 | 73 | |
91d150c0 AB |
74 | if (!strcmp(param, "nokaslr")) { |
75 | efi_nokaslr = true; | |
76 | } else if (!strcmp(param, "quiet")) { | |
23d5b73f | 77 | efi_loglevel = CONSOLE_LOGLEVEL_QUIET; |
79d3219d AB |
78 | } else if (!strcmp(param, "noinitrd")) { |
79 | efi_noinitrd = true; | |
cb1c9e02 AB |
80 | } else if (IS_ENABLED(CONFIG_X86_64) && !strcmp(param, "no5lvl")) { |
81 | efi_no5lvl = true; | |
7205f06e AB |
82 | } else if (IS_ENABLED(CONFIG_ARCH_HAS_MEM_ENCRYPT) && |
83 | !strcmp(param, "mem_encrypt") && val) { | |
84 | if (parse_option_str(val, "on")) | |
85 | efi_mem_encrypt = 1; | |
86 | else if (parse_option_str(val, "off")) | |
87 | efi_mem_encrypt = -1; | |
91d150c0 AB |
88 | } else if (!strcmp(param, "efi") && val) { |
89 | efi_nochunk = parse_option_str(val, "nochunk"); | |
d3549a93 | 90 | efi_novamap |= parse_option_str(val, "novamap"); |
5a17dae4 | 91 | |
91d150c0 AB |
92 | efi_nosoftreserve = IS_ENABLED(CONFIG_EFI_SOFT_RESERVE) && |
93 | parse_option_str(val, "nosoftreserve"); | |
5a17dae4 | 94 | |
91d150c0 AB |
95 | if (parse_option_str(val, "disable_early_pci_dma")) |
96 | efi_disable_pci_dma = true; | |
97 | if (parse_option_str(val, "no_disable_early_pci_dma")) | |
98 | efi_disable_pci_dma = false; | |
23d5b73f AS |
99 | if (parse_option_str(val, "debug")) |
100 | efi_loglevel = CONSOLE_LOGLEVEL_DEBUG; | |
fffb6804 AS |
101 | } else if (!strcmp(param, "video") && |
102 | val && strstarts(val, "efifb:")) { | |
103 | efi_parse_option_graphics(val + strlen("efifb:")); | |
91d150c0 AB |
104 | } |
105 | } | |
5a17dae4 MF |
106 | return EFI_SUCCESS; |
107 | } | |
7721da4c | 108 | |
4a568ce2 AS |
109 | /* |
110 | * The EFI_LOAD_OPTION descriptor has the following layout: | |
111 | * u32 Attributes; | |
112 | * u16 FilePathListLength; | |
113 | * u16 Description[]; | |
114 | * efi_device_path_protocol_t FilePathList[]; | |
115 | * u8 OptionalData[]; | |
116 | * | |
117 | * This function validates and unpacks the variable-size data fields. | |
118 | */ | |
119 | static | |
120 | bool efi_load_option_unpack(efi_load_option_unpacked_t *dest, | |
121 | const efi_load_option_t *src, size_t size) | |
122 | { | |
123 | const void *pos; | |
124 | u16 c; | |
125 | efi_device_path_protocol_t header; | |
126 | const efi_char16_t *description; | |
127 | const efi_device_path_protocol_t *file_path_list; | |
128 | ||
129 | if (size < offsetof(efi_load_option_t, variable_data)) | |
130 | return false; | |
131 | pos = src->variable_data; | |
132 | size -= offsetof(efi_load_option_t, variable_data); | |
133 | ||
134 | if ((src->attributes & ~EFI_LOAD_OPTION_MASK) != 0) | |
135 | return false; | |
136 | ||
137 | /* Scan description. */ | |
138 | description = pos; | |
139 | do { | |
140 | if (size < sizeof(c)) | |
141 | return false; | |
142 | c = *(const u16 *)pos; | |
143 | pos += sizeof(c); | |
144 | size -= sizeof(c); | |
145 | } while (c != L'\0'); | |
146 | ||
147 | /* Scan file_path_list. */ | |
148 | file_path_list = pos; | |
149 | do { | |
150 | if (size < sizeof(header)) | |
151 | return false; | |
152 | header = *(const efi_device_path_protocol_t *)pos; | |
153 | if (header.length < sizeof(header)) | |
154 | return false; | |
155 | if (size < header.length) | |
156 | return false; | |
157 | pos += header.length; | |
158 | size -= header.length; | |
159 | } while ((header.type != EFI_DEV_END_PATH && header.type != EFI_DEV_END_PATH2) || | |
160 | (header.sub_type != EFI_DEV_END_ENTIRE)); | |
161 | if (pos != (const void *)file_path_list + src->file_path_list_length) | |
162 | return false; | |
163 | ||
164 | dest->attributes = src->attributes; | |
165 | dest->file_path_list_length = src->file_path_list_length; | |
166 | dest->description = description; | |
167 | dest->file_path_list = file_path_list; | |
168 | dest->optional_data_size = size; | |
169 | dest->optional_data = size ? pos : NULL; | |
170 | ||
171 | return true; | |
172 | } | |
173 | ||
174 | /* | |
175 | * At least some versions of Dell firmware pass the entire contents of the | |
176 | * Boot#### variable, i.e. the EFI_LOAD_OPTION descriptor, rather than just the | |
177 | * OptionalData field. | |
178 | * | |
179 | * Detect this case and extract OptionalData. | |
180 | */ | |
a241d94b | 181 | void efi_apply_loadoptions_quirk(const void **load_options, u32 *load_options_size) |
4a568ce2 AS |
182 | { |
183 | const efi_load_option_t *load_option = *load_options; | |
184 | efi_load_option_unpacked_t load_option_unpacked; | |
185 | ||
186 | if (!IS_ENABLED(CONFIG_X86)) | |
187 | return; | |
188 | if (!load_option) | |
189 | return; | |
190 | if (*load_options_size < sizeof(*load_option)) | |
191 | return; | |
192 | if ((load_option->attributes & ~EFI_LOAD_OPTION_BOOT_MASK) != 0) | |
193 | return; | |
194 | ||
195 | if (!efi_load_option_unpack(&load_option_unpacked, load_option, *load_options_size)) | |
196 | return; | |
197 | ||
198 | efi_warn_once(FW_BUG "LoadOptions is an EFI_LOAD_OPTION descriptor\n"); | |
199 | efi_warn_once(FW_BUG "Using OptionalData as a workaround\n"); | |
200 | ||
201 | *load_options = load_option_unpacked.optional_data; | |
202 | *load_options_size = load_option_unpacked.optional_data_size; | |
203 | } | |
204 | ||
ac93cbfc | 205 | enum efistub_event_type { |
56633169 | 206 | EFISTUB_EVT_INITRD, |
71c7adc9 | 207 | EFISTUB_EVT_LOAD_OPTIONS, |
56633169 IA |
208 | EFISTUB_EVT_COUNT, |
209 | }; | |
210 | ||
211 | #define STR_WITH_SIZE(s) sizeof(s), s | |
212 | ||
213 | static const struct { | |
214 | u32 pcr_index; | |
215 | u32 event_id; | |
216 | u32 event_data_len; | |
217 | u8 event_data[52]; | |
218 | } events[] = { | |
219 | [EFISTUB_EVT_INITRD] = { | |
220 | 9, | |
221 | INITRD_EVENT_TAG_ID, | |
222 | STR_WITH_SIZE("Linux initrd") | |
223 | }, | |
71c7adc9 IA |
224 | [EFISTUB_EVT_LOAD_OPTIONS] = { |
225 | 9, | |
226 | LOAD_OPTIONS_EVENT_TAG_ID, | |
227 | STR_WITH_SIZE("LOADED_IMAGE::LoadOptions") | |
228 | }, | |
56633169 IA |
229 | }; |
230 | ||
ac93cbfc AB |
231 | static_assert(sizeof(efi_tcg2_event_t) == sizeof(efi_cc_event_t)); |
232 | ||
233 | union efistub_event { | |
234 | efi_tcg2_event_t tcg2_data; | |
235 | efi_cc_event_t cc_data; | |
236 | }; | |
237 | ||
3e0b0f88 | 238 | struct efistub_measured_event { |
ac93cbfc | 239 | union efistub_event event_data; |
3e0b0f88 AB |
240 | TCG_PCClientTaggedEvent tagged_event __packed; |
241 | }; | |
242 | ||
56633169 IA |
243 | static efi_status_t efi_measure_tagged_event(unsigned long load_addr, |
244 | unsigned long load_size, | |
ac93cbfc | 245 | enum efistub_event_type event) |
56633169 | 246 | { |
ac93cbfc AB |
247 | union { |
248 | efi_status_t | |
249 | (__efiapi *hash_log_extend_event)(void *, u64, efi_physical_addr_t, | |
250 | u64, const union efistub_event *); | |
251 | struct { u32 hash_log_extend_event; } mixed_mode; | |
252 | } method; | |
4e23c96b | 253 | struct efistub_measured_event *evt __free(efi_pool) = NULL; |
3e0b0f88 AB |
254 | int size = struct_size(evt, tagged_event.tagged_event_data, |
255 | events[event].event_data_len); | |
56633169 IA |
256 | efi_guid_t tcg2_guid = EFI_TCG2_PROTOCOL_GUID; |
257 | efi_tcg2_protocol_t *tcg2 = NULL; | |
ac93cbfc | 258 | union efistub_event ev; |
56633169 | 259 | efi_status_t status; |
ac93cbfc | 260 | void *protocol; |
56633169 IA |
261 | |
262 | efi_bs_call(locate_protocol, &tcg2_guid, NULL, (void **)&tcg2); | |
263 | if (tcg2) { | |
ac93cbfc | 264 | ev.tcg2_data = (struct efi_tcg2_event){ |
56633169 | 265 | .event_size = size, |
ac93cbfc | 266 | .event_header.header_size = sizeof(ev.tcg2_data.event_header), |
56633169 IA |
267 | .event_header.header_version = EFI_TCG2_EVENT_HEADER_VERSION, |
268 | .event_header.pcr_index = events[event].pcr_index, | |
269 | .event_header.event_type = EV_EVENT_TAG, | |
270 | }; | |
ac93cbfc AB |
271 | protocol = tcg2; |
272 | method.hash_log_extend_event = | |
273 | (void *)efi_table_attr(tcg2, hash_log_extend_event); | |
274 | } else { | |
275 | efi_guid_t cc_guid = EFI_CC_MEASUREMENT_PROTOCOL_GUID; | |
276 | efi_cc_protocol_t *cc = NULL; | |
277 | ||
278 | efi_bs_call(locate_protocol, &cc_guid, NULL, (void **)&cc); | |
279 | if (!cc) | |
280 | return EFI_UNSUPPORTED; | |
281 | ||
282 | ev.cc_data = (struct efi_cc_event){ | |
283 | .event_size = size, | |
284 | .event_header.header_size = sizeof(ev.cc_data.event_header), | |
285 | .event_header.header_version = EFI_CC_EVENT_HEADER_VERSION, | |
286 | .event_header.event_type = EV_EVENT_TAG, | |
56633169 IA |
287 | }; |
288 | ||
ac93cbfc AB |
289 | status = efi_call_proto(cc, map_pcr_to_mr_index, |
290 | events[event].pcr_index, | |
291 | &ev.cc_data.event_header.mr_index); | |
56633169 IA |
292 | if (status != EFI_SUCCESS) |
293 | goto fail; | |
ac93cbfc AB |
294 | |
295 | protocol = cc; | |
296 | method.hash_log_extend_event = | |
297 | (void *)efi_table_attr(cc, hash_log_extend_event); | |
56633169 IA |
298 | } |
299 | ||
ac93cbfc AB |
300 | status = efi_bs_call(allocate_pool, EFI_LOADER_DATA, size, (void **)&evt); |
301 | if (status != EFI_SUCCESS) | |
302 | goto fail; | |
303 | ||
304 | *evt = (struct efistub_measured_event) { | |
305 | .event_data = ev, | |
306 | .tagged_event.tagged_event_id = events[event].event_id, | |
307 | .tagged_event.tagged_event_data_size = events[event].event_data_len, | |
308 | }; | |
309 | ||
310 | memcpy(evt->tagged_event.tagged_event_data, events[event].event_data, | |
311 | events[event].event_data_len); | |
312 | ||
313 | status = efi_fn_call(&method, hash_log_extend_event, protocol, 0, | |
314 | load_addr, load_size, &evt->event_data); | |
ac93cbfc AB |
315 | |
316 | if (status == EFI_SUCCESS) | |
317 | return EFI_SUCCESS; | |
318 | ||
56633169 IA |
319 | fail: |
320 | efi_warn("Failed to measure data for event %d: 0x%lx\n", event, status); | |
321 | return status; | |
322 | } | |
323 | ||
5fef3870 RF |
324 | /* |
325 | * Convert the unicode UEFI command line to ASCII to pass to kernel. | |
326 | * Size of memory allocated return in *cmd_line_len. | |
327 | * Returns NULL on error. | |
328 | */ | |
ade7ccba | 329 | char *efi_convert_cmdline(efi_loaded_image_t *image) |
5fef3870 | 330 | { |
a241d94b AB |
331 | const efi_char16_t *options = efi_table_attr(image, load_options); |
332 | u32 options_size = efi_table_attr(image, load_options_size); | |
80b1bfe1 | 333 | int options_bytes = 0, safe_options_bytes = 0; /* UTF-8 bytes */ |
a241d94b AB |
334 | unsigned long cmdline_addr = 0; |
335 | const efi_char16_t *s2; | |
80b1bfe1 | 336 | bool in_quote = false; |
5fef3870 | 337 | efi_status_t status; |
a241d94b | 338 | u32 options_chars; |
5fef3870 | 339 | |
71c7adc9 IA |
340 | if (options_size > 0) |
341 | efi_measure_tagged_event((unsigned long)options, options_size, | |
342 | EFISTUB_EVT_LOAD_OPTIONS); | |
343 | ||
a241d94b AB |
344 | efi_apply_loadoptions_quirk((const void **)&options, &options_size); |
345 | options_chars = options_size / sizeof(efi_char16_t); | |
4a568ce2 | 346 | |
5fef3870 RF |
347 | if (options) { |
348 | s2 = options; | |
80b1bfe1 | 349 | while (options_bytes < COMMAND_LINE_SIZE && options_chars--) { |
a241d94b | 350 | efi_char16_t c = *s2++; |
15c316bc | 351 | |
80b1bfe1 AS |
352 | if (c < 0x80) { |
353 | if (c == L'\0' || c == L'\n') | |
354 | break; | |
355 | if (c == L'"') | |
356 | in_quote = !in_quote; | |
357 | else if (!in_quote && isspace((char)c)) | |
358 | safe_options_bytes = options_bytes; | |
359 | ||
360 | options_bytes++; | |
361 | continue; | |
362 | } | |
363 | ||
15c316bc AS |
364 | /* |
365 | * Get the number of UTF-8 bytes corresponding to a | |
366 | * UTF-16 character. | |
367 | * The first part handles everything in the BMP. | |
368 | */ | |
80b1bfe1 | 369 | options_bytes += 2 + (c >= 0x800); |
15c316bc AS |
370 | /* |
371 | * Add one more byte for valid surrogate pairs. Invalid | |
372 | * surrogates will be replaced with 0xfffd and take up | |
373 | * only 3 bytes. | |
374 | */ | |
375 | if ((c & 0xfc00) == 0xd800) { | |
376 | /* | |
377 | * If the very last word is a high surrogate, | |
378 | * we must ignore it since we can't access the | |
379 | * low surrogate. | |
380 | */ | |
04b24409 | 381 | if (!options_chars) { |
15c316bc | 382 | options_bytes -= 3; |
15c316bc AS |
383 | } else if ((*s2 & 0xfc00) == 0xdc00) { |
384 | options_bytes++; | |
04b24409 | 385 | options_chars--; |
15c316bc AS |
386 | s2++; |
387 | } | |
388 | } | |
5fef3870 | 389 | } |
80b1bfe1 AS |
390 | if (options_bytes >= COMMAND_LINE_SIZE) { |
391 | options_bytes = safe_options_bytes; | |
392 | efi_err("Command line is too long: truncated to %d bytes\n", | |
393 | options_bytes); | |
394 | } | |
5fef3870 RF |
395 | } |
396 | ||
c625d1c2 | 397 | options_bytes++; /* NUL termination */ |
9403e462 | 398 | |
27cd5511 AB |
399 | status = efi_bs_call(allocate_pool, EFI_LOADER_DATA, options_bytes, |
400 | (void **)&cmdline_addr); | |
5fef3870 RF |
401 | if (status != EFI_SUCCESS) |
402 | return NULL; | |
403 | ||
04b24409 AS |
404 | snprintf((char *)cmdline_addr, options_bytes, "%.*ls", |
405 | options_bytes - 1, options); | |
5fef3870 | 406 | |
5fef3870 RF |
407 | return (char *)cmdline_addr; |
408 | } | |
fc07716b | 409 | |
8c0a839c HS |
410 | /** |
411 | * efi_exit_boot_services() - Exit boot services | |
412 | * @handle: handle of the exiting image | |
8c0a839c HS |
413 | * @priv: argument to be passed to @priv_func |
414 | * @priv_func: function to process the memory map before exiting boot services | |
415 | * | |
fc07716b JH |
416 | * Handle calling ExitBootServices according to the requirements set out by the |
417 | * spec. Obtains the current memory map, and returns that info after calling | |
418 | * ExitBootServices. The client must specify a function to perform any | |
419 | * processing of the memory map data prior to ExitBootServices. A client | |
420 | * specific structure may be passed to the function via priv. The client | |
421 | * function may be called multiple times. | |
8c0a839c HS |
422 | * |
423 | * Return: status code | |
fc07716b | 424 | */ |
eab31265 | 425 | efi_status_t efi_exit_boot_services(void *handle, void *priv, |
fc07716b JH |
426 | efi_exit_boot_map_processing priv_func) |
427 | { | |
eab31265 | 428 | struct efi_boot_memmap *map; |
fc07716b JH |
429 | efi_status_t status; |
430 | ||
2e28a798 AB |
431 | if (efi_disable_pci_dma) |
432 | efi_pci_disable_bridge_busmaster(); | |
433 | ||
171539f5 | 434 | status = efi_get_memory_map(&map, true); |
fc07716b | 435 | if (status != EFI_SUCCESS) |
a12b78b5 | 436 | return status; |
fc07716b | 437 | |
cd33a5c1 | 438 | status = priv_func(map, priv); |
a12b78b5 AB |
439 | if (status != EFI_SUCCESS) { |
440 | efi_bs_call(free_pool, map); | |
441 | return status; | |
442 | } | |
fc07716b | 443 | |
eab31265 | 444 | status = efi_bs_call(exit_boot_services, handle, map->map_key); |
fc07716b JH |
445 | |
446 | if (status == EFI_INVALID_PARAMETER) { | |
447 | /* | |
448 | * The memory map changed between efi_get_memory_map() and | |
449 | * exit_boot_services(). Per the UEFI Spec v2.6, Section 6.4: | |
450 | * EFI_BOOT_SERVICES.ExitBootServices we need to get the | |
451 | * updated map, and try again. The spec implies one retry | |
452 | * should be sufficent, which is confirmed against the EDK2 | |
453 | * implementation. Per the spec, we can only invoke | |
454 | * get_memory_map() and exit_boot_services() - we cannot alloc | |
455 | * so efi_get_memory_map() cannot be used, and we must reuse | |
456 | * the buffer. For all practical purposes, the headroom in the | |
457 | * buffer should account for any changes in the map so the call | |
458 | * to get_memory_map() is expected to succeed here. | |
459 | */ | |
eab31265 | 460 | map->map_size = map->buff_size; |
966291f6 | 461 | status = efi_bs_call(get_memory_map, |
eab31265 AB |
462 | &map->map_size, |
463 | &map->map, | |
464 | &map->map_key, | |
465 | &map->desc_size, | |
466 | &map->desc_ver); | |
fc07716b JH |
467 | |
468 | /* exit_boot_services() was called, thus cannot free */ | |
469 | if (status != EFI_SUCCESS) | |
a12b78b5 | 470 | return status; |
fc07716b | 471 | |
cd33a5c1 | 472 | status = priv_func(map, priv); |
fc07716b JH |
473 | /* exit_boot_services() was called, thus cannot free */ |
474 | if (status != EFI_SUCCESS) | |
a12b78b5 | 475 | return status; |
fc07716b | 476 | |
eab31265 | 477 | status = efi_bs_call(exit_boot_services, handle, map->map_key); |
fc07716b JH |
478 | } |
479 | ||
fc07716b JH |
480 | return status; |
481 | } | |
82d736ac | 482 | |
8c0a839c HS |
483 | /** |
484 | * get_efi_config_table() - retrieve UEFI configuration table | |
485 | * @guid: GUID of the configuration table to be retrieved | |
486 | * Return: pointer to the configuration table or NULL | |
487 | */ | |
cd33a5c1 | 488 | void *get_efi_config_table(efi_guid_t guid) |
82d736ac | 489 | { |
ccc27ae7 AB |
490 | unsigned long tables = efi_table_attr(efi_system_table, tables); |
491 | int nr_tables = efi_table_attr(efi_system_table, nr_tables); | |
f958efe9 AB |
492 | int i; |
493 | ||
494 | for (i = 0; i < nr_tables; i++) { | |
495 | efi_config_table_t *t = (void *)tables; | |
496 | ||
497 | if (efi_guidcmp(t->guid, guid) == 0) | |
99ea8b1d | 498 | return efi_table_attr(t, table); |
f958efe9 AB |
499 | |
500 | tables += efi_is_native() ? sizeof(efi_config_table_t) | |
501 | : sizeof(efi_config_table_32_t); | |
502 | } | |
503 | return NULL; | |
82d736ac | 504 | } |
dc29da14 | 505 | |
ec93fc37 AB |
506 | /* |
507 | * The LINUX_EFI_INITRD_MEDIA_GUID vendor media device path below provides a way | |
508 | * for the firmware or bootloader to expose the initrd data directly to the stub | |
509 | * via the trivial LoadFile2 protocol, which is defined in the UEFI spec, and is | |
510 | * very easy to implement. It is a simple Linux initrd specific conduit between | |
511 | * kernel and firmware, allowing us to put the EFI stub (being part of the | |
512 | * kernel) in charge of where and when to load the initrd, while leaving it up | |
513 | * to the firmware to decide whether it needs to expose its filesystem hierarchy | |
514 | * via EFI protocols. | |
515 | */ | |
516 | static const struct { | |
517 | struct efi_vendor_dev_path vendor; | |
518 | struct efi_generic_dev_path end; | |
519 | } __packed initrd_dev_path = { | |
520 | { | |
521 | { | |
522 | EFI_DEV_MEDIA, | |
523 | EFI_DEV_MEDIA_VENDOR, | |
524 | sizeof(struct efi_vendor_dev_path), | |
525 | }, | |
526 | LINUX_EFI_INITRD_MEDIA_GUID | |
527 | }, { | |
528 | EFI_DEV_END_PATH, | |
529 | EFI_DEV_END_ENTIRE, | |
530 | sizeof(struct efi_generic_dev_path) | |
531 | } | |
532 | }; | |
533 | ||
534 | /** | |
8c0a839c | 535 | * efi_load_initrd_dev_path() - load the initrd from the Linux initrd device path |
d981a88c JZ |
536 | * @initrd: pointer of struct to store the address where the initrd was loaded |
537 | * and the size of the loaded initrd | |
ec93fc37 | 538 | * @max: upper limit for the initrd memory allocation |
8c0a839c HS |
539 | * |
540 | * Return: | |
541 | * * %EFI_SUCCESS if the initrd was loaded successfully, in which | |
542 | * case @load_addr and @load_size are assigned accordingly | |
543 | * * %EFI_NOT_FOUND if no LoadFile2 protocol exists on the initrd device path | |
8c0a839c HS |
544 | * * %EFI_OUT_OF_RESOURCES if memory allocation failed |
545 | * * %EFI_LOAD_ERROR in all other cases | |
ec93fc37 | 546 | */ |
f61900fd | 547 | static |
f4dc7fff | 548 | efi_status_t efi_load_initrd_dev_path(struct linux_efi_initrd *initrd, |
ec93fc37 AB |
549 | unsigned long max) |
550 | { | |
551 | efi_guid_t lf2_proto_guid = EFI_LOAD_FILE2_PROTOCOL_GUID; | |
552 | efi_device_path_protocol_t *dp; | |
553 | efi_load_file2_protocol_t *lf2; | |
ec93fc37 AB |
554 | efi_handle_t handle; |
555 | efi_status_t status; | |
556 | ||
ec93fc37 AB |
557 | dp = (efi_device_path_protocol_t *)&initrd_dev_path; |
558 | status = efi_bs_call(locate_device_path, &lf2_proto_guid, &dp, &handle); | |
559 | if (status != EFI_SUCCESS) | |
560 | return status; | |
561 | ||
562 | status = efi_bs_call(handle_protocol, handle, &lf2_proto_guid, | |
563 | (void **)&lf2); | |
564 | if (status != EFI_SUCCESS) | |
565 | return status; | |
566 | ||
f4dc7fff AB |
567 | initrd->size = 0; |
568 | status = efi_call_proto(lf2, load_file, dp, false, &initrd->size, NULL); | |
ec93fc37 AB |
569 | if (status != EFI_BUFFER_TOO_SMALL) |
570 | return EFI_LOAD_ERROR; | |
571 | ||
f4dc7fff | 572 | status = efi_allocate_pages(initrd->size, &initrd->base, max); |
ec93fc37 AB |
573 | if (status != EFI_SUCCESS) |
574 | return status; | |
575 | ||
f4dc7fff AB |
576 | status = efi_call_proto(lf2, load_file, dp, false, &initrd->size, |
577 | (void *)initrd->base); | |
ec93fc37 | 578 | if (status != EFI_SUCCESS) { |
f4dc7fff | 579 | efi_free(initrd->size, initrd->base); |
ec93fc37 AB |
580 | return EFI_LOAD_ERROR; |
581 | } | |
ec93fc37 AB |
582 | return EFI_SUCCESS; |
583 | } | |
f61900fd AS |
584 | |
585 | static | |
586 | efi_status_t efi_load_initrd_cmdline(efi_loaded_image_t *image, | |
f4dc7fff | 587 | struct linux_efi_initrd *initrd, |
f61900fd AS |
588 | unsigned long soft_limit, |
589 | unsigned long hard_limit) | |
590 | { | |
e346bebb | 591 | if (image == NULL) |
f4dc7fff | 592 | return EFI_UNSUPPORTED; |
f61900fd AS |
593 | |
594 | return handle_cmdline_files(image, L"initrd=", sizeof(L"initrd=") - 2, | |
595 | soft_limit, hard_limit, | |
f4dc7fff | 596 | &initrd->base, &initrd->size); |
f61900fd AS |
597 | } |
598 | ||
8c0a839c HS |
599 | /** |
600 | * efi_load_initrd() - Load initial RAM disk | |
601 | * @image: EFI loaded image protocol | |
947228cb AP |
602 | * @soft_limit: preferred address for loading the initrd |
603 | * @hard_limit: upper limit address for loading the initrd | |
c8e1927e | 604 | * @out: pointer to store the address of the initrd table |
8c0a839c HS |
605 | * |
606 | * Return: status code | |
607 | */ | |
f61900fd | 608 | efi_status_t efi_load_initrd(efi_loaded_image_t *image, |
f61900fd | 609 | unsigned long soft_limit, |
f4dc7fff AB |
610 | unsigned long hard_limit, |
611 | const struct linux_efi_initrd **out) | |
f61900fd | 612 | { |
f4dc7fff AB |
613 | efi_guid_t tbl_guid = LINUX_EFI_INITRD_MEDIA_GUID; |
614 | efi_status_t status = EFI_SUCCESS; | |
615 | struct linux_efi_initrd initrd, *tbl; | |
f61900fd | 616 | |
f4dc7fff AB |
617 | if (!IS_ENABLED(CONFIG_BLK_DEV_INITRD) || efi_noinitrd) |
618 | return EFI_SUCCESS; | |
619 | ||
620 | status = efi_load_initrd_dev_path(&initrd, hard_limit); | |
621 | if (status == EFI_SUCCESS) { | |
622 | efi_info("Loaded initrd from LINUX_EFI_INITRD_MEDIA_GUID device path\n"); | |
f4dc7fff AB |
623 | } else if (status == EFI_NOT_FOUND) { |
624 | status = efi_load_initrd_cmdline(image, &initrd, soft_limit, | |
625 | hard_limit); | |
626 | /* command line loader disabled or no initrd= passed? */ | |
627 | if (status == EFI_UNSUPPORTED || status == EFI_NOT_READY) | |
628 | return EFI_SUCCESS; | |
629 | if (status == EFI_SUCCESS) | |
630 | efi_info("Loaded initrd from command line option\n"); | |
20287d56 | 631 | } |
f4dc7fff AB |
632 | if (status != EFI_SUCCESS) |
633 | goto failed; | |
634 | ||
c004703e JL |
635 | if (initrd.size > 0 && |
636 | efi_measure_tagged_event(initrd.base, initrd.size, | |
637 | EFISTUB_EVT_INITRD) == EFI_SUCCESS) | |
638 | efi_info("Measured initrd data into PCR 9\n"); | |
639 | ||
f4dc7fff AB |
640 | status = efi_bs_call(allocate_pool, EFI_LOADER_DATA, sizeof(initrd), |
641 | (void **)&tbl); | |
642 | if (status != EFI_SUCCESS) | |
643 | goto free_initrd; | |
644 | ||
645 | *tbl = initrd; | |
646 | status = efi_bs_call(install_configuration_table, &tbl_guid, tbl); | |
647 | if (status != EFI_SUCCESS) | |
648 | goto free_tbl; | |
649 | ||
650 | if (out) | |
651 | *out = tbl; | |
652 | return EFI_SUCCESS; | |
f61900fd | 653 | |
f4dc7fff AB |
654 | free_tbl: |
655 | efi_bs_call(free_pool, tbl); | |
656 | free_initrd: | |
657 | efi_free(initrd.size, initrd.base); | |
658 | failed: | |
659 | efi_err("Failed to load initrd: 0x%lx\n", status); | |
f61900fd AS |
660 | return status; |
661 | } | |
14c574f3 | 662 | |
8c0a839c HS |
663 | /** |
664 | * efi_wait_for_key() - Wait for key stroke | |
665 | * @usec: number of microseconds to wait for key stroke | |
666 | * @key: key entered | |
667 | * | |
668 | * Wait for up to @usec microseconds for a key stroke. | |
669 | * | |
670 | * Return: status code, EFI_SUCCESS if key received | |
671 | */ | |
14c574f3 AS |
672 | efi_status_t efi_wait_for_key(unsigned long usec, efi_input_key_t *key) |
673 | { | |
674 | efi_event_t events[2], timer; | |
675 | unsigned long index; | |
676 | efi_simple_text_input_protocol_t *con_in; | |
677 | efi_status_t status; | |
678 | ||
679 | con_in = efi_table_attr(efi_system_table, con_in); | |
680 | if (!con_in) | |
681 | return EFI_UNSUPPORTED; | |
682 | efi_set_event_at(events, 0, efi_table_attr(con_in, wait_for_key)); | |
683 | ||
684 | status = efi_bs_call(create_event, EFI_EVT_TIMER, 0, NULL, NULL, &timer); | |
685 | if (status != EFI_SUCCESS) | |
686 | return status; | |
687 | ||
688 | status = efi_bs_call(set_timer, timer, EfiTimerRelative, | |
689 | EFI_100NSEC_PER_USEC * usec); | |
690 | if (status != EFI_SUCCESS) | |
691 | return status; | |
692 | efi_set_event_at(events, 1, timer); | |
693 | ||
694 | status = efi_bs_call(wait_for_event, 2, events, &index); | |
695 | if (status == EFI_SUCCESS) { | |
696 | if (index == 0) | |
697 | status = efi_call_proto(con_in, read_keystroke, key); | |
698 | else | |
699 | status = EFI_TIMEOUT; | |
700 | } | |
701 | ||
702 | efi_bs_call(close_event, timer); | |
703 | ||
704 | return status; | |
705 | } | |
ace013a5 AB |
706 | |
707 | /** | |
708 | * efi_remap_image - Remap a loaded image with the appropriate permissions | |
709 | * for code and data | |
710 | * | |
711 | * @image_base: the base of the image in memory | |
712 | * @alloc_size: the size of the area in memory occupied by the image | |
713 | * @code_size: the size of the leading part of the image containing code | |
714 | * and read-only data | |
715 | * | |
716 | * efi_remap_image() uses the EFI memory attribute protocol to remap the code | |
717 | * region of the loaded image read-only/executable, and the remainder | |
718 | * read-write/non-executable. The code region is assumed to start at the base | |
719 | * of the image, and will therefore cover the PE/COFF header as well. | |
720 | */ | |
721 | void efi_remap_image(unsigned long image_base, unsigned alloc_size, | |
722 | unsigned long code_size) | |
723 | { | |
724 | efi_guid_t guid = EFI_MEMORY_ATTRIBUTE_PROTOCOL_GUID; | |
725 | efi_memory_attribute_protocol_t *memattr; | |
726 | efi_status_t status; | |
727 | u64 attr; | |
728 | ||
729 | /* | |
730 | * If the firmware implements the EFI_MEMORY_ATTRIBUTE_PROTOCOL, let's | |
731 | * invoke it to remap the text/rodata region of the decompressed image | |
732 | * as read-only and the data/bss region as non-executable. | |
733 | */ | |
734 | status = efi_bs_call(locate_protocol, &guid, NULL, (void **)&memattr); | |
735 | if (status != EFI_SUCCESS) | |
736 | return; | |
737 | ||
738 | // Get the current attributes for the entire region | |
739 | status = memattr->get_memory_attributes(memattr, image_base, | |
740 | alloc_size, &attr); | |
741 | if (status != EFI_SUCCESS) { | |
742 | efi_warn("Failed to retrieve memory attributes for image region: 0x%lx\n", | |
743 | status); | |
744 | return; | |
745 | } | |
746 | ||
747 | // Mark the code region as read-only | |
748 | status = memattr->set_memory_attributes(memattr, image_base, code_size, | |
749 | EFI_MEMORY_RO); | |
750 | if (status != EFI_SUCCESS) { | |
751 | efi_warn("Failed to remap code region read-only\n"); | |
752 | return; | |
753 | } | |
754 | ||
755 | // If the entire region was already mapped as non-exec, clear the | |
756 | // attribute from the code region. Otherwise, set it on the data | |
757 | // region. | |
758 | if (attr & EFI_MEMORY_XP) { | |
759 | status = memattr->clear_memory_attributes(memattr, image_base, | |
760 | code_size, | |
761 | EFI_MEMORY_XP); | |
762 | if (status != EFI_SUCCESS) | |
763 | efi_warn("Failed to remap code region executable\n"); | |
764 | } else { | |
765 | status = memattr->set_memory_attributes(memattr, | |
766 | image_base + code_size, | |
767 | alloc_size - code_size, | |
768 | EFI_MEMORY_XP); | |
769 | if (status != EFI_SUCCESS) | |
770 | efi_warn("Failed to remap data region non-executable\n"); | |
771 | } | |
772 | } |