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