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