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 #define KEYBUFSZ 10
55 static unsigned keybuf[KEYBUFSZ]; /* keybuf for extended codes */
56 static int key_pending;
57
58 static void efi_cons_probe(struct console *);
59 static int efi_cons_init(int);
60 void efi_cons_putchar(int);
61 int efi_cons_getchar(void);
62 void efi_cons_efiputchar(int);
63 int efi_cons_poll(void);
64
65 struct console efi_console = {
66 "efi",
67 "EFI console",
68 C_WIDEOUT,
69 efi_cons_probe,
70 efi_cons_init,
71 efi_cons_putchar,
72 efi_cons_getchar,
73 efi_cons_poll
74 };
75
76 #ifdef TERM_EMU
77
78 /* Get cursor position. */
79 void
get_pos(int * x,int * y)80 get_pos(int *x, int *y)
81 {
82 *x = conout->Mode->CursorColumn;
83 *y = conout->Mode->CursorRow;
84 }
85
86 /* Move cursor to x rows and y cols (0-based). */
87 void
curs_move(int * _x,int * _y,int x,int y)88 curs_move(int *_x, int *_y, int x, int y)
89 {
90 conout->SetCursorPosition(conout, x, y);
91 if (_x != NULL)
92 *_x = conout->Mode->CursorColumn;
93 if (_y != NULL)
94 *_y = conout->Mode->CursorRow;
95 }
96
97 /* Clear internal state of the terminal emulation code. */
98 void
end_term(void)99 end_term(void)
100 {
101 esc = 0;
102 argc = -1;
103 }
104
105 #endif
106
107 static void
efi_cons_probe(struct console * cp)108 efi_cons_probe(struct console *cp)
109 {
110 conout = ST->ConOut;
111 conin = ST->ConIn;
112 cp->c_flags |= C_PRESENTIN | C_PRESENTOUT;
113 }
114
115 static int
efi_cons_init(int arg)116 efi_cons_init(int arg)
117 {
118 #ifdef TERM_EMU
119 conout->SetAttribute(conout, EFI_TEXT_ATTR(DEFAULT_FGCOLOR,
120 DEFAULT_BGCOLOR));
121 end_term();
122 get_pos(&curx, &cury);
123 curs_move(&curx, &cury, curx, cury);
124 fg_c = DEFAULT_FGCOLOR;
125 bg_c = DEFAULT_BGCOLOR;
126 #endif
127 conout->EnableCursor(conout, TRUE);
128 return 0;
129 }
130
131 static void
efi_cons_rawputchar(int c)132 efi_cons_rawputchar(int c)
133 {
134 int i;
135 UINTN x, y;
136 conout->QueryMode(conout, conout->Mode->Mode, &x, &y);
137
138 if (c == '\t')
139 /* XXX lame tab expansion */
140 for (i = 0; i < 8; i++)
141 efi_cons_rawputchar(' ');
142 else {
143 #ifndef TERM_EMU
144 if (c == '\n')
145 efi_cons_efiputchar('\r');
146 efi_cons_efiputchar(c);
147 #else
148 switch (c) {
149 case '\r':
150 curx = 0;
151 efi_cons_efiputchar('\r');
152 return;
153 case '\n':
154 efi_cons_efiputchar('\n');
155 efi_cons_efiputchar('\r');
156 cury++;
157 if (cury >= y)
158 cury--;
159 curx = 0;
160 return;
161 case '\b':
162 if (curx > 0) {
163 efi_cons_efiputchar('\b');
164 curx--;
165 }
166 return;
167 default:
168 efi_cons_efiputchar(c);
169 curx++;
170 if (curx > x-1) {
171 curx = 0;
172 cury++;
173 }
174 if (cury > y-1) {
175 curx = 0;
176 cury--;
177 }
178 }
179 #endif
180 }
181 }
182
183 #ifdef TERM_EMU
184 /* Gracefully exit ESC-sequence processing in case of misunderstanding. */
185 static void
bail_out(int c)186 bail_out(int c)
187 {
188 char buf[16], *ch;
189 int i;
190
191 if (esc) {
192 efi_cons_rawputchar('\033');
193 if (esc != '\033')
194 efi_cons_rawputchar(esc);
195 for (i = 0; i <= argc; ++i) {
196 sprintf(buf, "%d", args[i]);
197 ch = buf;
198 while (*ch)
199 efi_cons_rawputchar(*ch++);
200 }
201 }
202 efi_cons_rawputchar(c);
203 end_term();
204 }
205
206 /* Clear display from current position to end of screen. */
207 static void
CD(void)208 CD(void) {
209 int i;
210 UINTN x, y;
211
212 get_pos(&curx, &cury);
213 if (curx == 0 && cury == 0) {
214 conout->ClearScreen(conout);
215 end_term();
216 return;
217 }
218
219 conout->QueryMode(conout, conout->Mode->Mode, &x, &y);
220 CL(0); /* clear current line from cursor to end */
221 for (i = cury + 1; i < y-1; i++) {
222 curs_move(NULL, NULL, 0, i);
223 CL(0);
224 }
225 curs_move(NULL, NULL, curx, cury);
226 end_term();
227 }
228
229 /*
230 * Absolute cursor move to args[0] rows and args[1] columns
231 * (the coordinates are 1-based).
232 */
233 static void
CM(void)234 CM(void)
235 {
236 if (args[0] > 0)
237 args[0]--;
238 if (args[1] > 0)
239 args[1]--;
240 curs_move(&curx, &cury, args[1], args[0]);
241 end_term();
242 }
243
244 /* Home cursor (left top corner), also called from mode command. */
245 void
HO(void)246 HO(void)
247 {
248 argc = 1;
249 args[0] = args[1] = 1;
250 CM();
251 }
252
253 /* Clear line from current position to end of line */
254 static void
CL(int direction)255 CL(int direction)
256 {
257 int i, len;
258 UINTN x, y;
259 CHAR16 *line;
260
261 conout->QueryMode(conout, conout->Mode->Mode, &x, &y);
262 switch (direction) {
263 case 0: /* from cursor to end */
264 len = x - curx + 1;
265 break;
266 case 1: /* from beginning to cursor */
267 len = curx;
268 break;
269 case 2: /* entire line */
270 len = x;
271 break;
272 default: /* NOTREACHED */
273 __unreachable();
274 }
275
276 if (cury == y - 1)
277 len--;
278
279 line = malloc(len * sizeof (CHAR16));
280 if (line == NULL) {
281 printf("out of memory\n");
282 return;
283 }
284 for (i = 0; i < len; i++)
285 line[i] = ' ';
286 line[len-1] = 0;
287
288 if (direction != 0)
289 curs_move(NULL, NULL, 0, cury);
290
291 conout->OutputString(conout, line);
292 /* restore cursor position */
293 curs_move(NULL, NULL, curx, cury);
294 free(line);
295 end_term();
296 }
297
298 static void
get_arg(int c)299 get_arg(int c)
300 {
301 if (argc < 0)
302 argc = 0;
303 args[argc] *= 10;
304 args[argc] += c - '0';
305 }
306
307 /* Emulate basic capabilities of cons25 terminal */
308 static void
efi_term_emu(int c)309 efi_term_emu(int c)
310 {
311 static int ansi_col[] = {
312 0, 4, 2, 6, 1, 5, 3, 7
313 };
314 int t, i;
315
316 switch (esc) {
317 case 0:
318 switch (c) {
319 case '\033':
320 esc = c;
321 break;
322 default:
323 efi_cons_rawputchar(c);
324 break;
325 }
326 break;
327 case '\033':
328 switch (c) {
329 case '[':
330 esc = c;
331 args[0] = 0;
332 argc = -1;
333 break;
334 default:
335 bail_out(c);
336 break;
337 }
338 break;
339 case '[':
340 switch (c) {
341 case ';':
342 if (argc < 0)
343 argc = 0;
344 else if (argc + 1 >= MAXARGS)
345 bail_out(c);
346 else
347 args[++argc] = 0;
348 break;
349 case 'H': /* ho = \E[H */
350 if (argc < 0)
351 HO();
352 else if (argc == 1)
353 CM();
354 else
355 bail_out(c);
356 break;
357 case 'J': /* cd = \E[J */
358 if (argc < 0)
359 CD();
360 else
361 bail_out(c);
362 break;
363 case 'm':
364 if (argc < 0) {
365 fg_c = DEFAULT_FGCOLOR;
366 bg_c = DEFAULT_BGCOLOR;
367 }
368 for (i = 0; i <= argc; ++i) {
369 switch (args[i]) {
370 case 0: /* back to normal */
371 fg_c = DEFAULT_FGCOLOR;
372 bg_c = DEFAULT_BGCOLOR;
373 break;
374 case 1: /* bold */
375 fg_c |= 0x8;
376 break;
377 case 4: /* underline */
378 case 5: /* blink */
379 bg_c |= 0x8;
380 break;
381 case 7: /* reverse */
382 t = fg_c;
383 fg_c = bg_c;
384 bg_c = t;
385 break;
386 case 22: /* normal intensity */
387 fg_c &= ~0x8;
388 break;
389 case 24: /* not underline */
390 case 25: /* not blinking */
391 bg_c &= ~0x8;
392 break;
393 case 30: case 31: case 32: case 33:
394 case 34: case 35: case 36: case 37:
395 fg_c = ansi_col[args[i] - 30];
396 break;
397 case 39: /* normal */
398 fg_c = DEFAULT_FGCOLOR;
399 break;
400 case 40: case 41: case 42: case 43:
401 case 44: case 45: case 46: case 47:
402 bg_c = ansi_col[args[i] - 40];
403 break;
404 case 49: /* normal */
405 bg_c = DEFAULT_BGCOLOR;
406 break;
407 }
408 }
409 conout->SetAttribute(conout, EFI_TEXT_ATTR(fg_c, bg_c));
410 end_term();
411 break;
412 default:
413 if (isdigit(c))
414 get_arg(c);
415 else
416 bail_out(c);
417 break;
418 }
419 break;
420 default:
421 bail_out(c);
422 break;
423 }
424 }
425 #else
426 void
HO(void)427 HO(void)
428 {
429 }
430 #endif
431
432 void
efi_cons_putchar(int c)433 efi_cons_putchar(int c)
434 {
435 #ifdef TERM_EMU
436 efi_term_emu(c);
437 #else
438 efi_cons_rawputchar(c);
439 #endif
440 }
441
442 static int
keybuf_getchar(void)443 keybuf_getchar(void)
444 {
445 int i, c = 0;
446
447 for (i = 0; i < KEYBUFSZ; i++) {
448 if (keybuf[i] != 0) {
449 c = keybuf[i];
450 keybuf[i] = 0;
451 break;
452 }
453 }
454
455 return (c);
456 }
457
458 static bool
keybuf_ischar(void)459 keybuf_ischar(void)
460 {
461 int i;
462
463 for (i = 0; i < KEYBUFSZ; i++) {
464 if (keybuf[i] != 0)
465 return (true);
466 }
467 return (false);
468 }
469
470 /*
471 * We are not reading input before keybuf is empty, so we are safe
472 * just to fill keybuf from the beginning.
473 */
474 static void
keybuf_inschar(EFI_INPUT_KEY * key)475 keybuf_inschar(EFI_INPUT_KEY *key)
476 {
477
478 switch (key->ScanCode) {
479 case 0x1: /* UP */
480 keybuf[0] = 0x1b; /* esc */
481 keybuf[1] = '[';
482 keybuf[2] = 'A';
483 break;
484 case 0x2: /* DOWN */
485 keybuf[0] = 0x1b; /* esc */
486 keybuf[1] = '[';
487 keybuf[2] = 'B';
488 break;
489 case 0x3: /* RIGHT */
490 keybuf[0] = 0x1b; /* esc */
491 keybuf[1] = '[';
492 keybuf[2] = 'C';
493 break;
494 case 0x4: /* LEFT */
495 keybuf[0] = 0x1b; /* esc */
496 keybuf[1] = '[';
497 keybuf[2] = 'D';
498 break;
499 case 0x17:
500 keybuf[0] = 0x1b; /* esc */
501 break;
502 default:
503 keybuf[0] = key->UnicodeChar;
504 break;
505 }
506 }
507
508 static bool
efi_readkey(void)509 efi_readkey(void)
510 {
511 EFI_STATUS status;
512 EFI_INPUT_KEY key;
513
514 status = conin->ReadKeyStroke(conin, &key);
515 if (status == EFI_SUCCESS) {
516 keybuf_inschar(&key);
517 return (true);
518 }
519 return (false);
520 }
521
522 int
efi_cons_getchar(void)523 efi_cons_getchar(void)
524 {
525 int c;
526
527 if ((c = keybuf_getchar()) != 0)
528 return (c);
529
530 key_pending = 0;
531
532 if (efi_readkey())
533 return (keybuf_getchar());
534
535 return (-1);
536 }
537
538 int
efi_cons_poll(void)539 efi_cons_poll(void)
540 {
541
542 if (keybuf_ischar() || key_pending)
543 return (1);
544
545 /*
546 * Some EFI implementation (u-boot for example) do not support
547 * WaitForKey().
548 * CheckEvent() can clear the signaled state.
549 */
550 if (conin->WaitForKey == NULL)
551 key_pending = efi_readkey();
552 else
553 key_pending = BS->CheckEvent(conin->WaitForKey) == EFI_SUCCESS;
554
555 return (key_pending);
556 }
557
558 /* Plain direct access to EFI OutputString(). */
559 void
efi_cons_efiputchar(int c)560 efi_cons_efiputchar(int c)
561 {
562 CHAR16 buf[2];
563
564 /*
565 * translate box chars to unicode
566 */
567 switch (c) {
568 /* single frame */
569 case 0xb3: buf[0] = BOXDRAW_VERTICAL; break;
570 case 0xbf: buf[0] = BOXDRAW_DOWN_LEFT; break;
571 case 0xc0: buf[0] = BOXDRAW_UP_RIGHT; break;
572 case 0xc4: buf[0] = BOXDRAW_HORIZONTAL; break;
573 case 0xda: buf[0] = BOXDRAW_DOWN_RIGHT; break;
574 case 0xd9: buf[0] = BOXDRAW_UP_LEFT; break;
575
576 /* double frame */
577 case 0xba: buf[0] = BOXDRAW_DOUBLE_VERTICAL; break;
578 case 0xbb: buf[0] = BOXDRAW_DOUBLE_DOWN_LEFT; break;
579 case 0xbc: buf[0] = BOXDRAW_DOUBLE_UP_LEFT; break;
580 case 0xc8: buf[0] = BOXDRAW_DOUBLE_UP_RIGHT; break;
581 case 0xc9: buf[0] = BOXDRAW_DOUBLE_DOWN_RIGHT; break;
582 case 0xcd: buf[0] = BOXDRAW_DOUBLE_HORIZONTAL; break;
583
584 default:
585 buf[0] = c;
586 }
587 buf[1] = 0; /* terminate string */
588
589 conout->OutputString(conout, buf);
590 }
591