1 /* $NetBSD: eficons.c,v 1.15 2025/06/19 15:00:17 manu Exp $ */ 2 3 /*- 4 * Copyright (c) 2016 Kimihiro Nonaka <nonaka (at) netbsd.org> 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 * SUCH DAMAGE. 27 */ 28 29 #include <sys/param.h> 30 #include <sys/bitops.h> 31 #include <sys/stdint.h> 32 33 #include <comio_direct.h> 34 35 #include "efiboot.h" 36 37 #include "bootinfo.h" 38 #include "vbe.h" 39 40 #ifndef DEFAULT_GOP_MODE 41 #define DEFAULT_GOP_MODE "1024x768" 42 #endif 43 #define FALLBACK_GOP_MODE 0 44 45 extern struct x86_boot_params boot_params; 46 47 struct btinfo_console btinfo_console; 48 49 static EFI_GRAPHICS_OUTPUT_PROTOCOL *efi_gop; 50 static int efi_gop_mode = -1; 51 static CHAR16 keybuf[16]; 52 static int keybuf_read = 0; 53 static int keybuf_write = 0; 54 55 static SERIAL_IO_INTERFACE *serios[4]; 56 static int default_comspeed = 57 #if defined(CONSPEED) 58 CONSPEED; 59 #else 60 9600; 61 #endif 62 static u_char serbuf[16]; 63 static int serbuf_read = 0; 64 static int serbuf_write = 0; 65 66 static int raw_com_addr = 0; 67 68 static void eficons_init_video(void); 69 static void efi_switch_video_to_text_mode(void); 70 71 static int efi_cons_getc(void); 72 static int efi_cons_putc(int); 73 static int efi_cons_iskey(int); 74 static int efi_cons_waitforinputevent(uint64_t); 75 76 static void efi_com_probe(void); 77 static bool efi_valid_com(int); 78 static int efi_com_init(int, int); 79 static int efi_com_getc(void); 80 static int efi_com_putc(int); 81 static int efi_com_status(int); 82 static int efi_com_waitforinputevent(uint64_t); 83 84 static int raw_com_init(int, int); 85 static int raw_com_getc(void); 86 static int raw_com_putc(int); 87 static int raw_com_status(int); 88 static int raw_com_waitforinputevent(uint64_t); 89 90 static int efi_find_gop_mode(char *); 91 92 static int iodev; 93 static int (*internal_getchar)(void) = efi_cons_getc; 94 static int (*internal_putchar)(int) = efi_cons_putc; 95 static int (*internal_iskey)(int) = efi_cons_iskey; 96 static int (*internal_waitforinputevent)(uint64_t) = efi_cons_waitforinputevent; 97 98 static int 99 getcomaddr(int idx) 100 { 101 static const short comioport[4] = { 0x3f8, 0x2f8, 0x3e8, 0x2e8 }; 102 103 if (idx < __arraycount(comioport)) 104 return comioport[idx]; 105 return 0; 106 } 107 108 /* 109 * XXX only pass console parameters to kernel. 110 */ 111 void 112 efi_consinit(int dev, int ioport, int speed) 113 { 114 int i; 115 116 btinfo_console.speed = default_comspeed; 117 118 switch (dev) { 119 case CONSDEV_AUTO: 120 for (i = 0; i < __arraycount(serios); i++) { 121 iodev = CONSDEV_COM0 + i; 122 if (!efi_valid_com(iodev)) 123 continue; 124 btinfo_console.addr = getcomaddr(i); 125 126 efi_cons_putc('0' + i); 127 efi_com_init(btinfo_console.addr, btinfo_console.speed); 128 /* check for: 129 * 1. successful output 130 * 2. optionally, keypress within 7s 131 */ 132 if (efi_com_putc(':') && 133 efi_com_putc('-') && 134 efi_com_putc('(') && 135 awaitkey(7, 0)) 136 goto ok; 137 } 138 goto nocom; 139 ok: 140 break; 141 142 case CONSDEV_COM0: 143 case CONSDEV_COM1: 144 case CONSDEV_COM2: 145 case CONSDEV_COM3: 146 iodev = dev; 147 btinfo_console.addr = ioport; 148 if (btinfo_console.addr == 0) 149 btinfo_console.addr = getcomaddr(iodev - CONSDEV_COM0); 150 if (speed != 0) 151 btinfo_console.speed = speed; 152 efi_com_init(btinfo_console.addr, btinfo_console.speed); 153 break; 154 155 case CONSDEV_COM0KBD: 156 case CONSDEV_COM1KBD: 157 case CONSDEV_COM2KBD: 158 case CONSDEV_COM3KBD: 159 iodev = dev - CONSDEV_COM0KBD + CONSDEV_COM0; 160 btinfo_console.addr = getcomaddr(iodev - CONSDEV_COM0); 161 162 efi_cons_putc('0' + iodev - CONSDEV_COM0); 163 efi_com_init(btinfo_console.addr, btinfo_console.speed); 164 /* check for: 165 * 1. successful output 166 * 2. optionally, keypress within 7s 167 */ 168 if (efi_com_putc(':') && 169 efi_com_putc('-') && 170 efi_com_putc('(') && 171 awaitkey(7, 0)) 172 goto kbd; 173 /*FALLTHROUGH*/ 174 case CONSDEV_PC: 175 default: 176 nocom: 177 iodev = CONSDEV_PC; 178 internal_putchar = efi_cons_putc; 179 kbd: 180 internal_getchar = efi_cons_getc; 181 internal_iskey = efi_cons_iskey; 182 internal_waitforinputevent = efi_cons_waitforinputevent; 183 memset(keybuf, 0, sizeof(keybuf)); 184 keybuf_read = keybuf_write = 0; 185 break; 186 } 187 188 strlcpy(btinfo_console.devname, iodev == CONSDEV_PC ? "pc" : "com", 16); 189 } 190 191 int 192 cninit(void) 193 { 194 195 efi_switch_video_to_text_mode(); 196 eficons_init_video(); 197 efi_com_probe(); 198 199 efi_consinit(boot_params.bp_consdev, boot_params.bp_consaddr, 200 boot_params.bp_conspeed); 201 202 return 0; 203 } 204 205 void 206 efi_cons_show(void) 207 { 208 const bool pc_is_console = strcmp(btinfo_console.devname, "pc") == 0; 209 const bool com_is_console = strcmp(btinfo_console.devname, "com") == 0; 210 bool first = true; 211 bool found = false; 212 int i; 213 214 if (efi_gop != NULL) { 215 printf("pc"); 216 if (pc_is_console) 217 printf("*"); 218 first = false; 219 } 220 221 for (i = 0; i < __arraycount(serios); i++) { 222 if (serios[i] != NULL) { 223 if (!first) 224 printf(" "); 225 first = false; 226 227 printf("com%d", i); 228 if (com_is_console && 229 btinfo_console.addr == getcomaddr(i)) { 230 printf(",%d*", btinfo_console.speed); 231 found = true; 232 } 233 } 234 } 235 if (!found && com_is_console) { 236 if (!first) 237 printf(" "); 238 first = false; 239 240 printf("com,0x%x,%d*", btinfo_console.addr, 241 btinfo_console.speed); 242 } 243 244 printf("\n"); 245 } 246 247 static int 248 efi_cons_getc(void) 249 { 250 EFI_STATUS status; 251 EFI_INPUT_KEY key; 252 int c; 253 254 if (keybuf_read != keybuf_write) { 255 c = keybuf[keybuf_read]; 256 keybuf_read = (keybuf_read + 1) % __arraycount(keybuf); 257 return c; 258 } 259 260 status = uefi_call_wrapper(ST->ConIn->ReadKeyStroke, 2, ST->ConIn, 261 &key); 262 while (status == EFI_NOT_READY) { 263 WaitForSingleEvent(ST->ConIn->WaitForKey, 0); 264 status = uefi_call_wrapper(ST->ConIn->ReadKeyStroke, 2, 265 ST->ConIn, &key); 266 } 267 return key.UnicodeChar; 268 } 269 270 static int 271 efi_cons_putc(int c) 272 { 273 CHAR16 buf[2]; 274 275 buf[0] = c; 276 buf[1] = 0; 277 Output(buf); 278 279 return 1; 280 } 281 282 /*ARGSUSED*/ 283 static int 284 efi_cons_iskey(int intr) 285 { 286 EFI_STATUS status; 287 EFI_INPUT_KEY key; 288 289 if (keybuf_read != keybuf_write) 290 return 1; 291 292 status = uefi_call_wrapper(ST->ConIn->ReadKeyStroke, 2, ST->ConIn, 293 &key); 294 if (EFI_ERROR(status)) 295 return 0; 296 297 keybuf[keybuf_write] = key.UnicodeChar; 298 keybuf_write = (keybuf_write + 1) % __arraycount(keybuf); 299 return 1; 300 } 301 302 static int 303 efi_cons_waitforinputevent(uint64_t timeout) 304 { 305 EFI_STATUS status; 306 307 status = WaitForSingleEvent(ST->ConIn->WaitForKey, timeout); 308 if (!EFI_ERROR(status)) 309 return 0; 310 if (status == EFI_TIMEOUT) 311 return ETIMEDOUT; 312 return EINVAL; 313 } 314 315 int 316 getchar(void) 317 { 318 319 return internal_getchar(); 320 } 321 322 void 323 putchar(int c) 324 { 325 326 if (c == '\n') 327 internal_putchar('\r'); 328 internal_putchar(c); 329 } 330 331 int 332 iskey(int intr) 333 { 334 335 return internal_iskey(intr); 336 } 337 338 char 339 awaitkey(int timeout, int tell) 340 { 341 char c = 0; 342 343 for (;;) { 344 char numbuf[32]; 345 int len; 346 347 if (tell && timeout) { 348 len = snprintf(numbuf, sizeof(numbuf), "%d seconds. ", 349 timeout); 350 if (len > 0 && len < sizeof(numbuf)) { 351 char *p = numbuf; 352 353 printf("%s", numbuf); 354 while (*p) 355 *p++ = '\b'; 356 } 357 } 358 if (iskey(1)) { 359 /* flush input buffer */ 360 while (iskey(0)) 361 c = getchar(); 362 if (c == 0) 363 c = -1; 364 if (tell && timeout) 365 printf("%s", numbuf); 366 break; 367 } 368 if (timeout--) { 369 /* 370 * UEFI spec 2.10 section 7.1.7 371 * EFI_BOOT_SERVICES.SetTimer() timeout in 100ns units 372 * but the implementation may be broken and faster: 373 * check date to make sure we are slow enough. 374 */ 375 EFI_TIME t1, t2; 376 377 uefi_call_wrapper(RT->GetTime, 2, &t1, NULL); 378 do { 379 internal_waitforinputevent(10000000); /* 1s */ 380 uefi_call_wrapper(RT->GetTime, 2, &t2, NULL); 381 } while (t1.Year == t2.Year && 382 t1.Month == t2.Month && 383 t1.Day == t2.Day && 384 t1.Hour == t2.Hour && 385 t1.Minute == t2.Minute && 386 t1.Second == t2.Second); 387 } 388 else 389 break; 390 if (tell) 391 printf("%s", numbuf); 392 } 393 394 if (tell) 395 printf("0 seconds. \n"); 396 397 return c; 398 } 399 400 void 401 clear_pc_screen(void) 402 { 403 404 uefi_call_wrapper(ST->ConOut->ClearScreen, 1, ST->ConOut); 405 } 406 407 static uint8_t 408 getdepth(const EFI_GRAPHICS_OUTPUT_MODE_INFORMATION *info) 409 { 410 411 switch (info->PixelFormat) { 412 case PixelBlueGreenRedReserved8BitPerColor: 413 case PixelRedGreenBlueReserved8BitPerColor: 414 return 32; 415 416 case PixelBitMask: 417 return fls32(info->PixelInformation.RedMask 418 | info->PixelInformation.GreenMask 419 | info->PixelInformation.BlueMask 420 | info->PixelInformation.ReservedMask); 421 422 case PixelBltOnly: 423 case PixelFormatMax: 424 return 0; 425 } 426 return 0; 427 } 428 429 static void 430 setpixelformat(UINT32 mask, uint8_t *num, uint8_t *pos) 431 { 432 uint8_t n, p; 433 434 n = popcount32(mask); 435 p = ffs32(mask); 436 if (p > 0) 437 p--; 438 439 *num = n; 440 *pos = p; 441 } 442 443 static void 444 bi_framebuffer(void) 445 { 446 EFI_STATUS status; 447 EFI_GRAPHICS_OUTPUT_MODE_INFORMATION *info; 448 struct btinfo_framebuffer fb; 449 INT32 bestmode; 450 UINTN sz; 451 452 if (efi_gop == NULL) 453 goto nofb; 454 455 if (efi_gop_mode >= 0) { 456 bestmode = efi_gop_mode; 457 } else { 458 /* If a mode has not been selected, choose a default */ 459 bestmode = efi_find_gop_mode(DEFAULT_GOP_MODE); 460 if (bestmode == -1) 461 bestmode = FALLBACK_GOP_MODE; 462 } 463 464 status = uefi_call_wrapper(efi_gop->SetMode, 2, efi_gop, 465 bestmode); 466 if (EFI_ERROR(status) || efi_gop->Mode->Mode != bestmode) { 467 printf("GOP setmode failed: %" PRIxMAX "\n", 468 (uintmax_t)status); 469 goto nofb; 470 } 471 472 status = uefi_call_wrapper(efi_gop->QueryMode, 4, 473 efi_gop, bestmode, &sz, &info); 474 if (EFI_ERROR(status)) { 475 printf("GOP querymode failed: %" PRIxMAX "\n", 476 (uintmax_t)status); 477 goto nofb; 478 } 479 480 memset(&fb, 0, sizeof(fb)); 481 fb.physaddr = efi_gop->Mode->FrameBufferBase; 482 fb.flags = 0; 483 fb.width = info->HorizontalResolution; 484 fb.height = info->VerticalResolution; 485 fb.depth = getdepth(info); 486 fb.stride = info->PixelsPerScanLine * ((fb.depth + 7) / 8); 487 fb.vbemode = 0; /* XXX */ 488 489 switch (info->PixelFormat) { 490 case PixelBlueGreenRedReserved8BitPerColor: 491 fb.rnum = 8; 492 fb.gnum = 8; 493 fb.bnum = 8; 494 fb.rpos = 16; 495 fb.gpos = 8; 496 fb.bpos = 0; 497 break; 498 499 case PixelRedGreenBlueReserved8BitPerColor: 500 fb.rnum = 8; 501 fb.gnum = 8; 502 fb.bnum = 8; 503 fb.rpos = 0; 504 fb.gpos = 8; 505 fb.bpos = 16; 506 break; 507 508 case PixelBitMask: 509 setpixelformat(info->PixelInformation.RedMask, 510 &fb.rnum, &fb.rpos); 511 setpixelformat(info->PixelInformation.GreenMask, 512 &fb.gnum, &fb.gpos); 513 setpixelformat(info->PixelInformation.BlueMask, 514 &fb.bnum, &fb.bpos); 515 break; 516 517 case PixelBltOnly: 518 case PixelFormatMax: 519 panic("Error: invalid pixel format (%d)", info->PixelFormat); 520 break; 521 } 522 523 framebuffer_configure(&fb); 524 return; 525 526 nofb: 527 framebuffer_configure(NULL); 528 } 529 530 int 531 vbe_commit(void) 532 { 533 534 bi_framebuffer(); 535 return 0; 536 } 537 538 static void 539 print_text_modes(void) 540 { 541 EFI_STATUS status; 542 UINTN cols, rows; 543 INT32 i, curmode; 544 545 curmode = ST->ConOut->Mode->Mode; 546 for (i = 0; i < ST->ConOut->Mode->MaxMode; i++) { 547 status = uefi_call_wrapper(ST->ConOut->QueryMode, 4, 548 ST->ConOut, i, &cols, &rows); 549 if (EFI_ERROR(status)) 550 continue; 551 printf("%c%d: %" PRIxMAX "x%" PRIxMAX "\n", 552 i == curmode ? '*' : ' ', i, (uintmax_t)cols, (uintmax_t)rows); 553 } 554 } 555 556 static int 557 efi_find_text_mode(char *arg) 558 { 559 EFI_STATUS status; 560 UINTN cols, rows; 561 INT32 i; 562 char mode[32]; 563 564 for (i = 0; i < ST->ConOut->Mode->MaxMode; i++) { 565 status = uefi_call_wrapper(ST->ConOut->QueryMode, 4, 566 ST->ConOut, i, &cols, &rows); 567 if (EFI_ERROR(status)) 568 continue; 569 snprintf(mode, sizeof(mode), "%" PRIuMAX "x%" PRIuMAX, 570 (uintmax_t)cols, (uintmax_t)rows); 571 if (strcmp(arg, mode) == 0) 572 return i; 573 } 574 return -1; 575 } 576 577 void 578 command_text(char *arg) 579 { 580 EFI_STATUS status; 581 INT32 modenum; 582 583 if (*arg == '\0' || strcmp(arg, "list") == 0) { 584 print_text_modes(); 585 return; 586 } 587 588 if (strchr(arg, 'x') != NULL) { 589 modenum = efi_find_text_mode(arg); 590 if (modenum == -1) { 591 printf("mode %s not supported by firmware\n", arg); 592 return; 593 } 594 } else { 595 modenum = strtoul(arg, NULL, 0); 596 } 597 598 status = uefi_call_wrapper(ST->ConOut->SetMode, 2, ST->ConOut, modenum); 599 if (!EFI_ERROR(status)) 600 return; 601 602 printf("invalid flag, must be 'list', a display mode, " 603 "or a mode number\n"); 604 } 605 606 static int 607 print_gop_modes(void) 608 { 609 EFI_STATUS status; 610 EFI_GRAPHICS_OUTPUT_MODE_INFORMATION *info; 611 UINTN sz; 612 UINT32 i; 613 uint8_t depth; 614 615 if (efi_gop == NULL) 616 return 1; 617 618 for (i = 0; i < efi_gop->Mode->MaxMode; i++) { 619 status = uefi_call_wrapper(efi_gop->QueryMode, 4, efi_gop, i, 620 &sz, &info); 621 if (EFI_ERROR(status) && status == EFI_NOT_STARTED) { 622 status = uefi_call_wrapper(efi_gop->SetMode, 2, 623 efi_gop, efi_gop->Mode->Mode); 624 status = uefi_call_wrapper(efi_gop->QueryMode, 4, 625 efi_gop, i, &sz, &info); 626 } 627 if (EFI_ERROR(status)) 628 continue; 629 630 printf("%c%d: %dx%d ", 631 memcmp(info, efi_gop->Mode->Info, sizeof(*info)) == 0 ? 632 '*' : ' ', 633 i, info->HorizontalResolution, info->VerticalResolution); 634 switch (info->PixelFormat) { 635 case PixelRedGreenBlueReserved8BitPerColor: 636 printf("RGBR"); 637 break; 638 case PixelBlueGreenRedReserved8BitPerColor: 639 printf("BGRR"); 640 break; 641 case PixelBitMask: 642 printf("R:%08x G:%08x B:%08x X:%08x", 643 info->PixelInformation.RedMask, 644 info->PixelInformation.GreenMask, 645 info->PixelInformation.BlueMask, 646 info->PixelInformation.ReservedMask); 647 break; 648 case PixelBltOnly: 649 printf("(blt only)"); 650 break; 651 default: 652 printf("(Invalid pixel format)"); 653 break; 654 } 655 printf(" pitch %d", info->PixelsPerScanLine); 656 depth = getdepth(info); 657 if (depth > 0) 658 printf(" bpp %d", depth); 659 printf("\n"); 660 } 661 662 return 0; 663 } 664 665 static int 666 efi_find_gop_mode(char *arg) 667 { 668 EFI_STATUS status; 669 EFI_GRAPHICS_OUTPUT_MODE_INFORMATION *info; 670 UINTN sz; 671 UINT32 i; 672 char mode[32]; 673 uint8_t depth; 674 675 for (i = 0; i < efi_gop->Mode->MaxMode; i++) { 676 status = uefi_call_wrapper(efi_gop->QueryMode, 4, efi_gop, i, 677 &sz, &info); 678 if (EFI_ERROR(status)) 679 continue; 680 681 depth = getdepth(info); 682 if (depth == 0) 683 continue; 684 685 snprintf(mode, sizeof(mode), "%lux%lux%u", 686 (long)info->HorizontalResolution, 687 (long)info->VerticalResolution, 688 depth); 689 if (strcmp(arg, mode) == 0) 690 return i; 691 692 snprintf(mode, sizeof(mode), "%lux%lu", 693 (long)info->HorizontalResolution, 694 (long)info->VerticalResolution); 695 if (strcmp(arg, mode) == 0) 696 return i; 697 } 698 return -1; 699 } 700 701 void 702 command_gop(char *arg) 703 { 704 EFI_STATUS status; 705 INT32 modenum; 706 707 if (efi_gop == NULL) { 708 printf("GOP not supported by firmware\n"); 709 return; 710 } 711 712 if (*arg == '\0' || strcmp(arg, "list") == 0) { 713 print_gop_modes(); 714 return; 715 } 716 717 if (strchr(arg, 'x') != NULL) { 718 modenum = efi_find_gop_mode(arg); 719 if (modenum == -1) { 720 printf("mode %s not supported by firmware\n", arg); 721 return; 722 } 723 } else { 724 modenum = strtoul(arg, NULL, 0); 725 } 726 727 status = uefi_call_wrapper(efi_gop->SetMode, 2, efi_gop, modenum); 728 if (!EFI_ERROR(status) && efi_gop->Mode->Mode == modenum) { 729 efi_gop_mode = modenum; 730 return; 731 } 732 733 printf("invalid flag, must be 'list', a display mode, " 734 "or a mode number\n"); 735 } 736 737 static void 738 eficons_init_video(void) 739 { 740 EFI_STATUS status; 741 UINTN cols, rows; 742 INT32 i, best, mode80x25, mode100x31; 743 744 /* 745 * Setup text mode 746 */ 747 uefi_call_wrapper(ST->ConOut->Reset, 2, ST->ConOut, TRUE); 748 749 mode80x25 = mode100x31 = -1; 750 for (i = 0; i < ST->ConOut->Mode->MaxMode; i++) { 751 status = uefi_call_wrapper(ST->ConOut->QueryMode, 4, 752 ST->ConOut, i, &cols, &rows); 753 if (EFI_ERROR(status)) 754 continue; 755 756 if (mode80x25 < 0 && cols == 80 && rows == 25) 757 mode80x25 = i; 758 else if (mode100x31 < 0 && cols == 100 && rows == 31) 759 mode100x31 = i; 760 } 761 best = mode100x31 >= 0 ? mode100x31 : mode80x25 >= 0 ? mode80x25 : -1; 762 if (best >= 0) 763 uefi_call_wrapper(ST->ConOut->SetMode, 2, ST->ConOut, best); 764 uefi_call_wrapper(ST->ConOut->EnableCursor, 2, ST->ConOut, TRUE); 765 uefi_call_wrapper(ST->ConOut->ClearScreen, 1, ST->ConOut); 766 767 LibLocateProtocol(&GraphicsOutputProtocol, (void **)&efi_gop); 768 } 769 770 /* 771 * for Apple EFI 772 */ 773 #define CONSOLE_CONTROL_PROTOCOL \ 774 {0xf42f7782, 0x12e, 0x4c12, {0x99, 0x56, 0x49, 0xf9, 0x43, 0x4, 0xf7, 0x21}} 775 static EFI_GUID ConsoleControlProtocol = CONSOLE_CONTROL_PROTOCOL; 776 777 struct _EFI_CONSOLE_CONTROL_INTERFACE; 778 typedef struct _EFI_CONSOLE_CONTROL_INTERFACE EFI_CONSOLE_CONTROL_INTERFACE; 779 typedef enum { EfiConsoleControlScreenText } EFI_CONSOLE_CONTROL_SCREEN_MODE; 780 typedef EFI_STATUS (EFIAPI *EFI_CONSOLE_CONTROL_PROTOCOL_SET_MODE) ( 781 IN EFI_CONSOLE_CONTROL_INTERFACE *This, 782 IN EFI_CONSOLE_CONTROL_SCREEN_MODE Mode 783 ); 784 struct _EFI_CONSOLE_CONTROL_INTERFACE { 785 VOID *GetMode; 786 EFI_CONSOLE_CONTROL_PROTOCOL_SET_MODE SetMode; 787 VOID *LockStdIn; 788 }; 789 790 static void 791 efi_switch_video_to_text_mode(void) 792 { 793 EFI_STATUS status; 794 EFI_CONSOLE_CONTROL_INTERFACE *cci; 795 796 /* Set up the console, so printf works. */ 797 status = LibLocateProtocol(&ConsoleControlProtocol, (void **)&cci); 798 if (!EFI_ERROR(status)) { 799 uefi_call_wrapper(cci->SetMode, 2, cci, 800 EfiConsoleControlScreenText); 801 } 802 } 803 804 /* 805 * serial port 806 */ 807 static void 808 efi_com_probe(void) 809 { 810 EFI_STATUS status; 811 UINTN i, nhandles; 812 EFI_HANDLE *handles; 813 EFI_DEVICE_PATH *dp, *dp0; 814 EFI_DEV_PATH_PTR dpp; 815 SERIAL_IO_INTERFACE *serio; 816 int uid = -1; 817 818 status = LibLocateHandle(ByProtocol, &SerialIoProtocol, NULL, 819 &nhandles, &handles); 820 if (EFI_ERROR(status)) 821 return; 822 823 for (i = 0; i < nhandles; i++) { 824 /* 825 * Identify port number of the handle. This assumes ACPI 826 * UID 0-3 map to legacy COM[1-4] and they use the legacy 827 * port address. 828 */ 829 status = uefi_call_wrapper(BS->HandleProtocol, 3, handles[i], 830 &DevicePathProtocol, (void **)&dp0); 831 if (EFI_ERROR(status)) 832 continue; 833 834 for (uid = -1, dp = dp0; 835 !IsDevicePathEnd(dp); 836 dp = NextDevicePathNode(dp)) { 837 838 if (DevicePathType(dp) == ACPI_DEVICE_PATH && 839 DevicePathSubType(dp) == ACPI_DP) { 840 dpp = (EFI_DEV_PATH_PTR)dp; 841 if (dpp.Acpi->HID == EISA_PNP_ID(0x0501)) { 842 uid = dpp.Acpi->UID; 843 break; 844 } 845 } 846 } 847 if (uid < 0 || __arraycount(serios) <= uid) 848 continue; 849 850 /* Prepare SERIAL_IO_INTERFACE */ 851 status = uefi_call_wrapper(BS->HandleProtocol, 3, handles[i], 852 &SerialIoProtocol, (void **)&serio); 853 if (EFI_ERROR(status)) 854 continue; 855 856 serios[uid] = serio; 857 } 858 859 FreePool(handles); 860 861 } 862 863 static bool 864 efi_valid_com(int dev) 865 { 866 int idx; 867 868 switch (dev) { 869 default: 870 case CONSDEV_PC: 871 return false; 872 873 case CONSDEV_COM0: 874 case CONSDEV_COM1: 875 case CONSDEV_COM2: 876 case CONSDEV_COM3: 877 idx = dev - CONSDEV_COM0; 878 break; 879 } 880 881 return idx < __arraycount(serios) && 882 serios[idx] != NULL && 883 getcomaddr(idx) != 0; 884 } 885 886 static int 887 efi_com_init(int addr, int speed) 888 { 889 EFI_STATUS status; 890 SERIAL_IO_INTERFACE *serio; 891 892 if (speed <= 0) 893 return 0; 894 895 if (!efi_valid_com(iodev)) 896 return raw_com_init(addr, speed); 897 898 serio = serios[iodev - CONSDEV_COM0]; 899 900 if (serio->Mode->BaudRate != btinfo_console.speed) { 901 status = uefi_call_wrapper(serio->SetAttributes, 7, serio, 902 speed, serio->Mode->ReceiveFifoDepth, 903 serio->Mode->Timeout, serio->Mode->Parity, 904 serio->Mode->DataBits, serio->Mode->StopBits); 905 if (EFI_ERROR(status)) { 906 printf("com%d: SetAttribute() failed with status=%" PRIxMAX 907 "\n", iodev - CONSDEV_COM0, (uintmax_t)status); 908 return 0; 909 } 910 } 911 912 raw_com_addr = 0; 913 default_comspeed = speed; 914 internal_getchar = efi_com_getc; 915 internal_putchar = efi_com_putc; 916 internal_iskey = efi_com_status; 917 internal_waitforinputevent = efi_com_waitforinputevent; 918 memset(serbuf, 0, sizeof(serbuf)); 919 serbuf_read = serbuf_write = 0; 920 921 return speed; 922 } 923 924 static int 925 efi_com_getc(void) 926 { 927 EFI_STATUS status; 928 SERIAL_IO_INTERFACE *serio; 929 UINTN sz; 930 u_char c; 931 932 if (!efi_valid_com(iodev)) 933 panic("Invalid serial port: iodev=%d", iodev); 934 935 if (serbuf_read != serbuf_write) { 936 c = serbuf[serbuf_read]; 937 serbuf_read = (serbuf_read + 1) % __arraycount(serbuf); 938 return c; 939 } 940 941 serio = serios[iodev - CONSDEV_COM0]; 942 943 for (;;) { 944 sz = 1; 945 status = uefi_call_wrapper(serio->Read, 3, serio, &sz, &c); 946 if (!EFI_ERROR(status) && sz > 0) 947 break; 948 if (status != EFI_TIMEOUT && EFI_ERROR(status)) 949 panic("Error reading from serial status=%"PRIxMAX, 950 (uintmax_t)status); 951 } 952 return c; 953 } 954 955 static int 956 efi_com_putc(int c) 957 { 958 EFI_STATUS status; 959 SERIAL_IO_INTERFACE *serio; 960 UINTN sz = 1; 961 u_char buf; 962 963 if (!efi_valid_com(iodev)) 964 return 0; 965 966 serio = serios[iodev - CONSDEV_COM0]; 967 buf = c; 968 status = uefi_call_wrapper(serio->Write, 3, serio, &sz, &buf); 969 if (EFI_ERROR(status) || sz < 1) 970 return 0; 971 return 1; 972 } 973 974 /*ARGSUSED*/ 975 static int 976 efi_com_status(int intr) 977 { 978 EFI_STATUS status; 979 SERIAL_IO_INTERFACE *serio; 980 UINTN sz; 981 u_char c; 982 983 if (!efi_valid_com(iodev)) 984 panic("Invalid serial port: iodev=%d", iodev); 985 986 if (serbuf_read != serbuf_write) 987 return 1; 988 989 serio = serios[iodev - CONSDEV_COM0]; 990 sz = 1; 991 status = uefi_call_wrapper(serio->Read, 3, serio, &sz, &c); 992 if (EFI_ERROR(status) || sz < 1) 993 return 0; 994 995 serbuf[serbuf_write] = c; 996 serbuf_write = (serbuf_write + 1) % __arraycount(serbuf); 997 return 1; 998 } 999 1000 static void 1001 efi_com_periodic_event(EFI_EVENT event, void *ctx) 1002 { 1003 EFI_EVENT timer = ctx; 1004 1005 if (efi_com_status(0)) { 1006 uefi_call_wrapper(BS->SetTimer, 3, event, TimerCancel, 0); 1007 uefi_call_wrapper(BS->SignalEvent, 1, timer); 1008 } 1009 } 1010 1011 static int 1012 efi_com_waitforinputevent(uint64_t timeout) 1013 { 1014 EFI_STATUS status; 1015 EFI_EVENT timer, periodic; 1016 1017 status = uefi_call_wrapper(BS->CreateEvent, 5, EVT_TIMER, 0, NULL, NULL, 1018 &timer); 1019 if (EFI_ERROR(status)) 1020 return EINVAL; 1021 1022 status = uefi_call_wrapper(BS->CreateEvent, 5, 1023 EVT_TIMER | EVT_NOTIFY_SIGNAL, TPL_CALLBACK, efi_com_periodic_event, 1024 timer, &periodic); 1025 if (EFI_ERROR(status)) { 1026 uefi_call_wrapper(BS->CloseEvent, 1, timer); 1027 return EINVAL; 1028 } 1029 1030 status = uefi_call_wrapper(BS->SetTimer, 3, periodic, TimerPeriodic, 1031 1000000); /* 100ms */ 1032 if (EFI_ERROR(status)) { 1033 uefi_call_wrapper(BS->CloseEvent, 1, periodic); 1034 uefi_call_wrapper(BS->CloseEvent, 1, timer); 1035 return EINVAL; 1036 } 1037 status = WaitForSingleEvent(&timer, timeout); 1038 uefi_call_wrapper(BS->SetTimer, 3, periodic, TimerCancel, 0); 1039 uefi_call_wrapper(BS->CloseEvent, 1, periodic); 1040 uefi_call_wrapper(BS->CloseEvent, 1, timer); 1041 if (!EFI_ERROR(status)) 1042 return 0; 1043 if (status == EFI_TIMEOUT) 1044 return ETIMEDOUT; 1045 return EINVAL; 1046 } 1047 1048 static int 1049 raw_com_init(int addr, int speed) 1050 { 1051 1052 if (addr == 0 || speed <= 0) 1053 return 0; 1054 1055 speed = cominit_d(addr, speed); 1056 1057 raw_com_addr = addr; 1058 default_comspeed = speed; 1059 internal_getchar = raw_com_getc; 1060 internal_putchar = raw_com_putc; 1061 internal_iskey = raw_com_status; 1062 internal_waitforinputevent = raw_com_waitforinputevent; 1063 1064 return speed; 1065 } 1066 1067 static int 1068 raw_com_getc(void) 1069 { 1070 1071 if (raw_com_addr == 0) 1072 panic("%s: Invalid serial port", __func__); 1073 return comgetc_d(raw_com_addr); 1074 } 1075 1076 static int 1077 raw_com_putc(int c) 1078 { 1079 1080 if (raw_com_addr == 0) 1081 panic("%s: Invalid serial port", __func__); 1082 return computc_d(c, raw_com_addr); 1083 } 1084 1085 static int 1086 raw_com_status(int intr) 1087 { 1088 1089 if (raw_com_addr == 0) 1090 panic("%s: Invalid serial port", __func__); 1091 return comstatus_d(raw_com_addr); 1092 } 1093 1094 static int 1095 raw_com_waitforinputevent(uint64_t timeout /* in 0.1 usec */) 1096 { 1097 uint64_t ms; 1098 1099 if (raw_com_addr == 0) 1100 panic("%s: Invalid serial port", __func__); 1101 1102 for (ms = howmany(timeout, 10 * 1000); ms != 0; ms--) { 1103 if (raw_com_status(0)) 1104 return 0; 1105 uefi_call_wrapper(BS->Stall, 1, 1000); 1106 } 1107 return ETIMEDOUT; 1108 } 1109