eficons.c revision 1.17 1 /* $NetBSD: eficons.c,v 1.17 2025/10/14 07:16:18 skrll 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