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