1 /*-
2 * Copyright (c) 2013 The FreeBSD Foundation
3 * All rights reserved.
4 *
5 * This software was developed by Benno Rice under sponsorship from
6 * the FreeBSD Foundation.
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 AUTHOR 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 AUTHOR 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/cdefs.h>
30 __FBSDID("$FreeBSD$");
31
32 #include <bootstrap.h>
33 #include <sys/endian.h>
34 #include <sys/param.h>
35 #include <stand.h>
36
37 #include <efi.h>
38 #include <efilib.h>
39 #include <efiuga.h>
40 #include <efipciio.h>
41 #include <machine/metadata.h>
42
43 #include "framebuffer.h"
44
45 static EFI_GUID gop_guid = EFI_GRAPHICS_OUTPUT_PROTOCOL_GUID;
46 static EFI_GUID pciio_guid = EFI_PCI_IO_PROTOCOL_GUID;
47 static EFI_GUID uga_guid = EFI_UGA_DRAW_PROTOCOL_GUID;
48
49 static struct named_resolution {
50 const char *name;
51 const char *alias;
52 unsigned int width;
53 unsigned int height;
54 } resolutions[] = {
55 {
56 .name = "480p",
57 .width = 640,
58 .height = 480,
59 },
60 {
61 .name = "720p",
62 .width = 1280,
63 .height = 720,
64 },
65 {
66 .name = "1080p",
67 .width = 1920,
68 .height = 1080,
69 },
70 {
71 .name = "2160p",
72 .alias = "4k",
73 .width = 3840,
74 .height = 2160,
75 },
76 {
77 .name = "5k",
78 .width = 5120,
79 .height = 2880,
80 }
81 };
82
83 static u_int
efifb_color_depth(struct efi_fb * efifb)84 efifb_color_depth(struct efi_fb *efifb)
85 {
86 uint32_t mask;
87 u_int depth;
88
89 mask = efifb->fb_mask_red | efifb->fb_mask_green |
90 efifb->fb_mask_blue | efifb->fb_mask_reserved;
91 if (mask == 0)
92 return (0);
93 for (depth = 1; mask != 1; depth++)
94 mask >>= 1;
95 return (depth);
96 }
97
98 static int
efifb_mask_from_pixfmt(struct efi_fb * efifb,EFI_GRAPHICS_PIXEL_FORMAT pixfmt,EFI_PIXEL_BITMASK * pixinfo)99 efifb_mask_from_pixfmt(struct efi_fb *efifb, EFI_GRAPHICS_PIXEL_FORMAT pixfmt,
100 EFI_PIXEL_BITMASK *pixinfo)
101 {
102 int result;
103
104 result = 0;
105 switch (pixfmt) {
106 case PixelRedGreenBlueReserved8BitPerColor:
107 efifb->fb_mask_red = 0x000000ff;
108 efifb->fb_mask_green = 0x0000ff00;
109 efifb->fb_mask_blue = 0x00ff0000;
110 efifb->fb_mask_reserved = 0xff000000;
111 break;
112 case PixelBlueGreenRedReserved8BitPerColor:
113 efifb->fb_mask_red = 0x00ff0000;
114 efifb->fb_mask_green = 0x0000ff00;
115 efifb->fb_mask_blue = 0x000000ff;
116 efifb->fb_mask_reserved = 0xff000000;
117 break;
118 case PixelBitMask:
119 efifb->fb_mask_red = pixinfo->RedMask;
120 efifb->fb_mask_green = pixinfo->GreenMask;
121 efifb->fb_mask_blue = pixinfo->BlueMask;
122 efifb->fb_mask_reserved = pixinfo->ReservedMask;
123 break;
124 default:
125 result = 1;
126 break;
127 }
128 return (result);
129 }
130
131 static int
efifb_from_gop(struct efi_fb * efifb,EFI_GRAPHICS_OUTPUT_PROTOCOL_MODE * mode,EFI_GRAPHICS_OUTPUT_MODE_INFORMATION * info)132 efifb_from_gop(struct efi_fb *efifb, EFI_GRAPHICS_OUTPUT_PROTOCOL_MODE *mode,
133 EFI_GRAPHICS_OUTPUT_MODE_INFORMATION *info)
134 {
135 int result;
136
137 efifb->fb_addr = mode->FrameBufferBase;
138 efifb->fb_size = mode->FrameBufferSize;
139 efifb->fb_height = info->VerticalResolution;
140 efifb->fb_width = info->HorizontalResolution;
141 efifb->fb_stride = info->PixelsPerScanLine;
142 result = efifb_mask_from_pixfmt(efifb, info->PixelFormat,
143 &info->PixelInformation);
144 return (result);
145 }
146
147 static ssize_t
efifb_uga_find_pixel(EFI_UGA_DRAW_PROTOCOL * uga,u_int line,EFI_PCI_IO_PROTOCOL * pciio,uint64_t addr,uint64_t size)148 efifb_uga_find_pixel(EFI_UGA_DRAW_PROTOCOL *uga, u_int line,
149 EFI_PCI_IO_PROTOCOL *pciio, uint64_t addr, uint64_t size)
150 {
151 EFI_UGA_PIXEL pix0, pix1;
152 uint8_t *data1, *data2;
153 size_t count, maxcount = 1024;
154 ssize_t ofs;
155 EFI_STATUS status;
156 u_int idx;
157
158 status = uga->Blt(uga, &pix0, EfiUgaVideoToBltBuffer,
159 0, line, 0, 0, 1, 1, 0);
160 if (EFI_ERROR(status)) {
161 printf("UGA BLT operation failed (video->buffer)");
162 return (-1);
163 }
164 pix1.Red = ~pix0.Red;
165 pix1.Green = ~pix0.Green;
166 pix1.Blue = ~pix0.Blue;
167 pix1.Reserved = 0;
168
169 data1 = calloc(maxcount, 2);
170 if (data1 == NULL) {
171 printf("Unable to allocate memory");
172 return (-1);
173 }
174 data2 = data1 + maxcount;
175
176 ofs = 0;
177 while (size > 0) {
178 count = min(size, maxcount);
179
180 status = pciio->Mem.Read(pciio, EfiPciIoWidthUint32,
181 EFI_PCI_IO_PASS_THROUGH_BAR, addr + ofs, count >> 2,
182 data1);
183 if (EFI_ERROR(status)) {
184 printf("Error reading frame buffer (before)");
185 goto fail;
186 }
187 status = uga->Blt(uga, &pix1, EfiUgaBltBufferToVideo,
188 0, 0, 0, line, 1, 1, 0);
189 if (EFI_ERROR(status)) {
190 printf("UGA BLT operation failed (modify)");
191 goto fail;
192 }
193 status = pciio->Mem.Read(pciio, EfiPciIoWidthUint32,
194 EFI_PCI_IO_PASS_THROUGH_BAR, addr + ofs, count >> 2,
195 data2);
196 if (EFI_ERROR(status)) {
197 printf("Error reading frame buffer (after)");
198 goto fail;
199 }
200 status = uga->Blt(uga, &pix0, EfiUgaBltBufferToVideo,
201 0, 0, 0, line, 1, 1, 0);
202 if (EFI_ERROR(status)) {
203 printf("UGA BLT operation failed (restore)");
204 goto fail;
205 }
206 for (idx = 0; idx < count; idx++) {
207 if (data1[idx] != data2[idx]) {
208 free(data1);
209 return (ofs + (idx & ~3));
210 }
211 }
212 ofs += count;
213 size -= count;
214 }
215 printf("No change detected in frame buffer");
216
217 fail:
218 printf(" -- error %lu\n", EFI_ERROR_CODE(status));
219 free(data1);
220 return (-1);
221 }
222
223 static EFI_PCI_IO_PROTOCOL *
efifb_uga_get_pciio(void)224 efifb_uga_get_pciio(void)
225 {
226 EFI_PCI_IO_PROTOCOL *pciio;
227 EFI_HANDLE *buf, *hp;
228 EFI_STATUS status;
229 UINTN bufsz;
230
231 /* Get all handles that support the UGA protocol. */
232 bufsz = 0;
233 status = BS->LocateHandle(ByProtocol, &uga_guid, NULL, &bufsz, NULL);
234 if (status != EFI_BUFFER_TOO_SMALL)
235 return (NULL);
236 buf = malloc(bufsz);
237 status = BS->LocateHandle(ByProtocol, &uga_guid, NULL, &bufsz, buf);
238 if (status != EFI_SUCCESS) {
239 free(buf);
240 return (NULL);
241 }
242 bufsz /= sizeof(EFI_HANDLE);
243
244 /* Get the PCI I/O interface of the first handle that supports it. */
245 pciio = NULL;
246 for (hp = buf; hp < buf + bufsz; hp++) {
247 status = OpenProtocolByHandle(*hp, &pciio_guid,
248 (void **)&pciio);
249 if (status == EFI_SUCCESS) {
250 free(buf);
251 return (pciio);
252 }
253 }
254 free(buf);
255 return (NULL);
256 }
257
258 static EFI_STATUS
efifb_uga_locate_framebuffer(EFI_PCI_IO_PROTOCOL * pciio,uint64_t * addrp,uint64_t * sizep)259 efifb_uga_locate_framebuffer(EFI_PCI_IO_PROTOCOL *pciio, uint64_t *addrp,
260 uint64_t *sizep)
261 {
262 uint8_t *resattr;
263 uint64_t addr, size;
264 EFI_STATUS status;
265 u_int bar;
266
267 if (pciio == NULL)
268 return (EFI_DEVICE_ERROR);
269
270 /* Attempt to get the frame buffer address (imprecise). */
271 *addrp = 0;
272 *sizep = 0;
273 for (bar = 0; bar < 6; bar++) {
274 status = pciio->GetBarAttributes(pciio, bar, NULL,
275 (void **)&resattr);
276 if (status != EFI_SUCCESS)
277 continue;
278 /* XXX magic offsets and constants. */
279 if (resattr[0] == 0x87 && resattr[3] == 0) {
280 /* 32-bit address space descriptor (MEMIO) */
281 addr = le32dec(resattr + 10);
282 size = le32dec(resattr + 22);
283 } else if (resattr[0] == 0x8a && resattr[3] == 0) {
284 /* 64-bit address space descriptor (MEMIO) */
285 addr = le64dec(resattr + 14);
286 size = le64dec(resattr + 38);
287 } else {
288 addr = 0;
289 size = 0;
290 }
291 BS->FreePool(resattr);
292 if (addr == 0 || size == 0)
293 continue;
294
295 /* We assume the largest BAR is the frame buffer. */
296 if (size > *sizep) {
297 *addrp = addr;
298 *sizep = size;
299 }
300 }
301 return ((*addrp == 0 || *sizep == 0) ? EFI_DEVICE_ERROR : 0);
302 }
303
304 static int
efifb_from_uga(struct efi_fb * efifb,EFI_UGA_DRAW_PROTOCOL * uga)305 efifb_from_uga(struct efi_fb *efifb, EFI_UGA_DRAW_PROTOCOL *uga)
306 {
307 EFI_PCI_IO_PROTOCOL *pciio;
308 char *ev, *p;
309 EFI_STATUS status;
310 ssize_t offset;
311 uint64_t fbaddr;
312 uint32_t horiz, vert, stride;
313 uint32_t np, depth, refresh;
314
315 status = uga->GetMode(uga, &horiz, &vert, &depth, &refresh);
316 if (EFI_ERROR(status))
317 return (1);
318 efifb->fb_height = vert;
319 efifb->fb_width = horiz;
320 /* Paranoia... */
321 if (efifb->fb_height == 0 || efifb->fb_width == 0)
322 return (1);
323
324 /* The color masks are fixed AFAICT. */
325 efifb_mask_from_pixfmt(efifb, PixelBlueGreenRedReserved8BitPerColor,
326 NULL);
327
328 /* pciio can be NULL on return! */
329 pciio = efifb_uga_get_pciio();
330
331 /* Try to find the frame buffer. */
332 status = efifb_uga_locate_framebuffer(pciio, &efifb->fb_addr,
333 &efifb->fb_size);
334 if (EFI_ERROR(status)) {
335 efifb->fb_addr = 0;
336 efifb->fb_size = 0;
337 }
338
339 /*
340 * There's no reliable way to detect the frame buffer or the
341 * offset within the frame buffer of the visible region, nor
342 * the stride. Our only option is to look at the system and
343 * fill in the blanks based on that. Luckily, UGA was mostly
344 * only used on Apple hardware.
345 */
346 offset = -1;
347 ev = getenv("smbios.system.maker");
348 if (ev != NULL && !strcmp(ev, "Apple Inc.")) {
349 ev = getenv("smbios.system.product");
350 if (ev != NULL && !strcmp(ev, "iMac7,1")) {
351 /* These are the expected values we should have. */
352 horiz = 1680;
353 vert = 1050;
354 fbaddr = 0xc0000000;
355 /* These are the missing bits. */
356 offset = 0x10000;
357 stride = 1728;
358 } else if (ev != NULL && !strcmp(ev, "MacBook3,1")) {
359 /* These are the expected values we should have. */
360 horiz = 1280;
361 vert = 800;
362 fbaddr = 0xc0000000;
363 /* These are the missing bits. */
364 offset = 0x0;
365 stride = 2048;
366 }
367 }
368
369 /*
370 * If this is hardware we know, make sure that it looks familiar
371 * before we accept our hardcoded values.
372 */
373 if (offset >= 0 && efifb->fb_width == horiz &&
374 efifb->fb_height == vert && efifb->fb_addr == fbaddr) {
375 efifb->fb_addr += offset;
376 efifb->fb_size -= offset;
377 efifb->fb_stride = stride;
378 return (0);
379 } else if (offset >= 0) {
380 printf("Hardware make/model known, but graphics not "
381 "as expected.\n");
382 printf("Console may not work!\n");
383 }
384
385 /*
386 * The stride is equal or larger to the width. Often it's the
387 * next larger power of two. We'll start with that...
388 */
389 efifb->fb_stride = efifb->fb_width;
390 do {
391 np = efifb->fb_stride & (efifb->fb_stride - 1);
392 if (np) {
393 efifb->fb_stride |= (np - 1);
394 efifb->fb_stride++;
395 }
396 } while (np);
397
398 ev = getenv("hw.efifb.address");
399 if (ev == NULL) {
400 if (efifb->fb_addr == 0) {
401 printf("Please set hw.efifb.address and "
402 "hw.efifb.stride.\n");
403 return (1);
404 }
405
406 /*
407 * The visible part of the frame buffer may not start at
408 * offset 0, so try to detect it. Note that we may not
409 * always be able to read from the frame buffer, which
410 * means that we may not be able to detect anything. In
411 * that case, we would take a long time scanning for a
412 * pixel change in the frame buffer, which would have it
413 * appear that we're hanging, so we limit the scan to
414 * 1/256th of the frame buffer. This number is mostly
415 * based on PR 202730 and the fact that on a MacBoook,
416 * where we can't read from the frame buffer the offset
417 * of the visible region is 0. In short: we want to scan
418 * enough to handle all adapters that have an offset
419 * larger than 0 and we want to scan as little as we can
420 * to not appear to hang when we can't read from the
421 * frame buffer.
422 */
423 offset = efifb_uga_find_pixel(uga, 0, pciio, efifb->fb_addr,
424 efifb->fb_size >> 8);
425 if (offset == -1) {
426 printf("Unable to reliably detect frame buffer.\n");
427 } else if (offset > 0) {
428 efifb->fb_addr += offset;
429 efifb->fb_size -= offset;
430 }
431 } else {
432 offset = 0;
433 efifb->fb_size = efifb->fb_height * efifb->fb_stride * 4;
434 efifb->fb_addr = strtoul(ev, &p, 0);
435 if (*p != '\0')
436 return (1);
437 }
438
439 ev = getenv("hw.efifb.stride");
440 if (ev == NULL) {
441 if (pciio != NULL && offset != -1) {
442 /* Determine the stride. */
443 offset = efifb_uga_find_pixel(uga, 1, pciio,
444 efifb->fb_addr, horiz * 8);
445 if (offset != -1)
446 efifb->fb_stride = offset >> 2;
447 } else {
448 printf("Unable to reliably detect the stride.\n");
449 }
450 } else {
451 efifb->fb_stride = strtoul(ev, &p, 0);
452 if (*p != '\0')
453 return (1);
454 }
455
456 /*
457 * We finalized on the stride, so recalculate the size of the
458 * frame buffer.
459 */
460 efifb->fb_size = efifb->fb_height * efifb->fb_stride * 4;
461 return (0);
462 }
463
464 int
efi_find_framebuffer(struct efi_fb * efifb)465 efi_find_framebuffer(struct efi_fb *efifb)
466 {
467 EFI_GRAPHICS_OUTPUT *gop;
468 EFI_UGA_DRAW_PROTOCOL *uga;
469 EFI_STATUS status;
470
471 status = BS->LocateProtocol(&gop_guid, NULL, (VOID **)&gop);
472 if (status == EFI_SUCCESS)
473 return (efifb_from_gop(efifb, gop->Mode, gop->Mode->Info));
474
475 status = BS->LocateProtocol(&uga_guid, NULL, (VOID **)&uga);
476 if (status == EFI_SUCCESS)
477 return (efifb_from_uga(efifb, uga));
478
479 return (1);
480 }
481
482 static void
print_efifb(int mode,struct efi_fb * efifb,int verbose)483 print_efifb(int mode, struct efi_fb *efifb, int verbose)
484 {
485 u_int depth;
486
487 if (mode >= 0)
488 printf("mode %d: ", mode);
489 depth = efifb_color_depth(efifb);
490 printf("%ux%ux%u, stride=%u", efifb->fb_width, efifb->fb_height,
491 depth, efifb->fb_stride);
492 if (verbose) {
493 printf("\n frame buffer: address=%jx, size=%jx",
494 (uintmax_t)efifb->fb_addr, (uintmax_t)efifb->fb_size);
495 printf("\n color mask: R=%08x, G=%08x, B=%08x\n",
496 efifb->fb_mask_red, efifb->fb_mask_green,
497 efifb->fb_mask_blue);
498 }
499 }
500
501 static bool
efi_resolution_compare(struct named_resolution * res,const char * cmp)502 efi_resolution_compare(struct named_resolution *res, const char *cmp)
503 {
504
505 if (strcasecmp(res->name, cmp) == 0)
506 return (true);
507 if (res->alias != NULL && strcasecmp(res->alias, cmp) == 0)
508 return (true);
509 return (false);
510 }
511
512
513 static void
efi_get_max_resolution(int * width,int * height)514 efi_get_max_resolution(int *width, int *height)
515 {
516 struct named_resolution *res;
517 char *maxres;
518 char *height_start, *width_start;
519 int idx;
520
521 *width = *height = 0;
522 maxres = getenv("efi_max_resolution");
523 /* No max_resolution set? Bail out; choose highest resolution */
524 if (maxres == NULL)
525 return;
526 /* See if it matches one of our known resolutions */
527 for (idx = 0; idx < nitems(resolutions); ++idx) {
528 res = &resolutions[idx];
529 if (efi_resolution_compare(res, maxres)) {
530 *width = res->width;
531 *height = res->height;
532 return;
533 }
534 }
535 /* Not a known resolution, try to parse it; make a copy we can modify */
536 maxres = strdup(maxres);
537 if (maxres == NULL)
538 return;
539 height_start = strchr(maxres, 'x');
540 if (height_start == NULL) {
541 free(maxres);
542 return;
543 }
544 width_start = maxres;
545 *height_start++ = 0;
546 /* Errors from this will effectively mean "no max" */
547 *width = (int)strtol(width_start, NULL, 0);
548 *height = (int)strtol(height_start, NULL, 0);
549 free(maxres);
550 }
551
552 static int
gop_autoresize(EFI_GRAPHICS_OUTPUT * gop)553 gop_autoresize(EFI_GRAPHICS_OUTPUT *gop)
554 {
555 struct efi_fb efifb;
556 EFI_GRAPHICS_OUTPUT_MODE_INFORMATION *info;
557 EFI_STATUS status;
558 UINTN infosz;
559 UINT32 best_mode, currdim, maxdim, mode;
560 int height, max_height, max_width, width;
561
562 best_mode = maxdim = 0;
563 efi_get_max_resolution(&max_width, &max_height);
564 for (mode = 0; mode < gop->Mode->MaxMode; mode++) {
565 status = gop->QueryMode(gop, mode, &infosz, &info);
566 if (EFI_ERROR(status))
567 continue;
568 efifb_from_gop(&efifb, gop->Mode, info);
569 width = info->HorizontalResolution;
570 height = info->VerticalResolution;
571 currdim = width * height;
572 if (currdim > maxdim) {
573 if ((max_width != 0 && width > max_width) ||
574 (max_height != 0 && height > max_height))
575 continue;
576 maxdim = currdim;
577 best_mode = mode;
578 }
579 }
580
581 if (maxdim != 0) {
582 status = gop->SetMode(gop, best_mode);
583 if (EFI_ERROR(status)) {
584 snprintf(command_errbuf, sizeof(command_errbuf),
585 "gop_autoresize: Unable to set mode to %u (error=%lu)",
586 mode, EFI_ERROR_CODE(status));
587 return (CMD_ERROR);
588 }
589 }
590 return (CMD_OK);
591 }
592
593 static int
text_autoresize()594 text_autoresize()
595 {
596 SIMPLE_TEXT_OUTPUT_INTERFACE *conout;
597 EFI_STATUS status;
598 UINTN i, max_dim, best_mode, cols, rows;
599
600 conout = ST->ConOut;
601 max_dim = best_mode = 0;
602 for (i = 0; i < conout->Mode->MaxMode; i++) {
603 status = conout->QueryMode(conout, i, &cols, &rows);
604 if (EFI_ERROR(status))
605 continue;
606 if (cols * rows > max_dim) {
607 max_dim = cols * rows;
608 best_mode = i;
609 }
610 }
611 if (max_dim > 0)
612 conout->SetMode(conout, best_mode);
613 return (CMD_OK);
614 }
615
616 static int
uga_autoresize(EFI_UGA_DRAW_PROTOCOL * uga)617 uga_autoresize(EFI_UGA_DRAW_PROTOCOL *uga)
618 {
619
620 return (text_autoresize());
621 }
622
623 COMMAND_SET(efi_autoresize, "efi-autoresizecons", "EFI Auto-resize Console", command_autoresize);
624
625 static int
command_autoresize(int argc,char * argv[])626 command_autoresize(int argc, char *argv[])
627 {
628 EFI_GRAPHICS_OUTPUT *gop;
629 EFI_UGA_DRAW_PROTOCOL *uga;
630 char *textmode;
631 EFI_STATUS status;
632 u_int mode;
633
634 textmode = getenv("hw.vga.textmode");
635 /* If it's set and non-zero, we'll select a console mode instead */
636 if (textmode != NULL && strcmp(textmode, "0") != 0)
637 return (text_autoresize());
638
639 gop = NULL;
640 uga = NULL;
641 status = BS->LocateProtocol(&gop_guid, NULL, (VOID **)&gop);
642 if (EFI_ERROR(status) == 0)
643 return (gop_autoresize(gop));
644
645 status = BS->LocateProtocol(&uga_guid, NULL, (VOID **)&uga);
646 if (EFI_ERROR(status) == 0)
647 return (uga_autoresize(uga));
648
649 snprintf(command_errbuf, sizeof(command_errbuf),
650 "%s: Neither Graphics Output Protocol nor Universal Graphics Adapter present",
651 argv[0]);
652
653 /*
654 * Default to text_autoresize if we have neither GOP or UGA. This won't
655 * give us the most ideal resolution, but it will at least leave us
656 * functional rather than failing the boot for an objectively bad
657 * reason.
658 */
659 return (text_autoresize());
660 }
661
662 COMMAND_SET(gop, "gop", "graphics output protocol", command_gop);
663
664 static int
command_gop(int argc,char * argv[])665 command_gop(int argc, char *argv[])
666 {
667 struct efi_fb efifb;
668 EFI_GRAPHICS_OUTPUT *gop;
669 EFI_STATUS status;
670 u_int mode;
671
672 status = BS->LocateProtocol(&gop_guid, NULL, (VOID **)&gop);
673 if (EFI_ERROR(status)) {
674 snprintf(command_errbuf, sizeof(command_errbuf),
675 "%s: Graphics Output Protocol not present (error=%lu)",
676 argv[0], EFI_ERROR_CODE(status));
677 return (CMD_ERROR);
678 }
679
680 if (argc < 2)
681 goto usage;
682
683 if (!strcmp(argv[1], "set")) {
684 char *cp;
685
686 if (argc != 3)
687 goto usage;
688 mode = strtol(argv[2], &cp, 0);
689 if (cp[0] != '\0') {
690 sprintf(command_errbuf, "mode is an integer");
691 return (CMD_ERROR);
692 }
693 status = gop->SetMode(gop, mode);
694 if (EFI_ERROR(status)) {
695 snprintf(command_errbuf, sizeof(command_errbuf),
696 "%s: Unable to set mode to %u (error=%lu)",
697 argv[0], mode, EFI_ERROR_CODE(status));
698 return (CMD_ERROR);
699 }
700 } else if (!strcmp(argv[1], "get")) {
701 if (argc != 2)
702 goto usage;
703 efifb_from_gop(&efifb, gop->Mode, gop->Mode->Info);
704 print_efifb(gop->Mode->Mode, &efifb, 1);
705 printf("\n");
706 } else if (!strcmp(argv[1], "list")) {
707 EFI_GRAPHICS_OUTPUT_MODE_INFORMATION *info;
708 UINTN infosz;
709
710 if (argc != 2)
711 goto usage;
712 pager_open();
713 for (mode = 0; mode < gop->Mode->MaxMode; mode++) {
714 status = gop->QueryMode(gop, mode, &infosz, &info);
715 if (EFI_ERROR(status))
716 continue;
717 efifb_from_gop(&efifb, gop->Mode, info);
718 print_efifb(mode, &efifb, 0);
719 if (pager_output("\n"))
720 break;
721 }
722 pager_close();
723 }
724 return (CMD_OK);
725
726 usage:
727 snprintf(command_errbuf, sizeof(command_errbuf),
728 "usage: %s [list | get | set <mode>]", argv[0]);
729 return (CMD_ERROR);
730 }
731
732 COMMAND_SET(uga, "uga", "universal graphics adapter", command_uga);
733
734 static int
command_uga(int argc,char * argv[])735 command_uga(int argc, char *argv[])
736 {
737 struct efi_fb efifb;
738 EFI_UGA_DRAW_PROTOCOL *uga;
739 EFI_STATUS status;
740
741 status = BS->LocateProtocol(&uga_guid, NULL, (VOID **)&uga);
742 if (EFI_ERROR(status)) {
743 snprintf(command_errbuf, sizeof(command_errbuf),
744 "%s: UGA Protocol not present (error=%lu)",
745 argv[0], EFI_ERROR_CODE(status));
746 return (CMD_ERROR);
747 }
748
749 if (argc != 1)
750 goto usage;
751
752 if (efifb_from_uga(&efifb, uga) != CMD_OK) {
753 snprintf(command_errbuf, sizeof(command_errbuf),
754 "%s: Unable to get UGA information", argv[0]);
755 return (CMD_ERROR);
756 }
757
758 print_efifb(-1, &efifb, 1);
759 printf("\n");
760 return (CMD_OK);
761
762 usage:
763 snprintf(command_errbuf, sizeof(command_errbuf), "usage: %s", argv[0]);
764 return (CMD_ERROR);
765 }
766