]>
Commit | Line | Data |
---|---|---|
f739fcd8 | 1 | // SPDX-License-Identifier: GPL-2.0+ |
c1311ad4 AG |
2 | /* |
3 | * EFI application console interface | |
4 | * | |
5 | * Copyright (c) 2016 Alexander Graf | |
c1311ad4 AG |
6 | */ |
7 | ||
8 | #include <common.h> | |
78178bb0 | 9 | #include <charset.h> |
a18c5a83 | 10 | #include <dm/device.h> |
c1311ad4 | 11 | #include <efi_loader.h> |
a18c5a83 RC |
12 | #include <stdio_dev.h> |
13 | #include <video_console.h> | |
c1311ad4 | 14 | |
5be8b0a3 EV |
15 | #define EFI_COUT_MODE_2 2 |
16 | #define EFI_MAX_COUT_MODE 3 | |
17 | ||
18 | struct cout_mode { | |
19 | unsigned long columns; | |
20 | unsigned long rows; | |
21 | int present; | |
22 | }; | |
23 | ||
24 | static struct cout_mode efi_cout_modes[] = { | |
25 | /* EFI Mode 0 is 80x25 and always present */ | |
26 | { | |
27 | .columns = 80, | |
28 | .rows = 25, | |
29 | .present = 1, | |
30 | }, | |
31 | /* EFI Mode 1 is always 80x50 */ | |
32 | { | |
33 | .columns = 80, | |
34 | .rows = 50, | |
35 | .present = 0, | |
36 | }, | |
37 | /* Value are unknown until we query the console */ | |
38 | { | |
39 | .columns = 0, | |
40 | .rows = 0, | |
41 | .present = 0, | |
42 | }, | |
43 | }; | |
44 | ||
ebb4dd5b HS |
45 | const efi_guid_t efi_guid_text_output_protocol = |
46 | EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL_GUID; | |
47 | const efi_guid_t efi_guid_text_input_protocol = | |
48 | EFI_SIMPLE_TEXT_INPUT_PROTOCOL_GUID; | |
c1311ad4 AG |
49 | |
50 | #define cESC '\x1b' | |
51 | #define ESC "\x1b" | |
52 | ||
5be8b0a3 | 53 | /* Default to mode 0 */ |
c1311ad4 | 54 | static struct simple_text_output_mode efi_con_mode = { |
5be8b0a3 | 55 | .max_mode = 1, |
c1311ad4 AG |
56 | .mode = 0, |
57 | .attribute = 0, | |
58 | .cursor_column = 0, | |
59 | .cursor_row = 0, | |
60 | .cursor_visible = 1, | |
61 | }; | |
62 | ||
62217295 HS |
63 | /* |
64 | * Receive and parse a reply from the terminal. | |
65 | * | |
66 | * @n: array of return values | |
67 | * @num: number of return values expected | |
68 | * @end_char: character indicating end of terminal message | |
69 | * @return: non-zero indicates error | |
70 | */ | |
71 | static int term_read_reply(int *n, int num, char end_char) | |
c1311ad4 AG |
72 | { |
73 | char c; | |
74 | int i = 0; | |
75 | ||
76 | c = getc(); | |
77 | if (c != cESC) | |
78 | return -1; | |
79 | c = getc(); | |
80 | if (c != '[') | |
81 | return -1; | |
82 | ||
83 | n[0] = 0; | |
84 | while (1) { | |
85 | c = getc(); | |
86 | if (c == ';') { | |
87 | i++; | |
62217295 | 88 | if (i >= num) |
c1311ad4 AG |
89 | return -1; |
90 | n[i] = 0; | |
91 | continue; | |
92 | } else if (c == end_char) { | |
93 | break; | |
94 | } else if (c > '9' || c < '0') { | |
95 | return -1; | |
96 | } | |
97 | ||
98 | /* Read one more decimal position */ | |
99 | n[i] *= 10; | |
100 | n[i] += c - '0'; | |
101 | } | |
62217295 HS |
102 | if (i != num - 1) |
103 | return -1; | |
c1311ad4 AG |
104 | |
105 | return 0; | |
106 | } | |
107 | ||
c1311ad4 AG |
108 | static efi_status_t EFIAPI efi_cout_output_string( |
109 | struct efi_simple_text_output_protocol *this, | |
3a45bc7f | 110 | const efi_string_t string) |
c1311ad4 | 111 | { |
3a45bc7f RC |
112 | struct simple_text_output_mode *con = &efi_con_mode; |
113 | struct cout_mode *mode = &efi_cout_modes[con->mode]; | |
ba7bd5c2 | 114 | char *buf, *pos; |
7ca7c3c0 | 115 | u16 *p; |
ba7bd5c2 | 116 | efi_status_t ret = EFI_SUCCESS; |
3a45bc7f | 117 | |
ba7bd5c2 | 118 | EFI_ENTRY("%p, %p", this, string); |
3a45bc7f | 119 | |
ba7bd5c2 HS |
120 | buf = malloc(utf16_utf8_strlen(string) + 1); |
121 | if (!buf) { | |
122 | ret = EFI_OUT_OF_RESOURCES; | |
123 | goto out; | |
124 | } | |
125 | pos = buf; | |
126 | utf16_utf8_strcpy(&pos, string); | |
3a45bc7f | 127 | fputs(stdout, buf); |
ba7bd5c2 | 128 | free(buf); |
3a45bc7f | 129 | |
7ca7c3c0 HS |
130 | /* |
131 | * Update the cursor position. | |
132 | * | |
133 | * The UEFI spec provides advance rules for U+0000, U+0008, U+000A, | |
134 | * and U000D. All other characters, including control characters | |
14d103bb | 135 | * U+0007 (BEL) and U+0009 (TAB), have to increase the column by one. |
7ca7c3c0 HS |
136 | */ |
137 | for (p = string; *p; ++p) { | |
3a45bc7f | 138 | switch (*p) { |
7ca7c3c0 HS |
139 | case '\b': /* U+0008, backspace */ |
140 | con->cursor_column = max(0, con->cursor_column - 1); | |
3a45bc7f | 141 | break; |
7ca7c3c0 | 142 | case '\n': /* U+000A, newline */ |
3a45bc7f RC |
143 | con->cursor_column = 0; |
144 | con->cursor_row++; | |
145 | break; | |
7ca7c3c0 HS |
146 | case '\r': /* U+000D, carriage-return */ |
147 | con->cursor_column = 0; | |
3a45bc7f | 148 | break; |
7ca7c3c0 HS |
149 | case 0xd800 ... 0xdbff: |
150 | /* | |
151 | * Ignore high surrogates, we do not want to count a | |
152 | * Unicode character twice. | |
153 | */ | |
3a45bc7f RC |
154 | break; |
155 | default: | |
156 | con->cursor_column++; | |
157 | break; | |
158 | } | |
159 | if (con->cursor_column >= mode->columns) { | |
160 | con->cursor_column = 0; | |
161 | con->cursor_row++; | |
c1311ad4 | 162 | } |
3a45bc7f | 163 | con->cursor_row = min(con->cursor_row, (s32)mode->rows - 1); |
c1311ad4 AG |
164 | } |
165 | ||
ba7bd5c2 HS |
166 | out: |
167 | return EFI_EXIT(ret); | |
c1311ad4 AG |
168 | } |
169 | ||
170 | static efi_status_t EFIAPI efi_cout_test_string( | |
171 | struct efi_simple_text_output_protocol *this, | |
3a45bc7f | 172 | const efi_string_t string) |
c1311ad4 AG |
173 | { |
174 | EFI_ENTRY("%p, %p", this, string); | |
175 | return EFI_EXIT(EFI_SUCCESS); | |
176 | } | |
177 | ||
5be8b0a3 EV |
178 | static bool cout_mode_matches(struct cout_mode *mode, int rows, int cols) |
179 | { | |
180 | if (!mode->present) | |
181 | return false; | |
182 | ||
183 | return (mode->rows == rows) && (mode->columns == cols); | |
184 | } | |
185 | ||
71cc25c3 RC |
186 | static int query_console_serial(int *rows, int *cols) |
187 | { | |
188 | /* Ask the terminal about its size */ | |
189 | int n[3]; | |
190 | u64 timeout; | |
191 | ||
192 | /* Empty input buffer */ | |
193 | while (tstc()) | |
194 | getc(); | |
195 | ||
196 | printf(ESC"[18t"); | |
197 | ||
198 | /* Check if we have a terminal that understands */ | |
199 | timeout = timer_get_us() + 1000000; | |
200 | while (!tstc()) | |
201 | if (timer_get_us() > timeout) | |
202 | return -1; | |
203 | ||
204 | /* Read {depth,rows,cols} */ | |
205 | if (term_read_reply(n, 3, 't')) | |
206 | return -1; | |
207 | ||
208 | *cols = n[2]; | |
209 | *rows = n[1]; | |
210 | ||
211 | return 0; | |
212 | } | |
213 | ||
a4aa7bef HS |
214 | /* |
215 | * Update the mode table. | |
216 | * | |
217 | * By default the only mode available is 80x25. If the console has at least 50 | |
218 | * lines, enable mode 80x50. If we can query the console size and it is neither | |
219 | * 80x25 nor 80x50, set it as an additional mode. | |
220 | */ | |
221 | static void query_console_size(void) | |
222 | { | |
223 | const char *stdout_name = env_get("stdout"); | |
80483b2a | 224 | int rows = 25, cols = 80; |
a4aa7bef HS |
225 | |
226 | if (stdout_name && !strcmp(stdout_name, "vidconsole") && | |
227 | IS_ENABLED(CONFIG_DM_VIDEO)) { | |
228 | struct stdio_dev *stdout_dev = | |
229 | stdio_get_by_name("vidconsole"); | |
230 | struct udevice *dev = stdout_dev->priv; | |
231 | struct vidconsole_priv *priv = | |
232 | dev_get_uclass_priv(dev); | |
233 | rows = priv->rows; | |
234 | cols = priv->cols; | |
235 | } else if (query_console_serial(&rows, &cols)) { | |
236 | return; | |
237 | } | |
238 | ||
239 | /* Test if we can have Mode 1 */ | |
240 | if (cols >= 80 && rows >= 50) { | |
241 | efi_cout_modes[1].present = 1; | |
242 | efi_con_mode.max_mode = 2; | |
243 | } | |
244 | ||
245 | /* | |
246 | * Install our mode as mode 2 if it is different | |
247 | * than mode 0 or 1 and set it as the currently selected mode | |
248 | */ | |
249 | if (!cout_mode_matches(&efi_cout_modes[0], rows, cols) && | |
250 | !cout_mode_matches(&efi_cout_modes[1], rows, cols)) { | |
251 | efi_cout_modes[EFI_COUT_MODE_2].columns = cols; | |
252 | efi_cout_modes[EFI_COUT_MODE_2].rows = rows; | |
253 | efi_cout_modes[EFI_COUT_MODE_2].present = 1; | |
254 | efi_con_mode.max_mode = EFI_MAX_COUT_MODE; | |
255 | efi_con_mode.mode = EFI_COUT_MODE_2; | |
256 | } | |
257 | } | |
258 | ||
c1311ad4 AG |
259 | static efi_status_t EFIAPI efi_cout_query_mode( |
260 | struct efi_simple_text_output_protocol *this, | |
261 | unsigned long mode_number, unsigned long *columns, | |
262 | unsigned long *rows) | |
263 | { | |
264 | EFI_ENTRY("%p, %ld, %p, %p", this, mode_number, columns, rows); | |
265 | ||
5be8b0a3 EV |
266 | if (mode_number >= efi_con_mode.max_mode) |
267 | return EFI_EXIT(EFI_UNSUPPORTED); | |
268 | ||
269 | if (efi_cout_modes[mode_number].present != 1) | |
270 | return EFI_EXIT(EFI_UNSUPPORTED); | |
271 | ||
c1311ad4 | 272 | if (columns) |
5be8b0a3 | 273 | *columns = efi_cout_modes[mode_number].columns; |
c1311ad4 | 274 | if (rows) |
5be8b0a3 | 275 | *rows = efi_cout_modes[mode_number].rows; |
c1311ad4 AG |
276 | |
277 | return EFI_EXIT(EFI_SUCCESS); | |
278 | } | |
279 | ||
280 | static efi_status_t EFIAPI efi_cout_set_mode( | |
281 | struct efi_simple_text_output_protocol *this, | |
282 | unsigned long mode_number) | |
283 | { | |
284 | EFI_ENTRY("%p, %ld", this, mode_number); | |
285 | ||
c1311ad4 | 286 | |
5be8b0a3 EV |
287 | if (mode_number > efi_con_mode.max_mode) |
288 | return EFI_EXIT(EFI_UNSUPPORTED); | |
289 | ||
290 | efi_con_mode.mode = mode_number; | |
291 | efi_con_mode.cursor_column = 0; | |
292 | efi_con_mode.cursor_row = 0; | |
293 | ||
294 | return EFI_EXIT(EFI_SUCCESS); | |
c1311ad4 AG |
295 | } |
296 | ||
2d5dc2a5 RC |
297 | static const struct { |
298 | unsigned int fg; | |
299 | unsigned int bg; | |
300 | } color[] = { | |
301 | { 30, 40 }, /* 0: black */ | |
302 | { 34, 44 }, /* 1: blue */ | |
303 | { 32, 42 }, /* 2: green */ | |
304 | { 36, 46 }, /* 3: cyan */ | |
305 | { 31, 41 }, /* 4: red */ | |
306 | { 35, 45 }, /* 5: magenta */ | |
14d103bb HS |
307 | { 33, 43 }, /* 6: brown, map to yellow as EDK2 does*/ |
308 | { 37, 47 }, /* 7: light gray, map to white */ | |
2d5dc2a5 RC |
309 | }; |
310 | ||
311 | /* See EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL.SetAttribute(). */ | |
c1311ad4 AG |
312 | static efi_status_t EFIAPI efi_cout_set_attribute( |
313 | struct efi_simple_text_output_protocol *this, | |
314 | unsigned long attribute) | |
315 | { | |
2d5dc2a5 RC |
316 | unsigned int bold = EFI_ATTR_BOLD(attribute); |
317 | unsigned int fg = EFI_ATTR_FG(attribute); | |
318 | unsigned int bg = EFI_ATTR_BG(attribute); | |
319 | ||
c1311ad4 AG |
320 | EFI_ENTRY("%p, %lx", this, attribute); |
321 | ||
2d5dc2a5 RC |
322 | if (attribute) |
323 | printf(ESC"[%u;%u;%um", bold, color[fg].fg, color[bg].bg); | |
324 | else | |
325 | printf(ESC"[0;37;40m"); | |
326 | ||
327 | return EFI_EXIT(EFI_SUCCESS); | |
c1311ad4 AG |
328 | } |
329 | ||
330 | static efi_status_t EFIAPI efi_cout_clear_screen( | |
331 | struct efi_simple_text_output_protocol *this) | |
332 | { | |
333 | EFI_ENTRY("%p", this); | |
334 | ||
335 | printf(ESC"[2J"); | |
e67ff94d HS |
336 | efi_con_mode.cursor_column = 0; |
337 | efi_con_mode.cursor_row = 0; | |
c1311ad4 AG |
338 | |
339 | return EFI_EXIT(EFI_SUCCESS); | |
340 | } | |
341 | ||
9d12daff HS |
342 | static efi_status_t EFIAPI efi_cout_reset( |
343 | struct efi_simple_text_output_protocol *this, | |
344 | char extended_verification) | |
345 | { | |
346 | EFI_ENTRY("%p, %d", this, extended_verification); | |
347 | ||
348 | /* Clear screen */ | |
349 | EFI_CALL(efi_cout_clear_screen(this)); | |
350 | /* Set default colors */ | |
351 | printf(ESC "[0;37;40m"); | |
352 | ||
353 | return EFI_EXIT(EFI_SUCCESS); | |
354 | } | |
355 | ||
c1311ad4 AG |
356 | static efi_status_t EFIAPI efi_cout_set_cursor_position( |
357 | struct efi_simple_text_output_protocol *this, | |
358 | unsigned long column, unsigned long row) | |
359 | { | |
360 | EFI_ENTRY("%p, %ld, %ld", this, column, row); | |
361 | ||
362 | printf(ESC"[%d;%df", (int)row, (int)column); | |
363 | efi_con_mode.cursor_column = column; | |
364 | efi_con_mode.cursor_row = row; | |
365 | ||
366 | return EFI_EXIT(EFI_SUCCESS); | |
367 | } | |
368 | ||
369 | static efi_status_t EFIAPI efi_cout_enable_cursor( | |
370 | struct efi_simple_text_output_protocol *this, | |
371 | bool enable) | |
372 | { | |
373 | EFI_ENTRY("%p, %d", this, enable); | |
374 | ||
375 | printf(ESC"[?25%c", enable ? 'h' : 'l'); | |
376 | ||
377 | return EFI_EXIT(EFI_SUCCESS); | |
378 | } | |
379 | ||
ebb4dd5b | 380 | struct efi_simple_text_output_protocol efi_con_out = { |
c1311ad4 AG |
381 | .reset = efi_cout_reset, |
382 | .output_string = efi_cout_output_string, | |
383 | .test_string = efi_cout_test_string, | |
384 | .query_mode = efi_cout_query_mode, | |
385 | .set_mode = efi_cout_set_mode, | |
386 | .set_attribute = efi_cout_set_attribute, | |
387 | .clear_screen = efi_cout_clear_screen, | |
388 | .set_cursor_position = efi_cout_set_cursor_position, | |
389 | .enable_cursor = efi_cout_enable_cursor, | |
390 | .mode = (void*)&efi_con_mode, | |
391 | }; | |
392 | ||
0dfd13a4 HS |
393 | static bool key_available; |
394 | static struct efi_input_key next_key; | |
4f187897 | 395 | |
0dfd13a4 HS |
396 | /** |
397 | * skip_modifiers() - analyze modifiers (shift, alt, ctrl) for function keys | |
398 | * | |
0fb4169e HS |
399 | * This gets called when we have already parsed CSI. |
400 | * | |
401 | * @modifiers: bitmask (shift, alt, ctrl) | |
402 | * @return: the unmodified code | |
403 | */ | |
404 | static char skip_modifiers(int *modifiers) | |
405 | { | |
406 | char c, mod = 0, ret = 0; | |
407 | ||
408 | c = getc(); | |
409 | ||
410 | if (c != ';') { | |
411 | ret = c; | |
412 | if (c == '~') | |
413 | goto out; | |
414 | c = getc(); | |
415 | } | |
416 | for (;;) { | |
417 | switch (c) { | |
418 | case '0'...'9': | |
419 | mod *= 10; | |
420 | mod += c - '0'; | |
421 | /* fall through */ | |
422 | case ';': | |
423 | c = getc(); | |
424 | break; | |
425 | default: | |
426 | goto out; | |
427 | } | |
428 | } | |
429 | out: | |
430 | if (mod) | |
431 | --mod; | |
432 | if (modifiers) | |
433 | *modifiers = mod; | |
434 | if (!ret) | |
435 | ret = c; | |
436 | return ret; | |
437 | } | |
438 | ||
0dfd13a4 HS |
439 | /** |
440 | * efi_cin_read_key() - read a key from the console input | |
441 | * | |
442 | * @key: - key received | |
443 | * Return: - status code | |
444 | */ | |
445 | static efi_status_t efi_cin_read_key(struct efi_input_key *key) | |
c1311ad4 | 446 | { |
35cbb796 | 447 | efi_status_t ret; |
c1311ad4 AG |
448 | struct efi_input_key pressed_key = { |
449 | .scan_code = 0, | |
450 | .unicode_char = 0, | |
451 | }; | |
35cbb796 | 452 | s32 ch; |
c1311ad4 | 453 | |
35cbb796 HS |
454 | ret = console_read_unicode(&ch); |
455 | if (ret) | |
0dfd13a4 | 456 | return EFI_NOT_READY; |
35cbb796 HS |
457 | /* We do not support multi-word codes */ |
458 | if (ch >= 0x10000) | |
459 | ch = '?'; | |
c1311ad4 | 460 | if (ch == cESC) { |
0fb4169e HS |
461 | /* |
462 | * Xterm Control Sequences | |
463 | * https://www.xfree86.org/4.8.0/ctlseqs.html | |
464 | */ | |
c1311ad4 AG |
465 | ch = getc(); |
466 | switch (ch) { | |
467 | case cESC: /* ESC */ | |
468 | pressed_key.scan_code = 23; | |
469 | break; | |
470 | case 'O': /* F1 - F4 */ | |
0fb4169e HS |
471 | ch = getc(); |
472 | /* skip modifiers */ | |
473 | if (ch <= '9') | |
474 | ch = getc(); | |
475 | pressed_key.scan_code = ch - 'P' + 11; | |
c1311ad4 AG |
476 | break; |
477 | case 'a'...'z': | |
478 | ch = ch - 'a'; | |
479 | break; | |
480 | case '[': | |
481 | ch = getc(); | |
482 | switch (ch) { | |
483 | case 'A'...'D': /* up, down right, left */ | |
484 | pressed_key.scan_code = ch - 'A' + 1; | |
485 | break; | |
486 | case 'F': /* End */ | |
487 | pressed_key.scan_code = 6; | |
488 | break; | |
489 | case 'H': /* Home */ | |
490 | pressed_key.scan_code = 5; | |
491 | break; | |
0fb4169e HS |
492 | case '1': |
493 | ch = skip_modifiers(NULL); | |
494 | switch (ch) { | |
495 | case '1'...'5': /* F1 - F5 */ | |
496 | pressed_key.scan_code = ch - '1' + 11; | |
497 | break; | |
498 | case '7'...'9': /* F6 - F8 */ | |
499 | pressed_key.scan_code = ch - '7' + 16; | |
500 | break; | |
501 | case 'A'...'D': /* up, down right, left */ | |
502 | pressed_key.scan_code = ch - 'A' + 1; | |
503 | break; | |
504 | case 'F': | |
505 | pressed_key.scan_code = 6; /* End */ | |
506 | break; | |
507 | case 'H': | |
508 | pressed_key.scan_code = 5; /* Home */ | |
509 | break; | |
510 | } | |
c1311ad4 | 511 | break; |
0fb4169e HS |
512 | case '2': |
513 | ch = skip_modifiers(NULL); | |
514 | switch (ch) { | |
515 | case '0'...'1': /* F9 - F10 */ | |
516 | pressed_key.scan_code = ch - '0' + 19; | |
517 | break; | |
518 | case '3'...'4': /* F11 - F12 */ | |
519 | pressed_key.scan_code = ch - '3' + 21; | |
520 | break; | |
521 | case '~': /* INS */ | |
522 | pressed_key.scan_code = 7; | |
523 | break; | |
524 | } | |
c1311ad4 AG |
525 | break; |
526 | case '3': /* DEL */ | |
527 | pressed_key.scan_code = 8; | |
0fb4169e HS |
528 | skip_modifiers(NULL); |
529 | break; | |
530 | case '5': /* PG UP */ | |
531 | pressed_key.scan_code = 9; | |
532 | skip_modifiers(NULL); | |
533 | break; | |
534 | case '6': /* PG DOWN */ | |
535 | pressed_key.scan_code = 10; | |
536 | skip_modifiers(NULL); | |
c1311ad4 AG |
537 | break; |
538 | } | |
539 | break; | |
540 | } | |
541 | } else if (ch == 0x7f) { | |
542 | /* Backspace */ | |
543 | ch = 0x08; | |
544 | } | |
0fb4169e HS |
545 | if (!pressed_key.scan_code) |
546 | pressed_key.unicode_char = ch; | |
c1311ad4 AG |
547 | *key = pressed_key; |
548 | ||
0dfd13a4 HS |
549 | return EFI_SUCCESS; |
550 | } | |
551 | ||
552 | /** | |
553 | * efi_cin_check() - check if keyboard input is available | |
554 | */ | |
555 | static void efi_cin_check(void) | |
556 | { | |
557 | efi_status_t ret; | |
558 | ||
559 | if (key_available) { | |
560 | efi_signal_event(efi_con_in.wait_for_key, true); | |
561 | return; | |
562 | } | |
563 | ||
564 | if (tstc()) { | |
565 | ret = efi_cin_read_key(&next_key); | |
566 | if (ret == EFI_SUCCESS) { | |
567 | key_available = true; | |
568 | ||
569 | /* Queue the wait for key event */ | |
570 | efi_signal_event(efi_con_in.wait_for_key, true); | |
571 | } | |
572 | } | |
573 | } | |
574 | ||
575 | /** | |
576 | * efi_cin_reset() - drain the input buffer | |
577 | * | |
578 | * @this: instance of the EFI_SIMPLE_TEXT_INPUT_PROTOCOL | |
579 | * @extended_verification: allow for exhaustive verification | |
580 | * Return: status code | |
581 | * | |
582 | * This function implements the Reset service of the | |
583 | * EFI_SIMPLE_TEXT_INPUT_PROTOCOL. | |
584 | * | |
585 | * See the Unified Extensible Firmware Interface (UEFI) specification for | |
586 | * details. | |
587 | */ | |
588 | static efi_status_t EFIAPI efi_cin_reset | |
589 | (struct efi_simple_text_input_protocol *this, | |
590 | bool extended_verification) | |
591 | { | |
592 | efi_status_t ret = EFI_SUCCESS; | |
593 | ||
594 | EFI_ENTRY("%p, %d", this, extended_verification); | |
595 | ||
596 | /* Check parameters */ | |
597 | if (!this) { | |
598 | ret = EFI_INVALID_PARAMETER; | |
599 | goto out; | |
600 | } | |
601 | ||
602 | /* Empty input buffer */ | |
603 | while (tstc()) | |
604 | getc(); | |
605 | key_available = false; | |
606 | out: | |
607 | return EFI_EXIT(ret); | |
608 | } | |
609 | ||
610 | /** | |
611 | * efi_cin_reset() - drain the input buffer | |
612 | * | |
613 | * @this: instance of the EFI_SIMPLE_TEXT_INPUT_PROTOCOL | |
614 | * @key: key read from console | |
615 | * Return: status code | |
616 | * | |
617 | * This function implements the ReadKeyStroke service of the | |
618 | * EFI_SIMPLE_TEXT_INPUT_PROTOCOL. | |
619 | * | |
620 | * See the Unified Extensible Firmware Interface (UEFI) specification for | |
621 | * details. | |
622 | */ | |
623 | static efi_status_t EFIAPI efi_cin_read_key_stroke | |
624 | (struct efi_simple_text_input_protocol *this, | |
625 | struct efi_input_key *key) | |
626 | { | |
627 | efi_status_t ret = EFI_SUCCESS; | |
628 | ||
629 | EFI_ENTRY("%p, %p", this, key); | |
630 | ||
631 | /* Check parameters */ | |
632 | if (!this || !key) { | |
633 | ret = EFI_INVALID_PARAMETER; | |
634 | goto out; | |
635 | } | |
636 | ||
637 | /* We don't do interrupts, so check for timers cooperatively */ | |
638 | efi_timer_check(); | |
639 | ||
640 | /* Enable console input after ExitBootServices */ | |
641 | efi_cin_check(); | |
642 | ||
643 | if (!key_available) { | |
644 | ret = EFI_NOT_READY; | |
645 | goto out; | |
646 | } | |
647 | *key = next_key; | |
648 | key_available = false; | |
649 | efi_con_in.wait_for_key->is_signaled = false; | |
650 | out: | |
651 | return EFI_EXIT(ret); | |
c1311ad4 AG |
652 | } |
653 | ||
3e603ec7 | 654 | struct efi_simple_text_input_protocol efi_con_in = { |
c1311ad4 AG |
655 | .reset = efi_cin_reset, |
656 | .read_key_stroke = efi_cin_read_key_stroke, | |
657 | .wait_for_key = NULL, | |
658 | }; | |
91be9a77 HS |
659 | |
660 | static struct efi_event *console_timer_event; | |
661 | ||
9bc9664d | 662 | /* |
0dfd13a4 | 663 | * efi_console_timer_notify() - notify the console timer event |
9bc9664d | 664 | * |
0dfd13a4 HS |
665 | * @event: console timer event |
666 | * @context: not used | |
9bc9664d | 667 | */ |
ff925938 HS |
668 | static void EFIAPI efi_console_timer_notify(struct efi_event *event, |
669 | void *context) | |
91be9a77 HS |
670 | { |
671 | EFI_ENTRY("%p, %p", event, context); | |
0dfd13a4 HS |
672 | efi_cin_check(); |
673 | EFI_EXIT(EFI_SUCCESS); | |
674 | } | |
9bc9664d | 675 | |
0dfd13a4 HS |
676 | /** |
677 | * efi_key_notify() - notify the wait for key event | |
678 | * | |
679 | * @event: wait for key event | |
680 | * @context: not used | |
681 | */ | |
682 | static void EFIAPI efi_key_notify(struct efi_event *event, void *context) | |
683 | { | |
684 | EFI_ENTRY("%p, %p", event, context); | |
685 | efi_cin_check(); | |
91be9a77 HS |
686 | EFI_EXIT(EFI_SUCCESS); |
687 | } | |
688 | ||
0dfd13a4 HS |
689 | /** |
690 | * efi_console_register() - install the console protocols | |
691 | * | |
692 | * This function is called from do_bootefi_exec(). | |
693 | */ | |
91be9a77 HS |
694 | int efi_console_register(void) |
695 | { | |
696 | efi_status_t r; | |
ebb4dd5b HS |
697 | struct efi_object *efi_console_output_obj; |
698 | struct efi_object *efi_console_input_obj; | |
a17e62cc | 699 | |
a4aa7bef HS |
700 | /* Set up mode information */ |
701 | query_console_size(); | |
702 | ||
ebb4dd5b | 703 | /* Create handles */ |
2074f700 | 704 | r = efi_create_handle((efi_handle_t *)&efi_console_output_obj); |
ebb4dd5b HS |
705 | if (r != EFI_SUCCESS) |
706 | goto out_of_memory; | |
40e3e757 | 707 | |
ebb4dd5b HS |
708 | r = efi_add_protocol(efi_console_output_obj->handle, |
709 | &efi_guid_text_output_protocol, &efi_con_out); | |
710 | if (r != EFI_SUCCESS) | |
711 | goto out_of_memory; | |
40e3e757 AG |
712 | systab.con_out_handle = efi_console_output_obj->handle; |
713 | systab.stderr_handle = efi_console_output_obj->handle; | |
714 | ||
2074f700 | 715 | r = efi_create_handle((efi_handle_t *)&efi_console_input_obj); |
ebb4dd5b HS |
716 | if (r != EFI_SUCCESS) |
717 | goto out_of_memory; | |
40e3e757 | 718 | |
ebb4dd5b HS |
719 | r = efi_add_protocol(efi_console_input_obj->handle, |
720 | &efi_guid_text_input_protocol, &efi_con_in); | |
721 | if (r != EFI_SUCCESS) | |
722 | goto out_of_memory; | |
40e3e757 | 723 | systab.con_in_handle = efi_console_input_obj->handle; |
a17e62cc | 724 | |
ebb4dd5b | 725 | /* Create console events */ |
b095f3c8 HS |
726 | r = efi_create_event(EVT_NOTIFY_WAIT, TPL_CALLBACK, efi_key_notify, |
727 | NULL, NULL, &efi_con_in.wait_for_key); | |
91be9a77 HS |
728 | if (r != EFI_SUCCESS) { |
729 | printf("ERROR: Failed to register WaitForKey event\n"); | |
730 | return r; | |
731 | } | |
732 | r = efi_create_event(EVT_TIMER | EVT_NOTIFY_SIGNAL, TPL_CALLBACK, | |
b095f3c8 | 733 | efi_console_timer_notify, NULL, NULL, |
91be9a77 HS |
734 | &console_timer_event); |
735 | if (r != EFI_SUCCESS) { | |
736 | printf("ERROR: Failed to register console event\n"); | |
737 | return r; | |
738 | } | |
739 | /* 5000 ns cycle is sufficient for 2 MBaud */ | |
740 | r = efi_set_timer(console_timer_event, EFI_TIMER_PERIODIC, 50); | |
741 | if (r != EFI_SUCCESS) | |
742 | printf("ERROR: Failed to set console timer\n"); | |
743 | return r; | |
ebb4dd5b | 744 | out_of_memory: |
14d103bb | 745 | printf("ERROR: Out of memory\n"); |
ebb4dd5b | 746 | return r; |
91be9a77 | 747 | } |