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