xref: /dragonfly/stand/boot/efi/libefi/efi_console.c (revision 479ab7f0492f2a51b48e8537e4f1dc686fc6014b)
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: head/sys/boot/efi/libefi/efi_console.c 293724 2016-01-12 02:17:39Z smh $");
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 void efi_cons_probe(struct console *);
55 static int efi_cons_init(int);
56 void efi_cons_putchar(int);
57 int efi_cons_getchar(void);
58 void efi_cons_efiputchar(int);
59 int efi_cons_poll(void);
60 
61 struct console efi_console = {
62           "efi",
63           "EFI console",
64           0,
65           efi_cons_probe,
66           efi_cons_init,
67           efi_cons_putchar,
68           efi_cons_getchar,
69           efi_cons_poll
70 };
71 
72 #ifdef TERM_EMU
73 
74 /* Get cursor position. */
75 void
get_pos(int * x,int * y)76 get_pos(int *x, int *y)
77 {
78           *x = conout->Mode->CursorColumn;
79           *y = conout->Mode->CursorRow;
80 }
81 
82 /* Move cursor to x rows and y cols (0-based). */
83 void
curs_move(int * _x,int * _y,int x,int y)84 curs_move(int *_x, int *_y, int x, int y)
85 {
86           conout->SetCursorPosition(conout, x, y);
87           if (_x != NULL)
88                     *_x = conout->Mode->CursorColumn;
89           if (_y != NULL)
90                     *_y = conout->Mode->CursorRow;
91 }
92 
93 /* Clear internal state of the terminal emulation code. */
94 void
end_term(void)95 end_term(void)
96 {
97           esc = 0;
98           argc = -1;
99 }
100 
101 #endif
102 
103 static void
efi_cons_probe(struct console * cp)104 efi_cons_probe(struct console *cp)
105 {
106           conout = ST->ConOut;
107           conin = ST->ConIn;
108           cp->c_flags |= C_PRESENTIN | C_PRESENTOUT;
109 }
110 
111 static int
efi_cons_init(int arg)112 efi_cons_init(int arg)
113 {
114           conout->SetAttribute(conout, EFI_TEXT_ATTR(DEFAULT_FGCOLOR,
115               DEFAULT_BGCOLOR));
116 #ifdef TERM_EMU
117           end_term();
118           get_pos(&curx, &cury);
119           curs_move(&curx, &cury, curx, cury);
120           fg_c = DEFAULT_FGCOLOR;
121           bg_c = DEFAULT_BGCOLOR;
122 #endif
123           conout->EnableCursor(conout, TRUE);
124           return 0;
125 }
126 
127 static void
efi_cons_rawputchar(int c)128 efi_cons_rawputchar(int c)
129 {
130           int i;
131           UINTN x, y;
132           conout->QueryMode(conout, conout->Mode->Mode, &x, &y);
133 
134           if (c == '\t')
135                     /* XXX lame tab expansion */
136                     for (i = 0; i < 8; i++)
137                               efi_cons_rawputchar(' ');
138           else {
139 #ifndef   TERM_EMU
140                     if (c == '\n')
141                               efi_cons_efiputchar('\r');
142                     else
143                               efi_cons_efiputchar(c);
144 #else
145                     switch (c) {
146                     case '\r':
147                               curx = 0;
148                               curs_move(&curx, &cury, curx, cury);
149                               return;
150                     case '\n':
151                               cury++;
152                               if (cury >= y) {
153                                         efi_cons_efiputchar('\n');
154                                         cury--;
155                               } else
156                                         curs_move(&curx, &cury, curx, cury);
157                               return;
158                     case '\b':
159                               if (curx > 0) {
160                                         curx--;
161                                         curs_move(&curx, &cury, curx, cury);
162                               }
163                               return;
164                     default:
165                               efi_cons_efiputchar(c);
166                               curx++;
167                               if (curx > x-1) {
168                                         curx = 0;
169                                         cury++;
170                               }
171                               if (cury > y-1) {
172                                         curx = 0;
173                                         cury--;
174                               }
175                     }
176                     curs_move(&curx, &cury, curx, cury);
177 #endif
178           }
179 }
180 
181 /* Gracefully exit ESC-sequence processing in case of misunderstanding. */
182 static void
bail_out(int c)183 bail_out(int c)
184 {
185           char buf[16], *ch;
186           int i;
187 
188           if (esc) {
189                     efi_cons_rawputchar('\033');
190                     if (esc != '\033')
191                               efi_cons_rawputchar(esc);
192                     for (i = 0; i <= argc; ++i) {
193                               sprintf(buf, "%d", args[i]);
194                               ch = buf;
195                               while (*ch)
196                                         efi_cons_rawputchar(*ch++);
197                     }
198           }
199           efi_cons_rawputchar(c);
200           end_term();
201 }
202 
203 /* Clear display from current position to end of screen. */
204 static void
CD(void)205 CD(void) {
206           int i;
207           UINTN x, y;
208 
209           get_pos(&curx, &cury);
210           if (curx == 0 && cury == 0) {
211                     conout->ClearScreen(conout);
212                     end_term();
213                     return;
214           }
215 
216           conout->QueryMode(conout, conout->Mode->Mode, &x, &y);
217           CL(0);  /* clear current line from cursor to end */
218           for (i = cury + 1; i < y-1; i++) {
219                     curs_move(NULL, NULL, 0, i);
220                     CL(0);
221           }
222           curs_move(NULL, NULL, curx, cury);
223           end_term();
224 }
225 
226 /*
227  * Absolute cursor move to args[0] rows and args[1] columns
228  * (the coordinates are 1-based).
229  */
230 static void
CM(void)231 CM(void)
232 {
233           if (args[0] > 0)
234                     args[0]--;
235           if (args[1] > 0)
236                     args[1]--;
237           curs_move(&curx, &cury, args[1], args[0]);
238           end_term();
239 }
240 
241 /* Home cursor (left top corner), also called from mode command. */
242 void
HO(void)243 HO(void)
244 {
245           argc = 1;
246           args[0] = args[1] = 1;
247           CM();
248 }
249 
250 /* Clear line from current position to end of line */
251 static void
CL(int direction)252 CL(int direction)
253 {
254           int i, len;
255           UINTN x, y;
256           CHAR16 *line;
257 
258           conout->QueryMode(conout, conout->Mode->Mode, &x, &y);
259           switch (direction) {
260           case 0:         /* from cursor to end */
261                     len = x - curx + 1;
262                     break;
263           case 1:         /* from beginning to cursor */
264                     len = curx;
265                     break;
266           case 2:         /* entire line */
267                     len = x;
268                     break;
269           }
270 
271           if (cury == y - 1)
272                     len--;
273 
274           line = malloc(len * sizeof (CHAR16));
275           if (line == NULL) {
276                     printf("out of memory\n");
277                     return;
278           }
279           for (i = 0; i < len; i++)
280                     line[i] = ' ';
281           line[len-1] = 0;
282 
283           if (direction != 0)
284                     curs_move(NULL, NULL, 0, cury);
285 
286           conout->OutputString(conout, line);
287           /* restore cursor position */
288           curs_move(NULL, NULL, curx, cury);
289           free(line);
290           end_term();
291 }
292 
293 static void
get_arg(int c)294 get_arg(int c)
295 {
296           if (argc < 0)
297                     argc = 0;
298           args[argc] *= 10;
299           args[argc] += c - '0';
300 }
301 
302 /* Emulate basic capabilities of cons25 terminal */
303 static void
efi_term_emu(int c)304 efi_term_emu(int c)
305 {
306           static int ansi_col[] = {
307                     0, 4, 2, 6, 1, 5, 3, 7
308           };
309           int t, i;
310 
311           switch (esc) {
312           case 0:
313                     switch (c) {
314                     case '\033':
315                               esc = c;
316                               break;
317                     default:
318                               efi_cons_rawputchar(c);
319                               break;
320                     }
321                     break;
322           case '\033':
323                     switch (c) {
324                     case '[':
325                               esc = c;
326                               args[0] = 0;
327                               argc = -1;
328                               break;
329                     default:
330                               bail_out(c);
331                               break;
332                     }
333                     break;
334           case '[':
335                     switch (c) {
336                     case ';':
337                               if (argc < 0)
338                                         argc = 0;
339                               else if (argc + 1 >= MAXARGS)
340                                         bail_out(c);
341                               else
342                                         args[++argc] = 0;
343                               break;
344                     case 'H':               /* ho = \E[H */
345                               if (argc < 0)
346                                         HO();
347                               else if (argc == 1)
348                                         CM();
349                               else
350                                         bail_out(c);
351                               break;
352                     case 'J':               /* cd = \E[J */
353                               if (argc < 0)
354                                         CD();
355                               else
356                                         bail_out(c);
357                               break;
358                     case 'm':
359                               if (argc < 0) {
360                                         fg_c = DEFAULT_FGCOLOR;
361                                         bg_c = DEFAULT_BGCOLOR;
362                               }
363                               for (i = 0; i <= argc; ++i) {
364                                         switch (args[i]) {
365                                         case 0:         /* back to normal */
366                                                   fg_c = DEFAULT_FGCOLOR;
367                                                   bg_c = DEFAULT_BGCOLOR;
368                                                   break;
369                                         case 1:         /* bold */
370                                                   fg_c |= 0x8;
371                                                   break;
372                                         case 4:         /* underline */
373                                         case 5:         /* blink */
374                                                   bg_c |= 0x8;
375                                                   break;
376                                         case 7:         /* reverse */
377                                                   t = fg_c;
378                                                   fg_c = bg_c;
379                                                   bg_c = t;
380                                                   break;
381                                         case 30: case 31: case 32: case 33:
382                                         case 34: case 35: case 36: case 37:
383                                                   fg_c = ansi_col[args[i] - 30];
384                                                   break;
385                                         case 39:        /* normal */
386                                                   fg_c = DEFAULT_FGCOLOR;
387                                                   break;
388                                         case 40: case 41: case 42: case 43:
389                                         case 44: case 45: case 46: case 47:
390                                                   bg_c = ansi_col[args[i] - 40];
391                                                   break;
392                                         case 49:        /* normal */
393                                                   bg_c = DEFAULT_BGCOLOR;
394                                                   break;
395                                         }
396                               }
397                               conout->SetAttribute(conout, EFI_TEXT_ATTR(fg_c, bg_c));
398                               end_term();
399                               break;
400                     default:
401                               if (isdigit(c))
402                                         get_arg(c);
403                               else
404                                         bail_out(c);
405                               break;
406                     }
407                     break;
408           default:
409                     bail_out(c);
410                     break;
411           }
412 }
413 
414 void
efi_cons_putchar(int c)415 efi_cons_putchar(int c)
416 {
417 #ifdef TERM_EMU
418           efi_term_emu(c);
419 #else
420           efi_cons_rawputchar(c);
421 #endif
422 }
423 
424 int
efi_cons_getchar(void)425 efi_cons_getchar(void)
426 {
427           EFI_INPUT_KEY key;
428           EFI_STATUS status;
429           UINTN junk;
430 
431 again:
432           /* Try to read a key stroke. We wait for one if none is pending. */
433           status = conin->ReadKeyStroke(conin, &key);
434           if (status == EFI_NOT_READY) {
435                     BS->WaitForEvent(1, &conin->WaitForKey, &junk);
436                     goto again;
437           }
438           switch (key.ScanCode) {
439           case 0x17: /* ESC */
440                     return (0x1b);  /* esc */
441           }
442 
443           /* this can return  */
444           return (key.UnicodeChar);
445 }
446 
447 int
efi_cons_poll(void)448 efi_cons_poll(void)
449 {
450           /* This can clear the signaled state. */
451           return (BS->CheckEvent(conin->WaitForKey) == EFI_SUCCESS);
452 }
453 
454 /* Plain direct access to EFI OutputString(). */
455 void
efi_cons_efiputchar(int c)456 efi_cons_efiputchar(int c)
457 {
458           CHAR16 buf[2];
459 
460           /*
461            * translate box chars to unicode
462            */
463           switch (c) {
464           /* single frame */
465           case 0xb3: buf[0] = BOXDRAW_VERTICAL; break;
466           case 0xbf: buf[0] = BOXDRAW_DOWN_LEFT; break;
467           case 0xc0: buf[0] = BOXDRAW_UP_RIGHT; break;
468           case 0xc4: buf[0] = BOXDRAW_HORIZONTAL; break;
469           case 0xda: buf[0] = BOXDRAW_DOWN_RIGHT; break;
470           case 0xd9: buf[0] = BOXDRAW_UP_LEFT; break;
471 
472           /* double frame */
473           case 0xba: buf[0] = BOXDRAW_DOUBLE_VERTICAL; break;
474           case 0xbb: buf[0] = BOXDRAW_DOUBLE_DOWN_LEFT; break;
475           case 0xbc: buf[0] = BOXDRAW_DOUBLE_UP_LEFT; break;
476           case 0xc8: buf[0] = BOXDRAW_DOUBLE_UP_RIGHT; break;
477           case 0xc9: buf[0] = BOXDRAW_DOUBLE_DOWN_RIGHT; break;
478           case 0xcd: buf[0] = BOXDRAW_DOUBLE_HORIZONTAL; break;
479 
480           default:
481                     buf[0] = c;
482           }
483         buf[1] = 0;     /* terminate string */
484 
485           conout->OutputString(conout, buf);
486 }
487