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 #include <teken.h> 33 #include <sys/reboot.h> 34 35 #include "bootstrap.h" 36 37 static EFI_GUID simple_input_ex_guid = EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL_GUID; 38 static SIMPLE_TEXT_OUTPUT_INTERFACE *conout; 39 static SIMPLE_INPUT_INTERFACE *conin; 40 static EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *coninex; 41 42 static int mode; /* Does ConOut have serial console? */ 43 44 static uint32_t utf8_left; 45 static uint32_t utf8_partial; 46 #ifdef TERM_EMU 47 #define DEFAULT_FGCOLOR EFI_LIGHTGRAY 48 #define DEFAULT_BGCOLOR EFI_BLACK 49 50 #define MAXARGS 8 51 static int args[MAXARGS], argc; 52 static int fg_c, bg_c, curx, cury; 53 static int esc; 54 55 void get_pos(int *x, int *y); 56 void curs_move(int *_x, int *_y, int x, int y); 57 static void CL(int); 58 void HO(void); 59 void end_term(void); 60 #endif 61 62 static tf_bell_t efi_cons_bell; 63 static tf_cursor_t efi_text_cursor; 64 static tf_putchar_t efi_text_putchar; 65 static tf_fill_t efi_text_fill; 66 static tf_copy_t efi_text_copy; 67 static tf_param_t efi_text_param; 68 static tf_respond_t efi_cons_respond; 69 70 static teken_funcs_t tf = { 71 .tf_bell = efi_cons_bell, 72 .tf_cursor = efi_text_cursor, 73 .tf_putchar = efi_text_putchar, 74 .tf_fill = efi_text_fill, 75 .tf_copy = efi_text_copy, 76 .tf_param = efi_text_param, 77 .tf_respond = efi_cons_respond, 78 }; 79 80 teken_t teken; 81 teken_pos_t tp; 82 83 struct text_pixel { 84 teken_char_t c; 85 teken_attr_t a; 86 }; 87 88 static struct text_pixel *buffer; 89 90 #define KEYBUFSZ 10 91 static unsigned keybuf[KEYBUFSZ]; /* keybuf for extended codes */ 92 static int key_pending; 93 94 static const unsigned char teken_color_to_efi_color[16] = { 95 EFI_BLACK, 96 EFI_RED, 97 EFI_GREEN, 98 EFI_BROWN, 99 EFI_BLUE, 100 EFI_MAGENTA, 101 EFI_CYAN, 102 EFI_LIGHTGRAY, 103 EFI_DARKGRAY, 104 EFI_LIGHTRED, 105 EFI_LIGHTGREEN, 106 EFI_YELLOW, 107 EFI_LIGHTBLUE, 108 EFI_LIGHTMAGENTA, 109 EFI_LIGHTCYAN, 110 EFI_WHITE 111 }; 112 113 static void efi_cons_probe(struct console *); 114 static int efi_cons_init(int); 115 void efi_cons_putchar(int); 116 int efi_cons_getchar(void); 117 void efi_cons_efiputchar(int); 118 int efi_cons_poll(void); 119 120 struct console efi_console = { 121 "efi", 122 "EFI console", 123 C_WIDEOUT, 124 efi_cons_probe, 125 efi_cons_init, 126 efi_cons_putchar, 127 efi_cons_getchar, 128 efi_cons_poll 129 }; 130 131 /* 132 * Not implemented. 133 */ 134 static void 135 efi_cons_bell(void *s __unused) 136 { 137 } 138 139 static void 140 efi_text_cursor(void *s __unused, const teken_pos_t *p) 141 { 142 UINTN row, col; 143 144 (void) conout->QueryMode(conout, conout->Mode->Mode, &col, &row); 145 146 if (p->tp_col == col) 147 col = p->tp_col - 1; 148 else 149 col = p->tp_col; 150 151 if (p->tp_row == row) 152 row = p->tp_row - 1; 153 else 154 row = p->tp_row; 155 156 conout->SetCursorPosition(conout, col, row); 157 } 158 159 static void 160 efi_text_printchar(const teken_pos_t *p, bool autoscroll) 161 { 162 UINTN a, attr; 163 struct text_pixel *px; 164 teken_color_t fg, bg, tmp; 165 166 px = buffer + p->tp_col + p->tp_row * tp.tp_col; 167 a = conout->Mode->Attribute; 168 169 fg = teken_256to16(px->a.ta_fgcolor); 170 bg = teken_256to16(px->a.ta_bgcolor); 171 if (px->a.ta_format & TF_BOLD) 172 fg |= TC_LIGHT; 173 if (px->a.ta_format & TF_BLINK) 174 bg |= TC_LIGHT; 175 176 if (px->a.ta_format & TF_REVERSE) { 177 tmp = fg; 178 fg = bg; 179 bg = tmp; 180 } 181 182 attr = EFI_TEXT_ATTR(teken_color_to_efi_color[fg], 183 teken_color_to_efi_color[bg] & 0x7); 184 185 conout->SetCursorPosition(conout, p->tp_col, p->tp_row); 186 187 /* to prvent autoscroll, skip print of lower right char */ 188 if (!autoscroll && 189 p->tp_row == tp.tp_row - 1 && 190 p->tp_col == tp.tp_col - 1) 191 return; 192 193 (void) conout->SetAttribute(conout, attr); 194 efi_cons_efiputchar(px->c); 195 (void) conout->SetAttribute(conout, a); 196 } 197 198 static void 199 efi_text_putchar(void *s __unused, const teken_pos_t *p, teken_char_t c, 200 const teken_attr_t *a) 201 { 202 EFI_STATUS status; 203 int idx; 204 205 idx = p->tp_col + p->tp_row * tp.tp_col; 206 buffer[idx].c = c; 207 buffer[idx].a = *a; 208 efi_text_printchar(p, false); 209 } 210 211 static void 212 efi_text_fill(void *s, const teken_rect_t *r, teken_char_t c, 213 const teken_attr_t *a) 214 { 215 teken_pos_t p; 216 UINTN row, col; 217 218 (void) conout->QueryMode(conout, conout->Mode->Mode, &col, &row); 219 220 conout->EnableCursor(conout, FALSE); 221 for (p.tp_row = r->tr_begin.tp_row; p.tp_row < r->tr_end.tp_row; 222 p.tp_row++) 223 for (p.tp_col = r->tr_begin.tp_col; 224 p.tp_col < r->tr_end.tp_col; p.tp_col++) 225 efi_text_putchar(s, &p, c, a); 226 conout->EnableCursor(conout, TRUE); 227 } 228 229 static bool 230 efi_same_pixel(struct text_pixel *px1, struct text_pixel *px2) 231 { 232 if (px1->c != px2->c) 233 return (false); 234 235 if (px1->a.ta_format != px2->a.ta_format) 236 return (false); 237 if (px1->a.ta_fgcolor != px2->a.ta_fgcolor) 238 return (false); 239 if (px1->a.ta_bgcolor != px2->a.ta_bgcolor) 240 return (false); 241 242 return (true); 243 } 244 245 static void 246 efi_text_copy(void *ptr __unused, const teken_rect_t *r, const teken_pos_t *p) 247 { 248 int srow, drow; 249 int nrow, ncol, x, y; /* Has to be signed - >= 0 comparison */ 250 teken_pos_t d, s; 251 bool scroll = false; 252 253 /* 254 * Copying is a little tricky. We must make sure we do it in 255 * correct order, to make sure we don't overwrite our own data. 256 */ 257 258 nrow = r->tr_end.tp_row - r->tr_begin.tp_row; 259 ncol = r->tr_end.tp_col - r->tr_begin.tp_col; 260 261 /* 262 * Check if we do copy whole screen. 263 */ 264 if (p->tp_row == 0 && p->tp_col == 0 && 265 nrow == tp.tp_row - 2 && ncol == tp.tp_col - 2) 266 scroll = true; 267 268 conout->EnableCursor(conout, FALSE); 269 if (p->tp_row < r->tr_begin.tp_row) { 270 /* Copy from bottom to top. */ 271 for (y = 0; y < nrow; y++) { 272 d.tp_row = p->tp_row + y; 273 s.tp_row = r->tr_begin.tp_row + y; 274 drow = d.tp_row * tp.tp_col; 275 srow = s.tp_row * tp.tp_col; 276 for (x = 0; x < ncol; x++) { 277 d.tp_col = p->tp_col + x; 278 s.tp_col = r->tr_begin.tp_col + x; 279 280 if (!efi_same_pixel( 281 &buffer[d.tp_col + drow], 282 &buffer[s.tp_col + srow])) { 283 buffer[d.tp_col + drow] = 284 buffer[s.tp_col + srow]; 285 if (!scroll) 286 efi_text_printchar(&d, false); 287 } else if (scroll) { 288 /* 289 * Draw last char and trigger 290 * scroll. 291 */ 292 if (y == nrow - 1 && 293 x == ncol - 1) { 294 efi_text_printchar(&d, true); 295 } 296 } 297 } 298 } 299 } else { 300 /* Copy from top to bottom. */ 301 if (p->tp_col < r->tr_begin.tp_col) { 302 /* Copy from right to left. */ 303 for (y = nrow - 1; y >= 0; y--) { 304 d.tp_row = p->tp_row + y; 305 s.tp_row = r->tr_begin.tp_row + y; 306 drow = d.tp_row * tp.tp_col; 307 srow = s.tp_row * tp.tp_col; 308 for (x = 0; x < ncol; x++) { 309 d.tp_col = p->tp_col + x; 310 s.tp_col = r->tr_begin.tp_col + x; 311 312 if (!efi_same_pixel( 313 &buffer[d.tp_col + drow], 314 &buffer[s.tp_col + srow])) { 315 buffer[d.tp_col + drow] = 316 buffer[s.tp_col + srow]; 317 efi_text_printchar(&d, false); 318 } 319 } 320 } 321 } else { 322 /* Copy from left to right. */ 323 for (y = nrow - 1; y >= 0; y--) { 324 d.tp_row = p->tp_row + y; 325 s.tp_row = r->tr_begin.tp_row + y; 326 drow = d.tp_row * tp.tp_col; 327 srow = s.tp_row * tp.tp_col; 328 for (x = ncol - 1; x >= 0; x--) { 329 d.tp_col = p->tp_col + x; 330 s.tp_col = r->tr_begin.tp_col + x; 331 332 if (!efi_same_pixel( 333 &buffer[d.tp_col + drow], 334 &buffer[s.tp_col + srow])) { 335 buffer[d.tp_col + drow] = 336 buffer[s.tp_col + srow]; 337 efi_text_printchar(&d, false); 338 } 339 } 340 } 341 } 342 } 343 conout->EnableCursor(conout, TRUE); 344 } 345 346 static void 347 efi_text_param(void *s __unused, int cmd, unsigned int value) 348 { 349 switch (cmd) { 350 case TP_SETLOCALCURSOR: 351 /* 352 * 0 means normal (usually block), 1 means hidden, and 353 * 2 means blinking (always block) for compatibility with 354 * syscons. We don't support any changes except hiding, 355 * so must map 2 to 0. 356 */ 357 value = (value == 1) ? 0 : 1; 358 /* FALLTHROUGH */ 359 case TP_SHOWCURSOR: 360 if (value == 1) 361 conout->EnableCursor(conout, TRUE); 362 else 363 conout->EnableCursor(conout, FALSE); 364 break; 365 default: 366 /* Not yet implemented */ 367 break; 368 } 369 } 370 371 /* 372 * Not implemented. 373 */ 374 static void 375 efi_cons_respond(void *s __unused, const void *buf __unused, 376 size_t len __unused) 377 { 378 } 379 380 /* 381 * Set up conin/conout/coninex to make sure we have input ready. 382 */ 383 static void 384 efi_cons_probe(struct console *cp) 385 { 386 EFI_STATUS status; 387 388 conout = ST->ConOut; 389 conin = ST->ConIn; 390 391 /* 392 * Call SetMode to work around buggy firmware. 393 */ 394 status = conout->SetMode(conout, conout->Mode->Mode); 395 396 if (coninex == NULL) { 397 status = BS->OpenProtocol(ST->ConsoleInHandle, 398 &simple_input_ex_guid, (void **)&coninex, 399 IH, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL); 400 if (status != EFI_SUCCESS) 401 coninex = NULL; 402 } 403 404 cp->c_flags |= C_PRESENTIN | C_PRESENTOUT; 405 } 406 407 static bool 408 color_name_to_teken(const char *name, int *val) 409 { 410 if (strcasecmp(name, "black") == 0) { 411 *val = TC_BLACK; 412 return (true); 413 } 414 if (strcasecmp(name, "red") == 0) { 415 *val = TC_RED; 416 return (true); 417 } 418 if (strcasecmp(name, "green") == 0) { 419 *val = TC_GREEN; 420 return (true); 421 } 422 if (strcasecmp(name, "brown") == 0) { 423 *val = TC_BROWN; 424 return (true); 425 } 426 if (strcasecmp(name, "blue") == 0) { 427 *val = TC_BLUE; 428 return (true); 429 } 430 if (strcasecmp(name, "magenta") == 0) { 431 *val = TC_MAGENTA; 432 return (true); 433 } 434 if (strcasecmp(name, "cyan") == 0) { 435 *val = TC_CYAN; 436 return (true); 437 } 438 if (strcasecmp(name, "white") == 0) { 439 *val = TC_WHITE; 440 return (true); 441 } 442 return (false); 443 } 444 445 static int 446 efi_set_colors(struct env_var *ev, int flags, const void *value) 447 { 448 int val = 0; 449 char buf[2]; 450 const void *evalue; 451 const teken_attr_t *ap; 452 teken_attr_t a; 453 454 if (value == NULL) 455 return (CMD_OK); 456 457 if (color_name_to_teken(value, &val)) { 458 snprintf(buf, sizeof (buf), "%d", val); 459 evalue = buf; 460 } else { 461 char *end; 462 463 errno = 0; 464 val = (int)strtol(value, &end, 0); 465 if (errno != 0 || *end != '\0') { 466 printf("Allowed values are either ansi color name or " 467 "number from range [0-7].\n"); 468 return (CMD_OK); 469 } 470 evalue = value; 471 } 472 473 ap = teken_get_defattr(&teken); 474 a = *ap; 475 if (strcmp(ev->ev_name, "teken.fg_color") == 0) { 476 /* is it already set? */ 477 if (ap->ta_fgcolor == val) 478 return (CMD_OK); 479 a.ta_fgcolor = val; 480 } 481 if (strcmp(ev->ev_name, "teken.bg_color") == 0) { 482 /* is it already set? */ 483 if (ap->ta_bgcolor == val) 484 return (CMD_OK); 485 a.ta_bgcolor = val; 486 } 487 env_setenv(ev->ev_name, flags | EV_NOHOOK, evalue, NULL, NULL); 488 teken_set_defattr(&teken, &a); 489 return (CMD_OK); 490 } 491 492 #ifdef TERM_EMU 493 /* Get cursor position. */ 494 void 495 get_pos(int *x, int *y) 496 { 497 *x = conout->Mode->CursorColumn; 498 *y = conout->Mode->CursorRow; 499 } 500 501 /* Move cursor to x rows and y cols (0-based). */ 502 void 503 curs_move(int *_x, int *_y, int x, int y) 504 { 505 conout->SetCursorPosition(conout, x, y); 506 if (_x != NULL) 507 *_x = conout->Mode->CursorColumn; 508 if (_y != NULL) 509 *_y = conout->Mode->CursorRow; 510 } 511 512 /* Clear internal state of the terminal emulation code. */ 513 void 514 end_term(void) 515 { 516 esc = 0; 517 argc = -1; 518 } 519 #endif 520 521 static void 522 efi_cons_rawputchar(int c) 523 { 524 int i; 525 UINTN x, y; 526 conout->QueryMode(conout, conout->Mode->Mode, &x, &y); 527 528 if (c == '\t') { 529 int n; 530 531 n = 8 - ((conout->Mode->CursorColumn + 8) % 8); 532 for (i = 0; i < n; i++) 533 efi_cons_rawputchar(' '); 534 } else { 535 #ifndef TERM_EMU 536 if (c == '\n') 537 efi_cons_efiputchar('\r'); 538 efi_cons_efiputchar(c); 539 #else 540 switch (c) { 541 case '\r': 542 curx = 0; 543 efi_cons_efiputchar('\r'); 544 return; 545 case '\n': 546 efi_cons_efiputchar('\n'); 547 efi_cons_efiputchar('\r'); 548 cury++; 549 if (cury >= y) 550 cury--; 551 curx = 0; 552 return; 553 case '\b': 554 if (curx > 0) { 555 efi_cons_efiputchar('\b'); 556 curx--; 557 } 558 return; 559 default: 560 efi_cons_efiputchar(c); 561 curx++; 562 if (curx > x-1) { 563 curx = 0; 564 cury++; 565 } 566 if (cury > y-1) { 567 curx = 0; 568 cury--; 569 } 570 } 571 #endif 572 } 573 conout->EnableCursor(conout, TRUE); 574 } 575 576 #ifdef TERM_EMU 577 /* Gracefully exit ESC-sequence processing in case of misunderstanding. */ 578 static void 579 bail_out(int c) 580 { 581 char buf[16], *ch; 582 int i; 583 584 if (esc) { 585 efi_cons_rawputchar('\033'); 586 if (esc != '\033') 587 efi_cons_rawputchar(esc); 588 for (i = 0; i <= argc; ++i) { 589 sprintf(buf, "%d", args[i]); 590 ch = buf; 591 while (*ch) 592 efi_cons_rawputchar(*ch++); 593 } 594 } 595 efi_cons_rawputchar(c); 596 end_term(); 597 } 598 599 /* Clear display from current position to end of screen. */ 600 static void 601 CD(void) 602 { 603 int i; 604 UINTN x, y; 605 606 get_pos(&curx, &cury); 607 if (curx == 0 && cury == 0) { 608 conout->ClearScreen(conout); 609 end_term(); 610 return; 611 } 612 613 conout->QueryMode(conout, conout->Mode->Mode, &x, &y); 614 CL(0); /* clear current line from cursor to end */ 615 for (i = cury + 1; i < y-1; i++) { 616 curs_move(NULL, NULL, 0, i); 617 CL(0); 618 } 619 curs_move(NULL, NULL, curx, cury); 620 end_term(); 621 } 622 623 /* 624 * Absolute cursor move to args[0] rows and args[1] columns 625 * (the coordinates are 1-based). 626 */ 627 static void 628 CM(void) 629 { 630 if (args[0] > 0) 631 args[0]--; 632 if (args[1] > 0) 633 args[1]--; 634 curs_move(&curx, &cury, args[1], args[0]); 635 end_term(); 636 } 637 638 /* Home cursor (left top corner), also called from mode command. */ 639 void 640 HO(void) 641 { 642 argc = 1; 643 args[0] = args[1] = 1; 644 CM(); 645 } 646 647 /* Clear line from current position to end of line */ 648 static void 649 CL(int direction) 650 { 651 int i, len; 652 UINTN x, y; 653 CHAR16 *line; 654 655 conout->QueryMode(conout, conout->Mode->Mode, &x, &y); 656 switch (direction) { 657 case 0: /* from cursor to end */ 658 len = x - curx + 1; 659 break; 660 case 1: /* from beginning to cursor */ 661 len = curx; 662 break; 663 case 2: /* entire line */ 664 len = x; 665 break; 666 default: /* NOTREACHED */ 667 __unreachable(); 668 } 669 670 if (cury == y - 1) 671 len--; 672 673 line = malloc(len * sizeof (CHAR16)); 674 if (line == NULL) { 675 printf("out of memory\n"); 676 return; 677 } 678 for (i = 0; i < len; i++) 679 line[i] = ' '; 680 line[len-1] = 0; 681 682 if (direction != 0) 683 curs_move(NULL, NULL, 0, cury); 684 685 conout->OutputString(conout, line); 686 /* restore cursor position */ 687 curs_move(NULL, NULL, curx, cury); 688 free(line); 689 end_term(); 690 } 691 692 static void 693 get_arg(int c) 694 { 695 if (argc < 0) 696 argc = 0; 697 args[argc] *= 10; 698 args[argc] += c - '0'; 699 } 700 #endif 701 702 /* Emulate basic capabilities of cons25 terminal */ 703 static void 704 efi_term_emu(int c) 705 { 706 #ifdef TERM_EMU 707 static int ansi_col[] = { 708 0, 4, 2, 6, 1, 5, 3, 7 709 }; 710 int t, i; 711 EFI_STATUS status; 712 713 switch (esc) { 714 case 0: 715 switch (c) { 716 case '\033': 717 esc = c; 718 break; 719 default: 720 efi_cons_rawputchar(c); 721 break; 722 } 723 break; 724 case '\033': 725 switch (c) { 726 case '[': 727 esc = c; 728 args[0] = 0; 729 argc = -1; 730 break; 731 default: 732 bail_out(c); 733 break; 734 } 735 break; 736 case '[': 737 switch (c) { 738 case ';': 739 if (argc < 0) 740 argc = 0; 741 else if (argc + 1 >= MAXARGS) 742 bail_out(c); 743 else 744 args[++argc] = 0; 745 break; 746 case 'H': /* ho = \E[H */ 747 if (argc < 0) 748 HO(); 749 else if (argc == 1) 750 CM(); 751 else 752 bail_out(c); 753 break; 754 case 'J': /* cd = \E[J */ 755 if (argc < 0) 756 CD(); 757 else 758 bail_out(c); 759 break; 760 case 'm': 761 if (argc < 0) { 762 fg_c = DEFAULT_FGCOLOR; 763 bg_c = DEFAULT_BGCOLOR; 764 } 765 for (i = 0; i <= argc; ++i) { 766 switch (args[i]) { 767 case 0: /* back to normal */ 768 fg_c = DEFAULT_FGCOLOR; 769 bg_c = DEFAULT_BGCOLOR; 770 break; 771 case 1: /* bold */ 772 fg_c |= 0x8; 773 break; 774 case 4: /* underline */ 775 case 5: /* blink */ 776 bg_c |= 0x8; 777 break; 778 case 7: /* reverse */ 779 t = fg_c; 780 fg_c = bg_c; 781 bg_c = t; 782 break; 783 case 22: /* normal intensity */ 784 fg_c &= ~0x8; 785 break; 786 case 24: /* not underline */ 787 case 25: /* not blinking */ 788 bg_c &= ~0x8; 789 break; 790 case 30: case 31: case 32: case 33: 791 case 34: case 35: case 36: case 37: 792 fg_c = ansi_col[args[i] - 30]; 793 break; 794 case 39: /* normal */ 795 fg_c = DEFAULT_FGCOLOR; 796 break; 797 case 40: case 41: case 42: case 43: 798 case 44: case 45: case 46: case 47: 799 bg_c = ansi_col[args[i] - 40]; 800 break; 801 case 49: /* normal */ 802 bg_c = DEFAULT_BGCOLOR; 803 break; 804 } 805 } 806 conout->SetAttribute(conout, EFI_TEXT_ATTR(fg_c, bg_c)); 807 end_term(); 808 break; 809 default: 810 if (isdigit(c)) 811 get_arg(c); 812 else 813 bail_out(c); 814 break; 815 } 816 break; 817 default: 818 bail_out(c); 819 break; 820 } 821 #else 822 efi_cons_rawputchar(c); 823 #endif 824 } 825 826 bool 827 efi_cons_update_mode(void) 828 { 829 UINTN cols, rows; 830 const teken_attr_t *a; 831 teken_attr_t attr; 832 EFI_STATUS status; 833 char env[8], *ptr; 834 835 status = conout->QueryMode(conout, conout->Mode->Mode, &cols, &rows); 836 if (EFI_ERROR(status) || cols * rows == 0) { 837 cols = 80; 838 rows = 24; 839 } 840 841 /* 842 * When we have serial port listed in ConOut, use pre-teken emulator, 843 * if built with. 844 * The problem is, we can not output text on efi and comconsole when 845 * efi also has comconsole bound. But then again, we need to have 846 * terminal emulator for efi text mode to support the menu. 847 * While teken is too expensive to be used on serial console, the 848 * pre-teken emulator is light enough to be used on serial console. 849 * 850 * When doing multiple consoles (both serial and video), 851 * also just use the old emulator. RB_MULTIPLE also implies 852 * we're using a serial console. 853 */ 854 mode = parse_uefi_con_out(); 855 if ((mode & (RB_SERIAL | RB_MULTIPLE)) == 0) { 856 if (buffer != NULL) { 857 if (tp.tp_row == rows && tp.tp_col == cols) 858 return (true); 859 free(buffer); 860 } else { 861 teken_init(&teken, &tf, NULL); 862 } 863 864 tp.tp_row = rows; 865 tp.tp_col = cols; 866 buffer = malloc(rows * cols * sizeof(*buffer)); 867 if (buffer != NULL) { 868 teken_set_winsize(&teken, &tp); 869 a = teken_get_defattr(&teken); 870 attr = *a; 871 872 /* 873 * On first run, we set up the efi_set_colors() 874 * callback. If the env is already set, we 875 * pick up fg and bg color values from the environment. 876 */ 877 ptr = getenv("teken.fg_color"); 878 if (ptr != NULL) { 879 attr.ta_fgcolor = strtol(ptr, NULL, 10); 880 ptr = getenv("teken.bg_color"); 881 attr.ta_bgcolor = strtol(ptr, NULL, 10); 882 883 teken_set_defattr(&teken, &attr); 884 } else { 885 snprintf(env, sizeof(env), "%d", 886 attr.ta_fgcolor); 887 env_setenv("teken.fg_color", EV_VOLATILE, env, 888 efi_set_colors, env_nounset); 889 snprintf(env, sizeof(env), "%d", 890 attr.ta_bgcolor); 891 env_setenv("teken.bg_color", EV_VOLATILE, env, 892 efi_set_colors, env_nounset); 893 } 894 895 for (int row = 0; row < rows; row++) { 896 for (int col = 0; col < cols; col++) { 897 buffer[col + row * tp.tp_col].c = ' '; 898 buffer[col + row * tp.tp_col].a = attr; 899 } 900 } 901 } 902 } 903 904 #ifdef TERM_EMU 905 if (buffer == NULL) { 906 conout->SetAttribute(conout, EFI_TEXT_ATTR(DEFAULT_FGCOLOR, 907 DEFAULT_BGCOLOR)); 908 end_term(); 909 get_pos(&curx, &cury); 910 curs_move(&curx, &cury, curx, cury); 911 fg_c = DEFAULT_FGCOLOR; 912 bg_c = DEFAULT_BGCOLOR; 913 } 914 #endif 915 916 snprintf(env, sizeof (env), "%u", (unsigned)rows); 917 setenv("LINES", env, 1); 918 snprintf(env, sizeof (env), "%u", (unsigned)cols); 919 setenv("COLUMNS", env, 1); 920 921 return (true); 922 } 923 924 static int 925 efi_cons_init(int arg) 926 { 927 EFI_STATUS status; 928 929 conout->EnableCursor(conout, TRUE); 930 if (efi_cons_update_mode()) 931 return (0); 932 933 return (1); 934 } 935 936 static void 937 input_partial(void) 938 { 939 unsigned i; 940 uint32_t c; 941 942 if (utf8_left == 0) 943 return; 944 945 for (i = 0; i < sizeof(utf8_partial); i++) { 946 c = (utf8_partial >> (24 - (i << 3))) & 0xff; 947 if (c != 0) 948 efi_term_emu(c); 949 } 950 utf8_left = 0; 951 utf8_partial = 0; 952 } 953 954 static void 955 input_byte(uint8_t c) 956 { 957 if ((c & 0x80) == 0x00) { 958 /* One-byte sequence. */ 959 input_partial(); 960 efi_term_emu(c); 961 return; 962 } 963 if ((c & 0xe0) == 0xc0) { 964 /* Two-byte sequence. */ 965 input_partial(); 966 utf8_left = 1; 967 utf8_partial = c; 968 return; 969 } 970 if ((c & 0xf0) == 0xe0) { 971 /* Three-byte sequence. */ 972 input_partial(); 973 utf8_left = 2; 974 utf8_partial = c; 975 return; 976 } 977 if ((c & 0xf8) == 0xf0) { 978 /* Four-byte sequence. */ 979 input_partial(); 980 utf8_left = 3; 981 utf8_partial = c; 982 return; 983 } 984 if ((c & 0xc0) == 0x80) { 985 /* Invalid state? */ 986 if (utf8_left == 0) { 987 efi_term_emu(c); 988 return; 989 } 990 utf8_left--; 991 utf8_partial = (utf8_partial << 8) | c; 992 if (utf8_left == 0) { 993 uint32_t v, u; 994 uint8_t b; 995 996 v = 0; 997 u = utf8_partial; 998 b = (u >> 24) & 0xff; 999 if (b != 0) { /* Four-byte sequence */ 1000 v = b & 0x07; 1001 b = (u >> 16) & 0xff; 1002 v = (v << 6) | (b & 0x3f); 1003 b = (u >> 8) & 0xff; 1004 v = (v << 6) | (b & 0x3f); 1005 b = u & 0xff; 1006 v = (v << 6) | (b & 0x3f); 1007 } else if ((b = (u >> 16) & 0xff) != 0) { 1008 v = b & 0x0f; /* Three-byte sequence */ 1009 b = (u >> 8) & 0xff; 1010 v = (v << 6) | (b & 0x3f); 1011 b = u & 0xff; 1012 v = (v << 6) | (b & 0x3f); 1013 } else if ((b = (u >> 8) & 0xff) != 0) { 1014 v = b & 0x1f; /* Two-byte sequence */ 1015 b = u & 0xff; 1016 v = (v << 6) | (b & 0x3f); 1017 } 1018 /* Send unicode char directly to console. */ 1019 efi_cons_efiputchar(v); 1020 utf8_partial = 0; 1021 } 1022 return; 1023 } 1024 /* Anything left is illegal in UTF-8 sequence. */ 1025 input_partial(); 1026 efi_term_emu(c); 1027 } 1028 1029 void 1030 efi_cons_putchar(int c) 1031 { 1032 unsigned char ch = c; 1033 1034 /* 1035 * Don't use Teken when we're doing pure serial, or a multiple console 1036 * with video "primary" because that's also serial. 1037 */ 1038 if ((mode & (RB_SERIAL | RB_MULTIPLE)) != 0 || buffer == NULL) { 1039 input_byte(ch); 1040 return; 1041 } 1042 1043 teken_input(&teken, &ch, sizeof (ch)); 1044 } 1045 1046 static int 1047 keybuf_getchar(void) 1048 { 1049 int i, c = 0; 1050 1051 for (i = 0; i < KEYBUFSZ; i++) { 1052 if (keybuf[i] != 0) { 1053 c = keybuf[i]; 1054 keybuf[i] = 0; 1055 break; 1056 } 1057 } 1058 1059 return (c); 1060 } 1061 1062 static bool 1063 keybuf_ischar(void) 1064 { 1065 int i; 1066 1067 for (i = 0; i < KEYBUFSZ; i++) { 1068 if (keybuf[i] != 0) 1069 return (true); 1070 } 1071 return (false); 1072 } 1073 1074 /* 1075 * We are not reading input before keybuf is empty, so we are safe 1076 * just to fill keybuf from the beginning. 1077 */ 1078 static void 1079 keybuf_inschar(EFI_INPUT_KEY *key) 1080 { 1081 1082 switch (key->ScanCode) { 1083 case SCAN_UP: /* UP */ 1084 keybuf[0] = 0x1b; /* esc */ 1085 keybuf[1] = '['; 1086 keybuf[2] = 'A'; 1087 break; 1088 case SCAN_DOWN: /* DOWN */ 1089 keybuf[0] = 0x1b; /* esc */ 1090 keybuf[1] = '['; 1091 keybuf[2] = 'B'; 1092 break; 1093 case SCAN_RIGHT: /* RIGHT */ 1094 keybuf[0] = 0x1b; /* esc */ 1095 keybuf[1] = '['; 1096 keybuf[2] = 'C'; 1097 break; 1098 case SCAN_LEFT: /* LEFT */ 1099 keybuf[0] = 0x1b; /* esc */ 1100 keybuf[1] = '['; 1101 keybuf[2] = 'D'; 1102 break; 1103 case SCAN_DELETE: 1104 keybuf[0] = CHAR_BACKSPACE; 1105 break; 1106 case SCAN_ESC: 1107 keybuf[0] = 0x1b; /* esc */ 1108 break; 1109 default: 1110 keybuf[0] = key->UnicodeChar; 1111 break; 1112 } 1113 } 1114 1115 static bool 1116 efi_readkey(void) 1117 { 1118 EFI_STATUS status; 1119 EFI_INPUT_KEY key; 1120 1121 status = conin->ReadKeyStroke(conin, &key); 1122 if (status == EFI_SUCCESS) { 1123 keybuf_inschar(&key); 1124 return (true); 1125 } 1126 return (false); 1127 } 1128 1129 static bool 1130 efi_readkey_ex(void) 1131 { 1132 EFI_STATUS status; 1133 EFI_INPUT_KEY *kp; 1134 EFI_KEY_DATA key_data; 1135 uint32_t kss; 1136 1137 status = coninex->ReadKeyStrokeEx(coninex, &key_data); 1138 if (status == EFI_SUCCESS) { 1139 kss = key_data.KeyState.KeyShiftState; 1140 kp = &key_data.Key; 1141 if (kss & EFI_SHIFT_STATE_VALID) { 1142 1143 /* 1144 * quick mapping to control chars, replace with 1145 * map lookup later. 1146 */ 1147 if (kss & EFI_RIGHT_CONTROL_PRESSED || 1148 kss & EFI_LEFT_CONTROL_PRESSED) { 1149 if (kp->UnicodeChar >= 'a' && 1150 kp->UnicodeChar <= 'z') { 1151 kp->UnicodeChar -= 'a'; 1152 kp->UnicodeChar++; 1153 } 1154 } 1155 } 1156 /* 1157 * The shift state and/or toggle state may not be valid, 1158 * but we still can have ScanCode or UnicodeChar. 1159 */ 1160 if (kp->ScanCode == 0 && kp->UnicodeChar == 0) 1161 return (false); 1162 keybuf_inschar(kp); 1163 return (true); 1164 } 1165 return (false); 1166 } 1167 1168 int 1169 efi_cons_getchar(void) 1170 { 1171 int c; 1172 1173 if ((c = keybuf_getchar()) != 0) 1174 return (c); 1175 1176 key_pending = 0; 1177 1178 if (coninex == NULL) { 1179 if (efi_readkey()) 1180 return (keybuf_getchar()); 1181 } else { 1182 if (efi_readkey_ex()) 1183 return (keybuf_getchar()); 1184 } 1185 1186 return (-1); 1187 } 1188 1189 int 1190 efi_cons_poll(void) 1191 { 1192 EFI_STATUS status; 1193 1194 if (keybuf_ischar() || key_pending) 1195 return (1); 1196 1197 /* 1198 * Some EFI implementation (u-boot for example) do not support 1199 * WaitForKey(). 1200 * CheckEvent() can clear the signaled state. 1201 */ 1202 if (coninex != NULL) { 1203 if (coninex->WaitForKeyEx == NULL) { 1204 key_pending = efi_readkey_ex(); 1205 } else { 1206 status = BS->CheckEvent(coninex->WaitForKeyEx); 1207 key_pending = status == EFI_SUCCESS; 1208 } 1209 } else { 1210 if (conin->WaitForKey == NULL) { 1211 key_pending = efi_readkey(); 1212 } else { 1213 status = BS->CheckEvent(conin->WaitForKey); 1214 key_pending = status == EFI_SUCCESS; 1215 } 1216 } 1217 1218 return (key_pending); 1219 } 1220 1221 /* Plain direct access to EFI OutputString(). */ 1222 void 1223 efi_cons_efiputchar(int c) 1224 { 1225 CHAR16 buf[2]; 1226 EFI_STATUS status; 1227 1228 buf[0] = c; 1229 buf[1] = 0; /* terminate string */ 1230 1231 status = conout->TestString(conout, buf); 1232 if (EFI_ERROR(status)) 1233 buf[0] = '?'; 1234 conout->OutputString(conout, buf); 1235 } 1236