1 /* $NetBSD: eficons.c,v 1.13 2023/07/24 01:56:59 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/bitops.h>
30 #include <sys/stdint.h>
31
32 #include <comio_direct.h>
33
34 #include "efiboot.h"
35
36 #include "bootinfo.h"
37 #include "vbe.h"
38
39 #ifndef DEFAULT_GOP_MODE
40 #define DEFAULT_GOP_MODE "1024x768"
41 #endif
42 #define FALLBACK_GOP_MODE 0
43
44 extern struct x86_boot_params boot_params;
45
46 struct btinfo_console btinfo_console;
47
48 static EFI_GRAPHICS_OUTPUT_PROTOCOL *efi_gop;
49 static int efi_gop_mode = -1;
50 static CHAR16 keybuf[16];
51 static int keybuf_read = 0;
52 static int keybuf_write = 0;
53
54 static SERIAL_IO_INTERFACE *serios[4];
55 static int default_comspeed =
56 #if defined(CONSPEED)
57 CONSPEED;
58 #else
59 9600;
60 #endif
61 static u_char serbuf[16];
62 static int serbuf_read = 0;
63 static int serbuf_write = 0;
64
65 static int raw_com_addr = 0;
66
67 static void eficons_init_video(void);
68 static void efi_switch_video_to_text_mode(void);
69
70 static int efi_cons_getc(void);
71 static int efi_cons_putc(int);
72 static int efi_cons_iskey(int);
73 static int efi_cons_waitforinputevent(uint64_t);
74
75 static void efi_com_probe(void);
76 static bool efi_valid_com(int);
77 static int efi_com_init(int, int);
78 static int efi_com_getc(void);
79 static int efi_com_putc(int);
80 static int efi_com_status(int);
81 static int efi_com_waitforinputevent(uint64_t);
82
83 static int raw_com_init(int, int);
84 static int raw_com_getc(void);
85 static int raw_com_putc(int);
86 static int raw_com_status(int);
87 static int raw_com_waitforinputevent(uint64_t);
88
89 static int efi_find_gop_mode(char *);
90
91 static int iodev;
92 static int (*internal_getchar)(void) = efi_cons_getc;
93 static int (*internal_putchar)(int) = efi_cons_putc;
94 static int (*internal_iskey)(int) = efi_cons_iskey;
95 static int (*internal_waitforinputevent)(uint64_t) = efi_cons_waitforinputevent;
96
97 static int
getcomaddr(int idx)98 getcomaddr(int idx)
99 {
100 static const short comioport[4] = { 0x3f8, 0x2f8, 0x3e8, 0x2e8 };
101
102 if (idx < __arraycount(comioport))
103 return comioport[idx];
104 return 0;
105 }
106
107 /*
108 * XXX only pass console parameters to kernel.
109 */
110 void
efi_consinit(int dev,int ioport,int speed)111 efi_consinit(int dev, int ioport, int speed)
112 {
113 int i;
114
115 btinfo_console.speed = default_comspeed;
116
117 switch (dev) {
118 case CONSDEV_AUTO:
119 for (i = 0; i < __arraycount(serios); i++) {
120 iodev = CONSDEV_COM0 + i;
121 if (!efi_valid_com(iodev))
122 continue;
123 btinfo_console.addr = getcomaddr(i);
124
125 efi_cons_putc('0' + i);
126 efi_com_init(btinfo_console.addr, btinfo_console.speed);
127 /* check for:
128 * 1. successful output
129 * 2. optionally, keypress within 7s
130 */
131 if (efi_com_putc(':') &&
132 efi_com_putc('-') &&
133 efi_com_putc('(') &&
134 awaitkey(7, 0))
135 goto ok;
136 }
137 goto nocom;
138 ok:
139 break;
140
141 case CONSDEV_COM0:
142 case CONSDEV_COM1:
143 case CONSDEV_COM2:
144 case CONSDEV_COM3:
145 iodev = dev;
146 btinfo_console.addr = ioport;
147 if (btinfo_console.addr == 0)
148 btinfo_console.addr = getcomaddr(iodev - CONSDEV_COM0);
149 if (speed != 0)
150 btinfo_console.speed = speed;
151 efi_com_init(btinfo_console.addr, btinfo_console.speed);
152 break;
153
154 case CONSDEV_COM0KBD:
155 case CONSDEV_COM1KBD:
156 case CONSDEV_COM2KBD:
157 case CONSDEV_COM3KBD:
158 iodev = dev - CONSDEV_COM0KBD + CONSDEV_COM0;
159 btinfo_console.addr = getcomaddr(iodev - CONSDEV_COM0);
160
161 efi_cons_putc('0' + iodev - CONSDEV_COM0);
162 efi_com_init(btinfo_console.addr, btinfo_console.speed);
163 /* check for:
164 * 1. successful output
165 * 2. optionally, keypress within 7s
166 */
167 if (efi_com_putc(':') &&
168 efi_com_putc('-') &&
169 efi_com_putc('(') &&
170 awaitkey(7, 0))
171 goto kbd;
172 /*FALLTHROUGH*/
173 case CONSDEV_PC:
174 default:
175 nocom:
176 iodev = CONSDEV_PC;
177 internal_putchar = efi_cons_putc;
178 kbd:
179 internal_getchar = efi_cons_getc;
180 internal_iskey = efi_cons_iskey;
181 internal_waitforinputevent = efi_cons_waitforinputevent;
182 memset(keybuf, 0, sizeof(keybuf));
183 keybuf_read = keybuf_write = 0;
184 break;
185 }
186
187 strlcpy(btinfo_console.devname, iodev == CONSDEV_PC ? "pc" : "com", 16);
188 }
189
190 int
cninit(void)191 cninit(void)
192 {
193
194 efi_switch_video_to_text_mode();
195 eficons_init_video();
196 efi_com_probe();
197
198 efi_consinit(boot_params.bp_consdev, boot_params.bp_consaddr,
199 boot_params.bp_conspeed);
200
201 return 0;
202 }
203
204 void
efi_cons_show(void)205 efi_cons_show(void)
206 {
207 const bool pc_is_console = strcmp(btinfo_console.devname, "pc") == 0;
208 const bool com_is_console = strcmp(btinfo_console.devname, "com") == 0;
209 bool first = true;
210 bool found = false;
211 int i;
212
213 if (efi_gop != NULL) {
214 printf("pc");
215 if (pc_is_console)
216 printf("*");
217 first = false;
218 }
219
220 for (i = 0; i < __arraycount(serios); i++) {
221 if (serios[i] != NULL) {
222 if (!first)
223 printf(" ");
224 first = false;
225
226 printf("com%d", i);
227 if (com_is_console &&
228 btinfo_console.addr == getcomaddr(i)) {
229 printf(",%d*", btinfo_console.speed);
230 found = true;
231 }
232 }
233 }
234 if (!found && com_is_console) {
235 if (!first)
236 printf(" ");
237 first = false;
238
239 printf("com,0x%x,%d*", btinfo_console.addr,
240 btinfo_console.speed);
241 }
242
243 printf("\n");
244 }
245
246 static int
efi_cons_getc(void)247 efi_cons_getc(void)
248 {
249 EFI_STATUS status;
250 EFI_INPUT_KEY key;
251 int c;
252
253 if (keybuf_read != keybuf_write) {
254 c = keybuf[keybuf_read];
255 keybuf_read = (keybuf_read + 1) % __arraycount(keybuf);
256 return c;
257 }
258
259 status = uefi_call_wrapper(ST->ConIn->ReadKeyStroke, 2, ST->ConIn,
260 &key);
261 while (status == EFI_NOT_READY) {
262 WaitForSingleEvent(ST->ConIn->WaitForKey, 0);
263 status = uefi_call_wrapper(ST->ConIn->ReadKeyStroke, 2,
264 ST->ConIn, &key);
265 }
266 return key.UnicodeChar;
267 }
268
269 static int
efi_cons_putc(int c)270 efi_cons_putc(int c)
271 {
272 CHAR16 buf[2];
273
274 buf[0] = c;
275 buf[1] = 0;
276 Output(buf);
277
278 return 1;
279 }
280
281 /*ARGSUSED*/
282 static int
efi_cons_iskey(int intr)283 efi_cons_iskey(int intr)
284 {
285 EFI_STATUS status;
286 EFI_INPUT_KEY key;
287
288 if (keybuf_read != keybuf_write)
289 return 1;
290
291 status = uefi_call_wrapper(ST->ConIn->ReadKeyStroke, 2, ST->ConIn,
292 &key);
293 if (EFI_ERROR(status))
294 return 0;
295
296 keybuf[keybuf_write] = key.UnicodeChar;
297 keybuf_write = (keybuf_write + 1) % __arraycount(keybuf);
298 return 1;
299 }
300
301 static int
efi_cons_waitforinputevent(uint64_t timeout)302 efi_cons_waitforinputevent(uint64_t timeout)
303 {
304 EFI_STATUS status;
305
306 status = WaitForSingleEvent(ST->ConIn->WaitForKey, timeout);
307 if (!EFI_ERROR(status))
308 return 0;
309 if (status == EFI_TIMEOUT)
310 return ETIMEDOUT;
311 return EINVAL;
312 }
313
314 int
getchar(void)315 getchar(void)
316 {
317
318 return internal_getchar();
319 }
320
321 void
putchar(int c)322 putchar(int c)
323 {
324
325 if (c == '\n')
326 internal_putchar('\r');
327 internal_putchar(c);
328 }
329
330 int
iskey(int intr)331 iskey(int intr)
332 {
333
334 return internal_iskey(intr);
335 }
336
337 char
awaitkey(int timeout,int tell)338 awaitkey(int timeout, int tell)
339 {
340 char c = 0;
341
342 for (;;) {
343 char numbuf[32];
344 int len;
345
346 if (tell && timeout) {
347 len = snprintf(numbuf, sizeof(numbuf), "%d seconds. ",
348 timeout);
349 if (len > 0 && len < sizeof(numbuf)) {
350 char *p = numbuf;
351
352 printf("%s", numbuf);
353 while (*p)
354 *p++ = '\b';
355 }
356 }
357 if (iskey(1)) {
358 /* flush input buffer */
359 while (iskey(0))
360 c = getchar();
361 if (c == 0)
362 c = -1;
363 if (tell && timeout)
364 printf("%s", numbuf);
365 break;
366 }
367 if (timeout--)
368 internal_waitforinputevent(10000000);
369 else
370 break;
371 if (tell)
372 printf("%s", numbuf);
373 }
374
375 if (tell)
376 printf("0 seconds. \n");
377
378 return c;
379 }
380
381 void
clear_pc_screen(void)382 clear_pc_screen(void)
383 {
384
385 uefi_call_wrapper(ST->ConOut->ClearScreen, 1, ST->ConOut);
386 }
387
388 static uint8_t
getdepth(const EFI_GRAPHICS_OUTPUT_MODE_INFORMATION * info)389 getdepth(const EFI_GRAPHICS_OUTPUT_MODE_INFORMATION *info)
390 {
391
392 switch (info->PixelFormat) {
393 case PixelBlueGreenRedReserved8BitPerColor:
394 case PixelRedGreenBlueReserved8BitPerColor:
395 return 32;
396
397 case PixelBitMask:
398 return fls32(info->PixelInformation.RedMask
399 | info->PixelInformation.GreenMask
400 | info->PixelInformation.BlueMask
401 | info->PixelInformation.ReservedMask);
402
403 case PixelBltOnly:
404 case PixelFormatMax:
405 return 0;
406 }
407 return 0;
408 }
409
410 static void
setpixelformat(UINT32 mask,uint8_t * num,uint8_t * pos)411 setpixelformat(UINT32 mask, uint8_t *num, uint8_t *pos)
412 {
413 uint8_t n, p;
414
415 n = popcount32(mask);
416 p = ffs32(mask);
417 if (p > 0)
418 p--;
419
420 *num = n;
421 *pos = p;
422 }
423
424 static void
bi_framebuffer(void)425 bi_framebuffer(void)
426 {
427 EFI_STATUS status;
428 EFI_GRAPHICS_OUTPUT_MODE_INFORMATION *info;
429 struct btinfo_framebuffer fb;
430 INT32 bestmode;
431 UINTN sz;
432
433 if (efi_gop == NULL)
434 goto nofb;
435
436 if (efi_gop_mode >= 0) {
437 bestmode = efi_gop_mode;
438 } else {
439 /* If a mode has not been selected, choose a default */
440 bestmode = efi_find_gop_mode(DEFAULT_GOP_MODE);
441 if (bestmode == -1)
442 bestmode = FALLBACK_GOP_MODE;
443 }
444
445 status = uefi_call_wrapper(efi_gop->SetMode, 2, efi_gop,
446 bestmode);
447 if (EFI_ERROR(status) || efi_gop->Mode->Mode != bestmode) {
448 printf("GOP setmode failed: %" PRIxMAX "\n",
449 (uintmax_t)status);
450 goto nofb;
451 }
452
453 status = uefi_call_wrapper(efi_gop->QueryMode, 4,
454 efi_gop, bestmode, &sz, &info);
455 if (EFI_ERROR(status)) {
456 printf("GOP querymode failed: %" PRIxMAX "\n",
457 (uintmax_t)status);
458 goto nofb;
459 }
460
461 memset(&fb, 0, sizeof(fb));
462 fb.physaddr = efi_gop->Mode->FrameBufferBase;
463 fb.flags = 0;
464 fb.width = info->HorizontalResolution;
465 fb.height = info->VerticalResolution;
466 fb.depth = getdepth(info);
467 fb.stride = info->PixelsPerScanLine * ((fb.depth + 7) / 8);
468 fb.vbemode = 0; /* XXX */
469
470 switch (info->PixelFormat) {
471 case PixelBlueGreenRedReserved8BitPerColor:
472 fb.rnum = 8;
473 fb.gnum = 8;
474 fb.bnum = 8;
475 fb.rpos = 16;
476 fb.gpos = 8;
477 fb.bpos = 0;
478 break;
479
480 case PixelRedGreenBlueReserved8BitPerColor:
481 fb.rnum = 8;
482 fb.gnum = 8;
483 fb.bnum = 8;
484 fb.rpos = 0;
485 fb.gpos = 8;
486 fb.bpos = 16;
487 break;
488
489 case PixelBitMask:
490 setpixelformat(info->PixelInformation.RedMask,
491 &fb.rnum, &fb.rpos);
492 setpixelformat(info->PixelInformation.GreenMask,
493 &fb.gnum, &fb.gpos);
494 setpixelformat(info->PixelInformation.BlueMask,
495 &fb.bnum, &fb.bpos);
496 break;
497
498 case PixelBltOnly:
499 case PixelFormatMax:
500 panic("Error: invalid pixel format (%d)", info->PixelFormat);
501 break;
502 }
503
504 framebuffer_configure(&fb);
505 return;
506
507 nofb:
508 framebuffer_configure(NULL);
509 }
510
511 int
vbe_commit(void)512 vbe_commit(void)
513 {
514
515 bi_framebuffer();
516 return 0;
517 }
518
519 static void
print_text_modes(void)520 print_text_modes(void)
521 {
522 EFI_STATUS status;
523 UINTN cols, rows;
524 INT32 i, curmode;
525
526 curmode = ST->ConOut->Mode->Mode;
527 for (i = 0; i < ST->ConOut->Mode->MaxMode; i++) {
528 status = uefi_call_wrapper(ST->ConOut->QueryMode, 4,
529 ST->ConOut, i, &cols, &rows);
530 if (EFI_ERROR(status))
531 continue;
532 printf("%c%d: %" PRIxMAX "x%" PRIxMAX "\n",
533 i == curmode ? '*' : ' ', i, (uintmax_t)cols, (uintmax_t)rows);
534 }
535 }
536
537 static int
efi_find_text_mode(char * arg)538 efi_find_text_mode(char *arg)
539 {
540 EFI_STATUS status;
541 UINTN cols, rows;
542 INT32 i;
543 char mode[32];
544
545 for (i = 0; i < ST->ConOut->Mode->MaxMode; i++) {
546 status = uefi_call_wrapper(ST->ConOut->QueryMode, 4,
547 ST->ConOut, i, &cols, &rows);
548 if (EFI_ERROR(status))
549 continue;
550 snprintf(mode, sizeof(mode), "%" PRIuMAX "x%" PRIuMAX,
551 (uintmax_t)cols, (uintmax_t)rows);
552 if (strcmp(arg, mode) == 0)
553 return i;
554 }
555 return -1;
556 }
557
558 void
command_text(char * arg)559 command_text(char *arg)
560 {
561 EFI_STATUS status;
562 INT32 modenum;
563
564 if (*arg == '\0' || strcmp(arg, "list") == 0) {
565 print_text_modes();
566 return;
567 }
568
569 if (strchr(arg, 'x') != NULL) {
570 modenum = efi_find_text_mode(arg);
571 if (modenum == -1) {
572 printf("mode %s not supported by firmware\n", arg);
573 return;
574 }
575 } else {
576 modenum = strtoul(arg, NULL, 0);
577 }
578
579 status = uefi_call_wrapper(ST->ConOut->SetMode, 2, ST->ConOut, modenum);
580 if (!EFI_ERROR(status))
581 return;
582
583 printf("invalid flag, must be 'list', a display mode, "
584 "or a mode number\n");
585 }
586
587 static int
print_gop_modes(void)588 print_gop_modes(void)
589 {
590 EFI_STATUS status;
591 EFI_GRAPHICS_OUTPUT_MODE_INFORMATION *info;
592 UINTN sz;
593 UINT32 i;
594 uint8_t depth;
595
596 if (efi_gop == NULL)
597 return 1;
598
599 for (i = 0; i < efi_gop->Mode->MaxMode; i++) {
600 status = uefi_call_wrapper(efi_gop->QueryMode, 4, efi_gop, i,
601 &sz, &info);
602 if (EFI_ERROR(status) && status == EFI_NOT_STARTED) {
603 status = uefi_call_wrapper(efi_gop->SetMode, 2,
604 efi_gop, efi_gop->Mode->Mode);
605 status = uefi_call_wrapper(efi_gop->QueryMode, 4,
606 efi_gop, i, &sz, &info);
607 }
608 if (EFI_ERROR(status))
609 continue;
610
611 printf("%c%d: %dx%d ",
612 memcmp(info, efi_gop->Mode->Info, sizeof(*info)) == 0 ?
613 '*' : ' ',
614 i, info->HorizontalResolution, info->VerticalResolution);
615 switch (info->PixelFormat) {
616 case PixelRedGreenBlueReserved8BitPerColor:
617 printf("RGBR");
618 break;
619 case PixelBlueGreenRedReserved8BitPerColor:
620 printf("BGRR");
621 break;
622 case PixelBitMask:
623 printf("R:%08x G:%08x B:%08x X:%08x",
624 info->PixelInformation.RedMask,
625 info->PixelInformation.GreenMask,
626 info->PixelInformation.BlueMask,
627 info->PixelInformation.ReservedMask);
628 break;
629 case PixelBltOnly:
630 printf("(blt only)");
631 break;
632 default:
633 printf("(Invalid pixel format)");
634 break;
635 }
636 printf(" pitch %d", info->PixelsPerScanLine);
637 depth = getdepth(info);
638 if (depth > 0)
639 printf(" bpp %d", depth);
640 printf("\n");
641 }
642
643 return 0;
644 }
645
646 static int
efi_find_gop_mode(char * arg)647 efi_find_gop_mode(char *arg)
648 {
649 EFI_STATUS status;
650 EFI_GRAPHICS_OUTPUT_MODE_INFORMATION *info;
651 UINTN sz;
652 UINT32 i;
653 char mode[32];
654 uint8_t depth;
655
656 for (i = 0; i < efi_gop->Mode->MaxMode; i++) {
657 status = uefi_call_wrapper(efi_gop->QueryMode, 4, efi_gop, i,
658 &sz, &info);
659 if (EFI_ERROR(status))
660 continue;
661
662 depth = getdepth(info);
663 if (depth == 0)
664 continue;
665
666 snprintf(mode, sizeof(mode), "%lux%lux%u",
667 (long)info->HorizontalResolution,
668 (long)info->VerticalResolution,
669 depth);
670 if (strcmp(arg, mode) == 0)
671 return i;
672
673 snprintf(mode, sizeof(mode), "%lux%lu",
674 (long)info->HorizontalResolution,
675 (long)info->VerticalResolution);
676 if (strcmp(arg, mode) == 0)
677 return i;
678 }
679 return -1;
680 }
681
682 void
command_gop(char * arg)683 command_gop(char *arg)
684 {
685 EFI_STATUS status;
686 INT32 modenum;
687
688 if (efi_gop == NULL) {
689 printf("GOP not supported by firmware\n");
690 return;
691 }
692
693 if (*arg == '\0' || strcmp(arg, "list") == 0) {
694 print_gop_modes();
695 return;
696 }
697
698 if (strchr(arg, 'x') != NULL) {
699 modenum = efi_find_gop_mode(arg);
700 if (modenum == -1) {
701 printf("mode %s not supported by firmware\n", arg);
702 return;
703 }
704 } else {
705 modenum = strtoul(arg, NULL, 0);
706 }
707
708 status = uefi_call_wrapper(efi_gop->SetMode, 2, efi_gop, modenum);
709 if (!EFI_ERROR(status) && efi_gop->Mode->Mode == modenum) {
710 efi_gop_mode = modenum;
711 return;
712 }
713
714 printf("invalid flag, must be 'list', a display mode, "
715 "or a mode number\n");
716 }
717
718 static void
eficons_init_video(void)719 eficons_init_video(void)
720 {
721 EFI_STATUS status;
722 UINTN cols, rows;
723 INT32 i, best, mode80x25, mode100x31;
724
725 /*
726 * Setup text mode
727 */
728 uefi_call_wrapper(ST->ConOut->Reset, 2, ST->ConOut, TRUE);
729
730 mode80x25 = mode100x31 = -1;
731 for (i = 0; i < ST->ConOut->Mode->MaxMode; i++) {
732 status = uefi_call_wrapper(ST->ConOut->QueryMode, 4,
733 ST->ConOut, i, &cols, &rows);
734 if (EFI_ERROR(status))
735 continue;
736
737 if (mode80x25 < 0 && cols == 80 && rows == 25)
738 mode80x25 = i;
739 else if (mode100x31 < 0 && cols == 100 && rows == 31)
740 mode100x31 = i;
741 }
742 best = mode100x31 >= 0 ? mode100x31 : mode80x25 >= 0 ? mode80x25 : -1;
743 if (best >= 0)
744 uefi_call_wrapper(ST->ConOut->SetMode, 2, ST->ConOut, best);
745 uefi_call_wrapper(ST->ConOut->EnableCursor, 2, ST->ConOut, TRUE);
746 uefi_call_wrapper(ST->ConOut->ClearScreen, 1, ST->ConOut);
747
748 LibLocateProtocol(&GraphicsOutputProtocol, (void **)&efi_gop);
749 }
750
751 /*
752 * for Apple EFI
753 */
754 #define CONSOLE_CONTROL_PROTOCOL \
755 {0xf42f7782, 0x12e, 0x4c12, {0x99, 0x56, 0x49, 0xf9, 0x43, 0x4, 0xf7, 0x21}}
756 static EFI_GUID ConsoleControlProtocol = CONSOLE_CONTROL_PROTOCOL;
757
758 struct _EFI_CONSOLE_CONTROL_INTERFACE;
759 typedef struct _EFI_CONSOLE_CONTROL_INTERFACE EFI_CONSOLE_CONTROL_INTERFACE;
760 typedef enum { EfiConsoleControlScreenText } EFI_CONSOLE_CONTROL_SCREEN_MODE;
761 typedef EFI_STATUS (EFIAPI *EFI_CONSOLE_CONTROL_PROTOCOL_SET_MODE) (
762 IN EFI_CONSOLE_CONTROL_INTERFACE *This,
763 IN EFI_CONSOLE_CONTROL_SCREEN_MODE Mode
764 );
765 struct _EFI_CONSOLE_CONTROL_INTERFACE {
766 VOID *GetMode;
767 EFI_CONSOLE_CONTROL_PROTOCOL_SET_MODE SetMode;
768 VOID *LockStdIn;
769 };
770
771 static void
efi_switch_video_to_text_mode(void)772 efi_switch_video_to_text_mode(void)
773 {
774 EFI_STATUS status;
775 EFI_CONSOLE_CONTROL_INTERFACE *cci;
776
777 /* Set up the console, so printf works. */
778 status = LibLocateProtocol(&ConsoleControlProtocol, (void **)&cci);
779 if (!EFI_ERROR(status)) {
780 uefi_call_wrapper(cci->SetMode, 2, cci,
781 EfiConsoleControlScreenText);
782 }
783 }
784
785 /*
786 * serial port
787 */
788 static void
efi_com_probe(void)789 efi_com_probe(void)
790 {
791 EFI_STATUS status;
792 UINTN i, nhandles;
793 EFI_HANDLE *handles;
794 EFI_DEVICE_PATH *dp, *dp0;
795 EFI_DEV_PATH_PTR dpp;
796 SERIAL_IO_INTERFACE *serio;
797 int uid = -1;
798
799 status = LibLocateHandle(ByProtocol, &SerialIoProtocol, NULL,
800 &nhandles, &handles);
801 if (EFI_ERROR(status))
802 return;
803
804 for (i = 0; i < nhandles; i++) {
805 /*
806 * Identify port number of the handle. This assumes ACPI
807 * UID 0-3 map to legacy COM[1-4] and they use the legacy
808 * port address.
809 */
810 status = uefi_call_wrapper(BS->HandleProtocol, 3, handles[i],
811 &DevicePathProtocol, (void **)&dp0);
812 if (EFI_ERROR(status))
813 continue;
814
815 for (uid = -1, dp = dp0;
816 !IsDevicePathEnd(dp);
817 dp = NextDevicePathNode(dp)) {
818
819 if (DevicePathType(dp) == ACPI_DEVICE_PATH &&
820 DevicePathSubType(dp) == ACPI_DP) {
821 dpp = (EFI_DEV_PATH_PTR)dp;
822 if (dpp.Acpi->HID == EISA_PNP_ID(0x0501)) {
823 uid = dpp.Acpi->UID;
824 break;
825 }
826 }
827 }
828 if (uid < 0 || __arraycount(serios) <= uid)
829 continue;
830
831 /* Prepare SERIAL_IO_INTERFACE */
832 status = uefi_call_wrapper(BS->HandleProtocol, 3, handles[i],
833 &SerialIoProtocol, (void **)&serio);
834 if (EFI_ERROR(status))
835 continue;
836
837 serios[uid] = serio;
838 }
839
840 FreePool(handles);
841
842 }
843
844 static bool
efi_valid_com(int dev)845 efi_valid_com(int dev)
846 {
847 int idx;
848
849 switch (dev) {
850 default:
851 case CONSDEV_PC:
852 return false;
853
854 case CONSDEV_COM0:
855 case CONSDEV_COM1:
856 case CONSDEV_COM2:
857 case CONSDEV_COM3:
858 idx = dev - CONSDEV_COM0;
859 break;
860 }
861
862 return idx < __arraycount(serios) &&
863 serios[idx] != NULL &&
864 getcomaddr(idx) != 0;
865 }
866
867 static int
efi_com_init(int addr,int speed)868 efi_com_init(int addr, int speed)
869 {
870 EFI_STATUS status;
871 SERIAL_IO_INTERFACE *serio;
872
873 if (speed <= 0)
874 return 0;
875
876 if (!efi_valid_com(iodev))
877 return raw_com_init(addr, speed);
878
879 serio = serios[iodev - CONSDEV_COM0];
880
881 if (serio->Mode->BaudRate != btinfo_console.speed) {
882 status = uefi_call_wrapper(serio->SetAttributes, 7, serio,
883 speed, serio->Mode->ReceiveFifoDepth,
884 serio->Mode->Timeout, serio->Mode->Parity,
885 serio->Mode->DataBits, serio->Mode->StopBits);
886 if (EFI_ERROR(status)) {
887 printf("com%d: SetAttribute() failed with status=%" PRIxMAX
888 "\n", iodev - CONSDEV_COM0, (uintmax_t)status);
889 return 0;
890 }
891 }
892
893 raw_com_addr = 0;
894 default_comspeed = speed;
895 internal_getchar = efi_com_getc;
896 internal_putchar = efi_com_putc;
897 internal_iskey = efi_com_status;
898 internal_waitforinputevent = efi_com_waitforinputevent;
899 memset(serbuf, 0, sizeof(serbuf));
900 serbuf_read = serbuf_write = 0;
901
902 return speed;
903 }
904
905 static int
efi_com_getc(void)906 efi_com_getc(void)
907 {
908 EFI_STATUS status;
909 SERIAL_IO_INTERFACE *serio;
910 UINTN sz;
911 u_char c;
912
913 if (!efi_valid_com(iodev))
914 panic("Invalid serial port: iodev=%d", iodev);
915
916 if (serbuf_read != serbuf_write) {
917 c = serbuf[serbuf_read];
918 serbuf_read = (serbuf_read + 1) % __arraycount(serbuf);
919 return c;
920 }
921
922 serio = serios[iodev - CONSDEV_COM0];
923
924 for (;;) {
925 sz = 1;
926 status = uefi_call_wrapper(serio->Read, 3, serio, &sz, &c);
927 if (!EFI_ERROR(status) && sz > 0)
928 break;
929 if (status != EFI_TIMEOUT && EFI_ERROR(status))
930 panic("Error reading from serial status=%"PRIxMAX,
931 (uintmax_t)status);
932 }
933 return c;
934 }
935
936 static int
efi_com_putc(int c)937 efi_com_putc(int c)
938 {
939 EFI_STATUS status;
940 SERIAL_IO_INTERFACE *serio;
941 UINTN sz = 1;
942 u_char buf;
943
944 if (!efi_valid_com(iodev))
945 return 0;
946
947 serio = serios[iodev - CONSDEV_COM0];
948 buf = c;
949 status = uefi_call_wrapper(serio->Write, 3, serio, &sz, &buf);
950 if (EFI_ERROR(status) || sz < 1)
951 return 0;
952 return 1;
953 }
954
955 /*ARGSUSED*/
956 static int
efi_com_status(int intr)957 efi_com_status(int intr)
958 {
959 EFI_STATUS status;
960 SERIAL_IO_INTERFACE *serio;
961 UINTN sz;
962 u_char c;
963
964 if (!efi_valid_com(iodev))
965 panic("Invalid serial port: iodev=%d", iodev);
966
967 if (serbuf_read != serbuf_write)
968 return 1;
969
970 serio = serios[iodev - CONSDEV_COM0];
971 sz = 1;
972 status = uefi_call_wrapper(serio->Read, 3, serio, &sz, &c);
973 if (EFI_ERROR(status) || sz < 1)
974 return 0;
975
976 serbuf[serbuf_write] = c;
977 serbuf_write = (serbuf_write + 1) % __arraycount(serbuf);
978 return 1;
979 }
980
981 static void
efi_com_periodic_event(EFI_EVENT event,void * ctx)982 efi_com_periodic_event(EFI_EVENT event, void *ctx)
983 {
984 EFI_EVENT timer = ctx;
985
986 if (efi_com_status(0)) {
987 uefi_call_wrapper(BS->SetTimer, 3, event, TimerCancel, 0);
988 uefi_call_wrapper(BS->SignalEvent, 1, timer);
989 }
990 }
991
992 static int
efi_com_waitforinputevent(uint64_t timeout)993 efi_com_waitforinputevent(uint64_t timeout)
994 {
995 EFI_STATUS status;
996 EFI_EVENT timer, periodic;
997
998 status = uefi_call_wrapper(BS->CreateEvent, 5, EVT_TIMER, 0, NULL, NULL,
999 &timer);
1000 if (EFI_ERROR(status))
1001 return EINVAL;
1002
1003 status = uefi_call_wrapper(BS->CreateEvent, 5,
1004 EVT_TIMER | EVT_NOTIFY_SIGNAL, TPL_CALLBACK, efi_com_periodic_event,
1005 timer, &periodic);
1006 if (EFI_ERROR(status)) {
1007 uefi_call_wrapper(BS->CloseEvent, 1, timer);
1008 return EINVAL;
1009 }
1010
1011 status = uefi_call_wrapper(BS->SetTimer, 3, periodic, TimerPeriodic,
1012 1000000); /* 100ms */
1013 if (EFI_ERROR(status)) {
1014 uefi_call_wrapper(BS->CloseEvent, 1, periodic);
1015 uefi_call_wrapper(BS->CloseEvent, 1, timer);
1016 return EINVAL;
1017 }
1018 status = WaitForSingleEvent(&timer, timeout);
1019 uefi_call_wrapper(BS->SetTimer, 3, periodic, TimerCancel, 0);
1020 uefi_call_wrapper(BS->CloseEvent, 1, periodic);
1021 uefi_call_wrapper(BS->CloseEvent, 1, timer);
1022 if (!EFI_ERROR(status))
1023 return 0;
1024 if (status == EFI_TIMEOUT)
1025 return ETIMEDOUT;
1026 return EINVAL;
1027 }
1028
1029 static int
raw_com_init(int addr,int speed)1030 raw_com_init(int addr, int speed)
1031 {
1032
1033 if (addr == 0 || speed <= 0)
1034 return 0;
1035
1036 speed = cominit_d(addr, speed);
1037
1038 raw_com_addr = addr;
1039 default_comspeed = speed;
1040 internal_getchar = raw_com_getc;
1041 internal_putchar = raw_com_putc;
1042 internal_iskey = raw_com_status;
1043 internal_waitforinputevent = raw_com_waitforinputevent;
1044
1045 return speed;
1046 }
1047
1048 static int
raw_com_getc(void)1049 raw_com_getc(void)
1050 {
1051
1052 if (raw_com_addr == 0)
1053 panic("%s: Invalid serial port", __func__);
1054 return comgetc_d(raw_com_addr);
1055 }
1056
1057 static int
raw_com_putc(int c)1058 raw_com_putc(int c)
1059 {
1060
1061 if (raw_com_addr == 0)
1062 panic("%s: Invalid serial port", __func__);
1063 return computc_d(c, raw_com_addr);
1064 }
1065
1066 static int
raw_com_status(int intr)1067 raw_com_status(int intr)
1068 {
1069
1070 if (raw_com_addr == 0)
1071 panic("%s: Invalid serial port", __func__);
1072 return comstatus_d(raw_com_addr);
1073 }
1074
1075 static int
raw_com_waitforinputevent(uint64_t timeout)1076 raw_com_waitforinputevent(uint64_t timeout /* in 0.1 usec */)
1077 {
1078 uint64_t ms;
1079
1080 if (raw_com_addr == 0)
1081 panic("%s: Invalid serial port", __func__);
1082
1083 for (ms = howmany(timeout, 10 * 1000); ms != 0; ms--) {
1084 if (raw_com_status(0))
1085 return 0;
1086 uefi_call_wrapper(BS->Stall, 1, 1000);
1087 }
1088 return ETIMEDOUT;
1089 }
1090