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