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