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