efi_loader: always check parameters in efi_cout_query_mode()
[platform/kernel/u-boot.git] / lib / efi_loader / efi_console.c
1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  *  EFI application console interface
4  *
5  *  Copyright (c) 2016 Alexander Graf
6  */
7
8 #include <common.h>
9 #include <charset.h>
10 #include <dm/device.h>
11 #include <efi_loader.h>
12 #include <stdio_dev.h>
13 #include <video_console.h>
14
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
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;
49
50 #define cESC '\x1b'
51 #define ESC "\x1b"
52
53 /* Default to mode 0 */
54 static struct simple_text_output_mode efi_con_mode = {
55         .max_mode = 1,
56         .mode = 0,
57         .attribute = 0,
58         .cursor_column = 0,
59         .cursor_row = 0,
60         .cursor_visible = 1,
61 };
62
63 static int term_read_reply(int *n, int maxnum, char end_char)
64 {
65         char c;
66         int i = 0;
67
68         c = getc();
69         if (c != cESC)
70                 return -1;
71         c = getc();
72         if (c != '[')
73                 return -1;
74
75         n[0] = 0;
76         while (1) {
77                 c = getc();
78                 if (c == ';') {
79                         i++;
80                         if (i >= maxnum)
81                                 return -1;
82                         n[i] = 0;
83                         continue;
84                 } else if (c == end_char) {
85                         break;
86                 } else if (c > '9' || c < '0') {
87                         return -1;
88                 }
89
90                 /* Read one more decimal position */
91                 n[i] *= 10;
92                 n[i] += c - '0';
93         }
94
95         return 0;
96 }
97
98 static efi_status_t EFIAPI efi_cout_reset(
99                         struct efi_simple_text_output_protocol *this,
100                         char extended_verification)
101 {
102         EFI_ENTRY("%p, %d", this, extended_verification);
103         return EFI_EXIT(EFI_UNSUPPORTED);
104 }
105
106 static efi_status_t EFIAPI efi_cout_output_string(
107                         struct efi_simple_text_output_protocol *this,
108                         const efi_string_t string)
109 {
110         struct simple_text_output_mode *con = &efi_con_mode;
111         struct cout_mode *mode = &efi_cout_modes[con->mode];
112
113         EFI_ENTRY("%p, %p", this, string);
114
115         unsigned int n16 = utf16_strlen(string);
116         char buf[MAX_UTF8_PER_UTF16 * n16 + 1];
117         u16 *p;
118
119         *utf16_to_utf8((u8 *)buf, string, n16) = '\0';
120
121         fputs(stdout, buf);
122
123         /*
124          * Update the cursor position.
125          *
126          * The UEFI spec provides advance rules for U+0000, U+0008, U+000A,
127          * and U000D. All other characters, including control characters
128          * U+0007 (bel) and U+0009 (tab), have to increase the column by one.
129          */
130         for (p = string; *p; ++p) {
131                 switch (*p) {
132                 case '\b':      /* U+0008, backspace */
133                         con->cursor_column = max(0, con->cursor_column - 1);
134                         break;
135                 case '\n':      /* U+000A, newline */
136                         con->cursor_column = 0;
137                         con->cursor_row++;
138                         break;
139                 case '\r':      /* U+000D, carriage-return */
140                         con->cursor_column = 0;
141                         break;
142                 case 0xd800 ... 0xdbff:
143                         /*
144                          * Ignore high surrogates, we do not want to count a
145                          * Unicode character twice.
146                          */
147                         break;
148                 default:
149                         con->cursor_column++;
150                         break;
151                 }
152                 if (con->cursor_column >= mode->columns) {
153                         con->cursor_column = 0;
154                         con->cursor_row++;
155                 }
156                 con->cursor_row = min(con->cursor_row, (s32)mode->rows - 1);
157         }
158
159         return EFI_EXIT(EFI_SUCCESS);
160 }
161
162 static efi_status_t EFIAPI efi_cout_test_string(
163                         struct efi_simple_text_output_protocol *this,
164                         const efi_string_t string)
165 {
166         EFI_ENTRY("%p, %p", this, string);
167         return EFI_EXIT(EFI_SUCCESS);
168 }
169
170 static bool cout_mode_matches(struct cout_mode *mode, int rows, int cols)
171 {
172         if (!mode->present)
173                 return false;
174
175         return (mode->rows == rows) && (mode->columns == cols);
176 }
177
178 static int query_console_serial(int *rows, int *cols)
179 {
180         /* Ask the terminal about its size */
181         int n[3];
182         u64 timeout;
183
184         /* Empty input buffer */
185         while (tstc())
186                 getc();
187
188         printf(ESC"[18t");
189
190         /* Check if we have a terminal that understands */
191         timeout = timer_get_us() + 1000000;
192         while (!tstc())
193                 if (timer_get_us() > timeout)
194                         return -1;
195
196         /* Read {depth,rows,cols} */
197         if (term_read_reply(n, 3, 't'))
198                 return -1;
199
200         *cols = n[2];
201         *rows = n[1];
202
203         return 0;
204 }
205
206 /*
207  * Update the mode table.
208  *
209  * By default the only mode available is 80x25. If the console has at least 50
210  * lines, enable mode 80x50. If we can query the console size and it is neither
211  * 80x25 nor 80x50, set it as an additional mode.
212  */
213 static void query_console_size(void)
214 {
215         const char *stdout_name = env_get("stdout");
216         int rows, cols;
217
218         if (stdout_name && !strcmp(stdout_name, "vidconsole") &&
219             IS_ENABLED(CONFIG_DM_VIDEO)) {
220                 struct stdio_dev *stdout_dev =
221                         stdio_get_by_name("vidconsole");
222                 struct udevice *dev = stdout_dev->priv;
223                 struct vidconsole_priv *priv =
224                         dev_get_uclass_priv(dev);
225                 rows = priv->rows;
226                 cols = priv->cols;
227         } else if (query_console_serial(&rows, &cols)) {
228                 return;
229         }
230
231         /* Test if we can have Mode 1 */
232         if (cols >= 80 && rows >= 50) {
233                 efi_cout_modes[1].present = 1;
234                 efi_con_mode.max_mode = 2;
235         }
236
237         /*
238          * Install our mode as mode 2 if it is different
239          * than mode 0 or 1 and set it as the currently selected mode
240          */
241         if (!cout_mode_matches(&efi_cout_modes[0], rows, cols) &&
242             !cout_mode_matches(&efi_cout_modes[1], rows, cols)) {
243                 efi_cout_modes[EFI_COUT_MODE_2].columns = cols;
244                 efi_cout_modes[EFI_COUT_MODE_2].rows = rows;
245                 efi_cout_modes[EFI_COUT_MODE_2].present = 1;
246                 efi_con_mode.max_mode = EFI_MAX_COUT_MODE;
247                 efi_con_mode.mode = EFI_COUT_MODE_2;
248         }
249 }
250
251 static efi_status_t EFIAPI efi_cout_query_mode(
252                         struct efi_simple_text_output_protocol *this,
253                         unsigned long mode_number, unsigned long *columns,
254                         unsigned long *rows)
255 {
256         EFI_ENTRY("%p, %ld, %p, %p", this, mode_number, columns, rows);
257
258         if (mode_number >= efi_con_mode.max_mode)
259                 return EFI_EXIT(EFI_UNSUPPORTED);
260
261         if (efi_cout_modes[mode_number].present != 1)
262                 return EFI_EXIT(EFI_UNSUPPORTED);
263
264         if (columns)
265                 *columns = efi_cout_modes[mode_number].columns;
266         if (rows)
267                 *rows = efi_cout_modes[mode_number].rows;
268
269         return EFI_EXIT(EFI_SUCCESS);
270 }
271
272 static efi_status_t EFIAPI efi_cout_set_mode(
273                         struct efi_simple_text_output_protocol *this,
274                         unsigned long mode_number)
275 {
276         EFI_ENTRY("%p, %ld", this, mode_number);
277
278
279         if (mode_number > efi_con_mode.max_mode)
280                 return EFI_EXIT(EFI_UNSUPPORTED);
281
282         efi_con_mode.mode = mode_number;
283         efi_con_mode.cursor_column = 0;
284         efi_con_mode.cursor_row = 0;
285
286         return EFI_EXIT(EFI_SUCCESS);
287 }
288
289 static const struct {
290         unsigned int fg;
291         unsigned int bg;
292 } color[] = {
293         { 30, 40 },     /* 0: black */
294         { 34, 44 },     /* 1: blue */
295         { 32, 42 },     /* 2: green */
296         { 36, 46 },     /* 3: cyan */
297         { 31, 41 },     /* 4: red */
298         { 35, 45 },     /* 5: magenta */
299         { 33, 43 },     /* 6: brown, map to yellow as edk2 does*/
300         { 37, 47 },     /* 7: light grey, map to white */
301 };
302
303 /* See EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL.SetAttribute(). */
304 static efi_status_t EFIAPI efi_cout_set_attribute(
305                         struct efi_simple_text_output_protocol *this,
306                         unsigned long attribute)
307 {
308         unsigned int bold = EFI_ATTR_BOLD(attribute);
309         unsigned int fg = EFI_ATTR_FG(attribute);
310         unsigned int bg = EFI_ATTR_BG(attribute);
311
312         EFI_ENTRY("%p, %lx", this, attribute);
313
314         if (attribute)
315                 printf(ESC"[%u;%u;%um", bold, color[fg].fg, color[bg].bg);
316         else
317                 printf(ESC"[0;37;40m");
318
319         return EFI_EXIT(EFI_SUCCESS);
320 }
321
322 static efi_status_t EFIAPI efi_cout_clear_screen(
323                         struct efi_simple_text_output_protocol *this)
324 {
325         EFI_ENTRY("%p", this);
326
327         printf(ESC"[2J");
328
329         return EFI_EXIT(EFI_SUCCESS);
330 }
331
332 static efi_status_t EFIAPI efi_cout_set_cursor_position(
333                         struct efi_simple_text_output_protocol *this,
334                         unsigned long column, unsigned long row)
335 {
336         EFI_ENTRY("%p, %ld, %ld", this, column, row);
337
338         printf(ESC"[%d;%df", (int)row, (int)column);
339         efi_con_mode.cursor_column = column;
340         efi_con_mode.cursor_row = row;
341
342         return EFI_EXIT(EFI_SUCCESS);
343 }
344
345 static efi_status_t EFIAPI efi_cout_enable_cursor(
346                         struct efi_simple_text_output_protocol *this,
347                         bool enable)
348 {
349         EFI_ENTRY("%p, %d", this, enable);
350
351         printf(ESC"[?25%c", enable ? 'h' : 'l');
352
353         return EFI_EXIT(EFI_SUCCESS);
354 }
355
356 struct efi_simple_text_output_protocol efi_con_out = {
357         .reset = efi_cout_reset,
358         .output_string = efi_cout_output_string,
359         .test_string = efi_cout_test_string,
360         .query_mode = efi_cout_query_mode,
361         .set_mode = efi_cout_set_mode,
362         .set_attribute = efi_cout_set_attribute,
363         .clear_screen = efi_cout_clear_screen,
364         .set_cursor_position = efi_cout_set_cursor_position,
365         .enable_cursor = efi_cout_enable_cursor,
366         .mode = (void*)&efi_con_mode,
367 };
368
369 static efi_status_t EFIAPI efi_cin_reset(
370                         struct efi_simple_input_interface *this,
371                         bool extended_verification)
372 {
373         EFI_ENTRY("%p, %d", this, extended_verification);
374         return EFI_EXIT(EFI_UNSUPPORTED);
375 }
376
377 /*
378  * Analyze modifiers (shift, alt, ctrl) for function keys.
379  * This gets called when we have already parsed CSI.
380  *
381  * @modifiers:  bitmask (shift, alt, ctrl)
382  * @return:     the unmodified code
383  */
384 static char skip_modifiers(int *modifiers)
385 {
386         char c, mod = 0, ret = 0;
387
388         c = getc();
389
390         if (c != ';') {
391                 ret = c;
392                 if (c == '~')
393                         goto out;
394                 c = getc();
395         }
396         for (;;) {
397                 switch (c) {
398                 case '0'...'9':
399                         mod *= 10;
400                         mod += c - '0';
401                 /* fall through */
402                 case ';':
403                         c = getc();
404                         break;
405                 default:
406                         goto out;
407                 }
408         }
409 out:
410         if (mod)
411                 --mod;
412         if (modifiers)
413                 *modifiers = mod;
414         if (!ret)
415                 ret = c;
416         return ret;
417 }
418
419 static efi_status_t EFIAPI efi_cin_read_key_stroke(
420                         struct efi_simple_input_interface *this,
421                         struct efi_input_key *key)
422 {
423         struct efi_input_key pressed_key = {
424                 .scan_code = 0,
425                 .unicode_char = 0,
426         };
427         char ch;
428
429         EFI_ENTRY("%p, %p", this, key);
430
431         /* We don't do interrupts, so check for timers cooperatively */
432         efi_timer_check();
433
434         if (!tstc()) {
435                 /* No key pressed */
436                 return EFI_EXIT(EFI_NOT_READY);
437         }
438
439         ch = getc();
440         if (ch == cESC) {
441                 /*
442                  * Xterm Control Sequences
443                  * https://www.xfree86.org/4.8.0/ctlseqs.html
444                  */
445                 ch = getc();
446                 switch (ch) {
447                 case cESC: /* ESC */
448                         pressed_key.scan_code = 23;
449                         break;
450                 case 'O': /* F1 - F4 */
451                         ch = getc();
452                         /* skip modifiers */
453                         if (ch <= '9')
454                                 ch = getc();
455                         pressed_key.scan_code = ch - 'P' + 11;
456                         break;
457                 case 'a'...'z':
458                         ch = ch - 'a';
459                         break;
460                 case '[':
461                         ch = getc();
462                         switch (ch) {
463                         case 'A'...'D': /* up, down right, left */
464                                 pressed_key.scan_code = ch - 'A' + 1;
465                                 break;
466                         case 'F': /* End */
467                                 pressed_key.scan_code = 6;
468                                 break;
469                         case 'H': /* Home */
470                                 pressed_key.scan_code = 5;
471                                 break;
472                         case '1':
473                                 ch = skip_modifiers(NULL);
474                                 switch (ch) {
475                                 case '1'...'5': /* F1 - F5 */
476                                         pressed_key.scan_code = ch - '1' + 11;
477                                         break;
478                                 case '7'...'9': /* F6 - F8 */
479                                         pressed_key.scan_code = ch - '7' + 16;
480                                         break;
481                                 case 'A'...'D': /* up, down right, left */
482                                         pressed_key.scan_code = ch - 'A' + 1;
483                                         break;
484                                 case 'F':
485                                         pressed_key.scan_code = 6; /* End */
486                                         break;
487                                 case 'H':
488                                         pressed_key.scan_code = 5; /* Home */
489                                         break;
490                                 }
491                                 break;
492                         case '2':
493                                 ch = skip_modifiers(NULL);
494                                 switch (ch) {
495                                 case '0'...'1': /* F9 - F10 */
496                                         pressed_key.scan_code = ch - '0' + 19;
497                                         break;
498                                 case '3'...'4': /* F11 - F12 */
499                                         pressed_key.scan_code = ch - '3' + 21;
500                                         break;
501                                 case '~': /* INS */
502                                         pressed_key.scan_code = 7;
503                                         break;
504                                 }
505                                 break;
506                         case '3': /* DEL */
507                                 pressed_key.scan_code = 8;
508                                 skip_modifiers(NULL);
509                                 break;
510                         case '5': /* PG UP */
511                                 pressed_key.scan_code = 9;
512                                 skip_modifiers(NULL);
513                                 break;
514                         case '6': /* PG DOWN */
515                                 pressed_key.scan_code = 10;
516                                 skip_modifiers(NULL);
517                                 break;
518                         }
519                         break;
520                 }
521         } else if (ch == 0x7f) {
522                 /* Backspace */
523                 ch = 0x08;
524         }
525         if (!pressed_key.scan_code)
526                 pressed_key.unicode_char = ch;
527         *key = pressed_key;
528
529         return EFI_EXIT(EFI_SUCCESS);
530 }
531
532 struct efi_simple_input_interface efi_con_in = {
533         .reset = efi_cin_reset,
534         .read_key_stroke = efi_cin_read_key_stroke,
535         .wait_for_key = NULL,
536 };
537
538 static struct efi_event *console_timer_event;
539
540 static void EFIAPI efi_key_notify(struct efi_event *event, void *context)
541 {
542 }
543
544 /*
545  * Notification function of the console timer event.
546  *
547  * event:       console timer event
548  * context:     not used
549  */
550 static void EFIAPI efi_console_timer_notify(struct efi_event *event,
551                                             void *context)
552 {
553         EFI_ENTRY("%p, %p", event, context);
554
555         /* Check if input is available */
556         if (tstc()) {
557                 /* Queue the wait for key event */
558                 efi_con_in.wait_for_key->is_signaled = true;
559                 efi_signal_event(efi_con_in.wait_for_key, true);
560         }
561         EFI_EXIT(EFI_SUCCESS);
562 }
563
564 /* This gets called from do_bootefi_exec(). */
565 int efi_console_register(void)
566 {
567         efi_status_t r;
568         struct efi_object *efi_console_output_obj;
569         struct efi_object *efi_console_input_obj;
570
571         /* Set up mode information */
572         query_console_size();
573
574         /* Create handles */
575         r = efi_create_handle((efi_handle_t *)&efi_console_output_obj);
576         if (r != EFI_SUCCESS)
577                 goto out_of_memory;
578         r = efi_add_protocol(efi_console_output_obj->handle,
579                              &efi_guid_text_output_protocol, &efi_con_out);
580         if (r != EFI_SUCCESS)
581                 goto out_of_memory;
582         r = efi_create_handle((efi_handle_t *)&efi_console_input_obj);
583         if (r != EFI_SUCCESS)
584                 goto out_of_memory;
585         r = efi_add_protocol(efi_console_input_obj->handle,
586                              &efi_guid_text_input_protocol, &efi_con_in);
587         if (r != EFI_SUCCESS)
588                 goto out_of_memory;
589
590         /* Create console events */
591         r = efi_create_event(EVT_NOTIFY_WAIT, TPL_CALLBACK, efi_key_notify,
592                              NULL, NULL, &efi_con_in.wait_for_key);
593         if (r != EFI_SUCCESS) {
594                 printf("ERROR: Failed to register WaitForKey event\n");
595                 return r;
596         }
597         r = efi_create_event(EVT_TIMER | EVT_NOTIFY_SIGNAL, TPL_CALLBACK,
598                              efi_console_timer_notify, NULL, NULL,
599                              &console_timer_event);
600         if (r != EFI_SUCCESS) {
601                 printf("ERROR: Failed to register console event\n");
602                 return r;
603         }
604         /* 5000 ns cycle is sufficient for 2 MBaud */
605         r = efi_set_timer(console_timer_event, EFI_TIMER_PERIODIC, 50);
606         if (r != EFI_SUCCESS)
607                 printf("ERROR: Failed to set console timer\n");
608         return r;
609 out_of_memory:
610         printf("ERROR: Out of meemory\n");
611         return r;
612 }