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