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