eficons.c revision 1.1 1 /* $NetBSD: eficons.c,v 1.1 2017/01/24 11:09:14 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 UINT64 res, bestres = 0;
213 UINTN sz;
214 UINT32 i;
215 INT32 bestmode = -1;
216
217 if (efi_gop == NULL) {
218 framebuffer_configure(NULL);
219 return;
220 }
221
222 if (efi_gop_mode >= 0) {
223 bestmode = efi_gop_mode;
224 } else {
225 /* XXX EDID? EFI_EDID_DISCOVERD_PROTOCOL */
226 for (i = 0; i < efi_gop->Mode->MaxMode; i++) {
227 status = uefi_call_wrapper(efi_gop->QueryMode, 4,
228 efi_gop, i, &sz, &info);
229 if (EFI_ERROR(status))
230 continue;
231
232 res = (UINT64)info->HorizontalResolution *
233 (UINT64)info->VerticalResolution *
234 (UINT64)getdepth(info);
235 if (res > bestres) {
236 bestmode = i;
237 bestres = res;
238 }
239 }
240 }
241 if (bestmode >= 0) {
242 status = uefi_call_wrapper(efi_gop->SetMode, 2, efi_gop,
243 bestmode);
244 if (EFI_ERROR(status) || efi_gop->Mode->Mode != bestmode)
245 Print(L"GOP setmode failed: %r\n", status);
246 }
247
248 info = efi_gop->Mode->Info;
249 memset(&fb, 0, sizeof(fb));
250 fb.physaddr = efi_gop->Mode->FrameBufferBase;
251 fb.flags = 0;
252 fb.width = info->HorizontalResolution;
253 fb.height = info->VerticalResolution;
254 fb.depth = getdepth(info);
255 fb.stride = info->PixelsPerScanLine * ((fb.depth + 7) / 8);
256 fb.vbemode = 0; /* XXX */
257
258 switch (info->PixelFormat) {
259 case PixelBlueGreenRedReserved8BitPerColor:
260 fb.rnum = 8;
261 fb.gnum = 8;
262 fb.bnum = 8;
263 fb.rpos = 16;
264 fb.gpos = 8;
265 fb.bpos = 0;
266 break;
267
268 case PixelRedGreenBlueReserved8BitPerColor:
269 fb.rnum = 8;
270 fb.gnum = 8;
271 fb.bnum = 8;
272 fb.rpos = 0;
273 fb.gpos = 8;
274 fb.bpos = 16;
275 break;
276
277 case PixelBitMask:
278 setpixelformat(info->PixelInformation.RedMask,
279 &fb.rnum, &fb.rpos);
280 setpixelformat(info->PixelInformation.GreenMask,
281 &fb.gnum, &fb.gpos);
282 setpixelformat(info->PixelInformation.BlueMask,
283 &fb.bnum, &fb.bpos);
284 break;
285
286 case PixelBltOnly:
287 case PixelFormatMax:
288 Panic(L"Error: invalid pixel format (%d)", info->PixelFormat);
289 break;
290 }
291
292 framebuffer_configure(&fb);
293 }
294
295 int
296 vbe_commit(void)
297 {
298
299 bi_framebuffer();
300 return 0;
301 }
302
303 static void
304 print_text_modes(void)
305 {
306 EFI_STATUS status;
307 UINTN cols, rows;
308 INT32 i, curmode;
309
310 curmode = ST->ConOut->Mode->Mode;
311 for (i = 0; i < ST->ConOut->Mode->MaxMode; i++) {
312 status = uefi_call_wrapper(ST->ConOut->QueryMode, 4,
313 ST->ConOut, i, &cols, &rows);
314 if (EFI_ERROR(status))
315 continue;
316 Print(L"%c%d: %dx%d\n", i == curmode ? '*' : ' ',
317 i, cols, rows);
318 }
319 }
320
321 static int
322 efi_find_text_mode(char *arg)
323 {
324 EFI_STATUS status;
325 UINTN cols, rows;
326 INT32 i;
327 char mode[32];
328
329 for (i = 0; i < ST->ConOut->Mode->MaxMode; i++) {
330 status = uefi_call_wrapper(ST->ConOut->QueryMode, 4,
331 ST->ConOut, i, &cols, &rows);
332 if (EFI_ERROR(status))
333 continue;
334 snprintf(mode, sizeof(mode), "%lux%lu", (long)cols, (long)rows);
335 if (strcmp(arg, mode) == 0)
336 return i;
337 }
338 return -1;
339 }
340
341 void
342 command_text(char *arg)
343 {
344 EFI_STATUS status;
345 INT32 modenum;
346
347 if (*arg == '\0' || strcmp(arg, "list") == 0) {
348 print_text_modes();
349 return;
350 }
351
352 if (strchr(arg, 'x') != NULL) {
353 modenum = efi_find_text_mode(arg);
354 if (modenum == -1) {
355 printf("mode %s not supported by firmware\n", arg);
356 return;
357 }
358 } else {
359 modenum = strtoul(arg, NULL, 0);
360 }
361
362 status = uefi_call_wrapper(ST->ConOut->SetMode, 2, ST->ConOut, modenum);
363 if (!EFI_ERROR(status))
364 return;
365
366 printf("invalid flag, must be 'list', a display mode, "
367 "or a mode number\n");
368 }
369
370 static int
371 print_gop_modes(void)
372 {
373 EFI_STATUS status;
374 EFI_GRAPHICS_OUTPUT_MODE_INFORMATION *info;
375 UINTN sz;
376 UINT32 i;
377 uint8_t depth;
378
379 if (efi_gop == NULL)
380 return 1;
381
382 for (i = 0; i < efi_gop->Mode->MaxMode; i++) {
383 status = uefi_call_wrapper(efi_gop->QueryMode, 4, efi_gop, i,
384 &sz, &info);
385 if (EFI_ERROR(status) && status == EFI_NOT_STARTED) {
386 status = uefi_call_wrapper(efi_gop->SetMode, 2,
387 efi_gop, efi_gop->Mode->Mode);
388 status = uefi_call_wrapper(efi_gop->QueryMode, 4,
389 efi_gop, i, &sz, &info);
390 }
391 if (EFI_ERROR(status))
392 continue;
393
394 Print(L"%c%d: %dx%d ",
395 memcmp(info, efi_gop->Mode->Info, sizeof(*info)) == 0 ?
396 '*' : ' ',
397 i, info->HorizontalResolution, info->VerticalResolution);
398 switch (info->PixelFormat) {
399 case PixelRedGreenBlueReserved8BitPerColor:
400 Print(L"RGBR");
401 break;
402 case PixelBlueGreenRedReserved8BitPerColor:
403 Print(L"BGRR");
404 break;
405 case PixelBitMask:
406 Print(L"R:%08x G:%08x B:%08x X:%08x",
407 info->PixelInformation.RedMask,
408 info->PixelInformation.GreenMask,
409 info->PixelInformation.BlueMask,
410 info->PixelInformation.ReservedMask);
411 break;
412 case PixelBltOnly:
413 Print(L"(blt only)");
414 break;
415 default:
416 Print(L"(Invalid pixel format)");
417 break;
418 }
419 Print(L" pitch %d", info->PixelsPerScanLine);
420 depth = getdepth(info);
421 if (depth > 0)
422 Print(L" bpp %d", depth);
423 Print(L"\n");
424 }
425
426 return 0;
427 }
428
429 static int
430 efi_find_gop_mode(char *arg)
431 {
432 EFI_STATUS status;
433 EFI_GRAPHICS_OUTPUT_MODE_INFORMATION *info;
434 UINTN sz;
435 UINT32 i;
436 char mode[32];
437 uint8_t depth;
438
439 for (i = 0; i < efi_gop->Mode->MaxMode; i++) {
440 status = uefi_call_wrapper(efi_gop->QueryMode, 4, efi_gop, i,
441 &sz, &info);
442 if (EFI_ERROR(status))
443 continue;
444
445 depth = getdepth(info);
446 if (depth == 0)
447 continue;
448
449 snprintf(mode, sizeof(mode), "%lux%lux%u",
450 (long)info->HorizontalResolution,
451 (long)info->HorizontalResolution,
452 depth);
453 if (strcmp(arg, mode) == 0)
454 return i;
455 }
456 return -1;
457 }
458
459 void
460 command_gop(char *arg)
461 {
462 EFI_STATUS status;
463 INT32 modenum;
464
465 if (efi_gop == NULL) {
466 printf("GOP not supported by firmware\n");
467 return;
468 }
469
470 if (*arg == '\0' || strcmp(arg, "list") == 0) {
471 print_gop_modes();
472 return;
473 }
474
475 if (strchr(arg, 'x') != NULL) {
476 modenum = efi_find_gop_mode(arg);
477 if (modenum == -1) {
478 printf("mode %s not supported by firmware\n", arg);
479 return;
480 }
481 } else {
482 modenum = strtoul(arg, NULL, 0);
483 }
484
485 status = uefi_call_wrapper(efi_gop->SetMode, 2, efi_gop, modenum);
486 if (!EFI_ERROR(status) && efi_gop->Mode->Mode == modenum) {
487 efi_gop_mode = modenum;
488 return;
489 }
490
491 printf("invalid flag, must be 'list', a display mode, "
492 "or a mode number\n");
493 }
494
495 static void
496 eficons_init_video(void)
497 {
498 EFI_STATUS status;
499 UINTN cols, rows, dim = 0;
500 INT32 i, best = -1;
501
502 /*
503 * Setup text mode
504 */
505 uefi_call_wrapper(ST->ConOut->Reset, 2, ST->ConOut, TRUE);
506
507 for (i = 0; i < ST->ConOut->Mode->MaxMode; i++) {
508 status = uefi_call_wrapper(ST->ConOut->QueryMode, 4,
509 ST->ConOut, i, &cols, &rows);
510 if (EFI_ERROR(status))
511 continue;
512 if (dim < cols * rows) {
513 dim = cols * rows;
514 best = i;
515 }
516 }
517 if (best >= 0)
518 uefi_call_wrapper(ST->ConOut->SetMode, 2, ST->ConOut, best);
519 uefi_call_wrapper(ST->ConOut->EnableCursor, 2, ST->ConOut, TRUE);
520 uefi_call_wrapper(ST->ConOut->ClearScreen, 1, ST->ConOut);
521
522 LibLocateProtocol(&GraphicsOutputProtocol, (void **)&efi_gop);
523 }
524
525 /*
526 * for Apple EFI
527 */
528 #define CONSOLE_CONTROL_PROTOCOL \
529 {0xf42f7782, 0x12e, 0x4c12, {0x99, 0x56, 0x49, 0xf9, 0x43, 0x4, 0xf7, 0x21}}
530 static EFI_GUID ConsoleControlProtocol = CONSOLE_CONTROL_PROTOCOL;
531
532 struct _EFI_CONSOLE_CONTROL_INTERFACE;
533 typedef struct _EFI_CONSOLE_CONTROL_INTERFACE EFI_CONSOLE_CONTROL_INTERFACE;
534 typedef enum { EfiConsoleControlScreenText } EFI_CONSOLE_CONTROL_SCREEN_MODE;
535 typedef EFI_STATUS (EFIAPI *EFI_CONSOLE_CONTROL_PROTOCOL_SET_MODE) (
536 IN EFI_CONSOLE_CONTROL_INTERFACE *This,
537 IN EFI_CONSOLE_CONTROL_SCREEN_MODE Mode
538 );
539 struct _EFI_CONSOLE_CONTROL_INTERFACE {
540 VOID *GetMode;
541 EFI_CONSOLE_CONTROL_PROTOCOL_SET_MODE SetMode;
542 VOID *LockStdIn;
543 };
544
545 static void
546 efi_switch_video_to_text_mode(void)
547 {
548 EFI_STATUS status;
549 EFI_CONSOLE_CONTROL_INTERFACE *cci;
550
551 /* Set up the console, so printf works. */
552 status = LibLocateProtocol(&ConsoleControlProtocol, (void **)&cci);
553 if (!EFI_ERROR(status)) {
554 uefi_call_wrapper(cci->SetMode, 2, cci,
555 EfiConsoleControlScreenText);
556 }
557 }
558