1/* $NetBSD: eficons.c,v 1.14 2023/09/14 03:05:15 rin Exp $ */ 2 3/*- 4 * Copyright (c) 2016 Kimihiro Nonaka <nonaka@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 45extern struct x86_boot_params boot_params; 46 47struct btinfo_console btinfo_console; 48 49static EFI_GRAPHICS_OUTPUT_PROTOCOL *efi_gop; 50static int efi_gop_mode = -1; 51static CHAR16 keybuf[16]; 52static int keybuf_read = 0; 53static int keybuf_write = 0; 54 55static SERIAL_IO_INTERFACE *serios[4]; 56static int default_comspeed = 57#if defined(CONSPEED) 58 CONSPEED; 59#else 60 9600; 61#endif 62static u_char serbuf[16]; 63static int serbuf_read = 0; 64static int serbuf_write = 0; 65 66static int raw_com_addr = 0; 67 68static void eficons_init_video(void); 69static void efi_switch_video_to_text_mode(void); 70 71static int efi_cons_getc(void); 72static int efi_cons_putc(int); 73static int efi_cons_iskey(int); 74static int efi_cons_waitforinputevent(uint64_t); 75 76static void efi_com_probe(void); 77static bool efi_valid_com(int); 78static int efi_com_init(int, int); 79static int efi_com_getc(void); 80static int efi_com_putc(int); 81static int efi_com_status(int); 82static int efi_com_waitforinputevent(uint64_t); 83 84static int raw_com_init(int, int); 85static int raw_com_getc(void); 86static int raw_com_putc(int); 87static int raw_com_status(int); 88static int raw_com_waitforinputevent(uint64_t); 89 90static int efi_find_gop_mode(char *); 91 92static int iodev; 93static int (*internal_getchar)(void) = efi_cons_getc; 94static int (*internal_putchar)(int) = efi_cons_putc; 95static int (*internal_iskey)(int) = efi_cons_iskey; 96static int (*internal_waitforinputevent)(uint64_t) = efi_cons_waitforinputevent; 97 98static int 99getcomaddr(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 */ 111void 112efi_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; 139ok: 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: 176nocom: 177 iodev = CONSDEV_PC; 178 internal_putchar = efi_cons_putc; 179kbd: 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 191int 192cninit(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 205void 206efi_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 247static int 248efi_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 270static int 271efi_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*/ 283static int 284efi_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 302static int 303efi_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 315int 316getchar(void) 317{ 318 319 return internal_getchar(); 320} 321 322void 323putchar(int c) 324{ 325 326 if (c == '\n') 327 internal_putchar('\r'); 328 internal_putchar(c); 329} 330 331int 332iskey(int intr) 333{ 334 335 return internal_iskey(intr); 336} 337 338char 339awaitkey(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 internal_waitforinputevent(10000000); 370 else 371 break; 372 if (tell) 373 printf("%s", numbuf); 374 } 375 376 if (tell) 377 printf("0 seconds. \n"); 378 379 return c; 380} 381 382void 383clear_pc_screen(void) 384{ 385 386 uefi_call_wrapper(ST->ConOut->ClearScreen, 1, ST->ConOut); 387} 388 389static uint8_t 390getdepth(const EFI_GRAPHICS_OUTPUT_MODE_INFORMATION *info) 391{ 392 393 switch (info->PixelFormat) { 394 case PixelBlueGreenRedReserved8BitPerColor: 395 case PixelRedGreenBlueReserved8BitPerColor: 396 return 32; 397 398 case PixelBitMask: 399 return fls32(info->PixelInformation.RedMask 400 | info->PixelInformation.GreenMask 401 | info->PixelInformation.BlueMask 402 | info->PixelInformation.ReservedMask); 403 404 case PixelBltOnly: 405 case PixelFormatMax: 406 return 0; 407 } 408 return 0; 409} 410 411static void 412setpixelformat(UINT32 mask, uint8_t *num, uint8_t *pos) 413{ 414 uint8_t n, p; 415 416 n = popcount32(mask); 417 p = ffs32(mask); 418 if (p > 0) 419 p--; 420 421 *num = n; 422 *pos = p; 423} 424 425static void 426bi_framebuffer(void) 427{ 428 EFI_STATUS status; 429 EFI_GRAPHICS_OUTPUT_MODE_INFORMATION *info; 430 struct btinfo_framebuffer fb; 431 INT32 bestmode; 432 UINTN sz; 433 434 if (efi_gop == NULL) 435 goto nofb; 436 437 if (efi_gop_mode >= 0) { 438 bestmode = efi_gop_mode; 439 } else { 440 /* If a mode has not been selected, choose a default */ 441 bestmode = efi_find_gop_mode(DEFAULT_GOP_MODE); 442 if (bestmode == -1) 443 bestmode = FALLBACK_GOP_MODE; 444 } 445 446 status = uefi_call_wrapper(efi_gop->SetMode, 2, efi_gop, 447 bestmode); 448 if (EFI_ERROR(status) || efi_gop->Mode->Mode != bestmode) { 449 printf("GOP setmode failed: %" PRIxMAX "\n", 450 (uintmax_t)status); 451 goto nofb; 452 } 453 454 status = uefi_call_wrapper(efi_gop->QueryMode, 4, 455 efi_gop, bestmode, &sz, &info); 456 if (EFI_ERROR(status)) { 457 printf("GOP querymode failed: %" PRIxMAX "\n", 458 (uintmax_t)status); 459 goto nofb; 460 } 461 462 memset(&fb, 0, sizeof(fb)); 463 fb.physaddr = efi_gop->Mode->FrameBufferBase; 464 fb.flags = 0; 465 fb.width = info->HorizontalResolution; 466 fb.height = info->VerticalResolution; 467 fb.depth = getdepth(info); 468 fb.stride = info->PixelsPerScanLine * ((fb.depth + 7) / 8); 469 fb.vbemode = 0; /* XXX */ 470 471 switch (info->PixelFormat) { 472 case PixelBlueGreenRedReserved8BitPerColor: 473 fb.rnum = 8; 474 fb.gnum = 8; 475 fb.bnum = 8; 476 fb.rpos = 16; 477 fb.gpos = 8; 478 fb.bpos = 0; 479 break; 480 481 case PixelRedGreenBlueReserved8BitPerColor: 482 fb.rnum = 8; 483 fb.gnum = 8; 484 fb.bnum = 8; 485 fb.rpos = 0; 486 fb.gpos = 8; 487 fb.bpos = 16; 488 break; 489 490 case PixelBitMask: 491 setpixelformat(info->PixelInformation.RedMask, 492 &fb.rnum, &fb.rpos); 493 setpixelformat(info->PixelInformation.GreenMask, 494 &fb.gnum, &fb.gpos); 495 setpixelformat(info->PixelInformation.BlueMask, 496 &fb.bnum, &fb.bpos); 497 break; 498 499 case PixelBltOnly: 500 case PixelFormatMax: 501 panic("Error: invalid pixel format (%d)", info->PixelFormat); 502 break; 503 } 504 505 framebuffer_configure(&fb); 506 return; 507 508nofb: 509 framebuffer_configure(NULL); 510} 511 512int 513vbe_commit(void) 514{ 515 516 bi_framebuffer(); 517 return 0; 518} 519 520static void 521print_text_modes(void) 522{ 523 EFI_STATUS status; 524 UINTN cols, rows; 525 INT32 i, curmode; 526 527 curmode = ST->ConOut->Mode->Mode; 528 for (i = 0; i < ST->ConOut->Mode->MaxMode; i++) { 529 status = uefi_call_wrapper(ST->ConOut->QueryMode, 4, 530 ST->ConOut, i, &cols, &rows); 531 if (EFI_ERROR(status)) 532 continue; 533 printf("%c%d: %" PRIxMAX "x%" PRIxMAX "\n", 534 i == curmode ? '*' : ' ', i, (uintmax_t)cols, (uintmax_t)rows); 535 } 536} 537 538static int 539efi_find_text_mode(char *arg) 540{ 541 EFI_STATUS status; 542 UINTN cols, rows; 543 INT32 i; 544 char mode[32]; 545 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 snprintf(mode, sizeof(mode), "%" PRIuMAX "x%" PRIuMAX, 552 (uintmax_t)cols, (uintmax_t)rows); 553 if (strcmp(arg, mode) == 0) 554 return i; 555 } 556 return -1; 557} 558 559void 560command_text(char *arg) 561{ 562 EFI_STATUS status; 563 INT32 modenum; 564 565 if (*arg == '\0' || strcmp(arg, "list") == 0) { 566 print_text_modes(); 567 return; 568 } 569 570 if (strchr(arg, 'x') != NULL) { 571 modenum = efi_find_text_mode(arg); 572 if (modenum == -1) { 573 printf("mode %s not supported by firmware\n", arg); 574 return; 575 } 576 } else { 577 modenum = strtoul(arg, NULL, 0); 578 } 579 580 status = uefi_call_wrapper(ST->ConOut->SetMode, 2, ST->ConOut, modenum); 581 if (!EFI_ERROR(status)) 582 return; 583 584 printf("invalid flag, must be 'list', a display mode, " 585 "or a mode number\n"); 586} 587 588static int 589print_gop_modes(void) 590{ 591 EFI_STATUS status; 592 EFI_GRAPHICS_OUTPUT_MODE_INFORMATION *info; 593 UINTN sz; 594 UINT32 i; 595 uint8_t depth; 596 597 if (efi_gop == NULL) 598 return 1; 599 600 for (i = 0; i < efi_gop->Mode->MaxMode; i++) { 601 status = uefi_call_wrapper(efi_gop->QueryMode, 4, efi_gop, i, 602 &sz, &info); 603 if (EFI_ERROR(status) && status == EFI_NOT_STARTED) { 604 status = uefi_call_wrapper(efi_gop->SetMode, 2, 605 efi_gop, efi_gop->Mode->Mode); 606 status = uefi_call_wrapper(efi_gop->QueryMode, 4, 607 efi_gop, i, &sz, &info); 608 } 609 if (EFI_ERROR(status)) 610 continue; 611 612 printf("%c%d: %dx%d ", 613 memcmp(info, efi_gop->Mode->Info, sizeof(*info)) == 0 ? 614 '*' : ' ', 615 i, info->HorizontalResolution, info->VerticalResolution); 616 switch (info->PixelFormat) { 617 case PixelRedGreenBlueReserved8BitPerColor: 618 printf("RGBR"); 619 break; 620 case PixelBlueGreenRedReserved8BitPerColor: 621 printf("BGRR"); 622 break; 623 case PixelBitMask: 624 printf("R:%08x G:%08x B:%08x X:%08x", 625 info->PixelInformation.RedMask, 626 info->PixelInformation.GreenMask, 627 info->PixelInformation.BlueMask, 628 info->PixelInformation.ReservedMask); 629 break; 630 case PixelBltOnly: 631 printf("(blt only)"); 632 break; 633 default: 634 printf("(Invalid pixel format)"); 635 break; 636 } 637 printf(" pitch %d", info->PixelsPerScanLine); 638 depth = getdepth(info); 639 if (depth > 0) 640 printf(" bpp %d", depth); 641 printf("\n"); 642 } 643 644 return 0; 645} 646 647static int 648efi_find_gop_mode(char *arg) 649{ 650 EFI_STATUS status; 651 EFI_GRAPHICS_OUTPUT_MODE_INFORMATION *info; 652 UINTN sz; 653 UINT32 i; 654 char mode[32]; 655 uint8_t depth; 656 657 for (i = 0; i < efi_gop->Mode->MaxMode; i++) { 658 status = uefi_call_wrapper(efi_gop->QueryMode, 4, efi_gop, i, 659 &sz, &info); 660 if (EFI_ERROR(status)) 661 continue; 662 663 depth = getdepth(info); 664 if (depth == 0) 665 continue; 666 667 snprintf(mode, sizeof(mode), "%lux%lux%u", 668 (long)info->HorizontalResolution, 669 (long)info->VerticalResolution, 670 depth); 671 if (strcmp(arg, mode) == 0) 672 return i; 673 674 snprintf(mode, sizeof(mode), "%lux%lu", 675 (long)info->HorizontalResolution, 676 (long)info->VerticalResolution); 677 if (strcmp(arg, mode) == 0) 678 return i; 679 } 680 return -1; 681} 682 683void 684command_gop(char *arg) 685{ 686 EFI_STATUS status; 687 INT32 modenum; 688 689 if (efi_gop == NULL) { 690 printf("GOP not supported by firmware\n"); 691 return; 692 } 693 694 if (*arg == '\0' || strcmp(arg, "list") == 0) { 695 print_gop_modes(); 696 return; 697 } 698 699 if (strchr(arg, 'x') != NULL) { 700 modenum = efi_find_gop_mode(arg); 701 if (modenum == -1) { 702 printf("mode %s not supported by firmware\n", arg); 703 return; 704 } 705 } else { 706 modenum = strtoul(arg, NULL, 0); 707 } 708 709 status = uefi_call_wrapper(efi_gop->SetMode, 2, efi_gop, modenum); 710 if (!EFI_ERROR(status) && efi_gop->Mode->Mode == modenum) { 711 efi_gop_mode = modenum; 712 return; 713 } 714 715 printf("invalid flag, must be 'list', a display mode, " 716 "or a mode number\n"); 717} 718 719static void 720eficons_init_video(void) 721{ 722 EFI_STATUS status; 723 UINTN cols, rows; 724 INT32 i, best, mode80x25, mode100x31; 725 726 /* 727 * Setup text mode 728 */ 729 uefi_call_wrapper(ST->ConOut->Reset, 2, ST->ConOut, TRUE); 730 731 mode80x25 = mode100x31 = -1; 732 for (i = 0; i < ST->ConOut->Mode->MaxMode; i++) { 733 status = uefi_call_wrapper(ST->ConOut->QueryMode, 4, 734 ST->ConOut, i, &cols, &rows); 735 if (EFI_ERROR(status)) 736 continue; 737 738 if (mode80x25 < 0 && cols == 80 && rows == 25) 739 mode80x25 = i; 740 else if (mode100x31 < 0 && cols == 100 && rows == 31) 741 mode100x31 = i; 742 } 743 best = mode100x31 >= 0 ? mode100x31 : mode80x25 >= 0 ? mode80x25 : -1; 744 if (best >= 0) 745 uefi_call_wrapper(ST->ConOut->SetMode, 2, ST->ConOut, best); 746 uefi_call_wrapper(ST->ConOut->EnableCursor, 2, ST->ConOut, TRUE); 747 uefi_call_wrapper(ST->ConOut->ClearScreen, 1, ST->ConOut); 748 749 LibLocateProtocol(&GraphicsOutputProtocol, (void **)&efi_gop); 750} 751 752/* 753 * for Apple EFI 754 */ 755#define CONSOLE_CONTROL_PROTOCOL \ 756 {0xf42f7782, 0x12e, 0x4c12, {0x99, 0x56, 0x49, 0xf9, 0x43, 0x4, 0xf7, 0x21}} 757static EFI_GUID ConsoleControlProtocol = CONSOLE_CONTROL_PROTOCOL; 758 759struct _EFI_CONSOLE_CONTROL_INTERFACE; 760typedef struct _EFI_CONSOLE_CONTROL_INTERFACE EFI_CONSOLE_CONTROL_INTERFACE; 761typedef enum { EfiConsoleControlScreenText } EFI_CONSOLE_CONTROL_SCREEN_MODE; 762typedef EFI_STATUS (EFIAPI *EFI_CONSOLE_CONTROL_PROTOCOL_SET_MODE) ( 763 IN EFI_CONSOLE_CONTROL_INTERFACE *This, 764 IN EFI_CONSOLE_CONTROL_SCREEN_MODE Mode 765); 766struct _EFI_CONSOLE_CONTROL_INTERFACE { 767 VOID *GetMode; 768 EFI_CONSOLE_CONTROL_PROTOCOL_SET_MODE SetMode; 769 VOID *LockStdIn; 770}; 771 772static void 773efi_switch_video_to_text_mode(void) 774{ 775 EFI_STATUS status; 776 EFI_CONSOLE_CONTROL_INTERFACE *cci; 777 778 /* Set up the console, so printf works. */ 779 status = LibLocateProtocol(&ConsoleControlProtocol, (void **)&cci); 780 if (!EFI_ERROR(status)) { 781 uefi_call_wrapper(cci->SetMode, 2, cci, 782 EfiConsoleControlScreenText); 783 } 784} 785 786/* 787 * serial port 788 */ 789static void 790efi_com_probe(void) 791{ 792 EFI_STATUS status; 793 UINTN i, nhandles; 794 EFI_HANDLE *handles; 795 EFI_DEVICE_PATH *dp, *dp0; 796 EFI_DEV_PATH_PTR dpp; 797 SERIAL_IO_INTERFACE *serio; 798 int uid = -1; 799 800 status = LibLocateHandle(ByProtocol, &SerialIoProtocol, NULL, 801 &nhandles, &handles); 802 if (EFI_ERROR(status)) 803 return; 804 805 for (i = 0; i < nhandles; i++) { 806 /* 807 * Identify port number of the handle. This assumes ACPI 808 * UID 0-3 map to legacy COM[1-4] and they use the legacy 809 * port address. 810 */ 811 status = uefi_call_wrapper(BS->HandleProtocol, 3, handles[i], 812 &DevicePathProtocol, (void **)&dp0); 813 if (EFI_ERROR(status)) 814 continue; 815 816 for (uid = -1, dp = dp0; 817 !IsDevicePathEnd(dp); 818 dp = NextDevicePathNode(dp)) { 819 820 if (DevicePathType(dp) == ACPI_DEVICE_PATH && 821 DevicePathSubType(dp) == ACPI_DP) { 822 dpp = (EFI_DEV_PATH_PTR)dp; 823 if (dpp.Acpi->HID == EISA_PNP_ID(0x0501)) { 824 uid = dpp.Acpi->UID; 825 break; 826 } 827 } 828 } 829 if (uid < 0 || __arraycount(serios) <= uid) 830 continue; 831 832 /* Prepare SERIAL_IO_INTERFACE */ 833 status = uefi_call_wrapper(BS->HandleProtocol, 3, handles[i], 834 &SerialIoProtocol, (void **)&serio); 835 if (EFI_ERROR(status)) 836 continue; 837 838 serios[uid] = serio; 839 } 840 841 FreePool(handles); 842 843} 844 845static bool 846efi_valid_com(int dev) 847{ 848 int idx; 849 850 switch (dev) { 851 default: 852 case CONSDEV_PC: 853 return false; 854 855 case CONSDEV_COM0: 856 case CONSDEV_COM1: 857 case CONSDEV_COM2: 858 case CONSDEV_COM3: 859 idx = dev - CONSDEV_COM0; 860 break; 861 } 862 863 return idx < __arraycount(serios) && 864 serios[idx] != NULL && 865 getcomaddr(idx) != 0; 866} 867 868static int 869efi_com_init(int addr, int speed) 870{ 871 EFI_STATUS status; 872 SERIAL_IO_INTERFACE *serio; 873 874 if (speed <= 0) 875 return 0; 876 877 if (!efi_valid_com(iodev)) 878 return raw_com_init(addr, speed); 879 880 serio = serios[iodev - CONSDEV_COM0]; 881 882 if (serio->Mode->BaudRate != btinfo_console.speed) { 883 status = uefi_call_wrapper(serio->SetAttributes, 7, serio, 884 speed, serio->Mode->ReceiveFifoDepth, 885 serio->Mode->Timeout, serio->Mode->Parity, 886 serio->Mode->DataBits, serio->Mode->StopBits); 887 if (EFI_ERROR(status)) { 888 printf("com%d: SetAttribute() failed with status=%" PRIxMAX 889 "\n", iodev - CONSDEV_COM0, (uintmax_t)status); 890 return 0; 891 } 892 } 893 894 raw_com_addr = 0; 895 default_comspeed = speed; 896 internal_getchar = efi_com_getc; 897 internal_putchar = efi_com_putc; 898 internal_iskey = efi_com_status; 899 internal_waitforinputevent = efi_com_waitforinputevent; 900 memset(serbuf, 0, sizeof(serbuf)); 901 serbuf_read = serbuf_write = 0; 902 903 return speed; 904} 905 906static int 907efi_com_getc(void) 908{ 909 EFI_STATUS status; 910 SERIAL_IO_INTERFACE *serio; 911 UINTN sz; 912 u_char c; 913 914 if (!efi_valid_com(iodev)) 915 panic("Invalid serial port: iodev=%d", iodev); 916 917 if (serbuf_read != serbuf_write) { 918 c = serbuf[serbuf_read]; 919 serbuf_read = (serbuf_read + 1) % __arraycount(serbuf); 920 return c; 921 } 922 923 serio = serios[iodev - CONSDEV_COM0]; 924 925 for (;;) { 926 sz = 1; 927 status = uefi_call_wrapper(serio->Read, 3, serio, &sz, &c); 928 if (!EFI_ERROR(status) && sz > 0) 929 break; 930 if (status != EFI_TIMEOUT && EFI_ERROR(status)) 931 panic("Error reading from serial status=%"PRIxMAX, 932 (uintmax_t)status); 933 } 934 return c; 935} 936 937static int 938efi_com_putc(int c) 939{ 940 EFI_STATUS status; 941 SERIAL_IO_INTERFACE *serio; 942 UINTN sz = 1; 943 u_char buf; 944 945 if (!efi_valid_com(iodev)) 946 return 0; 947 948 serio = serios[iodev - CONSDEV_COM0]; 949 buf = c; 950 status = uefi_call_wrapper(serio->Write, 3, serio, &sz, &buf); 951 if (EFI_ERROR(status) || sz < 1) 952 return 0; 953 return 1; 954} 955 956/*ARGSUSED*/ 957static int 958efi_com_status(int intr) 959{ 960 EFI_STATUS status; 961 SERIAL_IO_INTERFACE *serio; 962 UINTN sz; 963 u_char c; 964 965 if (!efi_valid_com(iodev)) 966 panic("Invalid serial port: iodev=%d", iodev); 967 968 if (serbuf_read != serbuf_write) 969 return 1; 970 971 serio = serios[iodev - CONSDEV_COM0]; 972 sz = 1; 973 status = uefi_call_wrapper(serio->Read, 3, serio, &sz, &c); 974 if (EFI_ERROR(status) || sz < 1) 975 return 0; 976 977 serbuf[serbuf_write] = c; 978 serbuf_write = (serbuf_write + 1) % __arraycount(serbuf); 979 return 1; 980} 981 982static void 983efi_com_periodic_event(EFI_EVENT event, void *ctx) 984{ 985 EFI_EVENT timer = ctx; 986 987 if (efi_com_status(0)) { 988 uefi_call_wrapper(BS->SetTimer, 3, event, TimerCancel, 0); 989 uefi_call_wrapper(BS->SignalEvent, 1, timer); 990 } 991} 992 993static int 994efi_com_waitforinputevent(uint64_t timeout) 995{ 996 EFI_STATUS status; 997 EFI_EVENT timer, periodic; 998 999 status = uefi_call_wrapper(BS->CreateEvent, 5, EVT_TIMER, 0, NULL, NULL, 1000 &timer); 1001 if (EFI_ERROR(status)) 1002 return EINVAL; 1003 1004 status = uefi_call_wrapper(BS->CreateEvent, 5, 1005 EVT_TIMER | EVT_NOTIFY_SIGNAL, TPL_CALLBACK, efi_com_periodic_event, 1006 timer, &periodic); 1007 if (EFI_ERROR(status)) { 1008 uefi_call_wrapper(BS->CloseEvent, 1, timer); 1009 return EINVAL; 1010 } 1011 1012 status = uefi_call_wrapper(BS->SetTimer, 3, periodic, TimerPeriodic, 1013 1000000); /* 100ms */ 1014 if (EFI_ERROR(status)) { 1015 uefi_call_wrapper(BS->CloseEvent, 1, periodic); 1016 uefi_call_wrapper(BS->CloseEvent, 1, timer); 1017 return EINVAL; 1018 } 1019 status = WaitForSingleEvent(&timer, timeout); 1020 uefi_call_wrapper(BS->SetTimer, 3, periodic, TimerCancel, 0); 1021 uefi_call_wrapper(BS->CloseEvent, 1, periodic); 1022 uefi_call_wrapper(BS->CloseEvent, 1, timer); 1023 if (!EFI_ERROR(status)) 1024 return 0; 1025 if (status == EFI_TIMEOUT) 1026 return ETIMEDOUT; 1027 return EINVAL; 1028} 1029 1030static int 1031raw_com_init(int addr, int speed) 1032{ 1033 1034 if (addr == 0 || speed <= 0) 1035 return 0; 1036 1037 speed = cominit_d(addr, speed); 1038 1039 raw_com_addr = addr; 1040 default_comspeed = speed; 1041 internal_getchar = raw_com_getc; 1042 internal_putchar = raw_com_putc; 1043 internal_iskey = raw_com_status; 1044 internal_waitforinputevent = raw_com_waitforinputevent; 1045 1046 return speed; 1047} 1048 1049static int 1050raw_com_getc(void) 1051{ 1052 1053 if (raw_com_addr == 0) 1054 panic("%s: Invalid serial port", __func__); 1055 return comgetc_d(raw_com_addr); 1056} 1057 1058static int 1059raw_com_putc(int c) 1060{ 1061 1062 if (raw_com_addr == 0) 1063 panic("%s: Invalid serial port", __func__); 1064 return computc_d(c, raw_com_addr); 1065} 1066 1067static int 1068raw_com_status(int intr) 1069{ 1070 1071 if (raw_com_addr == 0) 1072 panic("%s: Invalid serial port", __func__); 1073 return comstatus_d(raw_com_addr); 1074} 1075 1076static int 1077raw_com_waitforinputevent(uint64_t timeout /* in 0.1 usec */) 1078{ 1079 uint64_t ms; 1080 1081 if (raw_com_addr == 0) 1082 panic("%s: Invalid serial port", __func__); 1083 1084 for (ms = howmany(timeout, 10 * 1000); ms != 0; ms--) { 1085 if (raw_com_status(0)) 1086 return 0; 1087 uefi_call_wrapper(BS->Stall, 1, 1000); 1088 } 1089 return ETIMEDOUT; 1090} 1091