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