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 29 #include <efi.h> 30 #include <efilib.h> 31 #include <eficonsctl.h> 32 #include <Guid/ConsoleInDevice.h> 33 #include <Guid/ConsoleOutDevice.h> 34 #include <Guid/StandardErrorDevice.h> 35 #include <Protocol/GraphicsOutput.h> 36 #include <Protocol/UgaDraw.h> 37 #include <Protocol/SimpleTextIn.h> 38 #include <Protocol/SimpleTextInEx.h> 39 #include <Protocol/SimpleTextOut.h> 40 #include <sys/tem_impl.h> 41 #include <sys/multiboot2.h> 42 #include <machine/metadata.h> 43 #include <gfx_fb.h> 44 45 #include "bootstrap.h" 46 47 struct efi_fb efifb; 48 EFI_GRAPHICS_OUTPUT_PROTOCOL *gop; 49 EFI_UGA_DRAW_PROTOCOL *uga; 50 51 EFI_GUID gEfiConsoleInDeviceGuid = EFI_CONSOLE_IN_DEVICE_GUID; 52 EFI_GUID gEfiConsoleOutDeviceGuid = EFI_CONSOLE_OUT_DEVICE_GUID; 53 EFI_GUID gEfiStandardErrorDeviceGuid = EFI_STANDARD_ERROR_DEVICE_GUID; 54 EFI_GUID gEfiConsoleControlProtocolGuid = EFI_CONSOLE_CONTROL_PROTOCOL_GUID; 55 EFI_GUID gEfiSimpleTextInProtocolGuid = EFI_SIMPLE_TEXT_INPUT_PROTOCOL_GUID; 56 EFI_GUID gEfiSimpleTextInputExProtocolGuid = 57 EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL_GUID; 58 EFI_GUID gEfiSimpleTextOutProtocolGuid = EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL_GUID; 59 static EFI_CONSOLE_CONTROL_PROTOCOL *console_control; 60 static EFI_CONSOLE_CONTROL_SCREEN_MODE console_mode; 61 static SIMPLE_TEXT_OUTPUT_INTERFACE *conout; 62 63 /* mode change callback and argument from tem */ 64 static vis_modechg_cb_t modechg_cb; 65 static struct vis_modechg_arg *modechg_arg; 66 static tem_vt_state_t tem; 67 68 struct efi_console_data { 69 struct visual_ops *ecd_visual_ops; 70 SIMPLE_INPUT_INTERFACE *ecd_conin; 71 EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *ecd_coninex; 72 }; 73 74 #define KEYBUFSZ 10 75 static unsigned keybuf[KEYBUFSZ]; /* keybuf for extended codes */ 76 77 static int key_pending; 78 79 static const unsigned char solaris_color_to_efi_color[16] = { 80 EFI_WHITE, 81 EFI_BLACK, 82 EFI_BLUE, 83 EFI_GREEN, 84 EFI_CYAN, 85 EFI_RED, 86 EFI_MAGENTA, 87 EFI_BROWN, 88 EFI_LIGHTGRAY, 89 EFI_DARKGRAY, 90 EFI_LIGHTBLUE, 91 EFI_LIGHTGREEN, 92 EFI_LIGHTCYAN, 93 EFI_LIGHTRED, 94 EFI_LIGHTMAGENTA, 95 EFI_YELLOW 96 }; 97 98 #define DEFAULT_FGCOLOR EFI_LIGHTGRAY 99 #define DEFAULT_BGCOLOR EFI_BLACK 100 101 extern int efi_find_framebuffer(struct efi_fb *efifb); 102 103 static void efi_framebuffer_setup(void); 104 static void efi_cons_probe(struct console *); 105 static int efi_cons_init(struct console *, int); 106 static void efi_cons_putchar(struct console *, int); 107 static void efi_cons_efiputchar(int); 108 static int efi_cons_getchar(struct console *); 109 static int efi_cons_poll(struct console *); 110 static int efi_cons_ioctl(struct console *cp, int cmd, void *data); 111 static void efi_cons_devinfo(struct console *); 112 113 static int efi_fb_devinit(struct vis_devinit *); 114 static void efi_cons_cursor(struct vis_conscursor *); 115 116 static int efi_text_devinit(struct vis_devinit *); 117 static int efi_text_cons_clear(struct vis_consclear *); 118 static void efi_text_cons_copy(struct vis_conscopy *); 119 static void efi_text_cons_display(struct vis_consdisplay *); 120 121 struct console efi_console = { 122 .c_name = "text", 123 .c_desc = "EFI console", 124 .c_flags = C_WIDEOUT, 125 .c_probe = efi_cons_probe, 126 .c_init = efi_cons_init, 127 .c_out = efi_cons_putchar, 128 .c_in = efi_cons_getchar, 129 .c_ready = efi_cons_poll, 130 .c_ioctl = efi_cons_ioctl, 131 .c_devinfo = efi_cons_devinfo, 132 .c_private = NULL 133 }; 134 135 static struct vis_identifier fb_ident = { "efi_fb" }; 136 static struct vis_identifier text_ident = { "efi_text" }; 137 138 struct visual_ops fb_ops = { 139 .ident = &fb_ident, 140 .kdsetmode = NULL, 141 .devinit = efi_fb_devinit, 142 .cons_copy = gfx_fb_cons_copy, 143 .cons_display = gfx_fb_cons_display, 144 .cons_cursor = efi_cons_cursor, 145 .cons_clear = gfx_fb_cons_clear, 146 .cons_put_cmap = NULL 147 }; 148 149 struct visual_ops text_ops = { 150 .ident = &text_ident, 151 .kdsetmode = NULL, 152 .devinit = efi_text_devinit, 153 .cons_copy = efi_text_cons_copy, 154 .cons_display = efi_text_cons_display, 155 .cons_cursor = efi_cons_cursor, 156 .cons_clear = efi_text_cons_clear, 157 .cons_put_cmap = NULL 158 }; 159 160 /* 161 * platform specific functions for tem 162 */ 163 int 164 plat_stdout_is_framebuffer(void) 165 { 166 return (console_mode == EfiConsoleControlScreenGraphics); 167 } 168 169 void 170 plat_tem_hide_prom_cursor(void) 171 { 172 if (has_boot_services) 173 conout->EnableCursor(conout, FALSE); 174 } 175 176 static void 177 plat_tem_display_prom_cursor(screen_pos_t row, screen_pos_t col) 178 { 179 180 if (has_boot_services) { 181 conout->SetCursorPosition(conout, col, row); 182 conout->EnableCursor(conout, TRUE); 183 } 184 } 185 186 void 187 plat_tem_get_prom_pos(uint32_t *row, uint32_t *col) 188 { 189 if (console_mode == EfiConsoleControlScreenText) { 190 *col = (uint32_t)conout->Mode->CursorColumn; 191 *row = (uint32_t)conout->Mode->CursorRow; 192 } else { 193 *col = 0; 194 *row = 0; 195 } 196 } 197 198 /* 199 * plat_tem_get_prom_size() is supposed to return screen size 200 * in chars. Return real data for text mode and TEM defaults for graphical 201 * mode, so the tem can compute values based on default and font. 202 */ 203 void 204 plat_tem_get_prom_size(size_t *height, size_t *width) 205 { 206 UINTN cols, rows; 207 if (console_mode == EfiConsoleControlScreenText) { 208 (void) conout->QueryMode(conout, conout->Mode->Mode, 209 &cols, &rows); 210 *height = (size_t)rows; 211 *width = (size_t)cols; 212 } else { 213 *height = TEM_DEFAULT_ROWS; 214 *width = TEM_DEFAULT_COLS; 215 } 216 } 217 218 /* 219 * Callback to notify about console mode change. 220 * mode is value from enum EFI_CONSOLE_CONTROL_SCREEN_MODE. 221 */ 222 void 223 plat_cons_update_mode(int mode) 224 { 225 UINTN cols, rows; 226 struct vis_devinit devinit; 227 struct efi_console_data *ecd = efi_console.c_private; 228 229 /* Make sure we have usable console. */ 230 if (efi_find_framebuffer(&efifb)) { 231 console_mode = EfiConsoleControlScreenText; 232 } else { 233 efi_framebuffer_setup(); 234 if (mode != -1 && console_mode != mode) 235 console_mode = mode; 236 } 237 238 if (console_control != NULL) 239 (void) console_control->SetMode(console_control, console_mode); 240 241 /* some firmware enables the cursor when switching modes */ 242 conout->EnableCursor(conout, FALSE); 243 if (console_mode == EfiConsoleControlScreenText) { 244 (void) conout->QueryMode(conout, conout->Mode->Mode, 245 &cols, &rows); 246 devinit.version = VIS_CONS_REV; 247 devinit.width = cols; 248 devinit.height = rows; 249 devinit.depth = 4; 250 devinit.linebytes = cols; 251 devinit.color_map = NULL; 252 devinit.mode = VIS_TEXT; 253 ecd->ecd_visual_ops = &text_ops; 254 } else { 255 devinit.version = VIS_CONS_REV; 256 devinit.width = gfx_fb.framebuffer_common.framebuffer_width; 257 devinit.height = gfx_fb.framebuffer_common.framebuffer_height; 258 devinit.depth = gfx_fb.framebuffer_common.framebuffer_bpp; 259 devinit.linebytes = gfx_fb.framebuffer_common.framebuffer_pitch; 260 devinit.color_map = gfx_fb_color_map; 261 devinit.mode = VIS_PIXEL; 262 ecd->ecd_visual_ops = &fb_ops; 263 } 264 265 modechg_cb(modechg_arg, &devinit); 266 } 267 268 static int 269 efi_fb_devinit(struct vis_devinit *data) 270 { 271 if (console_mode != EfiConsoleControlScreenGraphics) 272 return (1); 273 274 data->version = VIS_CONS_REV; 275 data->width = gfx_fb.framebuffer_common.framebuffer_width; 276 data->height = gfx_fb.framebuffer_common.framebuffer_height; 277 data->depth = gfx_fb.framebuffer_common.framebuffer_bpp; 278 data->linebytes = gfx_fb.framebuffer_common.framebuffer_pitch; 279 data->color_map = gfx_fb_color_map; 280 data->mode = VIS_PIXEL; 281 282 modechg_cb = data->modechg_cb; 283 modechg_arg = data->modechg_arg; 284 285 return (0); 286 } 287 288 static int 289 efi_text_devinit(struct vis_devinit *data) 290 { 291 UINTN cols, rows; 292 293 if (console_mode != EfiConsoleControlScreenText) 294 return (1); 295 296 (void) conout->QueryMode(conout, conout->Mode->Mode, &cols, &rows); 297 data->version = VIS_CONS_REV; 298 data->width = cols; 299 data->height = rows; 300 data->depth = 4; 301 data->linebytes = cols; 302 data->color_map = NULL; 303 data->mode = VIS_TEXT; 304 305 modechg_cb = data->modechg_cb; 306 modechg_arg = data->modechg_arg; 307 308 return (0); 309 } 310 311 static int 312 efi_text_cons_clear(struct vis_consclear *ca) 313 { 314 EFI_STATUS st; 315 UINTN attr = conout->Mode->Attribute & 0x0F; 316 uint8_t bg; 317 318 if (!has_boot_services) 319 return (0); 320 321 bg = solaris_color_to_efi_color[ca->bg_color.four & 0xF] & 0x7; 322 323 attr = EFI_TEXT_ATTR(attr, bg); 324 st = conout->SetAttribute(conout, attr); 325 if (EFI_ERROR(st)) 326 return (1); 327 st = conout->ClearScreen(conout); 328 if (EFI_ERROR(st)) 329 return (1); 330 return (0); 331 } 332 333 static void 334 efi_text_cons_copy(struct vis_conscopy *ma) 335 { 336 UINTN col, row; 337 338 if (!has_boot_services) 339 return; 340 341 col = 0; 342 row = ma->e_row; 343 conout->SetCursorPosition(conout, col, row); 344 345 efi_cons_efiputchar('\n'); 346 } 347 348 static void 349 efi_text_cons_display(struct vis_consdisplay *da) 350 { 351 EFI_STATUS st; 352 UINTN attr; 353 UINTN row, col; 354 tem_char_t *data; 355 uint8_t fg, bg; 356 int i; 357 358 if (!has_boot_services) 359 return; 360 361 (void) conout->QueryMode(conout, conout->Mode->Mode, &col, &row); 362 363 /* reduce clear line on bottom row by one to prevent autoscroll */ 364 if (row - 1 == da->row && da->col == 0 && da->width == col) 365 da->width--; 366 367 data = (tem_char_t *)da->data; 368 fg = solaris_color_to_efi_color[da->fg_color.four & 0xf]; 369 bg = solaris_color_to_efi_color[da->bg_color.four & 0xf] & 0x7; 370 attr = EFI_TEXT_ATTR(fg, bg); 371 372 st = conout->SetAttribute(conout, attr); 373 if (EFI_ERROR(st)) 374 return; 375 row = da->row; 376 col = da->col; 377 conout->SetCursorPosition(conout, col, row); 378 for (i = 0; i < da->width; i++) 379 efi_cons_efiputchar(data[i]); 380 } 381 382 static void efi_cons_cursor(struct vis_conscursor *cc) 383 { 384 switch (cc->action) { 385 case VIS_HIDE_CURSOR: 386 if (plat_stdout_is_framebuffer()) 387 gfx_fb_display_cursor(cc); 388 else 389 plat_tem_hide_prom_cursor(); 390 break; 391 case VIS_DISPLAY_CURSOR: 392 if (plat_stdout_is_framebuffer()) 393 gfx_fb_display_cursor(cc); 394 else 395 plat_tem_display_prom_cursor(cc->row, cc->col); 396 break; 397 case VIS_GET_CURSOR: { /* only used at startup */ 398 uint32_t row, col; 399 400 row = col = 0; 401 plat_tem_get_prom_pos(&row, &col); 402 cc->row = row; 403 cc->col = col; 404 } 405 break; 406 } 407 } 408 409 static int 410 efi_cons_ioctl(struct console *cp, int cmd, void *data) 411 { 412 struct efi_console_data *ecd = cp->c_private; 413 struct visual_ops *ops = ecd->ecd_visual_ops; 414 415 switch (cmd) { 416 case VIS_GETIDENTIFIER: 417 memmove(data, ops->ident, sizeof (struct vis_identifier)); 418 break; 419 case VIS_DEVINIT: 420 return (ops->devinit(data)); 421 case VIS_CONSCLEAR: 422 return (ops->cons_clear(data)); 423 case VIS_CONSCOPY: 424 ops->cons_copy(data); 425 break; 426 case VIS_CONSDISPLAY: 427 ops->cons_display(data); 428 break; 429 case VIS_CONSCURSOR: 430 ops->cons_cursor(data); 431 break; 432 default: 433 return (EINVAL); 434 } 435 return (0); 436 } 437 438 static void 439 efi_framebuffer_setup(void) 440 { 441 int bpp, pos; 442 extern EFI_GRAPHICS_OUTPUT_BLT_PIXEL *shadow_fb; 443 444 bpp = fls(efifb.fb_mask_red | efifb.fb_mask_green | 445 efifb.fb_mask_blue | efifb.fb_mask_reserved); 446 447 if (shadow_fb != NULL) 448 free(shadow_fb); 449 shadow_fb = malloc(efifb.fb_width * efifb.fb_height * 450 sizeof (*shadow_fb)); 451 452 gfx_fb.framebuffer_common.mb_type = MULTIBOOT_TAG_TYPE_FRAMEBUFFER; 453 gfx_fb.framebuffer_common.mb_size = sizeof (gfx_fb); 454 gfx_fb.framebuffer_common.framebuffer_addr = efifb.fb_addr; 455 gfx_fb.framebuffer_common.framebuffer_width = efifb.fb_width; 456 gfx_fb.framebuffer_common.framebuffer_height = efifb.fb_height; 457 gfx_fb.framebuffer_common.framebuffer_bpp = bpp; 458 gfx_fb.framebuffer_common.framebuffer_pitch = 459 efifb.fb_stride * (bpp >> 3); 460 gfx_fb.framebuffer_common.framebuffer_type = 461 MULTIBOOT_FRAMEBUFFER_TYPE_RGB; 462 gfx_fb.framebuffer_common.mb_reserved = 0; 463 464 pos = ffs(efifb.fb_mask_red); 465 if (pos != 0) 466 pos--; 467 gfx_fb.u.fb2.framebuffer_red_mask_size = fls(efifb.fb_mask_red >> pos); 468 gfx_fb.u.fb2.framebuffer_red_field_position = pos; 469 pos = ffs(efifb.fb_mask_green); 470 if (pos != 0) 471 pos--; 472 gfx_fb.u.fb2.framebuffer_green_mask_size = 473 fls(efifb.fb_mask_green >> pos); 474 gfx_fb.u.fb2.framebuffer_green_field_position = pos; 475 pos = ffs(efifb.fb_mask_blue); 476 if (pos != 0) 477 pos--; 478 gfx_fb.u.fb2.framebuffer_blue_mask_size = 479 fls(efifb.fb_mask_blue >> pos); 480 gfx_fb.u.fb2.framebuffer_blue_field_position = pos; 481 } 482 483 static void 484 efi_cons_probe(struct console *cp) 485 { 486 cp->c_flags |= C_PRESENTIN | C_PRESENTOUT; 487 } 488 489 static int 490 efi_cons_init(struct console *cp, int arg __unused) 491 { 492 struct efi_console_data *ecd; 493 void *coninex; 494 EFI_STATUS status; 495 UINTN i, max_dim, best_mode, cols, rows; 496 497 if (cp->c_private != NULL) 498 return (0); 499 500 ecd = calloc(1, sizeof (*ecd)); 501 /* 502 * As console probing is called very early, the only reason for 503 * out of memory can be that we just do not have enough memory. 504 */ 505 if (ecd == NULL) 506 panic("efi_cons_probe: This system has not enough memory\n"); 507 cp->c_private = ecd; 508 509 ecd->ecd_conin = ST->ConIn; 510 conout = ST->ConOut; 511 512 conout->SetAttribute(conout, EFI_TEXT_ATTR(DEFAULT_FGCOLOR, 513 DEFAULT_BGCOLOR)); 514 memset(keybuf, 0, KEYBUFSZ); 515 516 status = BS->LocateProtocol(&gEfiConsoleControlProtocolGuid, NULL, 517 (void **)&console_control); 518 if (status == EFI_SUCCESS) { 519 BOOLEAN GopUgaExists, StdInLocked; 520 status = console_control->GetMode(console_control, 521 &console_mode, &GopUgaExists, &StdInLocked); 522 } else { 523 console_mode = EfiConsoleControlScreenText; 524 } 525 526 max_dim = best_mode = 0; 527 for (i = 0; i <= conout->Mode->MaxMode; i++) { 528 status = conout->QueryMode(conout, i, &cols, &rows); 529 if (EFI_ERROR(status)) 530 continue; 531 if (cols * rows > max_dim) { 532 max_dim = cols * rows; 533 best_mode = i; 534 } 535 } 536 if (max_dim > 0) 537 conout->SetMode(conout, best_mode); 538 status = conout->QueryMode(conout, best_mode, &cols, &rows); 539 if (EFI_ERROR(status)) { 540 setenv("screen-#rows", "24", 1); 541 setenv("screen-#cols", "80", 1); 542 } else { 543 char env[8]; 544 snprintf(env, sizeof (env), "%u", (unsigned)rows); 545 setenv("screen-#rows", env, 1); 546 snprintf(env, sizeof (env), "%u", (unsigned)cols); 547 setenv("screen-#cols", env, 1); 548 } 549 550 if (efi_find_framebuffer(&efifb)) { 551 console_mode = EfiConsoleControlScreenText; 552 ecd->ecd_visual_ops = &text_ops; 553 } else { 554 efi_framebuffer_setup(); 555 console_mode = EfiConsoleControlScreenGraphics; 556 ecd->ecd_visual_ops = &fb_ops; 557 } 558 559 if (console_control != NULL) 560 (void) console_control->SetMode(console_control, console_mode); 561 562 /* some firmware enables the cursor when switching modes */ 563 conout->EnableCursor(conout, FALSE); 564 565 coninex = NULL; 566 /* 567 * Try to set up for SimpleTextInputEx protocol. If not available, 568 * we will use SimpleTextInput protocol. 569 */ 570 status = BS->OpenProtocol(ST->ConsoleInHandle, 571 &gEfiSimpleTextInputExProtocolGuid, 572 &coninex, IH, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL); 573 if (status == EFI_SUCCESS) 574 ecd->ecd_coninex = coninex; 575 576 gfx_framework_init(); 577 578 if (tem_info_init(cp) == 0 && tem == NULL) { 579 tem = tem_init(); 580 if (tem != NULL) 581 tem_activate(tem, B_TRUE); 582 } 583 584 if (tem == NULL) 585 panic("Failed to set up console terminal"); 586 587 return (0); 588 } 589 590 static void 591 efi_cons_putchar(struct console *cp __unused, int c) 592 { 593 uint8_t buf = c; 594 595 /* make sure we have some console output, support for panic() */ 596 if (tem == NULL) 597 efi_cons_efiputchar(c); 598 else 599 tem_write(tem, &buf, sizeof (buf)); 600 } 601 602 static int 603 keybuf_getchar(void) 604 { 605 int i, c = 0; 606 607 for (i = 0; i < KEYBUFSZ; i++) { 608 if (keybuf[i] != 0) { 609 c = keybuf[i]; 610 keybuf[i] = 0; 611 break; 612 } 613 } 614 615 return (c); 616 } 617 618 static bool 619 keybuf_ischar(void) 620 { 621 int i; 622 623 for (i = 0; i < KEYBUFSZ; i++) { 624 if (keybuf[i] != 0) 625 return (true); 626 } 627 return (false); 628 } 629 630 /* 631 * We are not reading input before keybuf is empty, so we are safe 632 * just to fill keybuf from the beginning. 633 */ 634 static void 635 keybuf_inschar(EFI_INPUT_KEY *key) 636 { 637 638 switch (key->ScanCode) { 639 case SCAN_UP: /* UP */ 640 keybuf[0] = 0x1b; /* esc */ 641 keybuf[1] = '['; 642 keybuf[2] = 'A'; 643 break; 644 case SCAN_DOWN: /* DOWN */ 645 keybuf[0] = 0x1b; /* esc */ 646 keybuf[1] = '['; 647 keybuf[2] = 'B'; 648 break; 649 case SCAN_RIGHT: /* RIGHT */ 650 keybuf[0] = 0x1b; /* esc */ 651 keybuf[1] = '['; 652 keybuf[2] = 'C'; 653 break; 654 case SCAN_LEFT: /* LEFT */ 655 keybuf[0] = 0x1b; /* esc */ 656 keybuf[1] = '['; 657 keybuf[2] = 'D'; 658 break; 659 case SCAN_DELETE: 660 keybuf[0] = CHAR_BACKSPACE; 661 break; 662 case SCAN_ESC: 663 keybuf[0] = 0x1b; /* esc */ 664 break; 665 default: 666 keybuf[0] = key->UnicodeChar; 667 break; 668 } 669 } 670 671 static bool 672 efi_readkey(SIMPLE_INPUT_INTERFACE *conin) 673 { 674 EFI_STATUS status; 675 EFI_INPUT_KEY key; 676 677 status = conin->ReadKeyStroke(conin, &key); 678 if (status == EFI_SUCCESS) { 679 keybuf_inschar(&key); 680 return (true); 681 } 682 return (false); 683 } 684 685 static bool 686 efi_readkey_ex(EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *coninex) 687 { 688 EFI_STATUS status; 689 EFI_INPUT_KEY *kp; 690 EFI_KEY_DATA key_data; 691 uint32_t kss; 692 693 status = coninex->ReadKeyStrokeEx(coninex, &key_data); 694 if (status == EFI_SUCCESS) { 695 kss = key_data.KeyState.KeyShiftState; 696 kp = &key_data.Key; 697 if (kss & EFI_SHIFT_STATE_VALID) { 698 699 /* 700 * quick mapping to control chars, replace with 701 * map lookup later. 702 */ 703 if (kss & EFI_RIGHT_CONTROL_PRESSED || 704 kss & EFI_LEFT_CONTROL_PRESSED) { 705 if (kp->UnicodeChar >= 'a' && 706 kp->UnicodeChar <= 'z') { 707 kp->UnicodeChar -= 'a'; 708 kp->UnicodeChar++; 709 } 710 } 711 } 712 /* 713 * The shift state and/or toggle state may not be valid, 714 * but we still can have ScanCode or UnicodeChar. 715 */ 716 if (kp->ScanCode == 0 && kp->UnicodeChar == 0) 717 return (false); 718 keybuf_inschar(kp); 719 return (true); 720 } 721 return (false); 722 } 723 724 static int 725 efi_cons_getchar(struct console *cp) 726 { 727 struct efi_console_data *ecd; 728 int c; 729 730 if ((c = keybuf_getchar()) != 0) 731 return (c); 732 733 if (!has_boot_services) 734 return (-1); 735 736 ecd = cp->c_private; 737 key_pending = 0; 738 739 if (ecd->ecd_coninex == NULL) { 740 if (efi_readkey(ecd->ecd_conin)) 741 return (keybuf_getchar()); 742 } else { 743 if (efi_readkey_ex(ecd->ecd_coninex)) 744 return (keybuf_getchar()); 745 } 746 747 return (-1); 748 } 749 750 static int 751 efi_cons_poll(struct console *cp) 752 { 753 struct efi_console_data *ecd; 754 EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *coninex; 755 SIMPLE_INPUT_INTERFACE *conin; 756 EFI_STATUS status; 757 758 if (keybuf_ischar() || key_pending) 759 return (1); 760 761 if (!has_boot_services) 762 return (0); 763 764 ecd = cp->c_private; 765 coninex = ecd->ecd_coninex; 766 conin = ecd->ecd_conin; 767 /* 768 * Some EFI implementation (u-boot for example) do not support 769 * WaitForKey(). 770 * CheckEvent() can clear the signaled state. 771 */ 772 if (coninex != NULL) { 773 if (coninex->WaitForKeyEx == NULL) 774 key_pending = efi_readkey_ex(coninex); 775 else { 776 status = BS->CheckEvent(coninex->WaitForKeyEx); 777 key_pending = status == EFI_SUCCESS; 778 } 779 } else { 780 if (conin->WaitForKey == NULL) 781 key_pending = efi_readkey(conin); 782 else { 783 status = BS->CheckEvent(conin->WaitForKey); 784 key_pending = status == EFI_SUCCESS; 785 } 786 } 787 788 return (key_pending); 789 } 790 791 /* Plain direct access to EFI OutputString(). */ 792 void 793 efi_cons_efiputchar(int c) 794 { 795 CHAR16 buf[2]; 796 EFI_STATUS status; 797 798 buf[0] = c; 799 buf[1] = 0; /* terminate string */ 800 801 status = conout->TestString(conout, buf); 802 if (EFI_ERROR(status)) 803 buf[0] = '?'; 804 conout->OutputString(conout, buf); 805 } 806 807 static void 808 efi_cons_devinfo_print(EFI_HANDLE handle) 809 { 810 EFI_DEVICE_PATH *dp; 811 CHAR16 *text; 812 813 dp = efi_lookup_devpath(handle); 814 if (dp == NULL) 815 return; 816 817 text = efi_devpath_name(dp); 818 if (text == NULL) 819 return; 820 821 printf("\t%S", text); 822 efi_free_devpath_name(text); 823 } 824 825 static void 826 efi_cons_devinfo(struct console *cp __unused) 827 { 828 EFI_HANDLE *handles; 829 uint_t nhandles; 830 EFI_STATUS status; 831 832 if (gop != NULL) 833 status = efi_get_protocol_handles( 834 &gEfiGraphicsOutputProtocolGuid, &nhandles, &handles); 835 else 836 status = efi_get_protocol_handles(&gEfiUgaDrawProtocolGuid, 837 &nhandles, &handles); 838 839 if (EFI_ERROR(status)) 840 return; 841 842 for (uint_t i = 0; i < nhandles; i++) 843 efi_cons_devinfo_print(handles[i]); 844 845 free(handles); 846 } 847