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