eficons.c revision 1.18 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