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 EFI_STATUS status; 832 char env[8]; 833 834 status = conout->QueryMode(conout, conout->Mode->Mode, &cols, &rows); 835 if (EFI_ERROR(status) || cols * rows == 0) { 836 cols = 80; 837 rows = 24; 838 } 839 840 /* 841 * When we have serial port listed in ConOut, use pre-teken emulator, 842 * if built with. 843 * The problem is, we can not output text on efi and comconsole when 844 * efi also has comconsole bound. But then again, we need to have 845 * terminal emulator for efi text mode to support the menu. 846 * While teken is too expensive to be used on serial console, the 847 * pre-teken emulator is light enough to be used on serial console. 848 * 849 * When doing multiple consoles (both serial and video), 850 * also just use the old emulator. RB_MULTIPLE also implies 851 * we're using a serial console. 852 */ 853 mode = parse_uefi_con_out(); 854 if ((mode & (RB_SERIAL | RB_MULTIPLE)) == 0) { 855 if (buffer != NULL) { 856 if (tp.tp_row == rows && tp.tp_col == cols) 857 return (true); 858 free(buffer); 859 } else { 860 teken_init(&teken, &tf, NULL); 861 } 862 863 tp.tp_row = rows; 864 tp.tp_col = cols; 865 buffer = malloc(rows * cols * sizeof(*buffer)); 866 if (buffer != NULL) { 867 teken_set_winsize(&teken, &tp); 868 a = teken_get_defattr(&teken); 869 870 snprintf(env, sizeof(env), "%d", a->ta_fgcolor); 871 env_setenv("teken.fg_color", EV_VOLATILE, env, 872 efi_set_colors, env_nounset); 873 snprintf(env, sizeof(env), "%d", a->ta_bgcolor); 874 env_setenv("teken.bg_color", EV_VOLATILE, env, 875 efi_set_colors, env_nounset); 876 877 for (int row = 0; row < rows; row++) { 878 for (int col = 0; col < cols; col++) { 879 buffer[col + row * tp.tp_col].c = ' '; 880 buffer[col + row * tp.tp_col].a = *a; 881 } 882 } 883 } 884 } 885 886 #ifdef TERM_EMU 887 if (buffer == NULL) { 888 conout->SetAttribute(conout, EFI_TEXT_ATTR(DEFAULT_FGCOLOR, 889 DEFAULT_BGCOLOR)); 890 end_term(); 891 get_pos(&curx, &cury); 892 curs_move(&curx, &cury, curx, cury); 893 fg_c = DEFAULT_FGCOLOR; 894 bg_c = DEFAULT_BGCOLOR; 895 } 896 #endif 897 898 snprintf(env, sizeof (env), "%u", (unsigned)rows); 899 setenv("LINES", env, 1); 900 snprintf(env, sizeof (env), "%u", (unsigned)cols); 901 setenv("COLUMNS", env, 1); 902 903 return (true); 904 } 905 906 static int 907 efi_cons_init(int arg) 908 { 909 EFI_STATUS status; 910 911 if (conin != NULL) 912 return (0); 913 914 conout->EnableCursor(conout, TRUE); 915 if (efi_cons_update_mode()) 916 return (0); 917 918 return (1); 919 } 920 921 static void 922 input_partial(void) 923 { 924 unsigned i; 925 uint32_t c; 926 927 if (utf8_left == 0) 928 return; 929 930 for (i = 0; i < sizeof(utf8_partial); i++) { 931 c = (utf8_partial >> (24 - (i << 3))) & 0xff; 932 if (c != 0) 933 efi_term_emu(c); 934 } 935 utf8_left = 0; 936 utf8_partial = 0; 937 } 938 939 static void 940 input_byte(uint8_t c) 941 { 942 if ((c & 0x80) == 0x00) { 943 /* One-byte sequence. */ 944 input_partial(); 945 efi_term_emu(c); 946 return; 947 } 948 if ((c & 0xe0) == 0xc0) { 949 /* Two-byte sequence. */ 950 input_partial(); 951 utf8_left = 1; 952 utf8_partial = c; 953 return; 954 } 955 if ((c & 0xf0) == 0xe0) { 956 /* Three-byte sequence. */ 957 input_partial(); 958 utf8_left = 2; 959 utf8_partial = c; 960 return; 961 } 962 if ((c & 0xf8) == 0xf0) { 963 /* Four-byte sequence. */ 964 input_partial(); 965 utf8_left = 3; 966 utf8_partial = c; 967 return; 968 } 969 if ((c & 0xc0) == 0x80) { 970 /* Invalid state? */ 971 if (utf8_left == 0) { 972 efi_term_emu(c); 973 return; 974 } 975 utf8_left--; 976 utf8_partial = (utf8_partial << 8) | c; 977 if (utf8_left == 0) { 978 uint32_t v, u; 979 uint8_t b; 980 981 v = 0; 982 u = utf8_partial; 983 b = (u >> 24) & 0xff; 984 if (b != 0) { /* Four-byte sequence */ 985 v = b & 0x07; 986 b = (u >> 16) & 0xff; 987 v = (v << 6) | (b & 0x3f); 988 b = (u >> 8) & 0xff; 989 v = (v << 6) | (b & 0x3f); 990 b = u & 0xff; 991 v = (v << 6) | (b & 0x3f); 992 } else if ((b = (u >> 16) & 0xff) != 0) { 993 v = b & 0x0f; /* Three-byte sequence */ 994 b = (u >> 8) & 0xff; 995 v = (v << 6) | (b & 0x3f); 996 b = u & 0xff; 997 v = (v << 6) | (b & 0x3f); 998 } else if ((b = (u >> 8) & 0xff) != 0) { 999 v = b & 0x1f; /* Two-byte sequence */ 1000 b = u & 0xff; 1001 v = (v << 6) | (b & 0x3f); 1002 } 1003 /* Send unicode char directly to console. */ 1004 efi_cons_efiputchar(v); 1005 utf8_partial = 0; 1006 } 1007 return; 1008 } 1009 /* Anything left is illegal in UTF-8 sequence. */ 1010 input_partial(); 1011 efi_term_emu(c); 1012 } 1013 1014 void 1015 efi_cons_putchar(int c) 1016 { 1017 unsigned char ch = c; 1018 1019 /* 1020 * Don't use Teken when we're doing pure serial, or a multiple console 1021 * with video "primary" because that's also serial. 1022 */ 1023 if ((mode & (RB_SERIAL | RB_MULTIPLE)) != 0 || buffer == NULL) { 1024 input_byte(ch); 1025 return; 1026 } 1027 1028 teken_input(&teken, &ch, sizeof (ch)); 1029 } 1030 1031 static int 1032 keybuf_getchar(void) 1033 { 1034 int i, c = 0; 1035 1036 for (i = 0; i < KEYBUFSZ; i++) { 1037 if (keybuf[i] != 0) { 1038 c = keybuf[i]; 1039 keybuf[i] = 0; 1040 break; 1041 } 1042 } 1043 1044 return (c); 1045 } 1046 1047 static bool 1048 keybuf_ischar(void) 1049 { 1050 int i; 1051 1052 for (i = 0; i < KEYBUFSZ; i++) { 1053 if (keybuf[i] != 0) 1054 return (true); 1055 } 1056 return (false); 1057 } 1058 1059 /* 1060 * We are not reading input before keybuf is empty, so we are safe 1061 * just to fill keybuf from the beginning. 1062 */ 1063 static void 1064 keybuf_inschar(EFI_INPUT_KEY *key) 1065 { 1066 1067 switch (key->ScanCode) { 1068 case SCAN_UP: /* UP */ 1069 keybuf[0] = 0x1b; /* esc */ 1070 keybuf[1] = '['; 1071 keybuf[2] = 'A'; 1072 break; 1073 case SCAN_DOWN: /* DOWN */ 1074 keybuf[0] = 0x1b; /* esc */ 1075 keybuf[1] = '['; 1076 keybuf[2] = 'B'; 1077 break; 1078 case SCAN_RIGHT: /* RIGHT */ 1079 keybuf[0] = 0x1b; /* esc */ 1080 keybuf[1] = '['; 1081 keybuf[2] = 'C'; 1082 break; 1083 case SCAN_LEFT: /* LEFT */ 1084 keybuf[0] = 0x1b; /* esc */ 1085 keybuf[1] = '['; 1086 keybuf[2] = 'D'; 1087 break; 1088 case SCAN_DELETE: 1089 keybuf[0] = CHAR_BACKSPACE; 1090 break; 1091 case SCAN_ESC: 1092 keybuf[0] = 0x1b; /* esc */ 1093 break; 1094 default: 1095 keybuf[0] = key->UnicodeChar; 1096 break; 1097 } 1098 } 1099 1100 static bool 1101 efi_readkey(void) 1102 { 1103 EFI_STATUS status; 1104 EFI_INPUT_KEY key; 1105 1106 status = conin->ReadKeyStroke(conin, &key); 1107 if (status == EFI_SUCCESS) { 1108 keybuf_inschar(&key); 1109 return (true); 1110 } 1111 return (false); 1112 } 1113 1114 static bool 1115 efi_readkey_ex(void) 1116 { 1117 EFI_STATUS status; 1118 EFI_INPUT_KEY *kp; 1119 EFI_KEY_DATA key_data; 1120 uint32_t kss; 1121 1122 status = coninex->ReadKeyStrokeEx(coninex, &key_data); 1123 if (status == EFI_SUCCESS) { 1124 kss = key_data.KeyState.KeyShiftState; 1125 kp = &key_data.Key; 1126 if (kss & EFI_SHIFT_STATE_VALID) { 1127 1128 /* 1129 * quick mapping to control chars, replace with 1130 * map lookup later. 1131 */ 1132 if (kss & EFI_RIGHT_CONTROL_PRESSED || 1133 kss & EFI_LEFT_CONTROL_PRESSED) { 1134 if (kp->UnicodeChar >= 'a' && 1135 kp->UnicodeChar <= 'z') { 1136 kp->UnicodeChar -= 'a'; 1137 kp->UnicodeChar++; 1138 } 1139 } 1140 } 1141 /* 1142 * The shift state and/or toggle state may not be valid, 1143 * but we still can have ScanCode or UnicodeChar. 1144 */ 1145 if (kp->ScanCode == 0 && kp->UnicodeChar == 0) 1146 return (false); 1147 keybuf_inschar(kp); 1148 return (true); 1149 } 1150 return (false); 1151 } 1152 1153 int 1154 efi_cons_getchar(void) 1155 { 1156 int c; 1157 1158 if ((c = keybuf_getchar()) != 0) 1159 return (c); 1160 1161 key_pending = 0; 1162 1163 if (coninex == NULL) { 1164 if (efi_readkey()) 1165 return (keybuf_getchar()); 1166 } else { 1167 if (efi_readkey_ex()) 1168 return (keybuf_getchar()); 1169 } 1170 1171 return (-1); 1172 } 1173 1174 int 1175 efi_cons_poll(void) 1176 { 1177 EFI_STATUS status; 1178 1179 if (keybuf_ischar() || key_pending) 1180 return (1); 1181 1182 /* 1183 * Some EFI implementation (u-boot for example) do not support 1184 * WaitForKey(). 1185 * CheckEvent() can clear the signaled state. 1186 */ 1187 if (coninex != NULL) { 1188 if (coninex->WaitForKeyEx == NULL) { 1189 key_pending = efi_readkey_ex(); 1190 } else { 1191 status = BS->CheckEvent(coninex->WaitForKeyEx); 1192 key_pending = status == EFI_SUCCESS; 1193 } 1194 } else { 1195 if (conin->WaitForKey == NULL) { 1196 key_pending = efi_readkey(); 1197 } else { 1198 status = BS->CheckEvent(conin->WaitForKey); 1199 key_pending = status == EFI_SUCCESS; 1200 } 1201 } 1202 1203 return (key_pending); 1204 } 1205 1206 /* Plain direct access to EFI OutputString(). */ 1207 void 1208 efi_cons_efiputchar(int c) 1209 { 1210 CHAR16 buf[2]; 1211 EFI_STATUS status; 1212 1213 buf[0] = c; 1214 buf[1] = 0; /* terminate string */ 1215 1216 status = conout->TestString(conout, buf); 1217 if (EFI_ERROR(status)) 1218 buf[0] = '?'; 1219 conout->OutputString(conout, buf); 1220 } 1221