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