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 <sys/param.h> 31 #include <efi.h> 32 #include <efilib.h> 33 #include <teken.h> 34 #include <sys/reboot.h> 35 #include <machine/metadata.h> 36 #include <gfx_fb.h> 37 #include "bootstrap.h" 38 39 extern EFI_GUID gop_guid; 40 extern int efi_find_framebuffer(struct efi_fb *efifb); 41 static EFI_GUID simple_input_ex_guid = EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL_GUID; 42 static SIMPLE_TEXT_OUTPUT_INTERFACE *conout; 43 static SIMPLE_INPUT_INTERFACE *conin; 44 static EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *coninex; 45 static bool efi_started; 46 47 static int mode; /* Does ConOut have serial console? */ 48 49 static uint32_t utf8_left; 50 static uint32_t utf8_partial; 51 #ifdef TERM_EMU 52 #define DEFAULT_FGCOLOR EFI_LIGHTGRAY 53 #define DEFAULT_BGCOLOR EFI_BLACK 54 55 #define MAXARGS 8 56 static int args[MAXARGS], argc; 57 static int fg_c, bg_c, curx, cury; 58 static int esc; 59 60 void get_pos(int *x, int *y); 61 void curs_move(int *_x, int *_y, int x, int y); 62 static void CL(int); 63 void HO(void); 64 void end_term(void); 65 #endif 66 67 #define TEXT_ROWS 24 68 #define TEXT_COLS 80 69 70 static tf_bell_t efi_cons_bell; 71 static tf_cursor_t efi_text_cursor; 72 static tf_putchar_t efi_text_putchar; 73 static tf_fill_t efi_text_fill; 74 static tf_copy_t efi_text_copy; 75 static tf_param_t efi_text_param; 76 static tf_respond_t efi_cons_respond; 77 78 static teken_funcs_t tf = { 79 .tf_bell = efi_cons_bell, 80 .tf_cursor = efi_text_cursor, 81 .tf_putchar = efi_text_putchar, 82 .tf_fill = efi_text_fill, 83 .tf_copy = efi_text_copy, 84 .tf_param = efi_text_param, 85 .tf_respond = efi_cons_respond, 86 }; 87 88 static teken_funcs_t tfx = { 89 .tf_bell = efi_cons_bell, 90 .tf_cursor = gfx_fb_cursor, 91 .tf_putchar = gfx_fb_putchar, 92 .tf_fill = gfx_fb_fill, 93 .tf_copy = gfx_fb_copy, 94 .tf_param = gfx_fb_param, 95 .tf_respond = efi_cons_respond, 96 }; 97 98 #define KEYBUFSZ 10 99 static unsigned keybuf[KEYBUFSZ]; /* keybuf for extended codes */ 100 static int key_pending; 101 102 static const unsigned char teken_color_to_efi_color[16] = { 103 EFI_BLACK, 104 EFI_RED, 105 EFI_GREEN, 106 EFI_BROWN, 107 EFI_BLUE, 108 EFI_MAGENTA, 109 EFI_CYAN, 110 EFI_LIGHTGRAY, 111 EFI_DARKGRAY, 112 EFI_LIGHTRED, 113 EFI_LIGHTGREEN, 114 EFI_YELLOW, 115 EFI_LIGHTBLUE, 116 EFI_LIGHTMAGENTA, 117 EFI_LIGHTCYAN, 118 EFI_WHITE 119 }; 120 121 static void efi_cons_probe(struct console *); 122 static int efi_cons_init(int); 123 void efi_cons_putchar(int); 124 int efi_cons_getchar(void); 125 void efi_cons_efiputchar(int); 126 int efi_cons_poll(void); 127 static void cons_draw_frame(teken_attr_t *); 128 129 struct console efi_console = { 130 "efi", 131 "EFI console", 132 C_WIDEOUT, 133 efi_cons_probe, 134 efi_cons_init, 135 efi_cons_putchar, 136 efi_cons_getchar, 137 efi_cons_poll 138 }; 139 140 /* 141 * This function is used to mark a rectangular image area so the scrolling 142 * will know we need to copy the data from there. 143 */ 144 void 145 term_image_display(teken_gfx_t *state, const teken_rect_t *r) 146 { 147 teken_pos_t p; 148 int idx; 149 150 for (p.tp_row = r->tr_begin.tp_row; 151 p.tp_row < r->tr_end.tp_row; p.tp_row++) { 152 for (p.tp_col = r->tr_begin.tp_col; 153 p.tp_col < r->tr_end.tp_col; p.tp_col++) { 154 idx = p.tp_col + p.tp_row * state->tg_tp.tp_col; 155 if (idx >= state->tg_tp.tp_col * state->tg_tp.tp_row) 156 return; 157 screen_buffer[idx].a.ta_format |= TF_IMAGE; 158 } 159 } 160 } 161 162 /* 163 * Not implemented. 164 */ 165 static void 166 efi_cons_bell(void *s __unused) 167 { 168 } 169 170 static void 171 efi_text_cursor(void *arg, const teken_pos_t *p) 172 { 173 teken_gfx_t *state = arg; 174 UINTN col, row; 175 176 row = p->tp_row; 177 if (p->tp_row >= state->tg_tp.tp_row) 178 row = state->tg_tp.tp_row - 1; 179 180 col = p->tp_col; 181 if (p->tp_col >= state->tg_tp.tp_col) 182 col = state->tg_tp.tp_col - 1; 183 184 conout->SetCursorPosition(conout, col, row); 185 } 186 187 static void 188 efi_text_printchar(teken_gfx_t *state, const teken_pos_t *p, bool autoscroll) 189 { 190 UINTN a, attr; 191 struct text_pixel *px; 192 teken_color_t fg, bg, tmp; 193 194 px = screen_buffer + p->tp_col + p->tp_row * state->tg_tp.tp_col; 195 a = conout->Mode->Attribute; 196 197 fg = teken_256to16(px->a.ta_fgcolor); 198 bg = teken_256to16(px->a.ta_bgcolor); 199 if (px->a.ta_format & TF_BOLD) 200 fg |= TC_LIGHT; 201 if (px->a.ta_format & TF_BLINK) 202 bg |= TC_LIGHT; 203 204 if (px->a.ta_format & TF_REVERSE) { 205 tmp = fg; 206 fg = bg; 207 bg = tmp; 208 } 209 210 attr = EFI_TEXT_ATTR(teken_color_to_efi_color[fg], 211 teken_color_to_efi_color[bg] & 0x7); 212 213 conout->SetCursorPosition(conout, p->tp_col, p->tp_row); 214 215 /* to prevent autoscroll, skip print of lower right char */ 216 if (!autoscroll && 217 p->tp_row == state->tg_tp.tp_row - 1 && 218 p->tp_col == state->tg_tp.tp_col - 1) 219 return; 220 221 (void) conout->SetAttribute(conout, attr); 222 efi_cons_efiputchar(px->c); 223 (void) conout->SetAttribute(conout, a); 224 } 225 226 static void 227 efi_text_putchar(void *s, const teken_pos_t *p, teken_char_t c, 228 const teken_attr_t *a) 229 { 230 teken_gfx_t *state = s; 231 EFI_STATUS status; 232 int idx; 233 234 idx = p->tp_col + p->tp_row * state->tg_tp.tp_col; 235 if (idx >= state->tg_tp.tp_col * state->tg_tp.tp_row) 236 return; 237 238 screen_buffer[idx].c = c; 239 screen_buffer[idx].a = *a; 240 241 efi_text_printchar(s, p, false); 242 } 243 244 static void 245 efi_text_fill(void *arg, const teken_rect_t *r, teken_char_t c, 246 const teken_attr_t *a) 247 { 248 teken_gfx_t *state = arg; 249 teken_pos_t p; 250 251 if (state->tg_cursor_visible) 252 conout->EnableCursor(conout, FALSE); 253 for (p.tp_row = r->tr_begin.tp_row; p.tp_row < r->tr_end.tp_row; 254 p.tp_row++) 255 for (p.tp_col = r->tr_begin.tp_col; 256 p.tp_col < r->tr_end.tp_col; p.tp_col++) 257 efi_text_putchar(state, &p, c, a); 258 if (state->tg_cursor_visible) 259 conout->EnableCursor(conout, TRUE); 260 } 261 262 static void 263 efi_text_copy_line(teken_gfx_t *state, int ncol, teken_pos_t *s, 264 teken_pos_t *d, bool scroll) 265 { 266 unsigned soffset, doffset; 267 teken_pos_t sp, dp; 268 int x; 269 270 soffset = s->tp_col + s->tp_row * state->tg_tp.tp_col; 271 doffset = d->tp_col + d->tp_row * state->tg_tp.tp_col; 272 273 sp = *s; 274 dp = *d; 275 for (x = 0; x < ncol; x++) { 276 sp.tp_col = s->tp_col + x; 277 dp.tp_col = d->tp_col + x; 278 if (!is_same_pixel(&screen_buffer[soffset + x], 279 &screen_buffer[doffset + x])) { 280 screen_buffer[doffset + x] = 281 screen_buffer[soffset + x]; 282 if (!scroll) 283 efi_text_printchar(state, &dp, false); 284 } else if (scroll) { 285 /* Draw last char and trigger scroll. */ 286 if (dp.tp_col + 1 == state->tg_tp.tp_col && 287 dp.tp_row + 1 == state->tg_tp.tp_row) { 288 efi_text_printchar(state, &dp, true); 289 } 290 } 291 } 292 } 293 294 static void 295 efi_text_copy(void *arg, const teken_rect_t *r, const teken_pos_t *p) 296 { 297 teken_gfx_t *state = arg; 298 unsigned doffset, soffset; 299 teken_pos_t d, s; 300 int nrow, ncol, x, y; /* Has to be signed - >= 0 comparison */ 301 bool scroll = false; 302 303 /* 304 * Copying is a little tricky. We must make sure we do it in 305 * correct order, to make sure we don't overwrite our own data. 306 */ 307 308 nrow = r->tr_end.tp_row - r->tr_begin.tp_row; 309 ncol = r->tr_end.tp_col - r->tr_begin.tp_col; 310 311 /* 312 * Check if we do copy whole screen. 313 */ 314 if (p->tp_row == 0 && p->tp_col == 0 && 315 nrow == state->tg_tp.tp_row - 2 && ncol == state->tg_tp.tp_col - 2) 316 scroll = true; 317 318 soffset = r->tr_begin.tp_col + r->tr_begin.tp_row * state->tg_tp.tp_col; 319 doffset = p->tp_col + p->tp_row * state->tg_tp.tp_col; 320 321 /* remove the cursor */ 322 if (state->tg_cursor_visible) 323 conout->EnableCursor(conout, FALSE); 324 325 /* 326 * Copy line by line. 327 */ 328 if (doffset <= soffset) { 329 s = r->tr_begin; 330 d = *p; 331 for (y = 0; y < nrow; y++) { 332 s.tp_row = r->tr_begin.tp_row + y; 333 d.tp_row = p->tp_row + y; 334 335 efi_text_copy_line(state, ncol, &s, &d, scroll); 336 } 337 } else { 338 for (y = nrow - 1; y >= 0; y--) { 339 s.tp_row = r->tr_begin.tp_row + y; 340 d.tp_row = p->tp_row + y; 341 342 efi_text_copy_line(state, ncol, &s, &d, false); 343 } 344 } 345 346 /* display the cursor */ 347 if (state->tg_cursor_visible) 348 conout->EnableCursor(conout, TRUE); 349 } 350 351 static void 352 efi_text_param(void *arg, int cmd, unsigned int value) 353 { 354 teken_gfx_t *state = arg; 355 356 switch (cmd) { 357 case TP_SETLOCALCURSOR: 358 /* 359 * 0 means normal (usually block), 1 means hidden, and 360 * 2 means blinking (always block) for compatibility with 361 * syscons. We don't support any changes except hiding, 362 * so must map 2 to 0. 363 */ 364 value = (value == 1) ? 0 : 1; 365 /* FALLTHROUGH */ 366 case TP_SHOWCURSOR: 367 if (value != 0) { 368 conout->EnableCursor(conout, TRUE); 369 state->tg_cursor_visible = true; 370 } else { 371 conout->EnableCursor(conout, FALSE); 372 state->tg_cursor_visible = false; 373 } 374 break; 375 default: 376 /* Not yet implemented */ 377 break; 378 } 379 } 380 381 /* 382 * Not implemented. 383 */ 384 static void 385 efi_cons_respond(void *s __unused, const void *buf __unused, 386 size_t len __unused) 387 { 388 } 389 390 /* 391 * Set up conin/conout/coninex to make sure we have input ready. 392 */ 393 static void 394 efi_cons_probe(struct console *cp) 395 { 396 EFI_STATUS status; 397 398 conout = ST->ConOut; 399 conin = ST->ConIn; 400 401 /* 402 * Call SetMode to work around buggy firmware. 403 */ 404 status = conout->SetMode(conout, conout->Mode->Mode); 405 406 if (coninex == NULL) { 407 status = BS->OpenProtocol(ST->ConsoleInHandle, 408 &simple_input_ex_guid, (void **)&coninex, 409 IH, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL); 410 if (status != EFI_SUCCESS) 411 coninex = NULL; 412 } 413 414 cp->c_flags |= C_PRESENTIN | C_PRESENTOUT; 415 } 416 417 static bool 418 color_name_to_teken(const char *name, int *val) 419 { 420 if (strcasecmp(name, "black") == 0) { 421 *val = TC_BLACK; 422 return (true); 423 } 424 if (strcasecmp(name, "red") == 0) { 425 *val = TC_RED; 426 return (true); 427 } 428 if (strcasecmp(name, "green") == 0) { 429 *val = TC_GREEN; 430 return (true); 431 } 432 if (strcasecmp(name, "brown") == 0) { 433 *val = TC_BROWN; 434 return (true); 435 } 436 if (strcasecmp(name, "blue") == 0) { 437 *val = TC_BLUE; 438 return (true); 439 } 440 if (strcasecmp(name, "magenta") == 0) { 441 *val = TC_MAGENTA; 442 return (true); 443 } 444 if (strcasecmp(name, "cyan") == 0) { 445 *val = TC_CYAN; 446 return (true); 447 } 448 if (strcasecmp(name, "white") == 0) { 449 *val = TC_WHITE; 450 return (true); 451 } 452 return (false); 453 } 454 455 static int 456 efi_set_colors(struct env_var *ev, int flags, const void *value) 457 { 458 int val = 0; 459 char buf[2]; 460 const void *evalue; 461 const teken_attr_t *ap; 462 teken_attr_t a; 463 464 if (value == NULL) 465 return (CMD_OK); 466 467 if (color_name_to_teken(value, &val)) { 468 snprintf(buf, sizeof (buf), "%d", val); 469 evalue = buf; 470 } else { 471 char *end; 472 473 errno = 0; 474 val = (int)strtol(value, &end, 0); 475 if (errno != 0 || *end != '\0') { 476 printf("Allowed values are either ansi color name or " 477 "number from range [0-7].\n"); 478 return (CMD_OK); 479 } 480 evalue = value; 481 } 482 483 ap = teken_get_defattr(&gfx_state.tg_teken); 484 a = *ap; 485 if (strcmp(ev->ev_name, "teken.fg_color") == 0) { 486 /* is it already set? */ 487 if (ap->ta_fgcolor == val) 488 return (CMD_OK); 489 a.ta_fgcolor = val; 490 } 491 if (strcmp(ev->ev_name, "teken.bg_color") == 0) { 492 /* is it already set? */ 493 if (ap->ta_bgcolor == val) 494 return (CMD_OK); 495 a.ta_bgcolor = val; 496 } 497 498 /* Improve visibility */ 499 if (a.ta_bgcolor == TC_WHITE) 500 a.ta_bgcolor |= TC_LIGHT; 501 502 teken_set_defattr(&gfx_state.tg_teken, &a); 503 cons_draw_frame(&a); 504 env_setenv(ev->ev_name, flags | EV_NOHOOK, evalue, NULL, NULL); 505 teken_input(&gfx_state.tg_teken, "\e[2J", 4); 506 return (CMD_OK); 507 } 508 509 #ifdef TERM_EMU 510 /* Get cursor position. */ 511 void 512 get_pos(int *x, int *y) 513 { 514 *x = conout->Mode->CursorColumn; 515 *y = conout->Mode->CursorRow; 516 } 517 518 /* Move cursor to x rows and y cols (0-based). */ 519 void 520 curs_move(int *_x, int *_y, int x, int y) 521 { 522 conout->SetCursorPosition(conout, x, y); 523 if (_x != NULL) 524 *_x = conout->Mode->CursorColumn; 525 if (_y != NULL) 526 *_y = conout->Mode->CursorRow; 527 } 528 529 /* Clear internal state of the terminal emulation code. */ 530 void 531 end_term(void) 532 { 533 esc = 0; 534 argc = -1; 535 } 536 #endif 537 538 static void 539 efi_cons_rawputchar(int c) 540 { 541 int i; 542 UINTN x, y; 543 conout->QueryMode(conout, conout->Mode->Mode, &x, &y); 544 545 if (c == '\t') { 546 int n; 547 548 n = 8 - ((conout->Mode->CursorColumn + 8) % 8); 549 for (i = 0; i < n; i++) 550 efi_cons_rawputchar(' '); 551 } else { 552 #ifndef TERM_EMU 553 if (c == '\n') 554 efi_cons_efiputchar('\r'); 555 efi_cons_efiputchar(c); 556 #else 557 switch (c) { 558 case '\r': 559 curx = 0; 560 efi_cons_efiputchar('\r'); 561 return; 562 case '\n': 563 efi_cons_efiputchar('\n'); 564 efi_cons_efiputchar('\r'); 565 cury++; 566 if (cury >= y) 567 cury--; 568 curx = 0; 569 return; 570 case '\b': 571 if (curx > 0) { 572 efi_cons_efiputchar('\b'); 573 curx--; 574 } 575 return; 576 default: 577 efi_cons_efiputchar(c); 578 curx++; 579 if (curx > x-1) { 580 curx = 0; 581 cury++; 582 } 583 if (cury > y-1) { 584 curx = 0; 585 cury--; 586 } 587 } 588 #endif 589 } 590 conout->EnableCursor(conout, TRUE); 591 } 592 593 #ifdef TERM_EMU 594 /* Gracefully exit ESC-sequence processing in case of misunderstanding. */ 595 static void 596 bail_out(int c) 597 { 598 char buf[16], *ch; 599 int i; 600 601 if (esc) { 602 efi_cons_rawputchar('\033'); 603 if (esc != '\033') 604 efi_cons_rawputchar(esc); 605 for (i = 0; i <= argc; ++i) { 606 sprintf(buf, "%d", args[i]); 607 ch = buf; 608 while (*ch) 609 efi_cons_rawputchar(*ch++); 610 } 611 } 612 efi_cons_rawputchar(c); 613 end_term(); 614 } 615 616 /* Clear display from current position to end of screen. */ 617 static void 618 CD(void) 619 { 620 int i; 621 UINTN x, y; 622 623 get_pos(&curx, &cury); 624 if (curx == 0 && cury == 0) { 625 conout->ClearScreen(conout); 626 end_term(); 627 return; 628 } 629 630 conout->QueryMode(conout, conout->Mode->Mode, &x, &y); 631 CL(0); /* clear current line from cursor to end */ 632 for (i = cury + 1; i < y-1; i++) { 633 curs_move(NULL, NULL, 0, i); 634 CL(0); 635 } 636 curs_move(NULL, NULL, curx, cury); 637 end_term(); 638 } 639 640 /* 641 * Absolute cursor move to args[0] rows and args[1] columns 642 * (the coordinates are 1-based). 643 */ 644 static void 645 CM(void) 646 { 647 if (args[0] > 0) 648 args[0]--; 649 if (args[1] > 0) 650 args[1]--; 651 curs_move(&curx, &cury, args[1], args[0]); 652 end_term(); 653 } 654 655 /* Home cursor (left top corner), also called from mode command. */ 656 void 657 HO(void) 658 { 659 argc = 1; 660 args[0] = args[1] = 1; 661 CM(); 662 } 663 664 /* Clear line from current position to end of line */ 665 static void 666 CL(int direction) 667 { 668 int i, len; 669 UINTN x, y; 670 CHAR16 *line; 671 672 conout->QueryMode(conout, conout->Mode->Mode, &x, &y); 673 switch (direction) { 674 case 0: /* from cursor to end */ 675 len = x - curx + 1; 676 break; 677 case 1: /* from beginning to cursor */ 678 len = curx; 679 break; 680 case 2: /* entire line */ 681 len = x; 682 break; 683 default: /* NOTREACHED */ 684 __unreachable(); 685 } 686 687 if (cury == y - 1) 688 len--; 689 690 line = malloc(len * sizeof (CHAR16)); 691 if (line == NULL) { 692 printf("out of memory\n"); 693 return; 694 } 695 for (i = 0; i < len; i++) 696 line[i] = ' '; 697 line[len-1] = 0; 698 699 if (direction != 0) 700 curs_move(NULL, NULL, 0, cury); 701 702 conout->OutputString(conout, line); 703 /* restore cursor position */ 704 curs_move(NULL, NULL, curx, cury); 705 free(line); 706 end_term(); 707 } 708 709 static void 710 get_arg(int c) 711 { 712 if (argc < 0) 713 argc = 0; 714 args[argc] *= 10; 715 args[argc] += c - '0'; 716 } 717 #endif 718 719 /* Emulate basic capabilities of cons25 terminal */ 720 static void 721 efi_term_emu(int c) 722 { 723 #ifdef TERM_EMU 724 static int ansi_col[] = { 725 0, 4, 2, 6, 1, 5, 3, 7 726 }; 727 int t, i; 728 EFI_STATUS status; 729 730 switch (esc) { 731 case 0: 732 switch (c) { 733 case '\033': 734 esc = c; 735 break; 736 default: 737 efi_cons_rawputchar(c); 738 break; 739 } 740 break; 741 case '\033': 742 switch (c) { 743 case '[': 744 esc = c; 745 args[0] = 0; 746 argc = -1; 747 break; 748 default: 749 bail_out(c); 750 break; 751 } 752 break; 753 case '[': 754 switch (c) { 755 case ';': 756 if (argc < 0) 757 argc = 0; 758 else if (argc + 1 >= MAXARGS) 759 bail_out(c); 760 else 761 args[++argc] = 0; 762 break; 763 case 'H': /* ho = \E[H */ 764 if (argc < 0) 765 HO(); 766 else if (argc == 1) 767 CM(); 768 else 769 bail_out(c); 770 break; 771 case 'J': /* cd = \E[J */ 772 if (argc < 0) 773 CD(); 774 else 775 bail_out(c); 776 break; 777 case 'm': 778 if (argc < 0) { 779 fg_c = DEFAULT_FGCOLOR; 780 bg_c = DEFAULT_BGCOLOR; 781 } 782 for (i = 0; i <= argc; ++i) { 783 switch (args[i]) { 784 case 0: /* back to normal */ 785 fg_c = DEFAULT_FGCOLOR; 786 bg_c = DEFAULT_BGCOLOR; 787 break; 788 case 1: /* bold */ 789 fg_c |= 0x8; 790 break; 791 case 4: /* underline */ 792 case 5: /* blink */ 793 bg_c |= 0x8; 794 break; 795 case 7: /* reverse */ 796 t = fg_c; 797 fg_c = bg_c; 798 bg_c = t; 799 break; 800 case 22: /* normal intensity */ 801 fg_c &= ~0x8; 802 break; 803 case 24: /* not underline */ 804 case 25: /* not blinking */ 805 bg_c &= ~0x8; 806 break; 807 case 30: case 31: case 32: case 33: 808 case 34: case 35: case 36: case 37: 809 fg_c = ansi_col[args[i] - 30]; 810 break; 811 case 39: /* normal */ 812 fg_c = DEFAULT_FGCOLOR; 813 break; 814 case 40: case 41: case 42: case 43: 815 case 44: case 45: case 46: case 47: 816 bg_c = ansi_col[args[i] - 40]; 817 break; 818 case 49: /* normal */ 819 bg_c = DEFAULT_BGCOLOR; 820 break; 821 } 822 } 823 conout->SetAttribute(conout, EFI_TEXT_ATTR(fg_c, bg_c)); 824 end_term(); 825 break; 826 default: 827 if (isdigit(c)) 828 get_arg(c); 829 else 830 bail_out(c); 831 break; 832 } 833 break; 834 default: 835 bail_out(c); 836 break; 837 } 838 #else 839 efi_cons_rawputchar(c); 840 #endif 841 } 842 843 static int 844 env_screen_nounset(struct env_var *ev __unused) 845 { 846 if (gfx_state.tg_fb_type == FB_TEXT) 847 return (0); 848 return (EPERM); 849 } 850 851 static void 852 cons_draw_frame(teken_attr_t *a) 853 { 854 teken_attr_t attr = *a; 855 teken_color_t fg = a->ta_fgcolor; 856 857 attr.ta_fgcolor = attr.ta_bgcolor; 858 teken_set_defattr(&gfx_state.tg_teken, &attr); 859 860 gfx_fb_drawrect(0, 0, gfx_state.tg_fb.fb_width, 861 gfx_state.tg_origin.tp_row, 1); 862 gfx_fb_drawrect(0, 863 gfx_state.tg_fb.fb_height - gfx_state.tg_origin.tp_row - 1, 864 gfx_state.tg_fb.fb_width, gfx_state.tg_fb.fb_height, 1); 865 gfx_fb_drawrect(0, gfx_state.tg_origin.tp_row, 866 gfx_state.tg_origin.tp_col, 867 gfx_state.tg_fb.fb_height - gfx_state.tg_origin.tp_row - 1, 1); 868 gfx_fb_drawrect( 869 gfx_state.tg_fb.fb_width - gfx_state.tg_origin.tp_col - 1, 870 gfx_state.tg_origin.tp_row, gfx_state.tg_fb.fb_width, 871 gfx_state.tg_fb.fb_height, 1); 872 873 attr.ta_fgcolor = fg; 874 teken_set_defattr(&gfx_state.tg_teken, &attr); 875 } 876 877 bool 878 cons_update_mode(bool use_gfx_mode) 879 { 880 UINTN cols, rows; 881 const teken_attr_t *a; 882 teken_attr_t attr; 883 EFI_STATUS status; 884 EFI_GRAPHICS_OUTPUT *gop = NULL; 885 struct efi_fb efifb; 886 char env[10], *ptr; 887 888 if (use_gfx_mode == true) { 889 gfx_state.tg_fb_type = FB_GOP; 890 if (gfx_state.tg_private == NULL) { 891 (void) BS->LocateProtocol(&gop_guid, NULL, 892 (void **)&gop); 893 gfx_state.tg_private = gop; 894 } else { 895 gop = gfx_state.tg_private; 896 } 897 898 /* 899 * We have FB but no GOP - it must be UGA. 900 */ 901 if (gop == NULL) 902 gfx_state.tg_fb_type = FB_UGA; 903 904 if (efi_find_framebuffer(&efifb) == 0) { 905 int roff, goff, boff; 906 907 gfx_state.tg_fb.fb_addr = efifb.fb_addr; 908 gfx_state.tg_fb.fb_size = efifb.fb_size; 909 gfx_state.tg_fb.fb_height = efifb.fb_height; 910 gfx_state.tg_fb.fb_width = efifb.fb_width; 911 gfx_state.tg_fb.fb_stride = efifb.fb_stride; 912 gfx_state.tg_fb.fb_mask_red = efifb.fb_mask_red; 913 gfx_state.tg_fb.fb_mask_green = efifb.fb_mask_green; 914 gfx_state.tg_fb.fb_mask_blue = efifb.fb_mask_blue; 915 gfx_state.tg_fb.fb_mask_reserved = 916 efifb.fb_mask_reserved; 917 roff = ffs(efifb.fb_mask_red) - 1; 918 goff = ffs(efifb.fb_mask_green) - 1; 919 boff = ffs(efifb.fb_mask_blue) - 1; 920 921 (void) generate_cons_palette(cmap, COLOR_FORMAT_RGB, 922 efifb.fb_mask_red >> roff, roff, 923 efifb.fb_mask_green >> goff, goff, 924 efifb.fb_mask_blue >> boff, boff); 925 gfx_state.tg_fb.fb_bpp = fls( 926 efifb.fb_mask_red | efifb.fb_mask_green | 927 efifb.fb_mask_blue | efifb.fb_mask_reserved); 928 } 929 } else { 930 gfx_state.tg_fb_type = FB_TEXT; 931 } 932 933 status = conout->QueryMode(conout, conout->Mode->Mode, &cols, &rows); 934 if (EFI_ERROR(status) || cols * rows == 0) { 935 cols = TEXT_COLS; 936 rows = TEXT_ROWS; 937 } 938 939 /* 940 * When we have serial port listed in ConOut, use pre-teken emulator, 941 * if built with. 942 * The problem is, we can not output text on efi and comconsole when 943 * efi also has comconsole bound. But then again, we need to have 944 * terminal emulator for efi text mode to support the menu. 945 * While teken is too expensive to be used on serial console, the 946 * pre-teken emulator is light enough to be used on serial console. 947 * 948 * When doing multiple consoles (both serial and video), 949 * also just use the old emulator. RB_MULTIPLE also implies 950 * we're using a serial console. 951 */ 952 mode = parse_uefi_con_out(); 953 if ((mode & (RB_SERIAL | RB_MULTIPLE)) == 0) { 954 conout->EnableCursor(conout, FALSE); 955 gfx_state.tg_cursor_visible = false; 956 957 if (gfx_state.tg_fb_type == FB_TEXT) { 958 959 gfx_state.tg_functions = &tf; 960 /* ensure the following are not set for text mode */ 961 unsetenv("screen.height"); 962 unsetenv("screen.width"); 963 unsetenv("screen.depth"); 964 } else { 965 uint32_t fb_height, fb_width; 966 967 fb_height = gfx_state.tg_fb.fb_height; 968 fb_width = gfx_state.tg_fb.fb_width; 969 970 /* 971 * setup_font() can adjust terminal size. 972 * Note, we assume 80x24 terminal first, this is 973 * because the font selection will attempt to achieve 974 * at least this terminal dimension and we do not 975 * end up with too small font. 976 */ 977 gfx_state.tg_tp.tp_row = TEXT_ROWS; 978 gfx_state.tg_tp.tp_col = TEXT_COLS; 979 setup_font(&gfx_state, fb_height, fb_width); 980 rows = gfx_state.tg_tp.tp_row; 981 cols = gfx_state.tg_tp.tp_col; 982 /* Point of origin in pixels. */ 983 gfx_state.tg_origin.tp_row = (fb_height - 984 (rows * gfx_state.tg_font.vf_height)) / 2; 985 gfx_state.tg_origin.tp_col = (fb_width - 986 (cols * gfx_state.tg_font.vf_width)) / 2; 987 988 /* UEFI gop has depth 32. */ 989 gfx_state.tg_glyph_size = gfx_state.tg_font.vf_height * 990 gfx_state.tg_font.vf_width * 4; 991 free(gfx_state.tg_glyph); 992 gfx_state.tg_glyph = malloc(gfx_state.tg_glyph_size); 993 if (gfx_state.tg_glyph == NULL) 994 return (false); 995 996 gfx_state.tg_functions = &tfx; 997 snprintf(env, sizeof (env), "%d", fb_height); 998 env_setenv("screen.height", EV_VOLATILE | EV_NOHOOK, 999 env, env_noset, env_screen_nounset); 1000 snprintf(env, sizeof (env), "%d", fb_width); 1001 env_setenv("screen.width", EV_VOLATILE | EV_NOHOOK, 1002 env, env_noset, env_screen_nounset); 1003 snprintf(env, sizeof (env), "%d", 1004 gfx_state.tg_fb.fb_bpp); 1005 env_setenv("screen.depth", EV_VOLATILE | EV_NOHOOK, 1006 env, env_noset, env_screen_nounset); 1007 } 1008 1009 /* Record our terminal screen size. */ 1010 gfx_state.tg_tp.tp_row = rows; 1011 gfx_state.tg_tp.tp_col = cols; 1012 1013 teken_init(&gfx_state.tg_teken, gfx_state.tg_functions, 1014 &gfx_state); 1015 1016 free(screen_buffer); 1017 screen_buffer = malloc(rows * cols * sizeof(*screen_buffer)); 1018 if (screen_buffer != NULL) { 1019 teken_set_winsize(&gfx_state.tg_teken, 1020 &gfx_state.tg_tp); 1021 a = teken_get_defattr(&gfx_state.tg_teken); 1022 attr = *a; 1023 1024 /* 1025 * On first run, we set up the efi_set_colors() 1026 * callback. If the env is already set, we 1027 * pick up fg and bg color values from the environment. 1028 */ 1029 ptr = getenv("teken.fg_color"); 1030 if (ptr != NULL) { 1031 attr.ta_fgcolor = strtol(ptr, NULL, 10); 1032 ptr = getenv("teken.bg_color"); 1033 attr.ta_bgcolor = strtol(ptr, NULL, 10); 1034 1035 teken_set_defattr(&gfx_state.tg_teken, &attr); 1036 } else { 1037 snprintf(env, sizeof(env), "%d", 1038 attr.ta_fgcolor); 1039 env_setenv("teken.fg_color", EV_VOLATILE, env, 1040 efi_set_colors, env_nounset); 1041 snprintf(env, sizeof(env), "%d", 1042 attr.ta_bgcolor); 1043 env_setenv("teken.bg_color", EV_VOLATILE, env, 1044 efi_set_colors, env_nounset); 1045 } 1046 } 1047 } 1048 1049 if (screen_buffer == NULL) { 1050 conout->EnableCursor(conout, TRUE); 1051 #ifdef TERM_EMU 1052 conout->SetAttribute(conout, EFI_TEXT_ATTR(DEFAULT_FGCOLOR, 1053 DEFAULT_BGCOLOR)); 1054 end_term(); 1055 get_pos(&curx, &cury); 1056 curs_move(&curx, &cury, curx, cury); 1057 fg_c = DEFAULT_FGCOLOR; 1058 bg_c = DEFAULT_BGCOLOR; 1059 #endif 1060 } else { 1061 /* Improve visibility */ 1062 if (attr.ta_bgcolor == TC_WHITE) 1063 attr.ta_bgcolor |= TC_LIGHT; 1064 teken_set_defattr(&gfx_state.tg_teken, &attr); 1065 1066 /* Draw frame around terminal area. */ 1067 cons_draw_frame(&attr); 1068 /* 1069 * Erase display, this will also fill our screen 1070 * buffer. 1071 */ 1072 teken_input(&gfx_state.tg_teken, "\e[2J", 4); 1073 gfx_state.tg_functions->tf_param(&gfx_state, 1074 TP_SHOWCURSOR, 1); 1075 } 1076 1077 snprintf(env, sizeof (env), "%u", (unsigned)rows); 1078 setenv("LINES", env, 1); 1079 snprintf(env, sizeof (env), "%u", (unsigned)cols); 1080 setenv("COLUMNS", env, 1); 1081 1082 return (true); 1083 } 1084 1085 static int 1086 efi_cons_init(int arg) 1087 { 1088 EFI_STATUS status; 1089 1090 if (efi_started) 1091 return (0); 1092 1093 efi_started = true; 1094 1095 gfx_framework_init(); 1096 if (cons_update_mode(gfx_state.tg_fb_type != FB_TEXT)) 1097 return (0); 1098 1099 return (1); 1100 } 1101 1102 static void 1103 input_partial(void) 1104 { 1105 unsigned i; 1106 uint32_t c; 1107 1108 if (utf8_left == 0) 1109 return; 1110 1111 for (i = 0; i < sizeof(utf8_partial); i++) { 1112 c = (utf8_partial >> (24 - (i << 3))) & 0xff; 1113 if (c != 0) 1114 efi_term_emu(c); 1115 } 1116 utf8_left = 0; 1117 utf8_partial = 0; 1118 } 1119 1120 static void 1121 input_byte(uint8_t c) 1122 { 1123 if ((c & 0x80) == 0x00) { 1124 /* One-byte sequence. */ 1125 input_partial(); 1126 efi_term_emu(c); 1127 return; 1128 } 1129 if ((c & 0xe0) == 0xc0) { 1130 /* Two-byte sequence. */ 1131 input_partial(); 1132 utf8_left = 1; 1133 utf8_partial = c; 1134 return; 1135 } 1136 if ((c & 0xf0) == 0xe0) { 1137 /* Three-byte sequence. */ 1138 input_partial(); 1139 utf8_left = 2; 1140 utf8_partial = c; 1141 return; 1142 } 1143 if ((c & 0xf8) == 0xf0) { 1144 /* Four-byte sequence. */ 1145 input_partial(); 1146 utf8_left = 3; 1147 utf8_partial = c; 1148 return; 1149 } 1150 if ((c & 0xc0) == 0x80) { 1151 /* Invalid state? */ 1152 if (utf8_left == 0) { 1153 efi_term_emu(c); 1154 return; 1155 } 1156 utf8_left--; 1157 utf8_partial = (utf8_partial << 8) | c; 1158 if (utf8_left == 0) { 1159 uint32_t v, u; 1160 uint8_t b; 1161 1162 v = 0; 1163 u = utf8_partial; 1164 b = (u >> 24) & 0xff; 1165 if (b != 0) { /* Four-byte sequence */ 1166 v = b & 0x07; 1167 b = (u >> 16) & 0xff; 1168 v = (v << 6) | (b & 0x3f); 1169 b = (u >> 8) & 0xff; 1170 v = (v << 6) | (b & 0x3f); 1171 b = u & 0xff; 1172 v = (v << 6) | (b & 0x3f); 1173 } else if ((b = (u >> 16) & 0xff) != 0) { 1174 v = b & 0x0f; /* Three-byte sequence */ 1175 b = (u >> 8) & 0xff; 1176 v = (v << 6) | (b & 0x3f); 1177 b = u & 0xff; 1178 v = (v << 6) | (b & 0x3f); 1179 } else if ((b = (u >> 8) & 0xff) != 0) { 1180 v = b & 0x1f; /* Two-byte sequence */ 1181 b = u & 0xff; 1182 v = (v << 6) | (b & 0x3f); 1183 } 1184 /* Send unicode char directly to console. */ 1185 efi_cons_efiputchar(v); 1186 utf8_partial = 0; 1187 } 1188 return; 1189 } 1190 /* Anything left is illegal in UTF-8 sequence. */ 1191 input_partial(); 1192 efi_term_emu(c); 1193 } 1194 1195 void 1196 efi_cons_putchar(int c) 1197 { 1198 unsigned char ch = c; 1199 1200 /* 1201 * Don't use Teken when we're doing pure serial, or a multiple console 1202 * with video "primary" because that's also serial. 1203 */ 1204 if ((mode & (RB_SERIAL | RB_MULTIPLE)) != 0 || screen_buffer == NULL) { 1205 input_byte(ch); 1206 return; 1207 } 1208 1209 teken_input(&gfx_state.tg_teken, &ch, sizeof (ch)); 1210 } 1211 1212 static int 1213 keybuf_getchar(void) 1214 { 1215 int i, c = 0; 1216 1217 for (i = 0; i < KEYBUFSZ; i++) { 1218 if (keybuf[i] != 0) { 1219 c = keybuf[i]; 1220 keybuf[i] = 0; 1221 break; 1222 } 1223 } 1224 1225 return (c); 1226 } 1227 1228 static bool 1229 keybuf_ischar(void) 1230 { 1231 int i; 1232 1233 for (i = 0; i < KEYBUFSZ; i++) { 1234 if (keybuf[i] != 0) 1235 return (true); 1236 } 1237 return (false); 1238 } 1239 1240 /* 1241 * We are not reading input before keybuf is empty, so we are safe 1242 * just to fill keybuf from the beginning. 1243 */ 1244 static void 1245 keybuf_inschar(EFI_INPUT_KEY *key) 1246 { 1247 1248 switch (key->ScanCode) { 1249 case SCAN_UP: /* UP */ 1250 keybuf[0] = 0x1b; /* esc */ 1251 keybuf[1] = '['; 1252 keybuf[2] = 'A'; 1253 break; 1254 case SCAN_DOWN: /* DOWN */ 1255 keybuf[0] = 0x1b; /* esc */ 1256 keybuf[1] = '['; 1257 keybuf[2] = 'B'; 1258 break; 1259 case SCAN_RIGHT: /* RIGHT */ 1260 keybuf[0] = 0x1b; /* esc */ 1261 keybuf[1] = '['; 1262 keybuf[2] = 'C'; 1263 break; 1264 case SCAN_LEFT: /* LEFT */ 1265 keybuf[0] = 0x1b; /* esc */ 1266 keybuf[1] = '['; 1267 keybuf[2] = 'D'; 1268 break; 1269 case SCAN_DELETE: 1270 keybuf[0] = CHAR_BACKSPACE; 1271 break; 1272 case SCAN_ESC: 1273 keybuf[0] = 0x1b; /* esc */ 1274 break; 1275 default: 1276 keybuf[0] = key->UnicodeChar; 1277 break; 1278 } 1279 } 1280 1281 static bool 1282 efi_readkey(void) 1283 { 1284 EFI_STATUS status; 1285 EFI_INPUT_KEY key; 1286 1287 status = conin->ReadKeyStroke(conin, &key); 1288 if (status == EFI_SUCCESS) { 1289 keybuf_inschar(&key); 1290 return (true); 1291 } 1292 return (false); 1293 } 1294 1295 static bool 1296 efi_readkey_ex(void) 1297 { 1298 EFI_STATUS status; 1299 EFI_INPUT_KEY *kp; 1300 EFI_KEY_DATA key_data; 1301 uint32_t kss; 1302 1303 status = coninex->ReadKeyStrokeEx(coninex, &key_data); 1304 if (status == EFI_SUCCESS) { 1305 kss = key_data.KeyState.KeyShiftState; 1306 kp = &key_data.Key; 1307 if (kss & EFI_SHIFT_STATE_VALID) { 1308 1309 /* 1310 * quick mapping to control chars, replace with 1311 * map lookup later. 1312 */ 1313 if (kss & EFI_RIGHT_CONTROL_PRESSED || 1314 kss & EFI_LEFT_CONTROL_PRESSED) { 1315 if (kp->UnicodeChar >= 'a' && 1316 kp->UnicodeChar <= 'z') { 1317 kp->UnicodeChar -= 'a'; 1318 kp->UnicodeChar++; 1319 } 1320 } 1321 } 1322 /* 1323 * The shift state and/or toggle state may not be valid, 1324 * but we still can have ScanCode or UnicodeChar. 1325 */ 1326 if (kp->ScanCode == 0 && kp->UnicodeChar == 0) 1327 return (false); 1328 keybuf_inschar(kp); 1329 return (true); 1330 } 1331 return (false); 1332 } 1333 1334 int 1335 efi_cons_getchar(void) 1336 { 1337 int c; 1338 1339 if ((c = keybuf_getchar()) != 0) 1340 return (c); 1341 1342 key_pending = 0; 1343 1344 if (coninex == NULL) { 1345 if (efi_readkey()) 1346 return (keybuf_getchar()); 1347 } else { 1348 if (efi_readkey_ex()) 1349 return (keybuf_getchar()); 1350 } 1351 1352 return (-1); 1353 } 1354 1355 int 1356 efi_cons_poll(void) 1357 { 1358 EFI_STATUS status; 1359 1360 if (keybuf_ischar() || key_pending) 1361 return (1); 1362 1363 /* 1364 * Some EFI implementation (u-boot for example) do not support 1365 * WaitForKey(). 1366 * CheckEvent() can clear the signaled state. 1367 */ 1368 if (coninex != NULL) { 1369 if (coninex->WaitForKeyEx == NULL) { 1370 key_pending = efi_readkey_ex(); 1371 } else { 1372 status = BS->CheckEvent(coninex->WaitForKeyEx); 1373 key_pending = status == EFI_SUCCESS; 1374 } 1375 } else { 1376 if (conin->WaitForKey == NULL) { 1377 key_pending = efi_readkey(); 1378 } else { 1379 status = BS->CheckEvent(conin->WaitForKey); 1380 key_pending = status == EFI_SUCCESS; 1381 } 1382 } 1383 1384 return (key_pending); 1385 } 1386 1387 /* Plain direct access to EFI OutputString(). */ 1388 void 1389 efi_cons_efiputchar(int c) 1390 { 1391 CHAR16 buf[2]; 1392 EFI_STATUS status; 1393 1394 buf[0] = c; 1395 buf[1] = 0; /* terminate string */ 1396 1397 status = conout->TestString(conout, buf); 1398 if (EFI_ERROR(status)) 1399 buf[0] = '?'; 1400 conout->OutputString(conout, buf); 1401 } 1402