eficons.c revision 1.4 1 /* $NetBSD: eficons.c,v 1.4 2017/05/01 13:03:01 nonaka 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
44 static CHAR16 keybuf[16];
45 static int keybuf_read = 0;
46 static int keybuf_write = 0;
47
48 static void eficons_init_video(void);
49 static void efi_switch_video_to_text_mode(void);
50
51 static int
52 getcomaddr(int idx)
53 {
54 static const short comioport[4] = { 0x3f8, 0x2f8, 0x3e8, 0x2e8 };
55
56 if (idx < __arraycount(comioport))
57 return comioport[idx];
58 return 0;
59 }
60
61 /*
62 * XXX only pass console parameters to kernel.
63 */
64 void
65 consinit(int dev, int ioport, int speed)
66 {
67 int iodev;
68
69 #if defined(CONSPEED)
70 btinfo_console.speed = CONSPEED;
71 #else
72 btinfo_console.speed = 9600;
73 #endif
74
75 switch (dev) {
76 case CONSDEV_AUTO:
77 /* XXX comport */
78 goto nocom;
79
80 case CONSDEV_COM0:
81 case CONSDEV_COM1:
82 case CONSDEV_COM2:
83 case CONSDEV_COM3:
84 iodev = dev;
85 comport:
86 btinfo_console.addr = ioport;
87 if (btinfo_console.addr == 0) {
88 btinfo_console.addr = getcomaddr(iodev - CONSDEV_COM0);
89 if (btinfo_console.addr == 0)
90 goto nocom;
91 }
92 if (speed != 0)
93 btinfo_console.speed = speed;
94 break;
95
96 case CONSDEV_COM0KBD:
97 case CONSDEV_COM1KBD:
98 case CONSDEV_COM2KBD:
99 case CONSDEV_COM3KBD:
100 iodev = dev - CONSDEV_COM0KBD + CONSDEV_COM0;
101 goto comport; /* XXX kbd */
102
103 case CONSDEV_PC:
104 default:
105 nocom:
106 iodev = CONSDEV_PC;
107 break;
108 }
109
110 strlcpy(btinfo_console.devname, iodev == CONSDEV_PC ? "pc" : "com", 16);
111 }
112
113 int
114 cninit(void)
115 {
116
117 efi_switch_video_to_text_mode();
118 eficons_init_video();
119
120 consinit(boot_params.bp_consdev, boot_params.bp_consaddr,
121 boot_params.bp_conspeed);
122
123 return 0;
124 }
125
126 int
127 getchar(void)
128 {
129 EFI_STATUS status;
130 EFI_INPUT_KEY key;
131 int c;
132
133 if (keybuf_read != keybuf_write) {
134 c = keybuf[keybuf_read];
135 keybuf_read = (keybuf_read + 1) % __arraycount(keybuf);
136 return c;
137 }
138
139 status = uefi_call_wrapper(ST->ConIn->ReadKeyStroke, 2, ST->ConIn,
140 &key);
141 while (status == EFI_NOT_READY) {
142 WaitForSingleEvent(ST->ConIn->WaitForKey, 0);
143 status = uefi_call_wrapper(ST->ConIn->ReadKeyStroke, 2,
144 ST->ConIn, &key);
145 }
146 return key.UnicodeChar;
147 }
148
149 void
150 putchar(int c)
151 {
152 CHAR16 buf[2];
153
154 buf[1] = 0;
155 if (c == '\n') {
156 buf[0] = '\r';
157 Output(buf);
158 }
159 buf[0] = c;
160 Output(buf);
161 }
162
163 /*ARGSUSED*/
164 int
165 iskey(int intr)
166 {
167 EFI_STATUS status;
168 EFI_INPUT_KEY key;
169
170 if (keybuf_read != keybuf_write)
171 return 1;
172
173 status = uefi_call_wrapper(ST->ConIn->ReadKeyStroke, 2, ST->ConIn,
174 &key);
175 if (EFI_ERROR(status))
176 return 0;
177
178 keybuf[keybuf_write] = key.UnicodeChar;
179 keybuf_write = (keybuf_write + 1) % __arraycount(keybuf);
180 return 1;
181 }
182
183 char
184 awaitkey(int timeout, int tell)
185 {
186 char c = 0;
187
188 for (;;) {
189 if (tell) {
190 char numbuf[32];
191 int len;
192
193 len = snprintf(numbuf, sizeof(numbuf), "%d seconds. ",
194 timeout);
195 if (len > 0 && len < sizeof(numbuf)) {
196 char *p = numbuf;
197
198 printf("%s", numbuf);
199 while (*p)
200 *p++ = '\b';
201 printf("%s", numbuf);
202 }
203 }
204 if (iskey(1)) {
205 /* flush input buffer */
206 while (iskey(0))
207 c = getchar();
208 if (c == 0)
209 c = -1;
210 goto out;
211 }
212 if (timeout--)
213 WaitForSingleEvent(ST->ConIn->WaitForKey, 10000000);
214 else
215 break;
216 }
217
218 out:
219 if (tell)
220 printf("0 seconds. \n");
221
222 return c;
223 }
224
225 void
226 clear_pc_screen(void)
227 {
228
229 uefi_call_wrapper(ST->ConOut->ClearScreen, 1, ST->ConOut);
230 }
231
232 static uint8_t
233 getdepth(const EFI_GRAPHICS_OUTPUT_MODE_INFORMATION *info)
234 {
235
236 switch (info->PixelFormat) {
237 case PixelBlueGreenRedReserved8BitPerColor:
238 case PixelRedGreenBlueReserved8BitPerColor:
239 return 32;
240
241 case PixelBitMask:
242 return fls32(info->PixelInformation.RedMask
243 | info->PixelInformation.GreenMask
244 | info->PixelInformation.BlueMask
245 | info->PixelInformation.ReservedMask);
246
247 case PixelBltOnly:
248 case PixelFormatMax:
249 return 0;
250 }
251 return 0;
252 }
253
254 static void
255 setpixelformat(UINT32 mask, uint8_t *num, uint8_t *pos)
256 {
257 uint8_t n, p;
258
259 n = popcount32(mask);
260 p = ffs32(mask);
261 if (p > 0)
262 p--;
263
264 *num = n;
265 *pos = p;
266 }
267
268 static void
269 bi_framebuffer(void)
270 {
271 EFI_STATUS status;
272 EFI_GRAPHICS_OUTPUT_MODE_INFORMATION *info;
273 struct btinfo_framebuffer fb;
274 INT32 bestmode = -1;
275
276 if (efi_gop == NULL) {
277 framebuffer_configure(NULL);
278 return;
279 }
280
281 if (efi_gop_mode >= 0) {
282 bestmode = efi_gop_mode;
283 } else {
284 #if 0
285 UINT64 res, bestres = 0;
286 UINTN sz;
287 UINT32 i;
288
289 /* XXX EDID? EFI_EDID_DISCOVERED_PROTOCOL */
290 for (i = 0; i < efi_gop->Mode->MaxMode; i++) {
291 status = uefi_call_wrapper(efi_gop->QueryMode, 4,
292 efi_gop, i, &sz, &info);
293 if (EFI_ERROR(status))
294 continue;
295
296 res = (UINT64)info->HorizontalResolution *
297 (UINT64)info->VerticalResolution *
298 (UINT64)getdepth(info);
299 if (res > bestres) {
300 bestmode = i;
301 bestres = res;
302 }
303 }
304 #endif
305 }
306 if (bestmode >= 0) {
307 status = uefi_call_wrapper(efi_gop->SetMode, 2, efi_gop,
308 bestmode);
309 if (EFI_ERROR(status) || efi_gop->Mode->Mode != bestmode)
310 Print(L"GOP setmode failed: %r\n", status);
311 }
312
313 info = efi_gop->Mode->Info;
314 memset(&fb, 0, sizeof(fb));
315 fb.physaddr = efi_gop->Mode->FrameBufferBase;
316 fb.flags = 0;
317 fb.width = info->HorizontalResolution;
318 fb.height = info->VerticalResolution;
319 fb.depth = getdepth(info);
320 fb.stride = info->PixelsPerScanLine * ((fb.depth + 7) / 8);
321 fb.vbemode = 0; /* XXX */
322
323 switch (info->PixelFormat) {
324 case PixelBlueGreenRedReserved8BitPerColor:
325 fb.rnum = 8;
326 fb.gnum = 8;
327 fb.bnum = 8;
328 fb.rpos = 16;
329 fb.gpos = 8;
330 fb.bpos = 0;
331 break;
332
333 case PixelRedGreenBlueReserved8BitPerColor:
334 fb.rnum = 8;
335 fb.gnum = 8;
336 fb.bnum = 8;
337 fb.rpos = 0;
338 fb.gpos = 8;
339 fb.bpos = 16;
340 break;
341
342 case PixelBitMask:
343 setpixelformat(info->PixelInformation.RedMask,
344 &fb.rnum, &fb.rpos);
345 setpixelformat(info->PixelInformation.GreenMask,
346 &fb.gnum, &fb.gpos);
347 setpixelformat(info->PixelInformation.BlueMask,
348 &fb.bnum, &fb.bpos);
349 break;
350
351 case PixelBltOnly:
352 case PixelFormatMax:
353 Panic(L"Error: invalid pixel format (%d)", info->PixelFormat);
354 break;
355 }
356
357 framebuffer_configure(&fb);
358 }
359
360 int
361 vbe_commit(void)
362 {
363
364 bi_framebuffer();
365 return 0;
366 }
367
368 static void
369 print_text_modes(void)
370 {
371 EFI_STATUS status;
372 UINTN cols, rows;
373 INT32 i, curmode;
374
375 curmode = ST->ConOut->Mode->Mode;
376 for (i = 0; i < ST->ConOut->Mode->MaxMode; i++) {
377 status = uefi_call_wrapper(ST->ConOut->QueryMode, 4,
378 ST->ConOut, i, &cols, &rows);
379 if (EFI_ERROR(status))
380 continue;
381 Print(L"%c%d: %dx%d\n", i == curmode ? '*' : ' ',
382 i, cols, rows);
383 }
384 }
385
386 static int
387 efi_find_text_mode(char *arg)
388 {
389 EFI_STATUS status;
390 UINTN cols, rows;
391 INT32 i;
392 char mode[32];
393
394 for (i = 0; i < ST->ConOut->Mode->MaxMode; i++) {
395 status = uefi_call_wrapper(ST->ConOut->QueryMode, 4,
396 ST->ConOut, i, &cols, &rows);
397 if (EFI_ERROR(status))
398 continue;
399 snprintf(mode, sizeof(mode), "%lux%lu", (long)cols, (long)rows);
400 if (strcmp(arg, mode) == 0)
401 return i;
402 }
403 return -1;
404 }
405
406 void
407 command_text(char *arg)
408 {
409 EFI_STATUS status;
410 INT32 modenum;
411
412 if (*arg == '\0' || strcmp(arg, "list") == 0) {
413 print_text_modes();
414 return;
415 }
416
417 if (strchr(arg, 'x') != NULL) {
418 modenum = efi_find_text_mode(arg);
419 if (modenum == -1) {
420 printf("mode %s not supported by firmware\n", arg);
421 return;
422 }
423 } else {
424 modenum = strtoul(arg, NULL, 0);
425 }
426
427 status = uefi_call_wrapper(ST->ConOut->SetMode, 2, ST->ConOut, modenum);
428 if (!EFI_ERROR(status))
429 return;
430
431 printf("invalid flag, must be 'list', a display mode, "
432 "or a mode number\n");
433 }
434
435 static int
436 print_gop_modes(void)
437 {
438 EFI_STATUS status;
439 EFI_GRAPHICS_OUTPUT_MODE_INFORMATION *info;
440 UINTN sz;
441 UINT32 i;
442 uint8_t depth;
443
444 if (efi_gop == NULL)
445 return 1;
446
447 for (i = 0; i < efi_gop->Mode->MaxMode; i++) {
448 status = uefi_call_wrapper(efi_gop->QueryMode, 4, efi_gop, i,
449 &sz, &info);
450 if (EFI_ERROR(status) && status == EFI_NOT_STARTED) {
451 status = uefi_call_wrapper(efi_gop->SetMode, 2,
452 efi_gop, efi_gop->Mode->Mode);
453 status = uefi_call_wrapper(efi_gop->QueryMode, 4,
454 efi_gop, i, &sz, &info);
455 }
456 if (EFI_ERROR(status))
457 continue;
458
459 Print(L"%c%d: %dx%d ",
460 memcmp(info, efi_gop->Mode->Info, sizeof(*info)) == 0 ?
461 '*' : ' ',
462 i, info->HorizontalResolution, info->VerticalResolution);
463 switch (info->PixelFormat) {
464 case PixelRedGreenBlueReserved8BitPerColor:
465 Print(L"RGBR");
466 break;
467 case PixelBlueGreenRedReserved8BitPerColor:
468 Print(L"BGRR");
469 break;
470 case PixelBitMask:
471 Print(L"R:%08x G:%08x B:%08x X:%08x",
472 info->PixelInformation.RedMask,
473 info->PixelInformation.GreenMask,
474 info->PixelInformation.BlueMask,
475 info->PixelInformation.ReservedMask);
476 break;
477 case PixelBltOnly:
478 Print(L"(blt only)");
479 break;
480 default:
481 Print(L"(Invalid pixel format)");
482 break;
483 }
484 Print(L" pitch %d", info->PixelsPerScanLine);
485 depth = getdepth(info);
486 if (depth > 0)
487 Print(L" bpp %d", depth);
488 Print(L"\n");
489 }
490
491 return 0;
492 }
493
494 static int
495 efi_find_gop_mode(char *arg)
496 {
497 EFI_STATUS status;
498 EFI_GRAPHICS_OUTPUT_MODE_INFORMATION *info;
499 UINTN sz;
500 UINT32 i;
501 char mode[32];
502 uint8_t depth;
503
504 for (i = 0; i < efi_gop->Mode->MaxMode; i++) {
505 status = uefi_call_wrapper(efi_gop->QueryMode, 4, efi_gop, i,
506 &sz, &info);
507 if (EFI_ERROR(status))
508 continue;
509
510 depth = getdepth(info);
511 if (depth == 0)
512 continue;
513
514 snprintf(mode, sizeof(mode), "%lux%lux%u",
515 (long)info->HorizontalResolution,
516 (long)info->HorizontalResolution,
517 depth);
518 if (strcmp(arg, mode) == 0)
519 return i;
520 }
521 return -1;
522 }
523
524 void
525 command_gop(char *arg)
526 {
527 EFI_STATUS status;
528 INT32 modenum;
529
530 if (efi_gop == NULL) {
531 printf("GOP not supported by firmware\n");
532 return;
533 }
534
535 if (*arg == '\0' || strcmp(arg, "list") == 0) {
536 print_gop_modes();
537 return;
538 }
539
540 if (strchr(arg, 'x') != NULL) {
541 modenum = efi_find_gop_mode(arg);
542 if (modenum == -1) {
543 printf("mode %s not supported by firmware\n", arg);
544 return;
545 }
546 } else {
547 modenum = strtoul(arg, NULL, 0);
548 }
549
550 status = uefi_call_wrapper(efi_gop->SetMode, 2, efi_gop, modenum);
551 if (!EFI_ERROR(status) && efi_gop->Mode->Mode == modenum) {
552 efi_gop_mode = modenum;
553 return;
554 }
555
556 printf("invalid flag, must be 'list', a display mode, "
557 "or a mode number\n");
558 }
559
560 static void
561 eficons_init_video(void)
562 {
563 EFI_STATUS status;
564 UINTN cols, rows;
565 INT32 i, best, mode80x25, mode100x31;
566
567 /*
568 * Setup text mode
569 */
570 uefi_call_wrapper(ST->ConOut->Reset, 2, ST->ConOut, TRUE);
571
572 mode80x25 = mode100x31 = -1;
573 for (i = 0; i < ST->ConOut->Mode->MaxMode; i++) {
574 status = uefi_call_wrapper(ST->ConOut->QueryMode, 4,
575 ST->ConOut, i, &cols, &rows);
576 if (EFI_ERROR(status))
577 continue;
578
579 if (mode80x25 < 0 && cols == 80 && rows == 25)
580 mode80x25 = i;
581 else if (mode100x31 < 0 && cols == 100 && rows == 31)
582 mode100x31 = i;
583 }
584 best = mode100x31 >= 0 ? mode100x31 : mode80x25 >= 0 ? mode80x25 : -1;
585 if (best >= 0)
586 uefi_call_wrapper(ST->ConOut->SetMode, 2, ST->ConOut, best);
587 uefi_call_wrapper(ST->ConOut->EnableCursor, 2, ST->ConOut, TRUE);
588 uefi_call_wrapper(ST->ConOut->ClearScreen, 1, ST->ConOut);
589
590 LibLocateProtocol(&GraphicsOutputProtocol, (void **)&efi_gop);
591 }
592
593 /*
594 * for Apple EFI
595 */
596 #define CONSOLE_CONTROL_PROTOCOL \
597 {0xf42f7782, 0x12e, 0x4c12, {0x99, 0x56, 0x49, 0xf9, 0x43, 0x4, 0xf7, 0x21}}
598 static EFI_GUID ConsoleControlProtocol = CONSOLE_CONTROL_PROTOCOL;
599
600 struct _EFI_CONSOLE_CONTROL_INTERFACE;
601 typedef struct _EFI_CONSOLE_CONTROL_INTERFACE EFI_CONSOLE_CONTROL_INTERFACE;
602 typedef enum { EfiConsoleControlScreenText } EFI_CONSOLE_CONTROL_SCREEN_MODE;
603 typedef EFI_STATUS (EFIAPI *EFI_CONSOLE_CONTROL_PROTOCOL_SET_MODE) (
604 IN EFI_CONSOLE_CONTROL_INTERFACE *This,
605 IN EFI_CONSOLE_CONTROL_SCREEN_MODE Mode
606 );
607 struct _EFI_CONSOLE_CONTROL_INTERFACE {
608 VOID *GetMode;
609 EFI_CONSOLE_CONTROL_PROTOCOL_SET_MODE SetMode;
610 VOID *LockStdIn;
611 };
612
613 static void
614 efi_switch_video_to_text_mode(void)
615 {
616 EFI_STATUS status;
617 EFI_CONSOLE_CONTROL_INTERFACE *cci;
618
619 /* Set up the console, so printf works. */
620 status = LibLocateProtocol(&ConsoleControlProtocol, (void **)&cci);
621 if (!EFI_ERROR(status)) {
622 uefi_call_wrapper(cci->SetMode, 2, cci,
623 EfiConsoleControlScreenText);
624 }
625 }
626