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 if (!efi_started) 890 return (false); 891 892 /* 893 * Despite the use_gfx_mode, we want to make sure we call 894 * efi_find_framebuffer(). This will populate the fb data, 895 * which will be passed to kernel. 896 */ 897 if (efi_find_framebuffer(&gfx_state) == 0 && use_gfx_mode) { 898 int roff, goff, boff; 899 900 roff = ffs(gfx_state.tg_fb.fb_mask_red) - 1; 901 goff = ffs(gfx_state.tg_fb.fb_mask_green) - 1; 902 boff = ffs(gfx_state.tg_fb.fb_mask_blue) - 1; 903 904 (void) generate_cons_palette(cmap, COLOR_FORMAT_RGB, 905 gfx_state.tg_fb.fb_mask_red >> roff, roff, 906 gfx_state.tg_fb.fb_mask_green >> goff, goff, 907 gfx_state.tg_fb.fb_mask_blue >> boff, boff); 908 } else { 909 /* 910 * Either text mode was asked by user or we failed to 911 * find frame buffer. 912 */ 913 gfx_state.tg_fb_type = FB_TEXT; 914 } 915 916 status = conout->QueryMode(conout, conout->Mode->Mode, &cols, &rows); 917 if (EFI_ERROR(status) || cols * rows == 0) { 918 cols = TEXT_COLS; 919 rows = TEXT_ROWS; 920 } 921 922 /* 923 * When we have serial port listed in ConOut, use pre-teken emulator, 924 * if built with. 925 * The problem is, we can not output text on efi and comconsole when 926 * efi also has comconsole bound. But then again, we need to have 927 * terminal emulator for efi text mode to support the menu. 928 * While teken is too expensive to be used on serial console, the 929 * pre-teken emulator is light enough to be used on serial console. 930 * 931 * When doing multiple consoles (both serial and video), 932 * also just use the old emulator. RB_MULTIPLE also implies 933 * we're using a serial console. 934 */ 935 mode = parse_uefi_con_out(); 936 if ((mode & (RB_SERIAL | RB_MULTIPLE)) == 0) { 937 conout->EnableCursor(conout, FALSE); 938 gfx_state.tg_cursor_visible = false; 939 940 if (gfx_state.tg_fb_type == FB_TEXT) { 941 942 gfx_state.tg_functions = &tf; 943 /* ensure the following are not set for text mode */ 944 unsetenv("screen.height"); 945 unsetenv("screen.width"); 946 unsetenv("screen.depth"); 947 } else { 948 uint32_t fb_height, fb_width; 949 950 fb_height = gfx_state.tg_fb.fb_height; 951 fb_width = gfx_state.tg_fb.fb_width; 952 953 /* 954 * setup_font() can adjust terminal size. 955 * We can see two kind of bad happening. 956 * We either can get too small console font - requested 957 * terminal size is large, display resolution is 958 * large, and we get very small font. 959 * Or, we can get too large font - requested 960 * terminal size is small and this will cause large 961 * font to be selected. 962 * Now, the setup_font() is updated to consider 963 * display density and this should give us mostly 964 * acceptable font. However, the catch is, not all 965 * display devices will give us display density. 966 * Still, we do hope, external monitors do - this is 967 * where the display size will matter the most. 968 * And for laptop screens, we should still get good 969 * results by requesting 80x25 terminal. 970 */ 971 gfx_state.tg_tp.tp_row = 25; 972 gfx_state.tg_tp.tp_col = 80; 973 setup_font(&gfx_state, fb_height, fb_width); 974 rows = gfx_state.tg_tp.tp_row; 975 cols = gfx_state.tg_tp.tp_col; 976 /* Point of origin in pixels. */ 977 gfx_state.tg_origin.tp_row = (fb_height - 978 (rows * gfx_state.tg_font.vf_height)) / 2; 979 gfx_state.tg_origin.tp_col = (fb_width - 980 (cols * gfx_state.tg_font.vf_width)) / 2; 981 982 /* UEFI gop has depth 32. */ 983 gfx_state.tg_glyph_size = gfx_state.tg_font.vf_height * 984 gfx_state.tg_font.vf_width * 4; 985 free(gfx_state.tg_glyph); 986 gfx_state.tg_glyph = malloc(gfx_state.tg_glyph_size); 987 if (gfx_state.tg_glyph == NULL) 988 return (false); 989 990 gfx_state.tg_functions = &tfx; 991 snprintf(env, sizeof (env), "%d", fb_height); 992 env_setenv("screen.height", EV_VOLATILE | EV_NOHOOK, 993 env, env_noset, env_screen_nounset); 994 snprintf(env, sizeof (env), "%d", fb_width); 995 env_setenv("screen.width", EV_VOLATILE | EV_NOHOOK, 996 env, env_noset, env_screen_nounset); 997 snprintf(env, sizeof (env), "%d", 998 gfx_state.tg_fb.fb_bpp); 999 env_setenv("screen.depth", EV_VOLATILE | EV_NOHOOK, 1000 env, env_noset, env_screen_nounset); 1001 } 1002 1003 /* Record our terminal screen size. */ 1004 gfx_state.tg_tp.tp_row = rows; 1005 gfx_state.tg_tp.tp_col = cols; 1006 1007 teken_init(&gfx_state.tg_teken, gfx_state.tg_functions, 1008 &gfx_state); 1009 1010 free(screen_buffer); 1011 screen_buffer = malloc(rows * cols * sizeof(*screen_buffer)); 1012 if (screen_buffer != NULL) { 1013 teken_set_winsize(&gfx_state.tg_teken, 1014 &gfx_state.tg_tp); 1015 a = teken_get_defattr(&gfx_state.tg_teken); 1016 attr = *a; 1017 1018 /* 1019 * On first run, we set up the efi_set_colors() 1020 * callback. If the env is already set, we 1021 * pick up fg and bg color values from the environment. 1022 */ 1023 ptr = getenv("teken.fg_color"); 1024 if (ptr != NULL) { 1025 attr.ta_fgcolor = strtol(ptr, NULL, 10); 1026 ptr = getenv("teken.bg_color"); 1027 attr.ta_bgcolor = strtol(ptr, NULL, 10); 1028 1029 teken_set_defattr(&gfx_state.tg_teken, &attr); 1030 } else { 1031 snprintf(env, sizeof(env), "%d", 1032 attr.ta_fgcolor); 1033 env_setenv("teken.fg_color", EV_VOLATILE, env, 1034 efi_set_colors, env_nounset); 1035 snprintf(env, sizeof(env), "%d", 1036 attr.ta_bgcolor); 1037 env_setenv("teken.bg_color", EV_VOLATILE, env, 1038 efi_set_colors, env_nounset); 1039 } 1040 } 1041 } 1042 1043 if (screen_buffer == NULL) { 1044 conout->EnableCursor(conout, TRUE); 1045 #ifdef TERM_EMU 1046 conout->SetAttribute(conout, EFI_TEXT_ATTR(DEFAULT_FGCOLOR, 1047 DEFAULT_BGCOLOR)); 1048 end_term(); 1049 get_pos(&curx, &cury); 1050 curs_move(&curx, &cury, curx, cury); 1051 fg_c = DEFAULT_FGCOLOR; 1052 bg_c = DEFAULT_BGCOLOR; 1053 #endif 1054 } else { 1055 /* Improve visibility */ 1056 if (attr.ta_bgcolor == TC_WHITE) 1057 attr.ta_bgcolor |= TC_LIGHT; 1058 teken_set_defattr(&gfx_state.tg_teken, &attr); 1059 1060 /* Draw frame around terminal area. */ 1061 cons_draw_frame(&attr); 1062 /* 1063 * Erase display, this will also fill our screen 1064 * buffer. 1065 */ 1066 teken_input(&gfx_state.tg_teken, "\e[2J", 4); 1067 gfx_state.tg_functions->tf_param(&gfx_state, 1068 TP_SHOWCURSOR, 1); 1069 } 1070 1071 snprintf(env, sizeof (env), "%u", (unsigned)rows); 1072 setenv("LINES", env, 1); 1073 snprintf(env, sizeof (env), "%u", (unsigned)cols); 1074 setenv("COLUMNS", env, 1); 1075 1076 return (true); 1077 } 1078 1079 static int 1080 efi_cons_init(int arg) 1081 { 1082 EFI_STATUS status; 1083 1084 if (efi_started) 1085 return (0); 1086 1087 efi_started = true; 1088 1089 gfx_framework_init(); 1090 if (cons_update_mode(gfx_state.tg_fb_type != FB_TEXT)) 1091 return (0); 1092 1093 return (1); 1094 } 1095 1096 static void 1097 input_partial(void) 1098 { 1099 unsigned i; 1100 uint32_t c; 1101 1102 if (utf8_left == 0) 1103 return; 1104 1105 for (i = 0; i < sizeof(utf8_partial); i++) { 1106 c = (utf8_partial >> (24 - (i << 3))) & 0xff; 1107 if (c != 0) 1108 efi_term_emu(c); 1109 } 1110 utf8_left = 0; 1111 utf8_partial = 0; 1112 } 1113 1114 static void 1115 input_byte(uint8_t c) 1116 { 1117 if ((c & 0x80) == 0x00) { 1118 /* One-byte sequence. */ 1119 input_partial(); 1120 efi_term_emu(c); 1121 return; 1122 } 1123 if ((c & 0xe0) == 0xc0) { 1124 /* Two-byte sequence. */ 1125 input_partial(); 1126 utf8_left = 1; 1127 utf8_partial = c; 1128 return; 1129 } 1130 if ((c & 0xf0) == 0xe0) { 1131 /* Three-byte sequence. */ 1132 input_partial(); 1133 utf8_left = 2; 1134 utf8_partial = c; 1135 return; 1136 } 1137 if ((c & 0xf8) == 0xf0) { 1138 /* Four-byte sequence. */ 1139 input_partial(); 1140 utf8_left = 3; 1141 utf8_partial = c; 1142 return; 1143 } 1144 if ((c & 0xc0) == 0x80) { 1145 /* Invalid state? */ 1146 if (utf8_left == 0) { 1147 efi_term_emu(c); 1148 return; 1149 } 1150 utf8_left--; 1151 utf8_partial = (utf8_partial << 8) | c; 1152 if (utf8_left == 0) { 1153 uint32_t v, u; 1154 uint8_t b; 1155 1156 v = 0; 1157 u = utf8_partial; 1158 b = (u >> 24) & 0xff; 1159 if (b != 0) { /* Four-byte sequence */ 1160 v = b & 0x07; 1161 b = (u >> 16) & 0xff; 1162 v = (v << 6) | (b & 0x3f); 1163 b = (u >> 8) & 0xff; 1164 v = (v << 6) | (b & 0x3f); 1165 b = u & 0xff; 1166 v = (v << 6) | (b & 0x3f); 1167 } else if ((b = (u >> 16) & 0xff) != 0) { 1168 v = b & 0x0f; /* Three-byte sequence */ 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 >> 8) & 0xff) != 0) { 1174 v = b & 0x1f; /* Two-byte sequence */ 1175 b = u & 0xff; 1176 v = (v << 6) | (b & 0x3f); 1177 } 1178 /* Send unicode char directly to console. */ 1179 efi_cons_efiputchar(v); 1180 utf8_partial = 0; 1181 } 1182 return; 1183 } 1184 /* Anything left is illegal in UTF-8 sequence. */ 1185 input_partial(); 1186 efi_term_emu(c); 1187 } 1188 1189 void 1190 efi_cons_putchar(int c) 1191 { 1192 unsigned char ch = c; 1193 1194 /* 1195 * Don't use Teken when we're doing pure serial, or a multiple console 1196 * with video "primary" because that's also serial. 1197 */ 1198 if ((mode & (RB_SERIAL | RB_MULTIPLE)) != 0 || screen_buffer == NULL) { 1199 input_byte(ch); 1200 return; 1201 } 1202 1203 teken_input(&gfx_state.tg_teken, &ch, sizeof (ch)); 1204 } 1205 1206 static int 1207 keybuf_getchar(void) 1208 { 1209 int i, c = 0; 1210 1211 for (i = 0; i < KEYBUFSZ; i++) { 1212 if (keybuf[i] != 0) { 1213 c = keybuf[i]; 1214 keybuf[i] = 0; 1215 break; 1216 } 1217 } 1218 1219 return (c); 1220 } 1221 1222 static bool 1223 keybuf_ischar(void) 1224 { 1225 int i; 1226 1227 for (i = 0; i < KEYBUFSZ; i++) { 1228 if (keybuf[i] != 0) 1229 return (true); 1230 } 1231 return (false); 1232 } 1233 1234 /* 1235 * We are not reading input before keybuf is empty, so we are safe 1236 * just to fill keybuf from the beginning. 1237 */ 1238 static void 1239 keybuf_inschar(EFI_INPUT_KEY *key) 1240 { 1241 1242 switch (key->ScanCode) { 1243 case SCAN_UP: /* UP */ 1244 keybuf[0] = 0x1b; /* esc */ 1245 keybuf[1] = '['; 1246 keybuf[2] = 'A'; 1247 break; 1248 case SCAN_DOWN: /* DOWN */ 1249 keybuf[0] = 0x1b; /* esc */ 1250 keybuf[1] = '['; 1251 keybuf[2] = 'B'; 1252 break; 1253 case SCAN_RIGHT: /* RIGHT */ 1254 keybuf[0] = 0x1b; /* esc */ 1255 keybuf[1] = '['; 1256 keybuf[2] = 'C'; 1257 break; 1258 case SCAN_LEFT: /* LEFT */ 1259 keybuf[0] = 0x1b; /* esc */ 1260 keybuf[1] = '['; 1261 keybuf[2] = 'D'; 1262 break; 1263 case SCAN_DELETE: 1264 keybuf[0] = CHAR_BACKSPACE; 1265 break; 1266 case SCAN_ESC: 1267 keybuf[0] = 0x1b; /* esc */ 1268 break; 1269 default: 1270 keybuf[0] = key->UnicodeChar; 1271 break; 1272 } 1273 } 1274 1275 static bool 1276 efi_readkey(void) 1277 { 1278 EFI_STATUS status; 1279 EFI_INPUT_KEY key; 1280 1281 status = conin->ReadKeyStroke(conin, &key); 1282 if (status == EFI_SUCCESS) { 1283 keybuf_inschar(&key); 1284 return (true); 1285 } 1286 return (false); 1287 } 1288 1289 static bool 1290 efi_readkey_ex(void) 1291 { 1292 EFI_STATUS status; 1293 EFI_INPUT_KEY *kp; 1294 EFI_KEY_DATA key_data; 1295 uint32_t kss; 1296 1297 status = coninex->ReadKeyStrokeEx(coninex, &key_data); 1298 if (status == EFI_SUCCESS) { 1299 kss = key_data.KeyState.KeyShiftState; 1300 kp = &key_data.Key; 1301 if (kss & EFI_SHIFT_STATE_VALID) { 1302 1303 /* 1304 * quick mapping to control chars, replace with 1305 * map lookup later. 1306 */ 1307 if (kss & EFI_RIGHT_CONTROL_PRESSED || 1308 kss & EFI_LEFT_CONTROL_PRESSED) { 1309 if (kp->UnicodeChar >= 'a' && 1310 kp->UnicodeChar <= 'z') { 1311 kp->UnicodeChar -= 'a'; 1312 kp->UnicodeChar++; 1313 } 1314 } 1315 } 1316 /* 1317 * The shift state and/or toggle state may not be valid, 1318 * but we still can have ScanCode or UnicodeChar. 1319 */ 1320 if (kp->ScanCode == 0 && kp->UnicodeChar == 0) 1321 return (false); 1322 keybuf_inschar(kp); 1323 return (true); 1324 } 1325 return (false); 1326 } 1327 1328 int 1329 efi_cons_getchar(void) 1330 { 1331 int c; 1332 1333 if ((c = keybuf_getchar()) != 0) 1334 return (c); 1335 1336 key_pending = 0; 1337 1338 if (coninex == NULL) { 1339 if (efi_readkey()) 1340 return (keybuf_getchar()); 1341 } else { 1342 if (efi_readkey_ex()) 1343 return (keybuf_getchar()); 1344 } 1345 1346 return (-1); 1347 } 1348 1349 int 1350 efi_cons_poll(void) 1351 { 1352 EFI_STATUS status; 1353 1354 if (keybuf_ischar() || key_pending) 1355 return (1); 1356 1357 /* 1358 * Some EFI implementation (u-boot for example) do not support 1359 * WaitForKey(). 1360 * CheckEvent() can clear the signaled state. 1361 */ 1362 if (coninex != NULL) { 1363 if (coninex->WaitForKeyEx == NULL) { 1364 key_pending = efi_readkey_ex(); 1365 } else { 1366 status = BS->CheckEvent(coninex->WaitForKeyEx); 1367 key_pending = status == EFI_SUCCESS; 1368 } 1369 } else { 1370 if (conin->WaitForKey == NULL) { 1371 key_pending = efi_readkey(); 1372 } else { 1373 status = BS->CheckEvent(conin->WaitForKey); 1374 key_pending = status == EFI_SUCCESS; 1375 } 1376 } 1377 1378 return (key_pending); 1379 } 1380 1381 /* Plain direct access to EFI OutputString(). */ 1382 void 1383 efi_cons_efiputchar(int c) 1384 { 1385 CHAR16 buf[2]; 1386 EFI_STATUS status; 1387 1388 buf[0] = c; 1389 buf[1] = 0; /* terminate string */ 1390 1391 status = conout->TestString(conout, buf); 1392 if (EFI_ERROR(status)) 1393 buf[0] = '?'; 1394 conout->OutputString(conout, buf); 1395 } 1396