]> git.sur5r.net Git - u-boot/blob - lib/efi_loader/efi_console.c
Merge git://www.denx.de/git/u-boot-imx
[u-boot] / lib / efi_loader / efi_console.c
1 /*
2  *  EFI application console interface
3  *
4  *  Copyright (c) 2016 Alexander Graf
5  *
6  *  SPDX-License-Identifier:     GPL-2.0+
7  */
8
9 #include <common.h>
10 #include <efi_loader.h>
11
12 static bool console_size_queried;
13
14 #define EFI_COUT_MODE_2 2
15 #define EFI_MAX_COUT_MODE 3
16
17 struct cout_mode {
18         unsigned long columns;
19         unsigned long rows;
20         int present;
21 };
22
23 static struct cout_mode efi_cout_modes[] = {
24         /* EFI Mode 0 is 80x25 and always present */
25         {
26                 .columns = 80,
27                 .rows = 25,
28                 .present = 1,
29         },
30         /* EFI Mode 1 is always 80x50 */
31         {
32                 .columns = 80,
33                 .rows = 50,
34                 .present = 0,
35         },
36         /* Value are unknown until we query the console */
37         {
38                 .columns = 0,
39                 .rows = 0,
40                 .present = 0,
41         },
42 };
43
44 const efi_guid_t efi_guid_console_control = CONSOLE_CONTROL_GUID;
45
46 #define cESC '\x1b'
47 #define ESC "\x1b"
48
49 static efi_status_t EFIAPI efi_cin_get_mode(
50                         struct efi_console_control_protocol *this,
51                         int *mode, char *uga_exists, char *std_in_locked)
52 {
53         EFI_ENTRY("%p, %p, %p, %p", this, mode, uga_exists, std_in_locked);
54
55         if (mode)
56                 *mode = EFI_CONSOLE_MODE_TEXT;
57         if (uga_exists)
58                 *uga_exists = 0;
59         if (std_in_locked)
60                 *std_in_locked = 0;
61
62         return EFI_EXIT(EFI_SUCCESS);
63 }
64
65 static efi_status_t EFIAPI efi_cin_set_mode(
66                         struct efi_console_control_protocol *this, int mode)
67 {
68         EFI_ENTRY("%p, %d", this, mode);
69         return EFI_EXIT(EFI_UNSUPPORTED);
70 }
71
72 static efi_status_t EFIAPI efi_cin_lock_std_in(
73                         struct efi_console_control_protocol *this,
74                         uint16_t *password)
75 {
76         EFI_ENTRY("%p, %p", this, password);
77         return EFI_EXIT(EFI_UNSUPPORTED);
78 }
79
80 const struct efi_console_control_protocol efi_console_control = {
81         .get_mode = efi_cin_get_mode,
82         .set_mode = efi_cin_set_mode,
83         .lock_std_in = efi_cin_lock_std_in,
84 };
85
86 /* Default to mode 0 */
87 static struct simple_text_output_mode efi_con_mode = {
88         .max_mode = 1,
89         .mode = 0,
90         .attribute = 0,
91         .cursor_column = 0,
92         .cursor_row = 0,
93         .cursor_visible = 1,
94 };
95
96 static int term_read_reply(int *n, int maxnum, char end_char)
97 {
98         char c;
99         int i = 0;
100
101         c = getc();
102         if (c != cESC)
103                 return -1;
104         c = getc();
105         if (c != '[')
106                 return -1;
107
108         n[0] = 0;
109         while (1) {
110                 c = getc();
111                 if (c == ';') {
112                         i++;
113                         if (i >= maxnum)
114                                 return -1;
115                         n[i] = 0;
116                         continue;
117                 } else if (c == end_char) {
118                         break;
119                 } else if (c > '9' || c < '0') {
120                         return -1;
121                 }
122
123                 /* Read one more decimal position */
124                 n[i] *= 10;
125                 n[i] += c - '0';
126         }
127
128         return 0;
129 }
130
131 static efi_status_t EFIAPI efi_cout_reset(
132                         struct efi_simple_text_output_protocol *this,
133                         char extended_verification)
134 {
135         EFI_ENTRY("%p, %d", this, extended_verification);
136         return EFI_EXIT(EFI_UNSUPPORTED);
137 }
138
139 static void print_unicode_in_utf8(u16 c)
140 {
141         char utf8[4] = { 0 };
142         char *b = utf8;
143
144         if (c < 0x80) {
145                 *(b++) = c;
146         } else if (c < 0x800) {
147                 *(b++) = 192 + c / 64;
148                 *(b++) = 128 + c % 64;
149         } else {
150                 *(b++) = 224 + c / 4096;
151                 *(b++) = 128 + c / 64 % 64;
152                 *(b++) = 128 + c % 64;
153         }
154
155         puts(utf8);
156 }
157
158 static efi_status_t EFIAPI efi_cout_output_string(
159                         struct efi_simple_text_output_protocol *this,
160                         const unsigned short *string)
161 {
162         struct cout_mode *mode;
163         u16 ch;
164
165         mode = &efi_cout_modes[efi_con_mode.mode];
166         EFI_ENTRY("%p, %p", this, string);
167         for (;(ch = *string); string++) {
168                 print_unicode_in_utf8(ch);
169                 efi_con_mode.cursor_column++;
170                 if (ch == '\n') {
171                         efi_con_mode.cursor_column = 1;
172                         efi_con_mode.cursor_row++;
173                 } else if (efi_con_mode.cursor_column > mode->columns) {
174                         efi_con_mode.cursor_column = 1;
175                         efi_con_mode.cursor_row++;
176                 }
177                 if (efi_con_mode.cursor_row > mode->rows)
178                         efi_con_mode.cursor_row = mode->rows;
179         }
180
181         return EFI_EXIT(EFI_SUCCESS);
182 }
183
184 static efi_status_t EFIAPI efi_cout_test_string(
185                         struct efi_simple_text_output_protocol *this,
186                         const unsigned short *string)
187 {
188         EFI_ENTRY("%p, %p", this, string);
189         return EFI_EXIT(EFI_SUCCESS);
190 }
191
192 static bool cout_mode_matches(struct cout_mode *mode, int rows, int cols)
193 {
194         if (!mode->present)
195                 return false;
196
197         return (mode->rows == rows) && (mode->columns == cols);
198 }
199
200 static efi_status_t EFIAPI efi_cout_query_mode(
201                         struct efi_simple_text_output_protocol *this,
202                         unsigned long mode_number, unsigned long *columns,
203                         unsigned long *rows)
204 {
205         EFI_ENTRY("%p, %ld, %p, %p", this, mode_number, columns, rows);
206
207         if (!console_size_queried) {
208                 /* Ask the terminal about its size */
209                 int n[3];
210                 int cols;
211                 int rows;
212                 u64 timeout;
213
214                 console_size_queried = true;
215
216                 /* Empty input buffer */
217                 while (tstc())
218                         getc();
219
220                 printf(ESC"[18t");
221
222                 /* Check if we have a terminal that understands */
223                 timeout = timer_get_us() + 1000000;
224                 while (!tstc())
225                         if (timer_get_us() > timeout)
226                                 goto out;
227
228                 /* Read {depth,rows,cols} */
229                 if (term_read_reply(n, 3, 't')) {
230                         goto out;
231                 }
232
233                 cols = n[2];
234                 rows = n[1];
235
236                 /* Test if we can have Mode 1 */
237                 if (cols >= 80 && rows >= 50) {
238                         efi_cout_modes[1].present = 1;
239                         efi_con_mode.max_mode = 2;
240                 }
241
242                 /*
243                  * Install our mode as mode 2 if it is different
244                  * than mode 0 or 1 and set it  as the currently selected mode
245                  */
246                 if (!cout_mode_matches(&efi_cout_modes[0], rows, cols) &&
247                     !cout_mode_matches(&efi_cout_modes[1], rows, cols)) {
248                         efi_cout_modes[EFI_COUT_MODE_2].columns = cols;
249                         efi_cout_modes[EFI_COUT_MODE_2].rows = rows;
250                         efi_cout_modes[EFI_COUT_MODE_2].present = 1;
251                         efi_con_mode.max_mode = EFI_MAX_COUT_MODE;
252                         efi_con_mode.mode = EFI_COUT_MODE_2;
253                 }
254         }
255
256         if (mode_number >= efi_con_mode.max_mode)
257                 return EFI_EXIT(EFI_UNSUPPORTED);
258
259         if (efi_cout_modes[mode_number].present != 1)
260                 return EFI_EXIT(EFI_UNSUPPORTED);
261
262 out:
263         if (columns)
264                 *columns = efi_cout_modes[mode_number].columns;
265         if (rows)
266                 *rows = efi_cout_modes[mode_number].rows;
267
268         return EFI_EXIT(EFI_SUCCESS);
269 }
270
271 static efi_status_t EFIAPI efi_cout_set_mode(
272                         struct efi_simple_text_output_protocol *this,
273                         unsigned long mode_number)
274 {
275         EFI_ENTRY("%p, %ld", this, mode_number);
276
277
278         if (mode_number > efi_con_mode.max_mode)
279                 return EFI_EXIT(EFI_UNSUPPORTED);
280
281         efi_con_mode.mode = mode_number;
282         efi_con_mode.cursor_column = 0;
283         efi_con_mode.cursor_row = 0;
284
285         return EFI_EXIT(EFI_SUCCESS);
286 }
287
288 static efi_status_t EFIAPI efi_cout_set_attribute(
289                         struct efi_simple_text_output_protocol *this,
290                         unsigned long attribute)
291 {
292         EFI_ENTRY("%p, %lx", this, attribute);
293
294         /* Just ignore attributes (colors) for now */
295         return EFI_EXIT(EFI_UNSUPPORTED);
296 }
297
298 static efi_status_t EFIAPI efi_cout_clear_screen(
299                         struct efi_simple_text_output_protocol *this)
300 {
301         EFI_ENTRY("%p", this);
302
303         printf(ESC"[2J");
304
305         return EFI_EXIT(EFI_SUCCESS);
306 }
307
308 static efi_status_t EFIAPI efi_cout_set_cursor_position(
309                         struct efi_simple_text_output_protocol *this,
310                         unsigned long column, unsigned long row)
311 {
312         EFI_ENTRY("%p, %ld, %ld", this, column, row);
313
314         printf(ESC"[%d;%df", (int)row, (int)column);
315         efi_con_mode.cursor_column = column;
316         efi_con_mode.cursor_row = row;
317
318         return EFI_EXIT(EFI_SUCCESS);
319 }
320
321 static efi_status_t EFIAPI efi_cout_enable_cursor(
322                         struct efi_simple_text_output_protocol *this,
323                         bool enable)
324 {
325         EFI_ENTRY("%p, %d", this, enable);
326
327         printf(ESC"[?25%c", enable ? 'h' : 'l');
328
329         return EFI_EXIT(EFI_SUCCESS);
330 }
331
332 const struct efi_simple_text_output_protocol efi_con_out = {
333         .reset = efi_cout_reset,
334         .output_string = efi_cout_output_string,
335         .test_string = efi_cout_test_string,
336         .query_mode = efi_cout_query_mode,
337         .set_mode = efi_cout_set_mode,
338         .set_attribute = efi_cout_set_attribute,
339         .clear_screen = efi_cout_clear_screen,
340         .set_cursor_position = efi_cout_set_cursor_position,
341         .enable_cursor = efi_cout_enable_cursor,
342         .mode = (void*)&efi_con_mode,
343 };
344
345 static efi_status_t EFIAPI efi_cin_reset(
346                         struct efi_simple_input_interface *this,
347                         bool extended_verification)
348 {
349         EFI_ENTRY("%p, %d", this, extended_verification);
350         return EFI_EXIT(EFI_UNSUPPORTED);
351 }
352
353 static efi_status_t EFIAPI efi_cin_read_key_stroke(
354                         struct efi_simple_input_interface *this,
355                         struct efi_input_key *key)
356 {
357         struct efi_input_key pressed_key = {
358                 .scan_code = 0,
359                 .unicode_char = 0,
360         };
361         char ch;
362
363         EFI_ENTRY("%p, %p", this, key);
364
365         /* We don't do interrupts, so check for timers cooperatively */
366         efi_timer_check();
367
368         if (!tstc()) {
369                 /* No key pressed */
370                 return EFI_EXIT(EFI_NOT_READY);
371         }
372
373         ch = getc();
374         if (ch == cESC) {
375                 /* Escape Sequence */
376                 ch = getc();
377                 switch (ch) {
378                 case cESC: /* ESC */
379                         pressed_key.scan_code = 23;
380                         break;
381                 case 'O': /* F1 - F4 */
382                         pressed_key.scan_code = getc() - 'P' + 11;
383                         break;
384                 case 'a'...'z':
385                         ch = ch - 'a';
386                         break;
387                 case '[':
388                         ch = getc();
389                         switch (ch) {
390                         case 'A'...'D': /* up, down right, left */
391                                 pressed_key.scan_code = ch - 'A' + 1;
392                                 break;
393                         case 'F': /* End */
394                                 pressed_key.scan_code = 6;
395                                 break;
396                         case 'H': /* Home */
397                                 pressed_key.scan_code = 5;
398                                 break;
399                         case '1': /* F5 - F8 */
400                                 pressed_key.scan_code = getc() - '0' + 11;
401                                 getc();
402                                 break;
403                         case '2': /* F9 - F12 */
404                                 pressed_key.scan_code = getc() - '0' + 19;
405                                 getc();
406                                 break;
407                         case '3': /* DEL */
408                                 pressed_key.scan_code = 8;
409                                 getc();
410                                 break;
411                         }
412                         break;
413                 }
414         } else if (ch == 0x7f) {
415                 /* Backspace */
416                 ch = 0x08;
417         }
418         pressed_key.unicode_char = ch;
419         *key = pressed_key;
420
421         return EFI_EXIT(EFI_SUCCESS);
422 }
423
424 struct efi_simple_input_interface efi_con_in = {
425         .reset = efi_cin_reset,
426         .read_key_stroke = efi_cin_read_key_stroke,
427         .wait_for_key = NULL,
428 };
429
430 static struct efi_event *console_timer_event;
431
432 static void EFIAPI efi_key_notify(struct efi_event *event, void *context)
433 {
434 }
435
436 static void EFIAPI efi_console_timer_notify(struct efi_event *event,
437                                             void *context)
438 {
439         EFI_ENTRY("%p, %p", event, context);
440         if (tstc())
441                 efi_signal_event(efi_con_in.wait_for_key);
442         EFI_EXIT(EFI_SUCCESS);
443 }
444
445
446 static struct efi_object efi_console_control_obj =
447         EFI_PROTOCOL_OBJECT(efi_guid_console_control, &efi_console_control);
448 static struct efi_object efi_console_output_obj =
449         EFI_PROTOCOL_OBJECT(EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL_GUID, &efi_con_out);
450 static struct efi_object efi_console_input_obj =
451         EFI_PROTOCOL_OBJECT(EFI_SIMPLE_TEXT_INPUT_PROTOCOL_GUID, &efi_con_in);
452
453 /* This gets called from do_bootefi_exec(). */
454 int efi_console_register(void)
455 {
456         efi_status_t r;
457
458         /* Hook up to the device list */
459         list_add_tail(&efi_console_control_obj.link, &efi_obj_list);
460         list_add_tail(&efi_console_output_obj.link, &efi_obj_list);
461         list_add_tail(&efi_console_input_obj.link, &efi_obj_list);
462
463         r = efi_create_event(EVT_NOTIFY_WAIT, TPL_CALLBACK,
464                              efi_key_notify, NULL, &efi_con_in.wait_for_key);
465         if (r != EFI_SUCCESS) {
466                 printf("ERROR: Failed to register WaitForKey event\n");
467                 return r;
468         }
469         r = efi_create_event(EVT_TIMER | EVT_NOTIFY_SIGNAL, TPL_CALLBACK,
470                              efi_console_timer_notify, NULL,
471                              &console_timer_event);
472         if (r != EFI_SUCCESS) {
473                 printf("ERROR: Failed to register console event\n");
474                 return r;
475         }
476         /* 5000 ns cycle is sufficient for 2 MBaud */
477         r = efi_set_timer(console_timer_event, EFI_TIMER_PERIODIC, 50);
478         if (r != EFI_SUCCESS)
479                 printf("ERROR: Failed to set console timer\n");
480         return r;
481 }