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