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