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