]>
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 | |
2c7d1e30 AS |
10 | #include <stdarg.h> |
11 | ||
80b1bfe1 | 12 | #include <linux/ctype.h> |
bd669475 | 13 | #include <linux/efi.h> |
fd0528a2 | 14 | #include <linux/kernel.h> |
23d5b73f | 15 | #include <linux/printk.h> /* For CONSOLE_LOGLEVEL_* */ |
bd669475 | 16 | #include <asm/efi.h> |
80b1bfe1 | 17 | #include <asm/setup.h> |
bd669475 AB |
18 | |
19 | #include "efistub.h" | |
9bb40191 | 20 | |
980771f6 AB |
21 | bool efi_nochunk; |
22 | bool efi_nokaslr; | |
23 | bool efi_noinitrd; | |
23d5b73f | 24 | int efi_loglevel = CONSOLE_LOGLEVEL_DEFAULT; |
980771f6 AB |
25 | bool efi_novamap; |
26 | ||
54439370 AS |
27 | static bool efi_nosoftreserve; |
28 | static bool efi_disable_pci_dma = IS_ENABLED(CONFIG_EFI_DISABLE_PCI_DMA); | |
60f38de7 | 29 | |
b617c526 DW |
30 | bool __pure __efi_soft_reserve_enabled(void) |
31 | { | |
32 | return !efi_nosoftreserve; | |
33 | } | |
60f38de7 | 34 | |
cb8c90a0 AS |
35 | void efi_char16_puts(efi_char16_t *str) |
36 | { | |
37 | efi_call_proto(efi_table_attr(efi_system_table, con_out), | |
38 | output_string, str); | |
39 | } | |
40 | ||
4b75bd36 AS |
41 | static |
42 | u32 utf8_to_utf32(const u8 **s8) | |
43 | { | |
44 | u32 c32; | |
45 | u8 c0, cx; | |
46 | size_t clen, i; | |
47 | ||
48 | c0 = cx = *(*s8)++; | |
49 | /* | |
50 | * The position of the most-significant 0 bit gives us the length of | |
51 | * a multi-octet encoding. | |
52 | */ | |
53 | for (clen = 0; cx & 0x80; ++clen) | |
54 | cx <<= 1; | |
55 | /* | |
56 | * If the 0 bit is in position 8, this is a valid single-octet | |
57 | * encoding. If the 0 bit is in position 7 or positions 1-3, the | |
58 | * encoding is invalid. | |
59 | * In either case, we just return the first octet. | |
60 | */ | |
61 | if (clen < 2 || clen > 4) | |
62 | return c0; | |
63 | /* Get the bits from the first octet. */ | |
64 | c32 = cx >> clen--; | |
65 | for (i = 0; i < clen; ++i) { | |
66 | /* Trailing octets must have 10 in most significant bits. */ | |
67 | cx = (*s8)[i] ^ 0x80; | |
68 | if (cx & 0xc0) | |
69 | return c0; | |
70 | c32 = (c32 << 6) | cx; | |
71 | } | |
72 | /* | |
73 | * Check for validity: | |
74 | * - The character must be in the Unicode range. | |
75 | * - It must not be a surrogate. | |
76 | * - It must be encoded using the correct number of octets. | |
77 | */ | |
78 | if (c32 > 0x10ffff || | |
79 | (c32 & 0xf800) == 0xd800 || | |
80 | clen != (c32 >= 0x80) + (c32 >= 0x800) + (c32 >= 0x10000)) | |
81 | return c0; | |
82 | *s8 += clen; | |
83 | return c32; | |
84 | } | |
85 | ||
cb8c90a0 | 86 | void efi_puts(const char *str) |
7721da4c | 87 | { |
fd0528a2 AS |
88 | efi_char16_t buf[128]; |
89 | size_t pos = 0, lim = ARRAY_SIZE(buf); | |
4b75bd36 AS |
90 | const u8 *s8 = (const u8 *)str; |
91 | u32 c32; | |
7721da4c | 92 | |
4b75bd36 AS |
93 | while (*s8) { |
94 | if (*s8 == '\n') | |
fd0528a2 | 95 | buf[pos++] = L'\r'; |
4b75bd36 AS |
96 | c32 = utf8_to_utf32(&s8); |
97 | if (c32 < 0x10000) { | |
98 | /* Characters in plane 0 use a single word. */ | |
99 | buf[pos++] = c32; | |
100 | } else { | |
101 | /* | |
102 | * Characters in other planes encode into a surrogate | |
103 | * pair. | |
104 | */ | |
105 | buf[pos++] = (0xd800 - (0x10000 >> 10)) + (c32 >> 10); | |
106 | buf[pos++] = 0xdc00 + (c32 & 0x3ff); | |
107 | } | |
108 | if (*s8 == '\0' || pos >= lim - 2) { | |
fd0528a2 AS |
109 | buf[pos] = L'\0'; |
110 | efi_char16_puts(buf); | |
111 | pos = 0; | |
112 | } | |
7721da4c RF |
113 | } |
114 | } | |
115 | ||
2c7d1e30 AS |
116 | int efi_printk(const char *fmt, ...) |
117 | { | |
118 | char printf_buf[256]; | |
119 | va_list args; | |
120 | int printed; | |
23d5b73f AS |
121 | int loglevel = printk_get_level(fmt); |
122 | ||
123 | switch (loglevel) { | |
124 | case '0' ... '9': | |
125 | loglevel -= '0'; | |
126 | break; | |
127 | default: | |
128 | /* | |
129 | * Use loglevel -1 for cases where we just want to print to | |
130 | * the screen. | |
131 | */ | |
132 | loglevel = -1; | |
133 | break; | |
134 | } | |
135 | ||
136 | if (loglevel >= efi_loglevel) | |
137 | return 0; | |
138 | ||
139 | if (loglevel >= 0) | |
140 | efi_puts("EFI stub: "); | |
141 | ||
142 | fmt = printk_skip_level(fmt); | |
2c7d1e30 AS |
143 | |
144 | va_start(args, fmt); | |
8fb331e1 | 145 | printed = vsnprintf(printf_buf, sizeof(printf_buf), fmt, args); |
2c7d1e30 AS |
146 | va_end(args); |
147 | ||
148 | efi_puts(printf_buf); | |
8fb331e1 AS |
149 | if (printed >= sizeof(printf_buf)) { |
150 | efi_puts("[Message truncated]\n"); | |
151 | return -1; | |
152 | } | |
2c7d1e30 AS |
153 | |
154 | return printed; | |
155 | } | |
156 | ||
5a17dae4 MF |
157 | /* |
158 | * Parse the ASCII string 'cmdline' for EFI options, denoted by the efi= | |
159 | * option, e.g. efi=nochunk. | |
160 | * | |
161 | * It should be noted that efi= is parsed in two very different | |
162 | * environments, first in the early boot environment of the EFI boot | |
163 | * stub, and subsequently during the kernel boot. | |
164 | */ | |
60f38de7 | 165 | efi_status_t efi_parse_options(char const *cmdline) |
5a17dae4 | 166 | { |
91d150c0 AB |
167 | size_t len = strlen(cmdline) + 1; |
168 | efi_status_t status; | |
169 | char *str, *buf; | |
5a17dae4 | 170 | |
91d150c0 AB |
171 | status = efi_bs_call(allocate_pool, EFI_LOADER_DATA, len, (void **)&buf); |
172 | if (status != EFI_SUCCESS) | |
173 | return status; | |
4e46c2a9 | 174 | |
91d150c0 | 175 | str = skip_spaces(memcpy(buf, cmdline, len)); |
b617c526 | 176 | |
91d150c0 AB |
177 | while (*str) { |
178 | char *param, *val; | |
4444f854 | 179 | |
91d150c0 | 180 | str = next_arg(str, ¶m, &val); |
4444f854 | 181 | |
91d150c0 AB |
182 | if (!strcmp(param, "nokaslr")) { |
183 | efi_nokaslr = true; | |
184 | } else if (!strcmp(param, "quiet")) { | |
23d5b73f | 185 | efi_loglevel = CONSOLE_LOGLEVEL_QUIET; |
79d3219d AB |
186 | } else if (!strcmp(param, "noinitrd")) { |
187 | efi_noinitrd = true; | |
91d150c0 AB |
188 | } else if (!strcmp(param, "efi") && val) { |
189 | efi_nochunk = parse_option_str(val, "nochunk"); | |
190 | efi_novamap = parse_option_str(val, "novamap"); | |
5a17dae4 | 191 | |
91d150c0 AB |
192 | efi_nosoftreserve = IS_ENABLED(CONFIG_EFI_SOFT_RESERVE) && |
193 | parse_option_str(val, "nosoftreserve"); | |
5a17dae4 | 194 | |
91d150c0 AB |
195 | if (parse_option_str(val, "disable_early_pci_dma")) |
196 | efi_disable_pci_dma = true; | |
197 | if (parse_option_str(val, "no_disable_early_pci_dma")) | |
198 | efi_disable_pci_dma = false; | |
23d5b73f AS |
199 | if (parse_option_str(val, "debug")) |
200 | efi_loglevel = CONSOLE_LOGLEVEL_DEBUG; | |
fffb6804 AS |
201 | } else if (!strcmp(param, "video") && |
202 | val && strstarts(val, "efifb:")) { | |
203 | efi_parse_option_graphics(val + strlen("efifb:")); | |
91d150c0 AB |
204 | } |
205 | } | |
206 | efi_bs_call(free_pool, buf); | |
5a17dae4 MF |
207 | return EFI_SUCCESS; |
208 | } | |
7721da4c | 209 | |
5fef3870 RF |
210 | /* |
211 | * Convert the unicode UEFI command line to ASCII to pass to kernel. | |
212 | * Size of memory allocated return in *cmd_line_len. | |
213 | * Returns NULL on error. | |
214 | */ | |
27cd5511 | 215 | char *efi_convert_cmdline(efi_loaded_image_t *image, int *cmd_line_len) |
5fef3870 | 216 | { |
c625d1c2 | 217 | const u16 *s2; |
5fef3870 | 218 | unsigned long cmdline_addr = 0; |
04b24409 | 219 | int options_chars = efi_table_attr(image, load_options_size) / 2; |
f7b85b33 | 220 | const u16 *options = efi_table_attr(image, load_options); |
80b1bfe1 AS |
221 | int options_bytes = 0, safe_options_bytes = 0; /* UTF-8 bytes */ |
222 | bool in_quote = false; | |
5fef3870 | 223 | efi_status_t status; |
5fef3870 RF |
224 | |
225 | if (options) { | |
226 | s2 = options; | |
80b1bfe1 | 227 | while (options_bytes < COMMAND_LINE_SIZE && options_chars--) { |
15c316bc AS |
228 | u16 c = *s2++; |
229 | ||
80b1bfe1 AS |
230 | if (c < 0x80) { |
231 | if (c == L'\0' || c == L'\n') | |
232 | break; | |
233 | if (c == L'"') | |
234 | in_quote = !in_quote; | |
235 | else if (!in_quote && isspace((char)c)) | |
236 | safe_options_bytes = options_bytes; | |
237 | ||
238 | options_bytes++; | |
239 | continue; | |
240 | } | |
241 | ||
15c316bc AS |
242 | /* |
243 | * Get the number of UTF-8 bytes corresponding to a | |
244 | * UTF-16 character. | |
245 | * The first part handles everything in the BMP. | |
246 | */ | |
80b1bfe1 | 247 | options_bytes += 2 + (c >= 0x800); |
15c316bc AS |
248 | /* |
249 | * Add one more byte for valid surrogate pairs. Invalid | |
250 | * surrogates will be replaced with 0xfffd and take up | |
251 | * only 3 bytes. | |
252 | */ | |
253 | if ((c & 0xfc00) == 0xd800) { | |
254 | /* | |
255 | * If the very last word is a high surrogate, | |
256 | * we must ignore it since we can't access the | |
257 | * low surrogate. | |
258 | */ | |
04b24409 | 259 | if (!options_chars) { |
15c316bc | 260 | options_bytes -= 3; |
15c316bc AS |
261 | } else if ((*s2 & 0xfc00) == 0xdc00) { |
262 | options_bytes++; | |
04b24409 | 263 | options_chars--; |
15c316bc AS |
264 | s2++; |
265 | } | |
266 | } | |
5fef3870 | 267 | } |
80b1bfe1 AS |
268 | if (options_bytes >= COMMAND_LINE_SIZE) { |
269 | options_bytes = safe_options_bytes; | |
270 | efi_err("Command line is too long: truncated to %d bytes\n", | |
271 | options_bytes); | |
272 | } | |
5fef3870 RF |
273 | } |
274 | ||
c625d1c2 | 275 | options_bytes++; /* NUL termination */ |
9403e462 | 276 | |
27cd5511 AB |
277 | status = efi_bs_call(allocate_pool, EFI_LOADER_DATA, options_bytes, |
278 | (void **)&cmdline_addr); | |
5fef3870 RF |
279 | if (status != EFI_SUCCESS) |
280 | return NULL; | |
281 | ||
04b24409 AS |
282 | snprintf((char *)cmdline_addr, options_bytes, "%.*ls", |
283 | options_bytes - 1, options); | |
5fef3870 | 284 | |
c625d1c2 | 285 | *cmd_line_len = options_bytes; |
5fef3870 RF |
286 | return (char *)cmdline_addr; |
287 | } | |
fc07716b JH |
288 | |
289 | /* | |
290 | * Handle calling ExitBootServices according to the requirements set out by the | |
291 | * spec. Obtains the current memory map, and returns that info after calling | |
292 | * ExitBootServices. The client must specify a function to perform any | |
293 | * processing of the memory map data prior to ExitBootServices. A client | |
294 | * specific structure may be passed to the function via priv. The client | |
295 | * function may be called multiple times. | |
296 | */ | |
cd33a5c1 | 297 | efi_status_t efi_exit_boot_services(void *handle, |
fc07716b JH |
298 | struct efi_boot_memmap *map, |
299 | void *priv, | |
300 | efi_exit_boot_map_processing priv_func) | |
301 | { | |
302 | efi_status_t status; | |
303 | ||
cd33a5c1 | 304 | status = efi_get_memory_map(map); |
fc07716b JH |
305 | |
306 | if (status != EFI_SUCCESS) | |
307 | goto fail; | |
308 | ||
cd33a5c1 | 309 | status = priv_func(map, priv); |
fc07716b JH |
310 | if (status != EFI_SUCCESS) |
311 | goto free_map; | |
312 | ||
4444f854 MG |
313 | if (efi_disable_pci_dma) |
314 | efi_pci_disable_bridge_busmaster(); | |
315 | ||
966291f6 | 316 | status = efi_bs_call(exit_boot_services, handle, *map->key_ptr); |
fc07716b JH |
317 | |
318 | if (status == EFI_INVALID_PARAMETER) { | |
319 | /* | |
320 | * The memory map changed between efi_get_memory_map() and | |
321 | * exit_boot_services(). Per the UEFI Spec v2.6, Section 6.4: | |
322 | * EFI_BOOT_SERVICES.ExitBootServices we need to get the | |
323 | * updated map, and try again. The spec implies one retry | |
324 | * should be sufficent, which is confirmed against the EDK2 | |
325 | * implementation. Per the spec, we can only invoke | |
326 | * get_memory_map() and exit_boot_services() - we cannot alloc | |
327 | * so efi_get_memory_map() cannot be used, and we must reuse | |
328 | * the buffer. For all practical purposes, the headroom in the | |
329 | * buffer should account for any changes in the map so the call | |
330 | * to get_memory_map() is expected to succeed here. | |
331 | */ | |
332 | *map->map_size = *map->buff_size; | |
966291f6 AB |
333 | status = efi_bs_call(get_memory_map, |
334 | map->map_size, | |
335 | *map->map, | |
336 | map->key_ptr, | |
337 | map->desc_size, | |
338 | map->desc_ver); | |
fc07716b JH |
339 | |
340 | /* exit_boot_services() was called, thus cannot free */ | |
341 | if (status != EFI_SUCCESS) | |
342 | goto fail; | |
343 | ||
cd33a5c1 | 344 | status = priv_func(map, priv); |
fc07716b JH |
345 | /* exit_boot_services() was called, thus cannot free */ |
346 | if (status != EFI_SUCCESS) | |
347 | goto fail; | |
348 | ||
966291f6 | 349 | status = efi_bs_call(exit_boot_services, handle, *map->key_ptr); |
fc07716b JH |
350 | } |
351 | ||
352 | /* exit_boot_services() was called, thus cannot free */ | |
353 | if (status != EFI_SUCCESS) | |
354 | goto fail; | |
355 | ||
356 | return EFI_SUCCESS; | |
357 | ||
358 | free_map: | |
966291f6 | 359 | efi_bs_call(free_pool, *map->map); |
fc07716b JH |
360 | fail: |
361 | return status; | |
362 | } | |
82d736ac | 363 | |
cd33a5c1 | 364 | void *get_efi_config_table(efi_guid_t guid) |
82d736ac | 365 | { |
ccc27ae7 AB |
366 | unsigned long tables = efi_table_attr(efi_system_table, tables); |
367 | int nr_tables = efi_table_attr(efi_system_table, nr_tables); | |
f958efe9 AB |
368 | int i; |
369 | ||
370 | for (i = 0; i < nr_tables; i++) { | |
371 | efi_config_table_t *t = (void *)tables; | |
372 | ||
373 | if (efi_guidcmp(t->guid, guid) == 0) | |
99ea8b1d | 374 | return efi_table_attr(t, table); |
f958efe9 AB |
375 | |
376 | tables += efi_is_native() ? sizeof(efi_config_table_t) | |
377 | : sizeof(efi_config_table_32_t); | |
378 | } | |
379 | return NULL; | |
82d736ac | 380 | } |
dc29da14 | 381 | |
ec93fc37 AB |
382 | /* |
383 | * The LINUX_EFI_INITRD_MEDIA_GUID vendor media device path below provides a way | |
384 | * for the firmware or bootloader to expose the initrd data directly to the stub | |
385 | * via the trivial LoadFile2 protocol, which is defined in the UEFI spec, and is | |
386 | * very easy to implement. It is a simple Linux initrd specific conduit between | |
387 | * kernel and firmware, allowing us to put the EFI stub (being part of the | |
388 | * kernel) in charge of where and when to load the initrd, while leaving it up | |
389 | * to the firmware to decide whether it needs to expose its filesystem hierarchy | |
390 | * via EFI protocols. | |
391 | */ | |
392 | static const struct { | |
393 | struct efi_vendor_dev_path vendor; | |
394 | struct efi_generic_dev_path end; | |
395 | } __packed initrd_dev_path = { | |
396 | { | |
397 | { | |
398 | EFI_DEV_MEDIA, | |
399 | EFI_DEV_MEDIA_VENDOR, | |
400 | sizeof(struct efi_vendor_dev_path), | |
401 | }, | |
402 | LINUX_EFI_INITRD_MEDIA_GUID | |
403 | }, { | |
404 | EFI_DEV_END_PATH, | |
405 | EFI_DEV_END_ENTIRE, | |
406 | sizeof(struct efi_generic_dev_path) | |
407 | } | |
408 | }; | |
409 | ||
410 | /** | |
411 | * efi_load_initrd_dev_path - load the initrd from the Linux initrd device path | |
412 | * @load_addr: pointer to store the address where the initrd was loaded | |
413 | * @load_size: pointer to store the size of the loaded initrd | |
414 | * @max: upper limit for the initrd memory allocation | |
415 | * @return: %EFI_SUCCESS if the initrd was loaded successfully, in which | |
416 | * case @load_addr and @load_size are assigned accordingly | |
417 | * %EFI_NOT_FOUND if no LoadFile2 protocol exists on the initrd | |
418 | * device path | |
419 | * %EFI_INVALID_PARAMETER if load_addr == NULL or load_size == NULL | |
420 | * %EFI_OUT_OF_RESOURCES if memory allocation failed | |
421 | * %EFI_LOAD_ERROR in all other cases | |
422 | */ | |
f61900fd | 423 | static |
ec93fc37 AB |
424 | efi_status_t efi_load_initrd_dev_path(unsigned long *load_addr, |
425 | unsigned long *load_size, | |
426 | unsigned long max) | |
427 | { | |
428 | efi_guid_t lf2_proto_guid = EFI_LOAD_FILE2_PROTOCOL_GUID; | |
429 | efi_device_path_protocol_t *dp; | |
430 | efi_load_file2_protocol_t *lf2; | |
431 | unsigned long initrd_addr; | |
432 | unsigned long initrd_size; | |
433 | efi_handle_t handle; | |
434 | efi_status_t status; | |
435 | ||
ec93fc37 AB |
436 | dp = (efi_device_path_protocol_t *)&initrd_dev_path; |
437 | status = efi_bs_call(locate_device_path, &lf2_proto_guid, &dp, &handle); | |
438 | if (status != EFI_SUCCESS) | |
439 | return status; | |
440 | ||
441 | status = efi_bs_call(handle_protocol, handle, &lf2_proto_guid, | |
442 | (void **)&lf2); | |
443 | if (status != EFI_SUCCESS) | |
444 | return status; | |
445 | ||
446 | status = efi_call_proto(lf2, load_file, dp, false, &initrd_size, NULL); | |
447 | if (status != EFI_BUFFER_TOO_SMALL) | |
448 | return EFI_LOAD_ERROR; | |
449 | ||
450 | status = efi_allocate_pages(initrd_size, &initrd_addr, max); | |
451 | if (status != EFI_SUCCESS) | |
452 | return status; | |
453 | ||
454 | status = efi_call_proto(lf2, load_file, dp, false, &initrd_size, | |
455 | (void *)initrd_addr); | |
456 | if (status != EFI_SUCCESS) { | |
457 | efi_free(initrd_size, initrd_addr); | |
458 | return EFI_LOAD_ERROR; | |
459 | } | |
460 | ||
461 | *load_addr = initrd_addr; | |
462 | *load_size = initrd_size; | |
463 | return EFI_SUCCESS; | |
464 | } | |
f61900fd AS |
465 | |
466 | static | |
467 | efi_status_t efi_load_initrd_cmdline(efi_loaded_image_t *image, | |
468 | unsigned long *load_addr, | |
469 | unsigned long *load_size, | |
470 | unsigned long soft_limit, | |
471 | unsigned long hard_limit) | |
472 | { | |
473 | if (!IS_ENABLED(CONFIG_EFI_GENERIC_STUB_INITRD_CMDLINE_LOADER) || | |
474 | (IS_ENABLED(CONFIG_X86) && (!efi_is_native() || image == NULL))) { | |
475 | *load_addr = *load_size = 0; | |
476 | return EFI_SUCCESS; | |
477 | } | |
478 | ||
479 | return handle_cmdline_files(image, L"initrd=", sizeof(L"initrd=") - 2, | |
480 | soft_limit, hard_limit, | |
481 | load_addr, load_size); | |
482 | } | |
483 | ||
484 | efi_status_t efi_load_initrd(efi_loaded_image_t *image, | |
485 | unsigned long *load_addr, | |
486 | unsigned long *load_size, | |
487 | unsigned long soft_limit, | |
488 | unsigned long hard_limit) | |
489 | { | |
490 | efi_status_t status; | |
491 | ||
492 | if (!load_addr || !load_size) | |
493 | return EFI_INVALID_PARAMETER; | |
494 | ||
495 | status = efi_load_initrd_dev_path(load_addr, load_size, hard_limit); | |
496 | if (status == EFI_SUCCESS) { | |
497 | efi_info("Loaded initrd from LINUX_EFI_INITRD_MEDIA_GUID device path\n"); | |
498 | } else if (status == EFI_NOT_FOUND) { | |
499 | status = efi_load_initrd_cmdline(image, load_addr, load_size, | |
500 | soft_limit, hard_limit); | |
501 | if (status == EFI_SUCCESS && *load_size > 0) | |
502 | efi_info("Loaded initrd from command line option\n"); | |
503 | } | |
504 | ||
505 | return status; | |
506 | } | |
14c574f3 AS |
507 | |
508 | efi_status_t efi_wait_for_key(unsigned long usec, efi_input_key_t *key) | |
509 | { | |
510 | efi_event_t events[2], timer; | |
511 | unsigned long index; | |
512 | efi_simple_text_input_protocol_t *con_in; | |
513 | efi_status_t status; | |
514 | ||
515 | con_in = efi_table_attr(efi_system_table, con_in); | |
516 | if (!con_in) | |
517 | return EFI_UNSUPPORTED; | |
518 | efi_set_event_at(events, 0, efi_table_attr(con_in, wait_for_key)); | |
519 | ||
520 | status = efi_bs_call(create_event, EFI_EVT_TIMER, 0, NULL, NULL, &timer); | |
521 | if (status != EFI_SUCCESS) | |
522 | return status; | |
523 | ||
524 | status = efi_bs_call(set_timer, timer, EfiTimerRelative, | |
525 | EFI_100NSEC_PER_USEC * usec); | |
526 | if (status != EFI_SUCCESS) | |
527 | return status; | |
528 | efi_set_event_at(events, 1, timer); | |
529 | ||
530 | status = efi_bs_call(wait_for_event, 2, events, &index); | |
531 | if (status == EFI_SUCCESS) { | |
532 | if (index == 0) | |
533 | status = efi_call_proto(con_in, read_keystroke, key); | |
534 | else | |
535 | status = EFI_TIMEOUT; | |
536 | } | |
537 | ||
538 | efi_bs_call(close_event, timer); | |
539 | ||
540 | return status; | |
541 | } |