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
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
getcomaddr(int idx)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
efi_consinit(int dev,int ioport,int speed)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
cninit(void)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
efi_cons_show(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
efi_cons_getc(void)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
efi_cons_putc(int c)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
efi_cons_iskey(int intr)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
efi_cons_waitforinputevent(uint64_t timeout)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
getchar(void)316 getchar(void)
317 {
318
319 return internal_getchar();
320 }
321
322 void
putchar(int c)323 putchar(int c)
324 {
325
326 if (c == '\n')
327 internal_putchar('\r');
328 internal_putchar(c);
329 }
330
331 int
iskey(int intr)332 iskey(int intr)
333 {
334
335 return internal_iskey(intr);
336 }
337
338 char
awaitkey(int timeout,int tell)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 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
382 void
clear_pc_screen(void)383 clear_pc_screen(void)
384 {
385
386 uefi_call_wrapper(ST->ConOut->ClearScreen, 1, ST->ConOut);
387 }
388
389 static uint8_t
getdepth(const EFI_GRAPHICS_OUTPUT_MODE_INFORMATION * info)390 getdepth(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
411 static void
setpixelformat(UINT32 mask,uint8_t * num,uint8_t * pos)412 setpixelformat(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
425 static void
bi_framebuffer(void)426 bi_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
508 nofb:
509 framebuffer_configure(NULL);
510 }
511
512 int
vbe_commit(void)513 vbe_commit(void)
514 {
515
516 bi_framebuffer();
517 return 0;
518 }
519
520 static void
print_text_modes(void)521 print_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
538 static int
efi_find_text_mode(char * arg)539 efi_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
559 void
command_text(char * arg)560 command_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
588 static int
print_gop_modes(void)589 print_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
647 static int
efi_find_gop_mode(char * arg)648 efi_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
683 void
command_gop(char * arg)684 command_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
719 static void
eficons_init_video(void)720 eficons_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}}
757 static EFI_GUID ConsoleControlProtocol = CONSOLE_CONTROL_PROTOCOL;
758
759 struct _EFI_CONSOLE_CONTROL_INTERFACE;
760 typedef struct _EFI_CONSOLE_CONTROL_INTERFACE EFI_CONSOLE_CONTROL_INTERFACE;
761 typedef enum { EfiConsoleControlScreenText } EFI_CONSOLE_CONTROL_SCREEN_MODE;
762 typedef 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 );
766 struct _EFI_CONSOLE_CONTROL_INTERFACE {
767 VOID *GetMode;
768 EFI_CONSOLE_CONTROL_PROTOCOL_SET_MODE SetMode;
769 VOID *LockStdIn;
770 };
771
772 static void
efi_switch_video_to_text_mode(void)773 efi_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 */
789 static void
efi_com_probe(void)790 efi_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
845 static bool
efi_valid_com(int dev)846 efi_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
868 static int
efi_com_init(int addr,int speed)869 efi_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
906 static int
efi_com_getc(void)907 efi_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
937 static int
efi_com_putc(int c)938 efi_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*/
957 static int
efi_com_status(int intr)958 efi_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
982 static void
efi_com_periodic_event(EFI_EVENT event,void * ctx)983 efi_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
993 static int
efi_com_waitforinputevent(uint64_t timeout)994 efi_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
1030 static int
raw_com_init(int addr,int speed)1031 raw_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
1049 static int
raw_com_getc(void)1050 raw_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
1058 static int
raw_com_putc(int c)1059 raw_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
1067 static int
raw_com_status(int intr)1068 raw_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
1076 static int
raw_com_waitforinputevent(uint64_t timeout)1077 raw_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