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