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