eficons.c revision 1.6 1 /* $NetBSD: eficons.c,v 1.6 2018/05/16 19:53:54 jakllsch 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 char numbuf[32];
332 int len;
333
334 if (tell && timeout) {
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 }
344 }
345 if (iskey(1)) {
346 /* flush input buffer */
347 while (iskey(0))
348 c = getchar();
349 if (c == 0)
350 c = -1;
351 goto out;
352 }
353 if (timeout--)
354 internal_waitforinputevent(10000000);
355 else
356 break;
357 if (tell)
358 printf("%s", numbuf);
359 }
360
361 out:
362 if (tell)
363 printf("0 seconds. \n");
364
365 return c;
366 }
367
368 void
369 clear_pc_screen(void)
370 {
371
372 uefi_call_wrapper(ST->ConOut->ClearScreen, 1, ST->ConOut);
373 }
374
375 static uint8_t
376 getdepth(const EFI_GRAPHICS_OUTPUT_MODE_INFORMATION *info)
377 {
378
379 switch (info->PixelFormat) {
380 case PixelBlueGreenRedReserved8BitPerColor:
381 case PixelRedGreenBlueReserved8BitPerColor:
382 return 32;
383
384 case PixelBitMask:
385 return fls32(info->PixelInformation.RedMask
386 | info->PixelInformation.GreenMask
387 | info->PixelInformation.BlueMask
388 | info->PixelInformation.ReservedMask);
389
390 case PixelBltOnly:
391 case PixelFormatMax:
392 return 0;
393 }
394 return 0;
395 }
396
397 static void
398 setpixelformat(UINT32 mask, uint8_t *num, uint8_t *pos)
399 {
400 uint8_t n, p;
401
402 n = popcount32(mask);
403 p = ffs32(mask);
404 if (p > 0)
405 p--;
406
407 *num = n;
408 *pos = p;
409 }
410
411 static void
412 bi_framebuffer(void)
413 {
414 EFI_STATUS status;
415 EFI_GRAPHICS_OUTPUT_MODE_INFORMATION *info;
416 struct btinfo_framebuffer fb;
417 INT32 bestmode = -1;
418
419 if (efi_gop == NULL) {
420 framebuffer_configure(NULL);
421 return;
422 }
423
424 if (efi_gop_mode >= 0) {
425 bestmode = efi_gop_mode;
426 } else {
427 #if 0
428 UINT64 res, bestres = 0;
429 UINTN sz;
430 UINT32 i;
431
432 /* XXX EDID? EFI_EDID_DISCOVERED_PROTOCOL */
433 for (i = 0; i < efi_gop->Mode->MaxMode; i++) {
434 status = uefi_call_wrapper(efi_gop->QueryMode, 4,
435 efi_gop, i, &sz, &info);
436 if (EFI_ERROR(status))
437 continue;
438
439 res = (UINT64)info->HorizontalResolution *
440 (UINT64)info->VerticalResolution *
441 (UINT64)getdepth(info);
442 if (res > bestres) {
443 bestmode = i;
444 bestres = res;
445 }
446 }
447 #endif
448 }
449 if (bestmode >= 0) {
450 status = uefi_call_wrapper(efi_gop->SetMode, 2, efi_gop,
451 bestmode);
452 if (EFI_ERROR(status) || efi_gop->Mode->Mode != bestmode)
453 printf("GOP setmode failed: %" PRIxMAX "\n",
454 (uintmax_t)status);
455 }
456
457 info = efi_gop->Mode->Info;
458 memset(&fb, 0, sizeof(fb));
459 fb.physaddr = efi_gop->Mode->FrameBufferBase;
460 fb.flags = 0;
461 fb.width = info->HorizontalResolution;
462 fb.height = info->VerticalResolution;
463 fb.depth = getdepth(info);
464 fb.stride = info->PixelsPerScanLine * ((fb.depth + 7) / 8);
465 fb.vbemode = 0; /* XXX */
466
467 switch (info->PixelFormat) {
468 case PixelBlueGreenRedReserved8BitPerColor:
469 fb.rnum = 8;
470 fb.gnum = 8;
471 fb.bnum = 8;
472 fb.rpos = 16;
473 fb.gpos = 8;
474 fb.bpos = 0;
475 break;
476
477 case PixelRedGreenBlueReserved8BitPerColor:
478 fb.rnum = 8;
479 fb.gnum = 8;
480 fb.bnum = 8;
481 fb.rpos = 0;
482 fb.gpos = 8;
483 fb.bpos = 16;
484 break;
485
486 case PixelBitMask:
487 setpixelformat(info->PixelInformation.RedMask,
488 &fb.rnum, &fb.rpos);
489 setpixelformat(info->PixelInformation.GreenMask,
490 &fb.gnum, &fb.gpos);
491 setpixelformat(info->PixelInformation.BlueMask,
492 &fb.bnum, &fb.bpos);
493 break;
494
495 case PixelBltOnly:
496 case PixelFormatMax:
497 panic("Error: invalid pixel format (%d)", info->PixelFormat);
498 break;
499 }
500
501 framebuffer_configure(&fb);
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->HorizontalResolution,
662 depth);
663 if (strcmp(arg, mode) == 0)
664 return i;
665 }
666 return -1;
667 }
668
669 void
670 command_gop(char *arg)
671 {
672 EFI_STATUS status;
673 INT32 modenum;
674
675 if (efi_gop == NULL) {
676 printf("GOP not supported by firmware\n");
677 return;
678 }
679
680 if (*arg == '\0' || strcmp(arg, "list") == 0) {
681 print_gop_modes();
682 return;
683 }
684
685 if (strchr(arg, 'x') != NULL) {
686 modenum = efi_find_gop_mode(arg);
687 if (modenum == -1) {
688 printf("mode %s not supported by firmware\n", arg);
689 return;
690 }
691 } else {
692 modenum = strtoul(arg, NULL, 0);
693 }
694
695 status = uefi_call_wrapper(efi_gop->SetMode, 2, efi_gop, modenum);
696 if (!EFI_ERROR(status) && efi_gop->Mode->Mode == modenum) {
697 efi_gop_mode = modenum;
698 return;
699 }
700
701 printf("invalid flag, must be 'list', a display mode, "
702 "or a mode number\n");
703 }
704
705 static void
706 eficons_init_video(void)
707 {
708 EFI_STATUS status;
709 UINTN cols, rows;
710 INT32 i, best, mode80x25, mode100x31;
711
712 /*
713 * Setup text mode
714 */
715 uefi_call_wrapper(ST->ConOut->Reset, 2, ST->ConOut, TRUE);
716
717 mode80x25 = mode100x31 = -1;
718 for (i = 0; i < ST->ConOut->Mode->MaxMode; i++) {
719 status = uefi_call_wrapper(ST->ConOut->QueryMode, 4,
720 ST->ConOut, i, &cols, &rows);
721 if (EFI_ERROR(status))
722 continue;
723
724 if (mode80x25 < 0 && cols == 80 && rows == 25)
725 mode80x25 = i;
726 else if (mode100x31 < 0 && cols == 100 && rows == 31)
727 mode100x31 = i;
728 }
729 best = mode100x31 >= 0 ? mode100x31 : mode80x25 >= 0 ? mode80x25 : -1;
730 if (best >= 0)
731 uefi_call_wrapper(ST->ConOut->SetMode, 2, ST->ConOut, best);
732 uefi_call_wrapper(ST->ConOut->EnableCursor, 2, ST->ConOut, TRUE);
733 uefi_call_wrapper(ST->ConOut->ClearScreen, 1, ST->ConOut);
734
735 LibLocateProtocol(&GraphicsOutputProtocol, (void **)&efi_gop);
736 }
737
738 /*
739 * for Apple EFI
740 */
741 #define CONSOLE_CONTROL_PROTOCOL \
742 {0xf42f7782, 0x12e, 0x4c12, {0x99, 0x56, 0x49, 0xf9, 0x43, 0x4, 0xf7, 0x21}}
743 static EFI_GUID ConsoleControlProtocol = CONSOLE_CONTROL_PROTOCOL;
744
745 struct _EFI_CONSOLE_CONTROL_INTERFACE;
746 typedef struct _EFI_CONSOLE_CONTROL_INTERFACE EFI_CONSOLE_CONTROL_INTERFACE;
747 typedef enum { EfiConsoleControlScreenText } EFI_CONSOLE_CONTROL_SCREEN_MODE;
748 typedef EFI_STATUS (EFIAPI *EFI_CONSOLE_CONTROL_PROTOCOL_SET_MODE) (
749 IN EFI_CONSOLE_CONTROL_INTERFACE *This,
750 IN EFI_CONSOLE_CONTROL_SCREEN_MODE Mode
751 );
752 struct _EFI_CONSOLE_CONTROL_INTERFACE {
753 VOID *GetMode;
754 EFI_CONSOLE_CONTROL_PROTOCOL_SET_MODE SetMode;
755 VOID *LockStdIn;
756 };
757
758 static void
759 efi_switch_video_to_text_mode(void)
760 {
761 EFI_STATUS status;
762 EFI_CONSOLE_CONTROL_INTERFACE *cci;
763
764 /* Set up the console, so printf works. */
765 status = LibLocateProtocol(&ConsoleControlProtocol, (void **)&cci);
766 if (!EFI_ERROR(status)) {
767 uefi_call_wrapper(cci->SetMode, 2, cci,
768 EfiConsoleControlScreenText);
769 }
770 }
771
772 /*
773 * serial port
774 */
775 static void
776 efi_com_probe(void)
777 {
778 EFI_STATUS status;
779 UINTN i, nhandles;
780 EFI_HANDLE *handles;
781 EFI_DEVICE_PATH *dp, *dp0;
782 EFI_DEV_PATH_PTR dpp;
783 SERIAL_IO_INTERFACE *serio;
784 int uid = -1;
785
786 status = LibLocateHandle(ByProtocol, &SerialIoProtocol, NULL,
787 &nhandles, &handles);
788 if (EFI_ERROR(status))
789 return;
790
791 for (i = 0; i < nhandles; i++) {
792 /*
793 * Identify port number of the handle. This assumes ACPI
794 * UID 0-3 map to legacy COM[1-4] and they use the legacy
795 * port address.
796 */
797 status = uefi_call_wrapper(BS->HandleProtocol, 3, handles[i],
798 &DevicePathProtocol, (void **)&dp0);
799 if (EFI_ERROR(status))
800 continue;
801
802 for (uid = -1, dp = dp0;
803 !IsDevicePathEnd(dp);
804 dp = NextDevicePathNode(dp)) {
805
806 if (DevicePathType(dp) == ACPI_DEVICE_PATH &&
807 DevicePathSubType(dp) == ACPI_DP) {
808 dpp = (EFI_DEV_PATH_PTR)dp;
809 if (dpp.Acpi->HID == EISA_PNP_ID(0x0501)) {
810 uid = dpp.Acpi->UID;
811 break;
812 }
813 }
814 }
815 if (uid < 0 || __arraycount(serios) <= uid)
816 continue;
817
818 /* Prepare SERIAL_IO_INTERFACE */
819 status = uefi_call_wrapper(BS->HandleProtocol, 3, handles[i],
820 &SerialIoProtocol, (void **)&serio);
821 if (EFI_ERROR(status))
822 continue;
823
824 serios[uid] = serio;
825 }
826
827 FreePool(handles);
828
829 }
830
831 static bool
832 efi_valid_com(int dev)
833 {
834 int idx;
835
836 switch (dev) {
837 default:
838 case CONSDEV_PC:
839 return false;
840
841 case CONSDEV_COM0:
842 case CONSDEV_COM1:
843 case CONSDEV_COM2:
844 case CONSDEV_COM3:
845 idx = dev - CONSDEV_COM0;
846 break;
847 }
848
849 return idx < __arraycount(serios) &&
850 serios[idx] != NULL &&
851 getcomaddr(idx) != 0;
852 }
853
854 static int
855 efi_com_init(int addr, int speed)
856 {
857 EFI_STATUS status;
858 SERIAL_IO_INTERFACE *serio;
859
860 if (speed <= 0)
861 return 0;
862
863 if (!efi_valid_com(iodev))
864 return 0;
865
866 serio = serios[iodev - CONSDEV_COM0];
867
868 if (serio->Mode->BaudRate != btinfo_console.speed) {
869 status = uefi_call_wrapper(serio->SetAttributes, 7, serio,
870 speed, serio->Mode->ReceiveFifoDepth,
871 serio->Mode->Timeout, serio->Mode->Parity,
872 serio->Mode->DataBits, serio->Mode->StopBits);
873 if (EFI_ERROR(status)) {
874 printf("com%d: SetAttribute() failed with status=%" PRIxMAX
875 "\n", iodev - CONSDEV_COM0, (uintmax_t)status);
876 return 0;
877 }
878 }
879
880 default_comspeed = speed;
881 internal_getchar = efi_com_getc;
882 internal_putchar = efi_com_putc;
883 internal_iskey = efi_com_status;
884 internal_waitforinputevent = efi_com_waitforinputevent;
885 memset(serbuf, 0, sizeof(serbuf));
886 serbuf_read = serbuf_write = 0;
887
888 return speed;
889 }
890
891 static int
892 efi_com_getc(void)
893 {
894 EFI_STATUS status;
895 SERIAL_IO_INTERFACE *serio;
896 UINTN sz;
897 u_char c;
898
899 if (!efi_valid_com(iodev))
900 panic("Invalid serial port: iodev=%d", iodev);
901
902 if (serbuf_read != serbuf_write) {
903 c = serbuf[serbuf_read];
904 serbuf_read = (serbuf_read + 1) % __arraycount(serbuf);
905 return c;
906 }
907
908 serio = serios[iodev - CONSDEV_COM0];
909
910 for (;;) {
911 sz = 1;
912 status = uefi_call_wrapper(serio->Read, 3, serio, &sz, &c);
913 if (!EFI_ERROR(status) && sz > 0)
914 break;
915 if (status != EFI_TIMEOUT && EFI_ERROR(status))
916 panic("Error reading from serial status=%"PRIxMAX,
917 (uintmax_t)status);
918 }
919 return c;
920 }
921
922 static int
923 efi_com_putc(int c)
924 {
925 EFI_STATUS status;
926 SERIAL_IO_INTERFACE *serio;
927 UINTN sz = 1;
928 u_char buf;
929
930 if (!efi_valid_com(iodev))
931 return 0;
932
933 serio = serios[iodev - CONSDEV_COM0];
934 buf = c;
935 status = uefi_call_wrapper(serio->Write, 3, serio, &sz, &buf);
936 if (EFI_ERROR(status) || sz < 1)
937 return 0;
938 return 1;
939 }
940
941 /*ARGSUSED*/
942 static int
943 efi_com_status(int intr)
944 {
945 EFI_STATUS status;
946 SERIAL_IO_INTERFACE *serio;
947 UINTN sz;
948 u_char c;
949
950 if (!efi_valid_com(iodev))
951 panic("Invalid serial port: iodev=%d", iodev);
952
953 if (serbuf_read != serbuf_write)
954 return 1;
955
956 serio = serios[iodev - CONSDEV_COM0];
957 sz = 1;
958 status = uefi_call_wrapper(serio->Read, 3, serio, &sz, &c);
959 if (EFI_ERROR(status) || sz < 1)
960 return 0;
961
962 serbuf[serbuf_write] = c;
963 serbuf_write = (serbuf_write + 1) % __arraycount(serbuf);
964 return 1;
965 }
966
967 static void
968 efi_com_periodic_event(EFI_EVENT event, void *ctx)
969 {
970 EFI_EVENT timer = ctx;
971
972 if (efi_com_status(0)) {
973 uefi_call_wrapper(BS->SetTimer, 3, event, TimerCancel, 0);
974 uefi_call_wrapper(BS->SignalEvent, 1, timer);
975 }
976 }
977
978 static int
979 efi_com_waitforinputevent(uint64_t timeout)
980 {
981 EFI_STATUS status;
982 EFI_EVENT timer, periodic;
983
984 status = uefi_call_wrapper(BS->CreateEvent, 5, EVT_TIMER, 0, NULL, NULL,
985 &timer);
986 if (EFI_ERROR(status))
987 return EINVAL;
988
989 status = uefi_call_wrapper(BS->CreateEvent, 5,
990 EVT_TIMER | EVT_NOTIFY_SIGNAL, TPL_CALLBACK, efi_com_periodic_event,
991 timer, &periodic);
992 if (EFI_ERROR(status)) {
993 uefi_call_wrapper(BS->CloseEvent, 1, timer);
994 return EINVAL;
995 }
996
997 status = uefi_call_wrapper(BS->SetTimer, 3, periodic, TimerPeriodic,
998 1000000); /* 100ms */
999 if (EFI_ERROR(status)) {
1000 uefi_call_wrapper(BS->CloseEvent, 1, periodic);
1001 uefi_call_wrapper(BS->CloseEvent, 1, timer);
1002 return EINVAL;
1003 }
1004 status = WaitForSingleEvent(&timer, timeout);
1005 uefi_call_wrapper(BS->SetTimer, 3, periodic, TimerCancel, 0);
1006 uefi_call_wrapper(BS->CloseEvent, 1, periodic);
1007 uefi_call_wrapper(BS->CloseEvent, 1, timer);
1008 if (!EFI_ERROR(status))
1009 return 0;
1010 if (status == EFI_TIMEOUT)
1011 return ETIMEDOUT;
1012 return EINVAL;
1013 }
1014