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 int light = 0; 441 if (strncasecmp(name, "light", 5) == 0) { 442 name += 5; 443 light = TC_LIGHT; 444 } else if (strncasecmp(name, "bright", 6) == 0) { 445 name += 6; 446 light = TC_LIGHT; 447 } 448 if (strcasecmp(name, "black") == 0) { 449 *val = TC_BLACK | light; 450 return (true); 451 } 452 if (strcasecmp(name, "red") == 0) { 453 *val = TC_RED | light; 454 return (true); 455 } 456 if (strcasecmp(name, "green") == 0) { 457 *val = TC_GREEN | light; 458 return (true); 459 } 460 if (strcasecmp(name, "yellow") == 0 || strcasecmp(name, "brown") == 0) { 461 *val = TC_YELLOW | light; 462 return (true); 463 } 464 if (strcasecmp(name, "blue") == 0) { 465 *val = TC_BLUE | light; 466 return (true); 467 } 468 if (strcasecmp(name, "magenta") == 0) { 469 *val = TC_MAGENTA | light; 470 return (true); 471 } 472 if (strcasecmp(name, "cyan") == 0) { 473 *val = TC_CYAN | light; 474 return (true); 475 } 476 if (strcasecmp(name, "white") == 0) { 477 *val = TC_WHITE | light; 478 return (true); 479 } 480 return (false); 481 } 482 483 static int 484 efi_set_colors(struct env_var *ev, int flags, const void *value) 485 { 486 int val = 0; 487 char buf[3]; 488 const void *evalue; 489 const teken_attr_t *ap; 490 teken_attr_t a; 491 492 if (value == NULL) 493 return (CMD_OK); 494 495 if (color_name_to_teken(value, &val)) { 496 snprintf(buf, sizeof (buf), "%d", val); 497 evalue = buf; 498 } else { 499 char *end; 500 long lval; 501 502 errno = 0; 503 lval = strtol(value, &end, 0); 504 if (errno != 0 || *end != '\0' || lval < 0 || lval > 15) { 505 printf("Allowed values are either ansi color name or " 506 "number from range [0-15].\n"); 507 return (CMD_OK); 508 } 509 val = (int)lval; 510 evalue = value; 511 } 512 513 ap = teken_get_defattr(&gfx_state.tg_teken); 514 a = *ap; 515 if (strcmp(ev->ev_name, "teken.fg_color") == 0) { 516 /* is it already set? */ 517 if (ap->ta_fgcolor == val) 518 return (CMD_OK); 519 a.ta_fgcolor = val; 520 } 521 if (strcmp(ev->ev_name, "teken.bg_color") == 0) { 522 /* is it already set? */ 523 if (ap->ta_bgcolor == val) 524 return (CMD_OK); 525 a.ta_bgcolor = val; 526 } 527 528 /* Improve visibility */ 529 if (a.ta_bgcolor == TC_WHITE) 530 a.ta_bgcolor |= TC_LIGHT; 531 532 teken_set_defattr(&gfx_state.tg_teken, &a); 533 cons_draw_frame(&a); 534 env_setenv(ev->ev_name, flags | EV_NOHOOK, evalue, NULL, NULL); 535 teken_input(&gfx_state.tg_teken, "\e[2J", 4); 536 return (CMD_OK); 537 } 538 539 #ifdef TERM_EMU 540 /* Get cursor position. */ 541 void 542 get_pos(int *x, int *y) 543 { 544 *x = conout->Mode->CursorColumn; 545 *y = conout->Mode->CursorRow; 546 } 547 548 /* Move cursor to x rows and y cols (0-based). */ 549 void 550 curs_move(int *_x, int *_y, int x, int y) 551 { 552 conout->SetCursorPosition(conout, x, y); 553 if (_x != NULL) 554 *_x = conout->Mode->CursorColumn; 555 if (_y != NULL) 556 *_y = conout->Mode->CursorRow; 557 } 558 559 /* Clear internal state of the terminal emulation code. */ 560 void 561 end_term(void) 562 { 563 esc = 0; 564 argc = -1; 565 } 566 #endif 567 568 static void 569 efi_cons_rawputchar(int c) 570 { 571 int i; 572 UINTN x, y; 573 conout->QueryMode(conout, conout->Mode->Mode, &x, &y); 574 575 if (c == '\t') { 576 int n; 577 578 n = 8 - ((conout->Mode->CursorColumn + 8) % 8); 579 for (i = 0; i < n; i++) 580 efi_cons_rawputchar(' '); 581 } else { 582 #ifndef TERM_EMU 583 if (c == '\n') 584 efi_cons_efiputchar('\r'); 585 efi_cons_efiputchar(c); 586 #else 587 switch (c) { 588 case '\r': 589 curx = 0; 590 efi_cons_efiputchar('\r'); 591 return; 592 case '\n': 593 efi_cons_efiputchar('\n'); 594 efi_cons_efiputchar('\r'); 595 cury++; 596 if (cury >= y) 597 cury--; 598 curx = 0; 599 return; 600 case '\b': 601 if (curx > 0) { 602 efi_cons_efiputchar('\b'); 603 curx--; 604 } 605 return; 606 default: 607 efi_cons_efiputchar(c); 608 curx++; 609 if (curx > x-1) { 610 curx = 0; 611 cury++; 612 } 613 if (cury > y-1) { 614 curx = 0; 615 cury--; 616 } 617 } 618 #endif 619 } 620 conout->EnableCursor(conout, TRUE); 621 } 622 623 #ifdef TERM_EMU 624 /* Gracefully exit ESC-sequence processing in case of misunderstanding. */ 625 static void 626 bail_out(int c) 627 { 628 char buf[16], *ch; 629 int i; 630 631 if (esc) { 632 efi_cons_rawputchar('\033'); 633 if (esc != '\033') 634 efi_cons_rawputchar(esc); 635 for (i = 0; i <= argc; ++i) { 636 sprintf(buf, "%d", args[i]); 637 ch = buf; 638 while (*ch) 639 efi_cons_rawputchar(*ch++); 640 } 641 } 642 efi_cons_rawputchar(c); 643 end_term(); 644 } 645 646 /* Clear display from current position to end of screen. */ 647 static void 648 CD(void) 649 { 650 int i; 651 UINTN x, y; 652 653 get_pos(&curx, &cury); 654 if (curx == 0 && cury == 0) { 655 conout->ClearScreen(conout); 656 end_term(); 657 return; 658 } 659 660 conout->QueryMode(conout, conout->Mode->Mode, &x, &y); 661 CL(0); /* clear current line from cursor to end */ 662 for (i = cury + 1; i < y-1; i++) { 663 curs_move(NULL, NULL, 0, i); 664 CL(0); 665 } 666 curs_move(NULL, NULL, curx, cury); 667 end_term(); 668 } 669 670 /* 671 * Absolute cursor move to args[0] rows and args[1] columns 672 * (the coordinates are 1-based). 673 */ 674 static void 675 CM(void) 676 { 677 if (args[0] > 0) 678 args[0]--; 679 if (args[1] > 0) 680 args[1]--; 681 curs_move(&curx, &cury, args[1], args[0]); 682 end_term(); 683 } 684 685 /* Home cursor (left top corner), also called from mode command. */ 686 void 687 HO(void) 688 { 689 argc = 1; 690 args[0] = args[1] = 1; 691 CM(); 692 } 693 694 /* Clear line from current position to end of line */ 695 static void 696 CL(int direction) 697 { 698 int i, len; 699 UINTN x, y; 700 CHAR16 *line; 701 702 conout->QueryMode(conout, conout->Mode->Mode, &x, &y); 703 switch (direction) { 704 case 0: /* from cursor to end */ 705 len = x - curx + 1; 706 break; 707 case 1: /* from beginning to cursor */ 708 len = curx; 709 break; 710 case 2: /* entire line */ 711 len = x; 712 break; 713 default: /* NOTREACHED */ 714 __unreachable(); 715 } 716 717 if (cury == y - 1) 718 len--; 719 720 line = malloc(len * sizeof (CHAR16)); 721 if (line == NULL) { 722 printf("out of memory\n"); 723 return; 724 } 725 for (i = 0; i < len; i++) 726 line[i] = ' '; 727 line[len-1] = 0; 728 729 if (direction != 0) 730 curs_move(NULL, NULL, 0, cury); 731 732 conout->OutputString(conout, line); 733 /* restore cursor position */ 734 curs_move(NULL, NULL, curx, cury); 735 free(line); 736 end_term(); 737 } 738 739 static void 740 get_arg(int c) 741 { 742 if (argc < 0) 743 argc = 0; 744 args[argc] *= 10; 745 args[argc] += c - '0'; 746 } 747 #endif 748 749 /* Emulate basic capabilities of cons25 terminal */ 750 static void 751 efi_term_emu(int c) 752 { 753 if (!boot_services_active) 754 return; 755 #ifdef TERM_EMU 756 static int ansi_col[] = { 757 0, 4, 2, 6, 1, 5, 3, 7 758 }; 759 int t, i; 760 EFI_STATUS status; 761 762 switch (esc) { 763 case 0: 764 switch (c) { 765 case '\033': 766 esc = c; 767 break; 768 default: 769 efi_cons_rawputchar(c); 770 break; 771 } 772 break; 773 case '\033': 774 switch (c) { 775 case '[': 776 esc = c; 777 args[0] = 0; 778 argc = -1; 779 break; 780 default: 781 bail_out(c); 782 break; 783 } 784 break; 785 case '[': 786 switch (c) { 787 case ';': 788 if (argc < 0) 789 argc = 0; 790 else if (argc + 1 >= MAXARGS) 791 bail_out(c); 792 else 793 args[++argc] = 0; 794 break; 795 case 'H': /* ho = \E[H */ 796 if (argc < 0) 797 HO(); 798 else if (argc == 1) 799 CM(); 800 else 801 bail_out(c); 802 break; 803 case 'J': /* cd = \E[J */ 804 if (argc < 0) 805 CD(); 806 else 807 bail_out(c); 808 break; 809 case 'm': 810 if (argc < 0) { 811 fg_c = DEFAULT_FGCOLOR; 812 bg_c = DEFAULT_BGCOLOR; 813 } 814 for (i = 0; i <= argc; ++i) { 815 switch (args[i]) { 816 case 0: /* back to normal */ 817 fg_c = DEFAULT_FGCOLOR; 818 bg_c = DEFAULT_BGCOLOR; 819 break; 820 case 1: /* bold */ 821 fg_c |= 0x8; 822 break; 823 case 4: /* underline */ 824 case 5: /* blink */ 825 bg_c |= 0x8; 826 break; 827 case 7: /* reverse */ 828 t = fg_c; 829 fg_c = bg_c; 830 bg_c = t; 831 break; 832 case 22: /* normal intensity */ 833 fg_c &= ~0x8; 834 break; 835 case 24: /* not underline */ 836 case 25: /* not blinking */ 837 bg_c &= ~0x8; 838 break; 839 case 30: case 31: case 32: case 33: 840 case 34: case 35: case 36: case 37: 841 fg_c = ansi_col[args[i] - 30]; 842 break; 843 case 39: /* normal */ 844 fg_c = DEFAULT_FGCOLOR; 845 break; 846 case 40: case 41: case 42: case 43: 847 case 44: case 45: case 46: case 47: 848 bg_c = ansi_col[args[i] - 40]; 849 break; 850 case 49: /* normal */ 851 bg_c = DEFAULT_BGCOLOR; 852 break; 853 } 854 } 855 conout->SetAttribute(conout, EFI_TEXT_ATTR(fg_c, bg_c)); 856 end_term(); 857 break; 858 default: 859 if (isdigit(c)) 860 get_arg(c); 861 else 862 bail_out(c); 863 break; 864 } 865 break; 866 default: 867 bail_out(c); 868 break; 869 } 870 #else 871 efi_cons_rawputchar(c); 872 #endif 873 } 874 875 static int 876 env_screen_nounset(struct env_var *ev __unused) 877 { 878 if (gfx_state.tg_fb_type == FB_TEXT) 879 return (0); 880 return (EPERM); 881 } 882 883 static void 884 cons_draw_frame(teken_attr_t *a) 885 { 886 teken_attr_t attr = *a; 887 teken_color_t fg = a->ta_fgcolor; 888 889 attr.ta_fgcolor = attr.ta_bgcolor; 890 teken_set_defattr(&gfx_state.tg_teken, &attr); 891 892 gfx_fb_drawrect(0, 0, gfx_state.tg_fb.fb_width, 893 gfx_state.tg_origin.tp_row, 1); 894 gfx_fb_drawrect(0, 895 gfx_state.tg_fb.fb_height - gfx_state.tg_origin.tp_row - 1, 896 gfx_state.tg_fb.fb_width, gfx_state.tg_fb.fb_height, 1); 897 gfx_fb_drawrect(0, gfx_state.tg_origin.tp_row, 898 gfx_state.tg_origin.tp_col, 899 gfx_state.tg_fb.fb_height - gfx_state.tg_origin.tp_row - 1, 1); 900 gfx_fb_drawrect( 901 gfx_state.tg_fb.fb_width - gfx_state.tg_origin.tp_col - 1, 902 gfx_state.tg_origin.tp_row, gfx_state.tg_fb.fb_width, 903 gfx_state.tg_fb.fb_height, 1); 904 905 attr.ta_fgcolor = fg; 906 teken_set_defattr(&gfx_state.tg_teken, &attr); 907 } 908 909 bool 910 cons_update_mode(bool use_gfx_mode) 911 { 912 UINTN cols, rows; 913 const teken_attr_t *a; 914 teken_attr_t attr; 915 EFI_STATUS status; 916 char env[10], *ptr; 917 918 if (!efi_started) 919 return (false); 920 921 /* 922 * Despite the use_gfx_mode, we want to make sure we call 923 * efi_find_framebuffer(). This will populate the fb data, 924 * which will be passed to kernel. 925 */ 926 if (efi_find_framebuffer(&gfx_state) == 0 && use_gfx_mode) { 927 int roff, goff, boff; 928 929 roff = ffs(gfx_state.tg_fb.fb_mask_red) - 1; 930 goff = ffs(gfx_state.tg_fb.fb_mask_green) - 1; 931 boff = ffs(gfx_state.tg_fb.fb_mask_blue) - 1; 932 933 (void) generate_cons_palette(cmap, COLOR_FORMAT_RGB, 934 gfx_state.tg_fb.fb_mask_red >> roff, roff, 935 gfx_state.tg_fb.fb_mask_green >> goff, goff, 936 gfx_state.tg_fb.fb_mask_blue >> boff, boff); 937 } else { 938 /* 939 * Either text mode was asked by user or we failed to 940 * find frame buffer. 941 */ 942 gfx_state.tg_fb_type = FB_TEXT; 943 } 944 945 status = conout->QueryMode(conout, conout->Mode->Mode, &cols, &rows); 946 if (EFI_ERROR(status) || cols * rows == 0) { 947 cols = TEXT_COLS; 948 rows = TEXT_ROWS; 949 } 950 951 /* 952 * When we have serial port listed in ConOut, use pre-teken emulator, 953 * if built with. 954 * The problem is, we can not output text on efi and comconsole when 955 * efi also has comconsole bound. But then again, we need to have 956 * terminal emulator for efi text mode to support the menu. 957 * While teken is too expensive to be used on serial console, the 958 * pre-teken emulator is light enough to be used on serial console. 959 * 960 * When doing multiple consoles (both serial and video), 961 * also just use the old emulator. RB_MULTIPLE also implies 962 * we're using a serial console. 963 */ 964 mode = parse_uefi_con_out(); 965 if ((mode & (RB_SERIAL | RB_MULTIPLE)) == 0) { 966 conout->EnableCursor(conout, FALSE); 967 gfx_state.tg_cursor_visible = false; 968 969 if (gfx_state.tg_fb_type == FB_TEXT) { 970 971 gfx_state.tg_functions = &tf; 972 /* ensure the following are not set for text mode */ 973 unsetenv("screen.height"); 974 unsetenv("screen.width"); 975 unsetenv("screen.depth"); 976 } else { 977 uint32_t fb_height, fb_width; 978 979 fb_height = gfx_state.tg_fb.fb_height; 980 fb_width = gfx_state.tg_fb.fb_width; 981 982 /* 983 * setup_font() can adjust terminal size. 984 * We can see two kind of bad happening. 985 * We either can get too small console font - requested 986 * terminal size is large, display resolution is 987 * large, and we get very small font. 988 * Or, we can get too large font - requested 989 * terminal size is small and this will cause large 990 * font to be selected. 991 * Now, the setup_font() is updated to consider 992 * display density and this should give us mostly 993 * acceptable font. However, the catch is, not all 994 * display devices will give us display density. 995 * Still, we do hope, external monitors do - this is 996 * where the display size will matter the most. 997 * And for laptop screens, we should still get good 998 * results by requesting 80x25 terminal. 999 */ 1000 gfx_state.tg_tp.tp_row = 25; 1001 gfx_state.tg_tp.tp_col = 80; 1002 setup_font(&gfx_state, fb_height, fb_width); 1003 rows = gfx_state.tg_tp.tp_row; 1004 cols = gfx_state.tg_tp.tp_col; 1005 /* Point of origin in pixels. */ 1006 gfx_state.tg_origin.tp_row = (fb_height - 1007 (rows * gfx_state.tg_font.vf_height)) / 2; 1008 gfx_state.tg_origin.tp_col = (fb_width - 1009 (cols * gfx_state.tg_font.vf_width)) / 2; 1010 1011 /* UEFI gop has depth 32. */ 1012 gfx_state.tg_glyph_size = gfx_state.tg_font.vf_height * 1013 gfx_state.tg_font.vf_width * 4; 1014 free(gfx_state.tg_glyph); 1015 gfx_state.tg_glyph = malloc(gfx_state.tg_glyph_size); 1016 if (gfx_state.tg_glyph == NULL) 1017 return (false); 1018 1019 gfx_state.tg_functions = &tfx; 1020 snprintf(env, sizeof (env), "%d", fb_height); 1021 env_setenv("screen.height", EV_VOLATILE | EV_NOHOOK, 1022 env, env_noset, env_screen_nounset); 1023 snprintf(env, sizeof (env), "%d", fb_width); 1024 env_setenv("screen.width", EV_VOLATILE | EV_NOHOOK, 1025 env, env_noset, env_screen_nounset); 1026 snprintf(env, sizeof (env), "%d", 1027 gfx_state.tg_fb.fb_bpp); 1028 env_setenv("screen.depth", EV_VOLATILE | EV_NOHOOK, 1029 env, env_noset, env_screen_nounset); 1030 } 1031 1032 /* Record our terminal screen size. */ 1033 gfx_state.tg_tp.tp_row = rows; 1034 gfx_state.tg_tp.tp_col = cols; 1035 1036 teken_init(&gfx_state.tg_teken, gfx_state.tg_functions, 1037 &gfx_state); 1038 1039 free(screen_buffer); 1040 screen_buffer = malloc(rows * cols * sizeof(*screen_buffer)); 1041 if (screen_buffer != NULL) { 1042 teken_set_winsize(&gfx_state.tg_teken, 1043 &gfx_state.tg_tp); 1044 a = teken_get_defattr(&gfx_state.tg_teken); 1045 attr = *a; 1046 1047 /* 1048 * On first run, we set up the efi_set_colors() 1049 * callback. If the env is already set, we 1050 * pick up fg and bg color values from the environment. 1051 */ 1052 ptr = getenv("teken.fg_color"); 1053 if (ptr != NULL) { 1054 attr.ta_fgcolor = strtol(ptr, NULL, 10); 1055 ptr = getenv("teken.bg_color"); 1056 attr.ta_bgcolor = strtol(ptr, NULL, 10); 1057 1058 teken_set_defattr(&gfx_state.tg_teken, &attr); 1059 } else { 1060 snprintf(env, sizeof(env), "%d", 1061 attr.ta_fgcolor); 1062 env_setenv("teken.fg_color", EV_VOLATILE, env, 1063 efi_set_colors, env_nounset); 1064 snprintf(env, sizeof(env), "%d", 1065 attr.ta_bgcolor); 1066 env_setenv("teken.bg_color", EV_VOLATILE, env, 1067 efi_set_colors, env_nounset); 1068 } 1069 } 1070 } 1071 1072 if (screen_buffer == NULL) { 1073 conout->EnableCursor(conout, TRUE); 1074 #ifdef TERM_EMU 1075 conout->SetAttribute(conout, EFI_TEXT_ATTR(DEFAULT_FGCOLOR, 1076 DEFAULT_BGCOLOR)); 1077 end_term(); 1078 get_pos(&curx, &cury); 1079 curs_move(&curx, &cury, curx, cury); 1080 fg_c = DEFAULT_FGCOLOR; 1081 bg_c = DEFAULT_BGCOLOR; 1082 #endif 1083 } else { 1084 /* Improve visibility */ 1085 if (attr.ta_bgcolor == TC_WHITE) 1086 attr.ta_bgcolor |= TC_LIGHT; 1087 teken_set_defattr(&gfx_state.tg_teken, &attr); 1088 1089 /* Draw frame around terminal area. */ 1090 cons_draw_frame(&attr); 1091 /* 1092 * Erase display, this will also fill our screen 1093 * buffer. 1094 */ 1095 teken_input(&gfx_state.tg_teken, "\e[2J", 4); 1096 gfx_state.tg_functions->tf_param(&gfx_state, 1097 TP_SHOWCURSOR, 1); 1098 } 1099 1100 snprintf(env, sizeof (env), "%u", (unsigned)rows); 1101 setenv("LINES", env, 1); 1102 snprintf(env, sizeof (env), "%u", (unsigned)cols); 1103 setenv("COLUMNS", env, 1); 1104 1105 return (true); 1106 } 1107 1108 static int 1109 efi_cons_init(int arg) 1110 { 1111 EFI_STATUS status; 1112 1113 if (efi_started) 1114 return (0); 1115 1116 efi_started = true; 1117 1118 gfx_framework_init(); 1119 if (cons_update_mode(gfx_state.tg_fb_type != FB_TEXT)) 1120 return (0); 1121 1122 return (1); 1123 } 1124 1125 static void 1126 input_partial(void) 1127 { 1128 unsigned i; 1129 uint32_t c; 1130 1131 if (utf8_left == 0) 1132 return; 1133 1134 for (i = 0; i < sizeof(utf8_partial); i++) { 1135 c = (utf8_partial >> (24 - (i << 3))) & 0xff; 1136 if (c != 0) 1137 efi_term_emu(c); 1138 } 1139 utf8_left = 0; 1140 utf8_partial = 0; 1141 } 1142 1143 static void 1144 input_byte(uint8_t c) 1145 { 1146 if ((c & 0x80) == 0x00) { 1147 /* One-byte sequence. */ 1148 input_partial(); 1149 efi_term_emu(c); 1150 return; 1151 } 1152 if ((c & 0xe0) == 0xc0) { 1153 /* Two-byte sequence. */ 1154 input_partial(); 1155 utf8_left = 1; 1156 utf8_partial = c; 1157 return; 1158 } 1159 if ((c & 0xf0) == 0xe0) { 1160 /* Three-byte sequence. */ 1161 input_partial(); 1162 utf8_left = 2; 1163 utf8_partial = c; 1164 return; 1165 } 1166 if ((c & 0xf8) == 0xf0) { 1167 /* Four-byte sequence. */ 1168 input_partial(); 1169 utf8_left = 3; 1170 utf8_partial = c; 1171 return; 1172 } 1173 if ((c & 0xc0) == 0x80) { 1174 /* Invalid state? */ 1175 if (utf8_left == 0) { 1176 efi_term_emu(c); 1177 return; 1178 } 1179 utf8_left--; 1180 utf8_partial = (utf8_partial << 8) | c; 1181 if (utf8_left == 0) { 1182 uint32_t v, u; 1183 uint8_t b; 1184 1185 v = 0; 1186 u = utf8_partial; 1187 b = (u >> 24) & 0xff; 1188 if (b != 0) { /* Four-byte sequence */ 1189 v = b & 0x07; 1190 b = (u >> 16) & 0xff; 1191 v = (v << 6) | (b & 0x3f); 1192 b = (u >> 8) & 0xff; 1193 v = (v << 6) | (b & 0x3f); 1194 b = u & 0xff; 1195 v = (v << 6) | (b & 0x3f); 1196 } else if ((b = (u >> 16) & 0xff) != 0) { 1197 v = b & 0x0f; /* Three-byte sequence */ 1198 b = (u >> 8) & 0xff; 1199 v = (v << 6) | (b & 0x3f); 1200 b = u & 0xff; 1201 v = (v << 6) | (b & 0x3f); 1202 } else if ((b = (u >> 8) & 0xff) != 0) { 1203 v = b & 0x1f; /* Two-byte sequence */ 1204 b = u & 0xff; 1205 v = (v << 6) | (b & 0x3f); 1206 } 1207 /* Send unicode char directly to console. */ 1208 efi_cons_efiputchar(v); 1209 utf8_partial = 0; 1210 } 1211 return; 1212 } 1213 /* Anything left is illegal in UTF-8 sequence. */ 1214 input_partial(); 1215 efi_term_emu(c); 1216 } 1217 1218 void 1219 efi_cons_putchar(int c) 1220 { 1221 unsigned char ch = c; 1222 1223 /* 1224 * Don't use Teken when we're doing pure serial, or a multiple console 1225 * with video "primary" because that's also serial. 1226 */ 1227 if ((mode & (RB_SERIAL | RB_MULTIPLE)) != 0 || screen_buffer == NULL) { 1228 input_byte(ch); 1229 return; 1230 } 1231 1232 teken_input(&gfx_state.tg_teken, &ch, sizeof (ch)); 1233 } 1234 1235 static int 1236 keybuf_getchar(void) 1237 { 1238 int i, c = 0; 1239 1240 for (i = 0; i < KEYBUFSZ; i++) { 1241 if (keybuf[i] != 0) { 1242 c = keybuf[i]; 1243 keybuf[i] = 0; 1244 break; 1245 } 1246 } 1247 1248 return (c); 1249 } 1250 1251 static bool 1252 keybuf_ischar(void) 1253 { 1254 int i; 1255 1256 for (i = 0; i < KEYBUFSZ; i++) { 1257 if (keybuf[i] != 0) 1258 return (true); 1259 } 1260 return (false); 1261 } 1262 1263 /* 1264 * We are not reading input before keybuf is empty, so we are safe 1265 * just to fill keybuf from the beginning. 1266 */ 1267 static void 1268 keybuf_inschar(EFI_INPUT_KEY *key) 1269 { 1270 1271 switch (key->ScanCode) { 1272 case SCAN_UP: /* UP */ 1273 keybuf[0] = 0x1b; /* esc */ 1274 keybuf[1] = '['; 1275 keybuf[2] = 'A'; 1276 break; 1277 case SCAN_DOWN: /* DOWN */ 1278 keybuf[0] = 0x1b; /* esc */ 1279 keybuf[1] = '['; 1280 keybuf[2] = 'B'; 1281 break; 1282 case SCAN_RIGHT: /* RIGHT */ 1283 keybuf[0] = 0x1b; /* esc */ 1284 keybuf[1] = '['; 1285 keybuf[2] = 'C'; 1286 break; 1287 case SCAN_LEFT: /* LEFT */ 1288 keybuf[0] = 0x1b; /* esc */ 1289 keybuf[1] = '['; 1290 keybuf[2] = 'D'; 1291 break; 1292 case SCAN_DELETE: 1293 keybuf[0] = CHAR_BACKSPACE; 1294 break; 1295 case SCAN_ESC: 1296 keybuf[0] = 0x1b; /* esc */ 1297 break; 1298 default: 1299 keybuf[0] = key->UnicodeChar; 1300 break; 1301 } 1302 } 1303 1304 static bool 1305 efi_readkey(void) 1306 { 1307 EFI_STATUS status; 1308 EFI_INPUT_KEY key; 1309 1310 status = conin->ReadKeyStroke(conin, &key); 1311 if (status == EFI_SUCCESS) { 1312 keybuf_inschar(&key); 1313 return (true); 1314 } 1315 return (false); 1316 } 1317 1318 static bool 1319 efi_readkey_ex(void) 1320 { 1321 EFI_STATUS status; 1322 EFI_INPUT_KEY *kp; 1323 EFI_KEY_DATA key_data; 1324 uint32_t kss; 1325 1326 status = coninex->ReadKeyStrokeEx(coninex, &key_data); 1327 if (status == EFI_SUCCESS) { 1328 kss = key_data.KeyState.KeyShiftState; 1329 kp = &key_data.Key; 1330 if (kss & EFI_SHIFT_STATE_VALID) { 1331 1332 /* 1333 * quick mapping to control chars, replace with 1334 * map lookup later. 1335 */ 1336 if (kss & EFI_RIGHT_CONTROL_PRESSED || 1337 kss & EFI_LEFT_CONTROL_PRESSED) { 1338 if (kp->UnicodeChar >= 'a' && 1339 kp->UnicodeChar <= 'z') { 1340 kp->UnicodeChar -= 'a'; 1341 kp->UnicodeChar++; 1342 } 1343 } 1344 } 1345 /* 1346 * The shift state and/or toggle state may not be valid, 1347 * but we still can have ScanCode or UnicodeChar. 1348 */ 1349 if (kp->ScanCode == 0 && kp->UnicodeChar == 0) 1350 return (false); 1351 keybuf_inschar(kp); 1352 return (true); 1353 } 1354 return (false); 1355 } 1356 1357 int 1358 efi_cons_getchar(void) 1359 { 1360 int c; 1361 1362 if ((c = keybuf_getchar()) != 0) 1363 return (c); 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 /* 1387 * Some EFI implementation (u-boot for example) do not support 1388 * WaitForKey(). 1389 * CheckEvent() can clear the signaled state. 1390 */ 1391 if (coninex != NULL) { 1392 if (coninex->WaitForKeyEx == NULL) { 1393 key_pending = efi_readkey_ex(); 1394 } else { 1395 status = BS->CheckEvent(coninex->WaitForKeyEx); 1396 key_pending = status == EFI_SUCCESS; 1397 } 1398 } else { 1399 if (conin->WaitForKey == NULL) { 1400 key_pending = efi_readkey(); 1401 } else { 1402 status = BS->CheckEvent(conin->WaitForKey); 1403 key_pending = status == EFI_SUCCESS; 1404 } 1405 } 1406 1407 return (key_pending); 1408 } 1409 1410 /* Plain direct access to EFI OutputString(). */ 1411 void 1412 efi_cons_efiputchar(int c) 1413 { 1414 CHAR16 buf[2]; 1415 EFI_STATUS status; 1416 1417 buf[0] = c; 1418 buf[1] = 0; /* terminate string */ 1419 1420 status = conout->TestString(conout, buf); 1421 if (EFI_ERROR(status)) 1422 buf[0] = '?'; 1423 conout->OutputString(conout, buf); 1424 } 1425