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