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) 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 (p->tp_row == tp.tp_row - 1 && 168 p->tp_col == tp.tp_col - 1) 169 return; 170 171 (void) conout->SetAttribute(conout, attr); 172 efi_cons_efiputchar(px->c); 173 (void) conout->SetAttribute(conout, a); 174 } 175 176 static void 177 efi_text_putchar(void *s __unused, const teken_pos_t *p, teken_char_t c, 178 const teken_attr_t *a) 179 { 180 EFI_STATUS status; 181 int idx; 182 183 idx = p->tp_col + p->tp_row * tp.tp_col; 184 buffer[idx].c = c; 185 buffer[idx].a = *a; 186 efi_text_printchar(p); 187 } 188 189 static void 190 efi_text_fill(void *s, const teken_rect_t *r, teken_char_t c, 191 const teken_attr_t *a) 192 { 193 teken_pos_t p; 194 UINTN row, col; 195 196 (void) conout->QueryMode(conout, conout->Mode->Mode, &col, &row); 197 198 conout->EnableCursor(conout, FALSE); 199 for (p.tp_row = r->tr_begin.tp_row; p.tp_row < r->tr_end.tp_row; 200 p.tp_row++) 201 for (p.tp_col = r->tr_begin.tp_col; 202 p.tp_col < r->tr_end.tp_col; p.tp_col++) 203 efi_text_putchar(s, &p, c, a); 204 conout->EnableCursor(conout, TRUE); 205 } 206 207 static bool 208 efi_same_pixel(struct text_pixel *px1, struct text_pixel *px2) 209 { 210 if (px1->c != px2->c) 211 return (false); 212 213 if (px1->a.ta_format != px2->a.ta_format) 214 return (false); 215 if (px1->a.ta_fgcolor != px2->a.ta_fgcolor) 216 return (false); 217 if (px1->a.ta_bgcolor != px2->a.ta_bgcolor) 218 return (false); 219 220 return (true); 221 } 222 223 static void 224 efi_text_copy(void *ptr __unused, const teken_rect_t *r, const teken_pos_t *p) 225 { 226 int srow, drow; 227 int nrow, ncol, x, y; /* Has to be signed - >= 0 comparison */ 228 teken_pos_t d, s; 229 230 /* 231 * Copying is a little tricky. We must make sure we do it in 232 * correct order, to make sure we don't overwrite our own data. 233 */ 234 235 nrow = r->tr_end.tp_row - r->tr_begin.tp_row; 236 ncol = r->tr_end.tp_col - r->tr_begin.tp_col; 237 238 conout->EnableCursor(conout, FALSE); 239 if (p->tp_row < r->tr_begin.tp_row) { 240 /* Copy from bottom to top. */ 241 for (y = 0; y < nrow; y++) { 242 d.tp_row = p->tp_row + y; 243 s.tp_row = r->tr_begin.tp_row + y; 244 drow = d.tp_row * tp.tp_col; 245 srow = s.tp_row * tp.tp_col; 246 for (x = 0; x < ncol; x++) { 247 d.tp_col = p->tp_col + x; 248 s.tp_col = r->tr_begin.tp_col + x; 249 250 if (!efi_same_pixel( 251 &buffer[d.tp_col + drow], 252 &buffer[s.tp_col + srow])) { 253 buffer[d.tp_col + drow] = 254 buffer[s.tp_col + srow]; 255 efi_text_printchar(&d); 256 } 257 } 258 } 259 } else { 260 /* Copy from top to bottom. */ 261 if (p->tp_col < r->tr_begin.tp_col) { 262 /* Copy from right to left. */ 263 for (y = nrow - 1; y >= 0; y--) { 264 d.tp_row = p->tp_row + y; 265 s.tp_row = r->tr_begin.tp_row + y; 266 drow = d.tp_row * tp.tp_col; 267 srow = s.tp_row * tp.tp_col; 268 for (x = 0; x < ncol; x++) { 269 d.tp_col = p->tp_col + x; 270 s.tp_col = r->tr_begin.tp_col + x; 271 272 if (!efi_same_pixel( 273 &buffer[d.tp_col + drow], 274 &buffer[s.tp_col + srow])) { 275 buffer[d.tp_col + drow] = 276 buffer[s.tp_col + srow]; 277 efi_text_printchar(&d); 278 } 279 } 280 } 281 } else { 282 /* Copy from left to right. */ 283 for (y = nrow - 1; y >= 0; y--) { 284 d.tp_row = p->tp_row + y; 285 s.tp_row = r->tr_begin.tp_row + y; 286 drow = d.tp_row * tp.tp_col; 287 srow = s.tp_row * tp.tp_col; 288 for (x = ncol - 1; x >= 0; x--) { 289 d.tp_col = p->tp_col + x; 290 s.tp_col = r->tr_begin.tp_col + x; 291 292 if (!efi_same_pixel( 293 &buffer[d.tp_col + drow], 294 &buffer[s.tp_col + srow])) { 295 buffer[d.tp_col + drow] = 296 buffer[s.tp_col + srow]; 297 efi_text_printchar(&d); 298 } 299 } 300 } 301 } 302 } 303 conout->EnableCursor(conout, TRUE); 304 } 305 306 static void 307 efi_text_param(void *s __unused, int cmd, unsigned int value) 308 { 309 switch (cmd) { 310 case TP_SETLOCALCURSOR: 311 /* 312 * 0 means normal (usually block), 1 means hidden, and 313 * 2 means blinking (always block) for compatibility with 314 * syscons. We don't support any changes except hiding, 315 * so must map 2 to 0. 316 */ 317 value = (value == 1) ? 0 : 1; 318 /* FALLTHROUGH */ 319 case TP_SHOWCURSOR: 320 if (value == 1) 321 conout->EnableCursor(conout, TRUE); 322 else 323 conout->EnableCursor(conout, FALSE); 324 break; 325 default: 326 /* Not yet implemented */ 327 break; 328 } 329 } 330 331 /* 332 * Not implemented. 333 */ 334 static void 335 efi_cons_respond(void *s __unused, const void *buf __unused, 336 size_t len __unused) 337 { 338 } 339 340 static void 341 efi_cons_probe(struct console *cp) 342 { 343 cp->c_flags |= C_PRESENTIN | C_PRESENTOUT; 344 } 345 346 static bool 347 color_name_to_teken(const char *name, int *val) 348 { 349 if (strcasecmp(name, "black") == 0) { 350 *val = TC_BLACK; 351 return (true); 352 } 353 if (strcasecmp(name, "red") == 0) { 354 *val = TC_RED; 355 return (true); 356 } 357 if (strcasecmp(name, "green") == 0) { 358 *val = TC_GREEN; 359 return (true); 360 } 361 if (strcasecmp(name, "brown") == 0) { 362 *val = TC_BROWN; 363 return (true); 364 } 365 if (strcasecmp(name, "blue") == 0) { 366 *val = TC_BLUE; 367 return (true); 368 } 369 if (strcasecmp(name, "magenta") == 0) { 370 *val = TC_MAGENTA; 371 return (true); 372 } 373 if (strcasecmp(name, "cyan") == 0) { 374 *val = TC_CYAN; 375 return (true); 376 } 377 if (strcasecmp(name, "white") == 0) { 378 *val = TC_WHITE; 379 return (true); 380 } 381 return (false); 382 } 383 384 static int 385 efi_set_colors(struct env_var *ev, int flags, const void *value) 386 { 387 int val = 0; 388 char buf[2]; 389 const void *evalue; 390 const teken_attr_t *ap; 391 teken_attr_t a; 392 393 if (value == NULL) 394 return (CMD_OK); 395 396 if (color_name_to_teken(value, &val)) { 397 snprintf(buf, sizeof (buf), "%d", val); 398 evalue = buf; 399 } else { 400 char *end; 401 402 errno = 0; 403 val = (int)strtol(value, &end, 0); 404 if (errno != 0 || *end != '\0') { 405 printf("Allowed values are either ansi color name or " 406 "number from range [0-7].\n"); 407 return (CMD_OK); 408 } 409 evalue = value; 410 } 411 412 ap = teken_get_defattr(&teken); 413 a = *ap; 414 if (strcmp(ev->ev_name, "teken.fg_color") == 0) { 415 /* is it already set? */ 416 if (ap->ta_fgcolor == val) 417 return (CMD_OK); 418 a.ta_fgcolor = val; 419 } 420 if (strcmp(ev->ev_name, "teken.bg_color") == 0) { 421 /* is it already set? */ 422 if (ap->ta_bgcolor == val) 423 return (CMD_OK); 424 a.ta_bgcolor = val; 425 } 426 env_setenv(ev->ev_name, flags | EV_NOHOOK, evalue, NULL, NULL); 427 teken_set_defattr(&teken, &a); 428 return (CMD_OK); 429 } 430 431 bool 432 efi_cons_update_mode(void) 433 { 434 UINTN cols, rows; 435 const teken_attr_t *a; 436 EFI_STATUS status; 437 char env[8]; 438 439 status = conout->QueryMode(conout, conout->Mode->Mode, &cols, &rows); 440 if (EFI_ERROR(status)) { 441 cols = 80; 442 rows = 24; 443 } 444 445 if (buffer != NULL) { 446 if (tp.tp_row == rows && tp.tp_col == cols) 447 return (true); 448 free(buffer); 449 } else { 450 teken_init(&teken, &tf, NULL); 451 } 452 453 tp.tp_row = rows; 454 tp.tp_col = cols; 455 buffer = malloc(rows * cols * sizeof(*buffer)); 456 if (buffer == NULL) 457 return (false); 458 459 teken_set_winsize(&teken, &tp); 460 a = teken_get_defattr(&teken); 461 462 snprintf(env, sizeof(env), "%d", a->ta_fgcolor); 463 env_setenv("teken.fg_color", EV_VOLATILE, env, efi_set_colors, 464 env_nounset); 465 snprintf(env, sizeof(env), "%d", a->ta_bgcolor); 466 env_setenv("teken.bg_color", EV_VOLATILE, env, efi_set_colors, 467 env_nounset); 468 469 for (int row = 0; row < rows; row++) 470 for (int col = 0; col < cols; col++) { 471 buffer[col + row * tp.tp_col].c = ' '; 472 buffer[col + row * tp.tp_col].a = *a; 473 } 474 475 snprintf(env, sizeof (env), "%u", (unsigned)rows); 476 setenv("LINES", env, 1); 477 snprintf(env, sizeof (env), "%u", (unsigned)cols); 478 setenv("COLUMNS", env, 1); 479 480 return (true); 481 } 482 483 static int 484 efi_cons_init(int arg) 485 { 486 EFI_STATUS status; 487 488 if (conin != NULL) 489 return (0); 490 491 conout = ST->ConOut; 492 conin = ST->ConIn; 493 494 conout->EnableCursor(conout, TRUE); 495 status = BS->OpenProtocol(ST->ConsoleInHandle, &simple_input_ex_guid, 496 (void **)&coninex, IH, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL); 497 if (status != EFI_SUCCESS) 498 coninex = NULL; 499 500 if (efi_cons_update_mode()) 501 return (0); 502 503 return (1); 504 } 505 506 void 507 efi_cons_putchar(int c) 508 { 509 unsigned char ch = c; 510 511 if (buffer != NULL) 512 teken_input(&teken, &ch, sizeof (ch)); 513 else 514 efi_cons_efiputchar(c); 515 } 516 517 static int 518 keybuf_getchar(void) 519 { 520 int i, c = 0; 521 522 for (i = 0; i < KEYBUFSZ; i++) { 523 if (keybuf[i] != 0) { 524 c = keybuf[i]; 525 keybuf[i] = 0; 526 break; 527 } 528 } 529 530 return (c); 531 } 532 533 static bool 534 keybuf_ischar(void) 535 { 536 int i; 537 538 for (i = 0; i < KEYBUFSZ; i++) { 539 if (keybuf[i] != 0) 540 return (true); 541 } 542 return (false); 543 } 544 545 /* 546 * We are not reading input before keybuf is empty, so we are safe 547 * just to fill keybuf from the beginning. 548 */ 549 static void 550 keybuf_inschar(EFI_INPUT_KEY *key) 551 { 552 553 switch (key->ScanCode) { 554 case SCAN_UP: /* UP */ 555 keybuf[0] = 0x1b; /* esc */ 556 keybuf[1] = '['; 557 keybuf[2] = 'A'; 558 break; 559 case SCAN_DOWN: /* DOWN */ 560 keybuf[0] = 0x1b; /* esc */ 561 keybuf[1] = '['; 562 keybuf[2] = 'B'; 563 break; 564 case SCAN_RIGHT: /* RIGHT */ 565 keybuf[0] = 0x1b; /* esc */ 566 keybuf[1] = '['; 567 keybuf[2] = 'C'; 568 break; 569 case SCAN_LEFT: /* LEFT */ 570 keybuf[0] = 0x1b; /* esc */ 571 keybuf[1] = '['; 572 keybuf[2] = 'D'; 573 break; 574 case SCAN_DELETE: 575 keybuf[0] = CHAR_BACKSPACE; 576 break; 577 case SCAN_ESC: 578 keybuf[0] = 0x1b; /* esc */ 579 break; 580 default: 581 keybuf[0] = key->UnicodeChar; 582 break; 583 } 584 } 585 586 static bool 587 efi_readkey(void) 588 { 589 EFI_STATUS status; 590 EFI_INPUT_KEY key; 591 592 status = conin->ReadKeyStroke(conin, &key); 593 if (status == EFI_SUCCESS) { 594 keybuf_inschar(&key); 595 return (true); 596 } 597 return (false); 598 } 599 600 static bool 601 efi_readkey_ex(void) 602 { 603 EFI_STATUS status; 604 EFI_INPUT_KEY *kp; 605 EFI_KEY_DATA key_data; 606 uint32_t kss; 607 608 status = coninex->ReadKeyStrokeEx(coninex, &key_data); 609 if (status == EFI_SUCCESS) { 610 kss = key_data.KeyState.KeyShiftState; 611 kp = &key_data.Key; 612 if (kss & EFI_SHIFT_STATE_VALID) { 613 614 /* 615 * quick mapping to control chars, replace with 616 * map lookup later. 617 */ 618 if (kss & EFI_RIGHT_CONTROL_PRESSED || 619 kss & EFI_LEFT_CONTROL_PRESSED) { 620 if (kp->UnicodeChar >= 'a' && 621 kp->UnicodeChar <= 'z') { 622 kp->UnicodeChar -= 'a'; 623 kp->UnicodeChar++; 624 } 625 } 626 } 627 628 keybuf_inschar(kp); 629 return (true); 630 } 631 return (false); 632 } 633 634 int 635 efi_cons_getchar(void) 636 { 637 int c; 638 639 if ((c = keybuf_getchar()) != 0) 640 return (c); 641 642 key_pending = 0; 643 644 if (coninex == NULL) { 645 if (efi_readkey()) 646 return (keybuf_getchar()); 647 } else { 648 if (efi_readkey_ex()) 649 return (keybuf_getchar()); 650 } 651 652 return (-1); 653 } 654 655 int 656 efi_cons_poll(void) 657 { 658 EFI_STATUS status; 659 660 if (keybuf_ischar() || key_pending) 661 return (1); 662 663 /* 664 * Some EFI implementation (u-boot for example) do not support 665 * WaitForKey(). 666 * CheckEvent() can clear the signaled state. 667 */ 668 if (coninex != NULL) { 669 if (coninex->WaitForKeyEx == NULL) { 670 key_pending = efi_readkey_ex(); 671 } else { 672 status = BS->CheckEvent(coninex->WaitForKeyEx); 673 key_pending = status == EFI_SUCCESS; 674 } 675 } else { 676 if (conin->WaitForKey == NULL) { 677 key_pending = efi_readkey(); 678 } else { 679 status = BS->CheckEvent(conin->WaitForKey); 680 key_pending = status == EFI_SUCCESS; 681 } 682 } 683 684 return (key_pending); 685 } 686 687 /* Plain direct access to EFI OutputString(). */ 688 void 689 efi_cons_efiputchar(int c) 690 { 691 CHAR16 buf[2]; 692 EFI_STATUS status; 693 694 buf[0] = c; 695 buf[1] = 0; /* terminate string */ 696 697 status = conout->TestString(conout, buf); 698 if (EFI_ERROR(status)) 699 buf[0] = '?'; 700 conout->OutputString(conout, buf); 701 } 702