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 static void 381 efi_cons_probe(struct console *cp) 382 { 383 cp->c_flags |= C_PRESENTIN | C_PRESENTOUT; 384 } 385 386 static bool 387 color_name_to_teken(const char *name, int *val) 388 { 389 if (strcasecmp(name, "black") == 0) { 390 *val = TC_BLACK; 391 return (true); 392 } 393 if (strcasecmp(name, "red") == 0) { 394 *val = TC_RED; 395 return (true); 396 } 397 if (strcasecmp(name, "green") == 0) { 398 *val = TC_GREEN; 399 return (true); 400 } 401 if (strcasecmp(name, "brown") == 0) { 402 *val = TC_BROWN; 403 return (true); 404 } 405 if (strcasecmp(name, "blue") == 0) { 406 *val = TC_BLUE; 407 return (true); 408 } 409 if (strcasecmp(name, "magenta") == 0) { 410 *val = TC_MAGENTA; 411 return (true); 412 } 413 if (strcasecmp(name, "cyan") == 0) { 414 *val = TC_CYAN; 415 return (true); 416 } 417 if (strcasecmp(name, "white") == 0) { 418 *val = TC_WHITE; 419 return (true); 420 } 421 return (false); 422 } 423 424 static int 425 efi_set_colors(struct env_var *ev, int flags, const void *value) 426 { 427 int val = 0; 428 char buf[2]; 429 const void *evalue; 430 const teken_attr_t *ap; 431 teken_attr_t a; 432 433 if (value == NULL) 434 return (CMD_OK); 435 436 if (color_name_to_teken(value, &val)) { 437 snprintf(buf, sizeof (buf), "%d", val); 438 evalue = buf; 439 } else { 440 char *end; 441 442 errno = 0; 443 val = (int)strtol(value, &end, 0); 444 if (errno != 0 || *end != '\0') { 445 printf("Allowed values are either ansi color name or " 446 "number from range [0-7].\n"); 447 return (CMD_OK); 448 } 449 evalue = value; 450 } 451 452 ap = teken_get_defattr(&teken); 453 a = *ap; 454 if (strcmp(ev->ev_name, "teken.fg_color") == 0) { 455 /* is it already set? */ 456 if (ap->ta_fgcolor == val) 457 return (CMD_OK); 458 a.ta_fgcolor = val; 459 } 460 if (strcmp(ev->ev_name, "teken.bg_color") == 0) { 461 /* is it already set? */ 462 if (ap->ta_bgcolor == val) 463 return (CMD_OK); 464 a.ta_bgcolor = val; 465 } 466 env_setenv(ev->ev_name, flags | EV_NOHOOK, evalue, NULL, NULL); 467 teken_set_defattr(&teken, &a); 468 return (CMD_OK); 469 } 470 471 #ifdef TERM_EMU 472 /* Get cursor position. */ 473 void 474 get_pos(int *x, int *y) 475 { 476 *x = conout->Mode->CursorColumn; 477 *y = conout->Mode->CursorRow; 478 } 479 480 /* Move cursor to x rows and y cols (0-based). */ 481 void 482 curs_move(int *_x, int *_y, int x, int y) 483 { 484 conout->SetCursorPosition(conout, x, y); 485 if (_x != NULL) 486 *_x = conout->Mode->CursorColumn; 487 if (_y != NULL) 488 *_y = conout->Mode->CursorRow; 489 } 490 491 /* Clear internal state of the terminal emulation code. */ 492 void 493 end_term(void) 494 { 495 esc = 0; 496 argc = -1; 497 } 498 #endif 499 500 static void 501 efi_cons_rawputchar(int c) 502 { 503 int i; 504 UINTN x, y; 505 conout->QueryMode(conout, conout->Mode->Mode, &x, &y); 506 507 if (c == '\t') { 508 int n; 509 510 n = 8 - ((conout->Mode->CursorColumn + 8) % 8); 511 for (i = 0; i < n; i++) 512 efi_cons_rawputchar(' '); 513 } else { 514 #ifndef TERM_EMU 515 if (c == '\n') 516 efi_cons_efiputchar('\r'); 517 efi_cons_efiputchar(c); 518 #else 519 switch (c) { 520 case '\r': 521 curx = 0; 522 efi_cons_efiputchar('\r'); 523 return; 524 case '\n': 525 efi_cons_efiputchar('\n'); 526 efi_cons_efiputchar('\r'); 527 cury++; 528 if (cury >= y) 529 cury--; 530 curx = 0; 531 return; 532 case '\b': 533 if (curx > 0) { 534 efi_cons_efiputchar('\b'); 535 curx--; 536 } 537 return; 538 default: 539 efi_cons_efiputchar(c); 540 curx++; 541 if (curx > x-1) { 542 curx = 0; 543 cury++; 544 } 545 if (cury > y-1) { 546 curx = 0; 547 cury--; 548 } 549 } 550 #endif 551 } 552 conout->EnableCursor(conout, TRUE); 553 } 554 555 #ifdef TERM_EMU 556 /* Gracefully exit ESC-sequence processing in case of misunderstanding. */ 557 static void 558 bail_out(int c) 559 { 560 char buf[16], *ch; 561 int i; 562 563 if (esc) { 564 efi_cons_rawputchar('\033'); 565 if (esc != '\033') 566 efi_cons_rawputchar(esc); 567 for (i = 0; i <= argc; ++i) { 568 sprintf(buf, "%d", args[i]); 569 ch = buf; 570 while (*ch) 571 efi_cons_rawputchar(*ch++); 572 } 573 } 574 efi_cons_rawputchar(c); 575 end_term(); 576 } 577 578 /* Clear display from current position to end of screen. */ 579 static void 580 CD(void) 581 { 582 int i; 583 UINTN x, y; 584 585 get_pos(&curx, &cury); 586 if (curx == 0 && cury == 0) { 587 conout->ClearScreen(conout); 588 end_term(); 589 return; 590 } 591 592 conout->QueryMode(conout, conout->Mode->Mode, &x, &y); 593 CL(0); /* clear current line from cursor to end */ 594 for (i = cury + 1; i < y-1; i++) { 595 curs_move(NULL, NULL, 0, i); 596 CL(0); 597 } 598 curs_move(NULL, NULL, curx, cury); 599 end_term(); 600 } 601 602 /* 603 * Absolute cursor move to args[0] rows and args[1] columns 604 * (the coordinates are 1-based). 605 */ 606 static void 607 CM(void) 608 { 609 if (args[0] > 0) 610 args[0]--; 611 if (args[1] > 0) 612 args[1]--; 613 curs_move(&curx, &cury, args[1], args[0]); 614 end_term(); 615 } 616 617 /* Home cursor (left top corner), also called from mode command. */ 618 void 619 HO(void) 620 { 621 argc = 1; 622 args[0] = args[1] = 1; 623 CM(); 624 } 625 626 /* Clear line from current position to end of line */ 627 static void 628 CL(int direction) 629 { 630 int i, len; 631 UINTN x, y; 632 CHAR16 *line; 633 634 conout->QueryMode(conout, conout->Mode->Mode, &x, &y); 635 switch (direction) { 636 case 0: /* from cursor to end */ 637 len = x - curx + 1; 638 break; 639 case 1: /* from beginning to cursor */ 640 len = curx; 641 break; 642 case 2: /* entire line */ 643 len = x; 644 break; 645 default: /* NOTREACHED */ 646 __unreachable(); 647 } 648 649 if (cury == y - 1) 650 len--; 651 652 line = malloc(len * sizeof (CHAR16)); 653 if (line == NULL) { 654 printf("out of memory\n"); 655 return; 656 } 657 for (i = 0; i < len; i++) 658 line[i] = ' '; 659 line[len-1] = 0; 660 661 if (direction != 0) 662 curs_move(NULL, NULL, 0, cury); 663 664 conout->OutputString(conout, line); 665 /* restore cursor position */ 666 curs_move(NULL, NULL, curx, cury); 667 free(line); 668 end_term(); 669 } 670 671 static void 672 get_arg(int c) 673 { 674 if (argc < 0) 675 argc = 0; 676 args[argc] *= 10; 677 args[argc] += c - '0'; 678 } 679 #endif 680 681 /* Emulate basic capabilities of cons25 terminal */ 682 static void 683 efi_term_emu(int c) 684 { 685 #ifdef TERM_EMU 686 static int ansi_col[] = { 687 0, 4, 2, 6, 1, 5, 3, 7 688 }; 689 int t, i; 690 EFI_STATUS status; 691 692 switch (esc) { 693 case 0: 694 switch (c) { 695 case '\033': 696 esc = c; 697 break; 698 default: 699 efi_cons_rawputchar(c); 700 break; 701 } 702 break; 703 case '\033': 704 switch (c) { 705 case '[': 706 esc = c; 707 args[0] = 0; 708 argc = -1; 709 break; 710 default: 711 bail_out(c); 712 break; 713 } 714 break; 715 case '[': 716 switch (c) { 717 case ';': 718 if (argc < 0) 719 argc = 0; 720 else if (argc + 1 >= MAXARGS) 721 bail_out(c); 722 else 723 args[++argc] = 0; 724 break; 725 case 'H': /* ho = \E[H */ 726 if (argc < 0) 727 HO(); 728 else if (argc == 1) 729 CM(); 730 else 731 bail_out(c); 732 break; 733 case 'J': /* cd = \E[J */ 734 if (argc < 0) 735 CD(); 736 else 737 bail_out(c); 738 break; 739 case 'm': 740 if (argc < 0) { 741 fg_c = DEFAULT_FGCOLOR; 742 bg_c = DEFAULT_BGCOLOR; 743 } 744 for (i = 0; i <= argc; ++i) { 745 switch (args[i]) { 746 case 0: /* back to normal */ 747 fg_c = DEFAULT_FGCOLOR; 748 bg_c = DEFAULT_BGCOLOR; 749 break; 750 case 1: /* bold */ 751 fg_c |= 0x8; 752 break; 753 case 4: /* underline */ 754 case 5: /* blink */ 755 bg_c |= 0x8; 756 break; 757 case 7: /* reverse */ 758 t = fg_c; 759 fg_c = bg_c; 760 bg_c = t; 761 break; 762 case 22: /* normal intensity */ 763 fg_c &= ~0x8; 764 break; 765 case 24: /* not underline */ 766 case 25: /* not blinking */ 767 bg_c &= ~0x8; 768 break; 769 case 30: case 31: case 32: case 33: 770 case 34: case 35: case 36: case 37: 771 fg_c = ansi_col[args[i] - 30]; 772 break; 773 case 39: /* normal */ 774 fg_c = DEFAULT_FGCOLOR; 775 break; 776 case 40: case 41: case 42: case 43: 777 case 44: case 45: case 46: case 47: 778 bg_c = ansi_col[args[i] - 40]; 779 break; 780 case 49: /* normal */ 781 bg_c = DEFAULT_BGCOLOR; 782 break; 783 } 784 } 785 conout->SetAttribute(conout, EFI_TEXT_ATTR(fg_c, bg_c)); 786 end_term(); 787 break; 788 default: 789 if (isdigit(c)) 790 get_arg(c); 791 else 792 bail_out(c); 793 break; 794 } 795 break; 796 default: 797 bail_out(c); 798 break; 799 } 800 #else 801 efi_cons_rawputchar(c); 802 #endif 803 } 804 805 bool 806 efi_cons_update_mode(void) 807 { 808 UINTN cols, rows; 809 const teken_attr_t *a; 810 EFI_STATUS status; 811 char env[8]; 812 813 status = conout->QueryMode(conout, conout->Mode->Mode, &cols, &rows); 814 if (EFI_ERROR(status)) { 815 cols = 80; 816 rows = 24; 817 } 818 819 /* 820 * When we have serial port listed in ConOut, use pre-teken emulator, 821 * if built with. 822 * The problem is, we can not output text on efi and comconsole when 823 * efi also has comconsole bound. But then again, we need to have 824 * terminal emulator for efi text mode to support the menu. 825 * While teken is too expensive to be used on serial console, the 826 * pre-teken emulator is light enough to be used on serial console. 827 * 828 * When doing multiple consoles (both serial and video), 829 * also just use the old emulator. RB_MULTIPLE also implies 830 * we're using a serial console. 831 */ 832 mode = parse_uefi_con_out(); 833 if ((mode & (RB_SERIAL | RB_MULTIPLE)) == 0) { 834 if (buffer != NULL) { 835 if (tp.tp_row == rows && tp.tp_col == cols) 836 return (true); 837 free(buffer); 838 } else { 839 teken_init(&teken, &tf, NULL); 840 } 841 842 tp.tp_row = rows; 843 tp.tp_col = cols; 844 buffer = malloc(rows * cols * sizeof(*buffer)); 845 if (buffer == NULL) 846 return (false); 847 848 teken_set_winsize(&teken, &tp); 849 a = teken_get_defattr(&teken); 850 851 snprintf(env, sizeof(env), "%d", a->ta_fgcolor); 852 env_setenv("teken.fg_color", EV_VOLATILE, env, efi_set_colors, 853 env_nounset); 854 snprintf(env, sizeof(env), "%d", a->ta_bgcolor); 855 env_setenv("teken.bg_color", EV_VOLATILE, env, efi_set_colors, 856 env_nounset); 857 858 for (int row = 0; row < rows; row++) { 859 for (int col = 0; col < cols; col++) { 860 buffer[col + row * tp.tp_col].c = ' '; 861 buffer[col + row * tp.tp_col].a = *a; 862 } 863 } 864 } else { 865 #ifdef TERM_EMU 866 conout->SetAttribute(conout, EFI_TEXT_ATTR(DEFAULT_FGCOLOR, 867 DEFAULT_BGCOLOR)); 868 end_term(); 869 get_pos(&curx, &cury); 870 curs_move(&curx, &cury, curx, cury); 871 fg_c = DEFAULT_FGCOLOR; 872 bg_c = DEFAULT_BGCOLOR; 873 #endif 874 } 875 876 snprintf(env, sizeof (env), "%u", (unsigned)rows); 877 setenv("LINES", env, 1); 878 snprintf(env, sizeof (env), "%u", (unsigned)cols); 879 setenv("COLUMNS", env, 1); 880 881 return (true); 882 } 883 884 static int 885 efi_cons_init(int arg) 886 { 887 EFI_STATUS status; 888 889 if (conin != NULL) 890 return (0); 891 892 conout = ST->ConOut; 893 conin = ST->ConIn; 894 895 conout->EnableCursor(conout, TRUE); 896 status = BS->OpenProtocol(ST->ConsoleInHandle, &simple_input_ex_guid, 897 (void **)&coninex, IH, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL); 898 if (status != EFI_SUCCESS) 899 coninex = NULL; 900 901 if (efi_cons_update_mode()) 902 return (0); 903 904 return (1); 905 } 906 907 static void 908 input_partial(void) 909 { 910 unsigned i; 911 uint32_t c; 912 913 if (utf8_left == 0) 914 return; 915 916 for (i = 0; i < sizeof(utf8_partial); i++) { 917 c = (utf8_partial >> (24 - (i << 3))) & 0xff; 918 if (c != 0) 919 efi_term_emu(c); 920 } 921 utf8_left = 0; 922 utf8_partial = 0; 923 } 924 925 static void 926 input_byte(uint8_t c) 927 { 928 if ((c & 0x80) == 0x00) { 929 /* One-byte sequence. */ 930 input_partial(); 931 efi_term_emu(c); 932 return; 933 } 934 if ((c & 0xe0) == 0xc0) { 935 /* Two-byte sequence. */ 936 input_partial(); 937 utf8_left = 1; 938 utf8_partial = c; 939 return; 940 } 941 if ((c & 0xf0) == 0xe0) { 942 /* Three-byte sequence. */ 943 input_partial(); 944 utf8_left = 2; 945 utf8_partial = c; 946 return; 947 } 948 if ((c & 0xf8) == 0xf0) { 949 /* Four-byte sequence. */ 950 input_partial(); 951 utf8_left = 3; 952 utf8_partial = c; 953 return; 954 } 955 if ((c & 0xc0) == 0x80) { 956 /* Invalid state? */ 957 if (utf8_left == 0) { 958 efi_term_emu(c); 959 return; 960 } 961 utf8_left--; 962 utf8_partial = (utf8_partial << 8) | c; 963 if (utf8_left == 0) { 964 uint32_t v, u; 965 uint8_t b; 966 967 v = 0; 968 u = utf8_partial; 969 b = (u >> 24) & 0xff; 970 if (b != 0) { /* Four-byte sequence */ 971 v = b & 0x07; 972 b = (u >> 16) & 0xff; 973 v = (v << 6) | (b & 0x3f); 974 b = (u >> 8) & 0xff; 975 v = (v << 6) | (b & 0x3f); 976 b = u & 0xff; 977 v = (v << 6) | (b & 0x3f); 978 } else if ((b = (u >> 16) & 0xff) != 0) { 979 v = b & 0x0f; /* Three-byte sequence */ 980 b = (u >> 8) & 0xff; 981 v = (v << 6) | (b & 0x3f); 982 b = u & 0xff; 983 v = (v << 6) | (b & 0x3f); 984 } else if ((b = (u >> 8) & 0xff) != 0) { 985 v = b & 0x1f; /* Two-byte sequence */ 986 b = u & 0xff; 987 v = (v << 6) | (b & 0x3f); 988 } 989 /* Send unicode char directly to console. */ 990 efi_cons_efiputchar(v); 991 utf8_partial = 0; 992 } 993 return; 994 } 995 /* Anything left is illegal in UTF-8 sequence. */ 996 input_partial(); 997 efi_term_emu(c); 998 } 999 1000 void 1001 efi_cons_putchar(int c) 1002 { 1003 unsigned char ch = c; 1004 1005 /* 1006 * Don't use Teken when we're doing pure serial, or a multiple console 1007 * with video "primary" because that's also serial. 1008 */ 1009 if ((mode & (RB_SERIAL | RB_MULTIPLE)) != 0) { 1010 input_byte(ch); 1011 return; 1012 } 1013 1014 if (buffer != NULL) 1015 teken_input(&teken, &ch, sizeof (ch)); 1016 else 1017 efi_cons_efiputchar(c); 1018 } 1019 1020 static int 1021 keybuf_getchar(void) 1022 { 1023 int i, c = 0; 1024 1025 for (i = 0; i < KEYBUFSZ; i++) { 1026 if (keybuf[i] != 0) { 1027 c = keybuf[i]; 1028 keybuf[i] = 0; 1029 break; 1030 } 1031 } 1032 1033 return (c); 1034 } 1035 1036 static bool 1037 keybuf_ischar(void) 1038 { 1039 int i; 1040 1041 for (i = 0; i < KEYBUFSZ; i++) { 1042 if (keybuf[i] != 0) 1043 return (true); 1044 } 1045 return (false); 1046 } 1047 1048 /* 1049 * We are not reading input before keybuf is empty, so we are safe 1050 * just to fill keybuf from the beginning. 1051 */ 1052 static void 1053 keybuf_inschar(EFI_INPUT_KEY *key) 1054 { 1055 1056 switch (key->ScanCode) { 1057 case SCAN_UP: /* UP */ 1058 keybuf[0] = 0x1b; /* esc */ 1059 keybuf[1] = '['; 1060 keybuf[2] = 'A'; 1061 break; 1062 case SCAN_DOWN: /* DOWN */ 1063 keybuf[0] = 0x1b; /* esc */ 1064 keybuf[1] = '['; 1065 keybuf[2] = 'B'; 1066 break; 1067 case SCAN_RIGHT: /* RIGHT */ 1068 keybuf[0] = 0x1b; /* esc */ 1069 keybuf[1] = '['; 1070 keybuf[2] = 'C'; 1071 break; 1072 case SCAN_LEFT: /* LEFT */ 1073 keybuf[0] = 0x1b; /* esc */ 1074 keybuf[1] = '['; 1075 keybuf[2] = 'D'; 1076 break; 1077 case SCAN_DELETE: 1078 keybuf[0] = CHAR_BACKSPACE; 1079 break; 1080 case SCAN_ESC: 1081 keybuf[0] = 0x1b; /* esc */ 1082 break; 1083 default: 1084 keybuf[0] = key->UnicodeChar; 1085 break; 1086 } 1087 } 1088 1089 static bool 1090 efi_readkey(void) 1091 { 1092 EFI_STATUS status; 1093 EFI_INPUT_KEY key; 1094 1095 status = conin->ReadKeyStroke(conin, &key); 1096 if (status == EFI_SUCCESS) { 1097 keybuf_inschar(&key); 1098 return (true); 1099 } 1100 return (false); 1101 } 1102 1103 static bool 1104 efi_readkey_ex(void) 1105 { 1106 EFI_STATUS status; 1107 EFI_INPUT_KEY *kp; 1108 EFI_KEY_DATA key_data; 1109 uint32_t kss; 1110 1111 status = coninex->ReadKeyStrokeEx(coninex, &key_data); 1112 if (status == EFI_SUCCESS) { 1113 kss = key_data.KeyState.KeyShiftState; 1114 kp = &key_data.Key; 1115 if (kss & EFI_SHIFT_STATE_VALID) { 1116 1117 /* 1118 * quick mapping to control chars, replace with 1119 * map lookup later. 1120 */ 1121 if (kss & EFI_RIGHT_CONTROL_PRESSED || 1122 kss & EFI_LEFT_CONTROL_PRESSED) { 1123 if (kp->UnicodeChar >= 'a' && 1124 kp->UnicodeChar <= 'z') { 1125 kp->UnicodeChar -= 'a'; 1126 kp->UnicodeChar++; 1127 } 1128 } 1129 } 1130 /* 1131 * The shift state and/or toggle state may not be valid, 1132 * but we still can have ScanCode or UnicodeChar. 1133 */ 1134 if (kp->ScanCode == 0 && kp->UnicodeChar == 0) 1135 return (false); 1136 keybuf_inschar(kp); 1137 return (true); 1138 } 1139 return (false); 1140 } 1141 1142 int 1143 efi_cons_getchar(void) 1144 { 1145 int c; 1146 1147 if ((c = keybuf_getchar()) != 0) 1148 return (c); 1149 1150 key_pending = 0; 1151 1152 if (coninex == NULL) { 1153 if (efi_readkey()) 1154 return (keybuf_getchar()); 1155 } else { 1156 if (efi_readkey_ex()) 1157 return (keybuf_getchar()); 1158 } 1159 1160 return (-1); 1161 } 1162 1163 int 1164 efi_cons_poll(void) 1165 { 1166 EFI_STATUS status; 1167 1168 if (keybuf_ischar() || key_pending) 1169 return (1); 1170 1171 /* 1172 * Some EFI implementation (u-boot for example) do not support 1173 * WaitForKey(). 1174 * CheckEvent() can clear the signaled state. 1175 */ 1176 if (coninex != NULL) { 1177 if (coninex->WaitForKeyEx == NULL) { 1178 key_pending = efi_readkey_ex(); 1179 } else { 1180 status = BS->CheckEvent(coninex->WaitForKeyEx); 1181 key_pending = status == EFI_SUCCESS; 1182 } 1183 } else { 1184 if (conin->WaitForKey == NULL) { 1185 key_pending = efi_readkey(); 1186 } else { 1187 status = BS->CheckEvent(conin->WaitForKey); 1188 key_pending = status == EFI_SUCCESS; 1189 } 1190 } 1191 1192 return (key_pending); 1193 } 1194 1195 /* Plain direct access to EFI OutputString(). */ 1196 void 1197 efi_cons_efiputchar(int c) 1198 { 1199 CHAR16 buf[2]; 1200 EFI_STATUS status; 1201 1202 buf[0] = c; 1203 buf[1] = 0; /* terminate string */ 1204 1205 status = conout->TestString(conout, buf); 1206 if (EFI_ERROR(status)) 1207 buf[0] = '?'; 1208 conout->OutputString(conout, buf); 1209 } 1210