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 <efi.h> 31 #include <efilib.h> 32 #include <teken.h> 33 34 #include "bootstrap.h" 35 36 static EFI_GUID simple_input_ex_guid = EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL_GUID; 37 static SIMPLE_TEXT_OUTPUT_INTERFACE *conout; 38 static SIMPLE_INPUT_INTERFACE *conin; 39 static EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *coninex; 40 41 static tf_bell_t efi_cons_bell; 42 static tf_cursor_t efi_text_cursor; 43 static tf_putchar_t efi_text_putchar; 44 static tf_fill_t efi_text_fill; 45 static tf_copy_t efi_text_copy; 46 static tf_param_t efi_text_param; 47 static tf_respond_t efi_cons_respond; 48 49 static teken_funcs_t tf = { 50 .tf_bell = efi_cons_bell, 51 .tf_cursor = efi_text_cursor, 52 .tf_putchar = efi_text_putchar, 53 .tf_fill = efi_text_fill, 54 .tf_copy = efi_text_copy, 55 .tf_param = efi_text_param, 56 .tf_respond = efi_cons_respond, 57 }; 58 59 teken_t teken; 60 teken_pos_t tp; 61 62 struct text_pixel { 63 teken_char_t c; 64 teken_attr_t a; 65 }; 66 67 static struct text_pixel *buffer; 68 69 #define KEYBUFSZ 10 70 static unsigned keybuf[KEYBUFSZ]; /* keybuf for extended codes */ 71 static int key_pending; 72 73 static const unsigned char teken_color_to_efi_color[16] = { 74 EFI_BLACK, 75 EFI_RED, 76 EFI_GREEN, 77 EFI_BROWN, 78 EFI_BLUE, 79 EFI_MAGENTA, 80 EFI_CYAN, 81 EFI_LIGHTGRAY, 82 EFI_DARKGRAY, 83 EFI_LIGHTRED, 84 EFI_LIGHTGREEN, 85 EFI_YELLOW, 86 EFI_LIGHTBLUE, 87 EFI_LIGHTMAGENTA, 88 EFI_LIGHTCYAN, 89 EFI_WHITE 90 }; 91 92 static void efi_cons_probe(struct console *); 93 static int efi_cons_init(int); 94 void efi_cons_putchar(int); 95 int efi_cons_getchar(void); 96 void efi_cons_efiputchar(int); 97 int efi_cons_poll(void); 98 99 struct console efi_console = { 100 "efi", 101 "EFI console", 102 C_WIDEOUT, 103 efi_cons_probe, 104 efi_cons_init, 105 efi_cons_putchar, 106 efi_cons_getchar, 107 efi_cons_poll 108 }; 109 110 /* 111 * Not implemented. 112 */ 113 static void 114 efi_cons_bell(void *s __unused) 115 { 116 } 117 118 static void 119 efi_text_cursor(void *s __unused, const teken_pos_t *p) 120 { 121 UINTN row, col; 122 123 (void) conout->QueryMode(conout, conout->Mode->Mode, &col, &row); 124 125 if (p->tp_col == col) 126 col = p->tp_col - 1; 127 else 128 col = p->tp_col; 129 130 if (p->tp_row == row) 131 row = p->tp_row - 1; 132 else 133 row = p->tp_row; 134 135 conout->SetCursorPosition(conout, col, row); 136 } 137 138 static void 139 efi_text_printchar(const teken_pos_t *p, bool autoscroll) 140 { 141 UINTN a, attr; 142 struct text_pixel *px; 143 teken_color_t fg, bg, tmp; 144 145 px = buffer + p->tp_col + p->tp_row * tp.tp_col; 146 a = conout->Mode->Attribute; 147 148 fg = teken_256to16(px->a.ta_fgcolor); 149 bg = teken_256to16(px->a.ta_bgcolor); 150 if (px->a.ta_format & TF_BOLD) 151 fg |= TC_LIGHT; 152 if (px->a.ta_format & TF_BLINK) 153 bg |= TC_LIGHT; 154 155 if (px->a.ta_format & TF_REVERSE) { 156 tmp = fg; 157 fg = bg; 158 bg = tmp; 159 } 160 161 attr = EFI_TEXT_ATTR(teken_color_to_efi_color[fg], 162 teken_color_to_efi_color[bg] & 0x7); 163 164 conout->SetCursorPosition(conout, p->tp_col, p->tp_row); 165 166 /* to prvent autoscroll, skip print of lower right char */ 167 if (!autoscroll && 168 p->tp_row == tp.tp_row - 1 && 169 p->tp_col == tp.tp_col - 1) 170 return; 171 172 (void) conout->SetAttribute(conout, attr); 173 efi_cons_efiputchar(px->c); 174 (void) conout->SetAttribute(conout, a); 175 } 176 177 static void 178 efi_text_putchar(void *s __unused, const teken_pos_t *p, teken_char_t c, 179 const teken_attr_t *a) 180 { 181 EFI_STATUS status; 182 int idx; 183 184 idx = p->tp_col + p->tp_row * tp.tp_col; 185 buffer[idx].c = c; 186 buffer[idx].a = *a; 187 efi_text_printchar(p, false); 188 } 189 190 static void 191 efi_text_fill(void *s, const teken_rect_t *r, teken_char_t c, 192 const teken_attr_t *a) 193 { 194 teken_pos_t p; 195 UINTN row, col; 196 197 (void) conout->QueryMode(conout, conout->Mode->Mode, &col, &row); 198 199 conout->EnableCursor(conout, FALSE); 200 for (p.tp_row = r->tr_begin.tp_row; p.tp_row < r->tr_end.tp_row; 201 p.tp_row++) 202 for (p.tp_col = r->tr_begin.tp_col; 203 p.tp_col < r->tr_end.tp_col; p.tp_col++) 204 efi_text_putchar(s, &p, c, a); 205 conout->EnableCursor(conout, TRUE); 206 } 207 208 static bool 209 efi_same_pixel(struct text_pixel *px1, struct text_pixel *px2) 210 { 211 if (px1->c != px2->c) 212 return (false); 213 214 if (px1->a.ta_format != px2->a.ta_format) 215 return (false); 216 if (px1->a.ta_fgcolor != px2->a.ta_fgcolor) 217 return (false); 218 if (px1->a.ta_bgcolor != px2->a.ta_bgcolor) 219 return (false); 220 221 return (true); 222 } 223 224 static void 225 efi_text_copy(void *ptr __unused, const teken_rect_t *r, const teken_pos_t *p) 226 { 227 int srow, drow; 228 int nrow, ncol, x, y; /* Has to be signed - >= 0 comparison */ 229 teken_pos_t d, s; 230 bool scroll = false; 231 232 /* 233 * Copying is a little tricky. We must make sure we do it in 234 * correct order, to make sure we don't overwrite our own data. 235 */ 236 237 nrow = r->tr_end.tp_row - r->tr_begin.tp_row; 238 ncol = r->tr_end.tp_col - r->tr_begin.tp_col; 239 240 /* 241 * Check if we do copy whole screen. 242 */ 243 if (p->tp_row == 0 && p->tp_col == 0 && 244 nrow == tp.tp_row - 2 && ncol == tp.tp_col - 2) 245 scroll = true; 246 247 conout->EnableCursor(conout, FALSE); 248 if (p->tp_row < r->tr_begin.tp_row) { 249 /* Copy from bottom to top. */ 250 for (y = 0; y < nrow; y++) { 251 d.tp_row = p->tp_row + y; 252 s.tp_row = r->tr_begin.tp_row + y; 253 drow = d.tp_row * tp.tp_col; 254 srow = s.tp_row * tp.tp_col; 255 for (x = 0; x < ncol; x++) { 256 d.tp_col = p->tp_col + x; 257 s.tp_col = r->tr_begin.tp_col + x; 258 259 if (!efi_same_pixel( 260 &buffer[d.tp_col + drow], 261 &buffer[s.tp_col + srow])) { 262 buffer[d.tp_col + drow] = 263 buffer[s.tp_col + srow]; 264 if (!scroll) 265 efi_text_printchar(&d, false); 266 } else if (scroll) { 267 /* 268 * Draw last char and trigger 269 * scroll. 270 */ 271 if (y == nrow - 1 && 272 x == ncol - 1) { 273 efi_text_printchar(&d, true); 274 } 275 } 276 } 277 } 278 } else { 279 /* Copy from top to bottom. */ 280 if (p->tp_col < r->tr_begin.tp_col) { 281 /* Copy from right to left. */ 282 for (y = nrow - 1; y >= 0; y--) { 283 d.tp_row = p->tp_row + y; 284 s.tp_row = r->tr_begin.tp_row + y; 285 drow = d.tp_row * tp.tp_col; 286 srow = s.tp_row * tp.tp_col; 287 for (x = 0; x < ncol; x++) { 288 d.tp_col = p->tp_col + x; 289 s.tp_col = r->tr_begin.tp_col + x; 290 291 if (!efi_same_pixel( 292 &buffer[d.tp_col + drow], 293 &buffer[s.tp_col + srow])) { 294 buffer[d.tp_col + drow] = 295 buffer[s.tp_col + srow]; 296 efi_text_printchar(&d, false); 297 } 298 } 299 } 300 } else { 301 /* Copy from left to right. */ 302 for (y = nrow - 1; y >= 0; y--) { 303 d.tp_row = p->tp_row + y; 304 s.tp_row = r->tr_begin.tp_row + y; 305 drow = d.tp_row * tp.tp_col; 306 srow = s.tp_row * tp.tp_col; 307 for (x = ncol - 1; x >= 0; x--) { 308 d.tp_col = p->tp_col + x; 309 s.tp_col = r->tr_begin.tp_col + x; 310 311 if (!efi_same_pixel( 312 &buffer[d.tp_col + drow], 313 &buffer[s.tp_col + srow])) { 314 buffer[d.tp_col + drow] = 315 buffer[s.tp_col + srow]; 316 efi_text_printchar(&d, false); 317 } 318 } 319 } 320 } 321 } 322 conout->EnableCursor(conout, TRUE); 323 } 324 325 static void 326 efi_text_param(void *s __unused, int cmd, unsigned int value) 327 { 328 switch (cmd) { 329 case TP_SETLOCALCURSOR: 330 /* 331 * 0 means normal (usually block), 1 means hidden, and 332 * 2 means blinking (always block) for compatibility with 333 * syscons. We don't support any changes except hiding, 334 * so must map 2 to 0. 335 */ 336 value = (value == 1) ? 0 : 1; 337 /* FALLTHROUGH */ 338 case TP_SHOWCURSOR: 339 if (value == 1) 340 conout->EnableCursor(conout, TRUE); 341 else 342 conout->EnableCursor(conout, FALSE); 343 break; 344 default: 345 /* Not yet implemented */ 346 break; 347 } 348 } 349 350 /* 351 * Not implemented. 352 */ 353 static void 354 efi_cons_respond(void *s __unused, const void *buf __unused, 355 size_t len __unused) 356 { 357 } 358 359 static void 360 efi_cons_probe(struct console *cp) 361 { 362 cp->c_flags |= C_PRESENTIN | C_PRESENTOUT; 363 } 364 365 static bool 366 color_name_to_teken(const char *name, int *val) 367 { 368 if (strcasecmp(name, "black") == 0) { 369 *val = TC_BLACK; 370 return (true); 371 } 372 if (strcasecmp(name, "red") == 0) { 373 *val = TC_RED; 374 return (true); 375 } 376 if (strcasecmp(name, "green") == 0) { 377 *val = TC_GREEN; 378 return (true); 379 } 380 if (strcasecmp(name, "brown") == 0) { 381 *val = TC_BROWN; 382 return (true); 383 } 384 if (strcasecmp(name, "blue") == 0) { 385 *val = TC_BLUE; 386 return (true); 387 } 388 if (strcasecmp(name, "magenta") == 0) { 389 *val = TC_MAGENTA; 390 return (true); 391 } 392 if (strcasecmp(name, "cyan") == 0) { 393 *val = TC_CYAN; 394 return (true); 395 } 396 if (strcasecmp(name, "white") == 0) { 397 *val = TC_WHITE; 398 return (true); 399 } 400 return (false); 401 } 402 403 static int 404 efi_set_colors(struct env_var *ev, int flags, const void *value) 405 { 406 int val = 0; 407 char buf[2]; 408 const void *evalue; 409 const teken_attr_t *ap; 410 teken_attr_t a; 411 412 if (value == NULL) 413 return (CMD_OK); 414 415 if (color_name_to_teken(value, &val)) { 416 snprintf(buf, sizeof (buf), "%d", val); 417 evalue = buf; 418 } else { 419 char *end; 420 421 errno = 0; 422 val = (int)strtol(value, &end, 0); 423 if (errno != 0 || *end != '\0') { 424 printf("Allowed values are either ansi color name or " 425 "number from range [0-7].\n"); 426 return (CMD_OK); 427 } 428 evalue = value; 429 } 430 431 ap = teken_get_defattr(&teken); 432 a = *ap; 433 if (strcmp(ev->ev_name, "teken.fg_color") == 0) { 434 /* is it already set? */ 435 if (ap->ta_fgcolor == val) 436 return (CMD_OK); 437 a.ta_fgcolor = val; 438 } 439 if (strcmp(ev->ev_name, "teken.bg_color") == 0) { 440 /* is it already set? */ 441 if (ap->ta_bgcolor == val) 442 return (CMD_OK); 443 a.ta_bgcolor = val; 444 } 445 env_setenv(ev->ev_name, flags | EV_NOHOOK, evalue, NULL, NULL); 446 teken_set_defattr(&teken, &a); 447 return (CMD_OK); 448 } 449 450 bool 451 efi_cons_update_mode(void) 452 { 453 UINTN cols, rows; 454 const teken_attr_t *a; 455 EFI_STATUS status; 456 char env[8]; 457 458 status = conout->QueryMode(conout, conout->Mode->Mode, &cols, &rows); 459 if (EFI_ERROR(status)) { 460 cols = 80; 461 rows = 24; 462 } 463 464 if (buffer != NULL) { 465 if (tp.tp_row == rows && tp.tp_col == cols) 466 return (true); 467 free(buffer); 468 } else { 469 teken_init(&teken, &tf, NULL); 470 } 471 472 tp.tp_row = rows; 473 tp.tp_col = cols; 474 buffer = malloc(rows * cols * sizeof(*buffer)); 475 if (buffer == NULL) 476 return (false); 477 478 teken_set_winsize(&teken, &tp); 479 a = teken_get_defattr(&teken); 480 481 snprintf(env, sizeof(env), "%d", a->ta_fgcolor); 482 env_setenv("teken.fg_color", EV_VOLATILE, env, efi_set_colors, 483 env_nounset); 484 snprintf(env, sizeof(env), "%d", a->ta_bgcolor); 485 env_setenv("teken.bg_color", EV_VOLATILE, env, efi_set_colors, 486 env_nounset); 487 488 for (int row = 0; row < rows; row++) 489 for (int col = 0; col < cols; col++) { 490 buffer[col + row * tp.tp_col].c = ' '; 491 buffer[col + row * tp.tp_col].a = *a; 492 } 493 494 snprintf(env, sizeof (env), "%u", (unsigned)rows); 495 setenv("LINES", env, 1); 496 snprintf(env, sizeof (env), "%u", (unsigned)cols); 497 setenv("COLUMNS", env, 1); 498 499 return (true); 500 } 501 502 static int 503 efi_cons_init(int arg) 504 { 505 EFI_STATUS status; 506 507 if (conin != NULL) 508 return (0); 509 510 conout = ST->ConOut; 511 conin = ST->ConIn; 512 513 conout->EnableCursor(conout, TRUE); 514 status = BS->OpenProtocol(ST->ConsoleInHandle, &simple_input_ex_guid, 515 (void **)&coninex, IH, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL); 516 if (status != EFI_SUCCESS) 517 coninex = NULL; 518 519 if (efi_cons_update_mode()) 520 return (0); 521 522 return (1); 523 } 524 525 void 526 efi_cons_putchar(int c) 527 { 528 unsigned char ch = c; 529 530 if (buffer != NULL) 531 teken_input(&teken, &ch, sizeof (ch)); 532 else 533 efi_cons_efiputchar(c); 534 } 535 536 static int 537 keybuf_getchar(void) 538 { 539 int i, c = 0; 540 541 for (i = 0; i < KEYBUFSZ; i++) { 542 if (keybuf[i] != 0) { 543 c = keybuf[i]; 544 keybuf[i] = 0; 545 break; 546 } 547 } 548 549 return (c); 550 } 551 552 static bool 553 keybuf_ischar(void) 554 { 555 int i; 556 557 for (i = 0; i < KEYBUFSZ; i++) { 558 if (keybuf[i] != 0) 559 return (true); 560 } 561 return (false); 562 } 563 564 /* 565 * We are not reading input before keybuf is empty, so we are safe 566 * just to fill keybuf from the beginning. 567 */ 568 static void 569 keybuf_inschar(EFI_INPUT_KEY *key) 570 { 571 572 switch (key->ScanCode) { 573 case SCAN_UP: /* UP */ 574 keybuf[0] = 0x1b; /* esc */ 575 keybuf[1] = '['; 576 keybuf[2] = 'A'; 577 break; 578 case SCAN_DOWN: /* DOWN */ 579 keybuf[0] = 0x1b; /* esc */ 580 keybuf[1] = '['; 581 keybuf[2] = 'B'; 582 break; 583 case SCAN_RIGHT: /* RIGHT */ 584 keybuf[0] = 0x1b; /* esc */ 585 keybuf[1] = '['; 586 keybuf[2] = 'C'; 587 break; 588 case SCAN_LEFT: /* LEFT */ 589 keybuf[0] = 0x1b; /* esc */ 590 keybuf[1] = '['; 591 keybuf[2] = 'D'; 592 break; 593 case SCAN_DELETE: 594 keybuf[0] = CHAR_BACKSPACE; 595 break; 596 case SCAN_ESC: 597 keybuf[0] = 0x1b; /* esc */ 598 break; 599 default: 600 keybuf[0] = key->UnicodeChar; 601 break; 602 } 603 } 604 605 static bool 606 efi_readkey(void) 607 { 608 EFI_STATUS status; 609 EFI_INPUT_KEY key; 610 611 status = conin->ReadKeyStroke(conin, &key); 612 if (status == EFI_SUCCESS) { 613 keybuf_inschar(&key); 614 return (true); 615 } 616 return (false); 617 } 618 619 static bool 620 efi_readkey_ex(void) 621 { 622 EFI_STATUS status; 623 EFI_INPUT_KEY *kp; 624 EFI_KEY_DATA key_data; 625 uint32_t kss; 626 627 status = coninex->ReadKeyStrokeEx(coninex, &key_data); 628 if (status == EFI_SUCCESS) { 629 kss = key_data.KeyState.KeyShiftState; 630 kp = &key_data.Key; 631 if (kss & EFI_SHIFT_STATE_VALID) { 632 633 /* 634 * quick mapping to control chars, replace with 635 * map lookup later. 636 */ 637 if (kss & EFI_RIGHT_CONTROL_PRESSED || 638 kss & EFI_LEFT_CONTROL_PRESSED) { 639 if (kp->UnicodeChar >= 'a' && 640 kp->UnicodeChar <= 'z') { 641 kp->UnicodeChar -= 'a'; 642 kp->UnicodeChar++; 643 } 644 } 645 } 646 647 keybuf_inschar(kp); 648 return (true); 649 } 650 return (false); 651 } 652 653 int 654 efi_cons_getchar(void) 655 { 656 int c; 657 658 if ((c = keybuf_getchar()) != 0) 659 return (c); 660 661 key_pending = 0; 662 663 if (coninex == NULL) { 664 if (efi_readkey()) 665 return (keybuf_getchar()); 666 } else { 667 if (efi_readkey_ex()) 668 return (keybuf_getchar()); 669 } 670 671 return (-1); 672 } 673 674 int 675 efi_cons_poll(void) 676 { 677 EFI_STATUS status; 678 679 if (keybuf_ischar() || key_pending) 680 return (1); 681 682 /* 683 * Some EFI implementation (u-boot for example) do not support 684 * WaitForKey(). 685 * CheckEvent() can clear the signaled state. 686 */ 687 if (coninex != NULL) { 688 if (coninex->WaitForKeyEx == NULL) { 689 key_pending = efi_readkey_ex(); 690 } else { 691 status = BS->CheckEvent(coninex->WaitForKeyEx); 692 key_pending = status == EFI_SUCCESS; 693 } 694 } else { 695 if (conin->WaitForKey == NULL) { 696 key_pending = efi_readkey(); 697 } else { 698 status = BS->CheckEvent(conin->WaitForKey); 699 key_pending = status == EFI_SUCCESS; 700 } 701 } 702 703 return (key_pending); 704 } 705 706 /* Plain direct access to EFI OutputString(). */ 707 void 708 efi_cons_efiputchar(int c) 709 { 710 CHAR16 buf[2]; 711 EFI_STATUS status; 712 713 buf[0] = c; 714 buf[1] = 0; /* terminate string */ 715 716 status = conout->TestString(conout, buf); 717 if (EFI_ERROR(status)) 718 buf[0] = '?'; 719 conout->OutputString(conout, buf); 720 } 721