]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - stand/efi/libefi/efi_console.c
MFC r336431: stand: Fix UEFI console scrolling
[FreeBSD/FreeBSD.git] / stand / efi / libefi / efi_console.c
1 /*-
2  * Copyright (c) 2000 Doug Rabson
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  */
26
27 #include <sys/cdefs.h>
28 __FBSDID("$FreeBSD$");
29
30 #include <efi.h>
31 #include <efilib.h>
32
33 #include "bootstrap.h"
34
35 static SIMPLE_TEXT_OUTPUT_INTERFACE     *conout;
36 static SIMPLE_INPUT_INTERFACE           *conin;
37
38 #ifdef TERM_EMU
39 #define DEFAULT_FGCOLOR EFI_LIGHTGRAY
40 #define DEFAULT_BGCOLOR EFI_BLACK
41
42 #define MAXARGS 8
43 static int args[MAXARGS], argc;
44 static int fg_c, bg_c, curx, cury;
45 static int esc;
46
47 void get_pos(int *x, int *y);
48 void curs_move(int *_x, int *_y, int x, int y);
49 static void CL(int);
50 void HO(void);
51 void end_term(void);
52 #endif
53
54 static EFI_INPUT_KEY key_cur;
55 static int key_pending;
56
57 static void efi_cons_probe(struct console *);
58 static int efi_cons_init(int);
59 void efi_cons_putchar(int);
60 int efi_cons_getchar(void);
61 void efi_cons_efiputchar(int);
62 int efi_cons_poll(void);
63
64 struct console efi_console = {
65         "efi",
66         "EFI console",
67         C_WIDEOUT,
68         efi_cons_probe,
69         efi_cons_init,
70         efi_cons_putchar,
71         efi_cons_getchar,
72         efi_cons_poll
73 };
74
75 #ifdef TERM_EMU
76
77 /* Get cursor position. */
78 void
79 get_pos(int *x, int *y)
80 {
81         *x = conout->Mode->CursorColumn;
82         *y = conout->Mode->CursorRow;
83 }
84
85 /* Move cursor to x rows and y cols (0-based). */
86 void
87 curs_move(int *_x, int *_y, int x, int y)
88 {
89         conout->SetCursorPosition(conout, x, y);
90         if (_x != NULL)
91                 *_x = conout->Mode->CursorColumn;
92         if (_y != NULL)
93                 *_y = conout->Mode->CursorRow;
94 }
95
96 /* Clear internal state of the terminal emulation code. */
97 void
98 end_term(void)
99 {
100         esc = 0;
101         argc = -1;
102 }
103
104 #endif
105
106 static void
107 efi_cons_probe(struct console *cp)
108 {
109         conout = ST->ConOut;
110         conin = ST->ConIn;
111         cp->c_flags |= C_PRESENTIN | C_PRESENTOUT;
112 }
113
114 static int
115 efi_cons_init(int arg)
116 {
117 #ifdef TERM_EMU
118         conout->SetAttribute(conout, EFI_TEXT_ATTR(DEFAULT_FGCOLOR,
119             DEFAULT_BGCOLOR));
120         end_term();
121         get_pos(&curx, &cury);
122         curs_move(&curx, &cury, curx, cury);
123         fg_c = DEFAULT_FGCOLOR;
124         bg_c = DEFAULT_BGCOLOR;
125 #endif
126         conout->EnableCursor(conout, TRUE);
127         return 0;
128 }
129
130 static void
131 efi_cons_rawputchar(int c)
132 {
133         int i;
134         UINTN x, y;
135         conout->QueryMode(conout, conout->Mode->Mode, &x, &y);
136
137         if (c == '\t')
138                 /* XXX lame tab expansion */
139                 for (i = 0; i < 8; i++)
140                         efi_cons_rawputchar(' ');
141         else {
142 #ifndef TERM_EMU
143                 if (c == '\n')
144                         efi_cons_efiputchar('\r');
145                 efi_cons_efiputchar(c);
146 #else
147                 switch (c) {
148                 case '\r':
149                         curx = 0;
150                         efi_cons_efiputchar('\r');
151                         return;
152                 case '\n':
153                         efi_cons_efiputchar('\n');
154                         efi_cons_efiputchar('\r');
155                         cury++;
156                         if (cury >= y)
157                                 cury--;
158                         curx = 0;
159                         return;
160                 case '\b':
161                         if (curx > 0) {
162                                 efi_cons_efiputchar('\b');
163                                 curx--;
164                         }
165                         return;
166                 default:
167                         efi_cons_efiputchar(c);
168                         curx++;
169                         if (curx > x-1) {
170                                 curx = 0;
171                                 cury++;
172                         }
173                         if (cury > y-1) {
174                                 curx = 0;
175                                 cury--;
176                         }
177                 }
178 #endif
179         }
180 }
181
182 #ifdef TERM_EMU
183 /* Gracefully exit ESC-sequence processing in case of misunderstanding. */
184 static void
185 bail_out(int c)
186 {
187         char buf[16], *ch;
188         int i;
189
190         if (esc) {
191                 efi_cons_rawputchar('\033');
192                 if (esc != '\033')
193                         efi_cons_rawputchar(esc);
194                 for (i = 0; i <= argc; ++i) {
195                         sprintf(buf, "%d", args[i]);
196                         ch = buf;
197                         while (*ch)
198                                 efi_cons_rawputchar(*ch++);
199                 }
200         }
201         efi_cons_rawputchar(c);
202         end_term();
203 }
204
205 /* Clear display from current position to end of screen. */
206 static void
207 CD(void) {
208         int i;
209         UINTN x, y;
210
211         get_pos(&curx, &cury);
212         if (curx == 0 && cury == 0) {
213                 conout->ClearScreen(conout);
214                 end_term();
215                 return;
216         }
217
218         conout->QueryMode(conout, conout->Mode->Mode, &x, &y);
219         CL(0);  /* clear current line from cursor to end */
220         for (i = cury + 1; i < y-1; i++) {
221                 curs_move(NULL, NULL, 0, i);
222                 CL(0);
223         }
224         curs_move(NULL, NULL, curx, cury);
225         end_term();
226 }
227
228 /*
229  * Absolute cursor move to args[0] rows and args[1] columns
230  * (the coordinates are 1-based).
231  */
232 static void
233 CM(void)
234 {
235         if (args[0] > 0)
236                 args[0]--;
237         if (args[1] > 0)
238                 args[1]--;
239         curs_move(&curx, &cury, args[1], args[0]);
240         end_term();
241 }
242
243 /* Home cursor (left top corner), also called from mode command. */
244 void
245 HO(void)
246 {
247         argc = 1;
248         args[0] = args[1] = 1;
249         CM();
250 }
251
252 /* Clear line from current position to end of line */
253 static void
254 CL(int direction)
255 {
256         int i, len;
257         UINTN x, y;
258         CHAR16 *line;
259
260         conout->QueryMode(conout, conout->Mode->Mode, &x, &y);
261         switch (direction) {
262         case 0:         /* from cursor to end */
263                 len = x - curx + 1;
264                 break;
265         case 1:         /* from beginning to cursor */
266                 len = curx;
267                 break;
268         case 2:         /* entire line */
269                 len = x;
270                 break;
271         default:        /* NOTREACHED */
272                 __unreachable();
273         }
274
275         if (cury == y - 1)
276                 len--;
277
278         line = malloc(len * sizeof (CHAR16));
279         if (line == NULL) {
280                 printf("out of memory\n");
281                 return;
282         }
283         for (i = 0; i < len; i++)
284                 line[i] = ' ';
285         line[len-1] = 0;
286
287         if (direction != 0)
288                 curs_move(NULL, NULL, 0, cury);
289
290         conout->OutputString(conout, line);
291         /* restore cursor position */
292         curs_move(NULL, NULL, curx, cury);
293         free(line);
294         end_term();
295 }
296
297 static void
298 get_arg(int c)
299 {
300         if (argc < 0)
301                 argc = 0;
302         args[argc] *= 10;
303         args[argc] += c - '0';
304 }
305
306 /* Emulate basic capabilities of cons25 terminal */
307 static void
308 efi_term_emu(int c)
309 {
310         static int ansi_col[] = {
311                 0, 4, 2, 6, 1, 5, 3, 7
312         };
313         int t, i;
314
315         switch (esc) {
316         case 0:
317                 switch (c) {
318                 case '\033':
319                         esc = c;
320                         break;
321                 default:
322                         efi_cons_rawputchar(c);
323                         break;
324                 }
325                 break;
326         case '\033':
327                 switch (c) {
328                 case '[':
329                         esc = c;
330                         args[0] = 0;
331                         argc = -1;
332                         break;
333                 default:
334                         bail_out(c);
335                         break;
336                 }
337                 break;
338         case '[':
339                 switch (c) {
340                 case ';':
341                         if (argc < 0)
342                                 argc = 0;
343                         else if (argc + 1 >= MAXARGS)
344                                 bail_out(c);
345                         else
346                                 args[++argc] = 0;
347                         break;
348                 case 'H':               /* ho = \E[H */
349                         if (argc < 0)
350                                 HO();
351                         else if (argc == 1)
352                                 CM();
353                         else
354                                 bail_out(c);
355                         break;
356                 case 'J':               /* cd = \E[J */
357                         if (argc < 0)
358                                 CD();
359                         else
360                                 bail_out(c);
361                         break;
362                 case 'm':
363                         if (argc < 0) {
364                                 fg_c = DEFAULT_FGCOLOR;
365                                 bg_c = DEFAULT_BGCOLOR;
366                         }
367                         for (i = 0; i <= argc; ++i) {
368                                 switch (args[i]) {
369                                 case 0:         /* back to normal */
370                                         fg_c = DEFAULT_FGCOLOR;
371                                         bg_c = DEFAULT_BGCOLOR;
372                                         break;
373                                 case 1:         /* bold */
374                                         fg_c |= 0x8;
375                                         break;
376                                 case 4:         /* underline */
377                                 case 5:         /* blink */
378                                         bg_c |= 0x8;
379                                         break;
380                                 case 7:         /* reverse */
381                                         t = fg_c;
382                                         fg_c = bg_c;
383                                         bg_c = t;
384                                         break;
385                                 case 22:        /* normal intensity */
386                                         fg_c &= ~0x8;
387                                         break;
388                                 case 24:        /* not underline */
389                                 case 25:        /* not blinking */
390                                         bg_c &= ~0x8;
391                                         break;
392                                 case 30: case 31: case 32: case 33:
393                                 case 34: case 35: case 36: case 37:
394                                         fg_c = ansi_col[args[i] - 30];
395                                         break;
396                                 case 39:        /* normal */
397                                         fg_c = DEFAULT_FGCOLOR;
398                                         break;
399                                 case 40: case 41: case 42: case 43:
400                                 case 44: case 45: case 46: case 47:
401                                         bg_c = ansi_col[args[i] - 40];
402                                         break;
403                                 case 49:        /* normal */
404                                         bg_c = DEFAULT_BGCOLOR;
405                                         break;
406                                 }
407                         }
408                         conout->SetAttribute(conout, EFI_TEXT_ATTR(fg_c, bg_c));
409                         end_term();
410                         break;
411                 default:
412                         if (isdigit(c))
413                                 get_arg(c);
414                         else
415                                 bail_out(c);
416                         break;
417                 }
418                 break;
419         default:
420                 bail_out(c);
421                 break;
422         }
423 }
424 #else
425 void
426 HO(void)
427 {
428 }
429 #endif
430
431 void
432 efi_cons_putchar(int c)
433 {
434 #ifdef TERM_EMU
435         efi_term_emu(c);
436 #else
437         efi_cons_rawputchar(c);
438 #endif
439 }
440
441 int
442 efi_cons_getchar()
443 {
444         EFI_INPUT_KEY key;
445         EFI_STATUS status;
446         UINTN junk;
447
448         if (key_pending) {
449                 key = key_cur;
450                 key_pending = 0;
451         } else {
452                 /* Try to read a key stroke. We wait for one if none is pending. */
453                 status = conin->ReadKeyStroke(conin, &key);
454                 while (status == EFI_NOT_READY) {
455                         /* Some EFI implementation (u-boot for example) do not support WaitForKey */
456                         if (conin->WaitForKey != NULL)
457                                 BS->WaitForEvent(1, &conin->WaitForKey, &junk);
458                         status = conin->ReadKeyStroke(conin, &key);
459                 }
460         }
461
462         switch (key.ScanCode) {
463         case 0x17: /* ESC */
464                 return (0x1b);  /* esc */
465         }
466
467         /* this can return  */
468         return (key.UnicodeChar);
469 }
470
471 int
472 efi_cons_poll()
473 {
474         EFI_INPUT_KEY key;
475         EFI_STATUS status;
476
477         if (conin->WaitForKey == NULL) {
478                 if (key_pending)
479                         return (1);
480                 status = conin->ReadKeyStroke(conin, &key);
481                 if (status == EFI_SUCCESS) {
482                         key_cur = key;
483                         key_pending = 1;
484                 }
485                 return (key_pending);
486         }
487
488         /* This can clear the signaled state. */
489         return (BS->CheckEvent(conin->WaitForKey) == EFI_SUCCESS);
490 }
491
492 /* Plain direct access to EFI OutputString(). */
493 void
494 efi_cons_efiputchar(int c)
495 {
496         CHAR16 buf[2];
497
498         /*
499          * translate box chars to unicode
500          */
501         switch (c) {
502         /* single frame */
503         case 0xb3: buf[0] = BOXDRAW_VERTICAL; break;
504         case 0xbf: buf[0] = BOXDRAW_DOWN_LEFT; break;
505         case 0xc0: buf[0] = BOXDRAW_UP_RIGHT; break;
506         case 0xc4: buf[0] = BOXDRAW_HORIZONTAL; break;
507         case 0xda: buf[0] = BOXDRAW_DOWN_RIGHT; break;
508         case 0xd9: buf[0] = BOXDRAW_UP_LEFT; break;
509
510         /* double frame */
511         case 0xba: buf[0] = BOXDRAW_DOUBLE_VERTICAL; break;
512         case 0xbb: buf[0] = BOXDRAW_DOUBLE_DOWN_LEFT; break;
513         case 0xbc: buf[0] = BOXDRAW_DOUBLE_UP_LEFT; break;
514         case 0xc8: buf[0] = BOXDRAW_DOUBLE_UP_RIGHT; break;
515         case 0xc9: buf[0] = BOXDRAW_DOUBLE_DOWN_RIGHT; break;
516         case 0xcd: buf[0] = BOXDRAW_DOUBLE_HORIZONTAL; break;
517
518         default:
519                 buf[0] = c;
520         }
521         buf[1] = 0;     /* terminate string */
522
523         conout->OutputString(conout, buf);
524 }