Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/stand/efi/loader/framebuffer.c
34860 views
1
/*-
2
* Copyright (c) 2013 The FreeBSD Foundation
3
*
4
* This software was developed by Benno Rice under sponsorship from
5
* the FreeBSD Foundation.
6
* Redistribution and use in source and binary forms, with or without
7
* modification, are permitted provided that the following conditions
8
* are met:
9
* 1. Redistributions of source code must retain the above copyright
10
* notice, this list of conditions and the following disclaimer.
11
* 2. Redistributions in binary form must reproduce the above copyright
12
* notice, this list of conditions and the following disclaimer in the
13
* documentation and/or other materials provided with the distribution.
14
*
15
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25
* SUCH DAMAGE.
26
*/
27
28
#include <bootstrap.h>
29
#include <sys/endian.h>
30
#include <sys/param.h>
31
#include <stand.h>
32
33
#include <efi.h>
34
#include <efilib.h>
35
#include <efiuga.h>
36
#include <efipciio.h>
37
#include <Protocol/EdidActive.h>
38
#include <Protocol/EdidDiscovered.h>
39
#include <machine/metadata.h>
40
41
#include "bootstrap.h"
42
#include "framebuffer.h"
43
44
static EFI_GUID conout_guid = EFI_CONSOLE_OUT_DEVICE_GUID;
45
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
static EFI_GUID active_edid_guid = EFI_EDID_ACTIVE_PROTOCOL_GUID;
49
static EFI_GUID discovered_edid_guid = EFI_EDID_DISCOVERED_PROTOCOL_GUID;
50
static EFI_HANDLE gop_handle;
51
52
/* Cached EDID. */
53
struct vesa_edid_info *edid_info = NULL;
54
55
static EFI_GRAPHICS_OUTPUT *gop;
56
static EFI_UGA_DRAW_PROTOCOL *uga;
57
58
static struct named_resolution {
59
const char *name;
60
const char *alias;
61
unsigned int width;
62
unsigned int height;
63
} resolutions[] = {
64
{
65
.name = "480p",
66
.width = 640,
67
.height = 480,
68
},
69
{
70
.name = "720p",
71
.width = 1280,
72
.height = 720,
73
},
74
{
75
.name = "1080p",
76
.width = 1920,
77
.height = 1080,
78
},
79
{
80
.name = "1440p",
81
.width = 2560,
82
.height = 1440,
83
},
84
{
85
.name = "2160p",
86
.alias = "4k",
87
.width = 3840,
88
.height = 2160,
89
},
90
{
91
.name = "5k",
92
.width = 5120,
93
.height = 2880,
94
}
95
};
96
97
static u_int
98
efifb_color_depth(struct efi_fb *efifb)
99
{
100
uint32_t mask;
101
u_int depth;
102
103
mask = efifb->fb_mask_red | efifb->fb_mask_green |
104
efifb->fb_mask_blue | efifb->fb_mask_reserved;
105
if (mask == 0)
106
return (0);
107
for (depth = 1; mask != 1; depth++)
108
mask >>= 1;
109
return (depth);
110
}
111
112
static int
113
efifb_mask_from_pixfmt(struct efi_fb *efifb, EFI_GRAPHICS_PIXEL_FORMAT pixfmt,
114
EFI_PIXEL_BITMASK *pixinfo)
115
{
116
int result;
117
118
result = 0;
119
switch (pixfmt) {
120
case PixelRedGreenBlueReserved8BitPerColor:
121
case PixelBltOnly:
122
efifb->fb_mask_red = 0x000000ff;
123
efifb->fb_mask_green = 0x0000ff00;
124
efifb->fb_mask_blue = 0x00ff0000;
125
efifb->fb_mask_reserved = 0xff000000;
126
break;
127
case PixelBlueGreenRedReserved8BitPerColor:
128
efifb->fb_mask_red = 0x00ff0000;
129
efifb->fb_mask_green = 0x0000ff00;
130
efifb->fb_mask_blue = 0x000000ff;
131
efifb->fb_mask_reserved = 0xff000000;
132
break;
133
case PixelBitMask:
134
efifb->fb_mask_red = pixinfo->RedMask;
135
efifb->fb_mask_green = pixinfo->GreenMask;
136
efifb->fb_mask_blue = pixinfo->BlueMask;
137
efifb->fb_mask_reserved = pixinfo->ReservedMask;
138
break;
139
default:
140
result = 1;
141
break;
142
}
143
return (result);
144
}
145
146
static int
147
efifb_from_gop(struct efi_fb *efifb, EFI_GRAPHICS_OUTPUT_PROTOCOL_MODE *mode,
148
EFI_GRAPHICS_OUTPUT_MODE_INFORMATION *info)
149
{
150
int result;
151
152
/*
153
* The Asus EEEPC 1025C, and possibly others,
154
* require the address to be masked.
155
*/
156
efifb->fb_addr =
157
#ifdef __i386__
158
mode->FrameBufferBase & 0xffffffff;
159
#else
160
mode->FrameBufferBase;
161
#endif
162
efifb->fb_size = mode->FrameBufferSize;
163
efifb->fb_height = info->VerticalResolution;
164
efifb->fb_width = info->HorizontalResolution;
165
efifb->fb_stride = info->PixelsPerScanLine;
166
result = efifb_mask_from_pixfmt(efifb, info->PixelFormat,
167
&info->PixelInformation);
168
return (result);
169
}
170
171
static ssize_t
172
efifb_uga_find_pixel(EFI_UGA_DRAW_PROTOCOL *uga, u_int line,
173
EFI_PCI_IO_PROTOCOL *pciio, uint64_t addr, uint64_t size)
174
{
175
EFI_UGA_PIXEL pix0, pix1;
176
uint8_t *data1, *data2;
177
size_t count, maxcount = 1024;
178
ssize_t ofs;
179
EFI_STATUS status;
180
u_int idx;
181
182
status = uga->Blt(uga, &pix0, EfiUgaVideoToBltBuffer,
183
0, line, 0, 0, 1, 1, 0);
184
if (EFI_ERROR(status)) {
185
printf("UGA BLT operation failed (video->buffer)");
186
return (-1);
187
}
188
pix1.Red = ~pix0.Red;
189
pix1.Green = ~pix0.Green;
190
pix1.Blue = ~pix0.Blue;
191
pix1.Reserved = 0;
192
193
data1 = calloc(maxcount, 2);
194
if (data1 == NULL) {
195
printf("Unable to allocate memory");
196
return (-1);
197
}
198
data2 = data1 + maxcount;
199
200
ofs = 0;
201
while (size > 0) {
202
count = min(size, maxcount);
203
204
status = pciio->Mem.Read(pciio, EfiPciIoWidthUint32,
205
EFI_PCI_IO_PASS_THROUGH_BAR, addr + ofs, count >> 2,
206
data1);
207
if (EFI_ERROR(status)) {
208
printf("Error reading frame buffer (before)");
209
goto fail;
210
}
211
status = uga->Blt(uga, &pix1, EfiUgaBltBufferToVideo,
212
0, 0, 0, line, 1, 1, 0);
213
if (EFI_ERROR(status)) {
214
printf("UGA BLT operation failed (modify)");
215
goto fail;
216
}
217
status = pciio->Mem.Read(pciio, EfiPciIoWidthUint32,
218
EFI_PCI_IO_PASS_THROUGH_BAR, addr + ofs, count >> 2,
219
data2);
220
if (EFI_ERROR(status)) {
221
printf("Error reading frame buffer (after)");
222
goto fail;
223
}
224
status = uga->Blt(uga, &pix0, EfiUgaBltBufferToVideo,
225
0, 0, 0, line, 1, 1, 0);
226
if (EFI_ERROR(status)) {
227
printf("UGA BLT operation failed (restore)");
228
goto fail;
229
}
230
for (idx = 0; idx < count; idx++) {
231
if (data1[idx] != data2[idx]) {
232
free(data1);
233
return (ofs + (idx & ~3));
234
}
235
}
236
ofs += count;
237
size -= count;
238
}
239
printf("No change detected in frame buffer");
240
241
fail:
242
printf(" -- error %lu\n", EFI_ERROR_CODE(status));
243
free(data1);
244
return (-1);
245
}
246
247
static EFI_PCI_IO_PROTOCOL *
248
efifb_uga_get_pciio(void)
249
{
250
EFI_PCI_IO_PROTOCOL *pciio;
251
EFI_HANDLE *buf, *hp;
252
EFI_STATUS status;
253
UINTN bufsz;
254
255
/* Get all handles that support the UGA protocol. */
256
bufsz = 0;
257
status = BS->LocateHandle(ByProtocol, &uga_guid, NULL, &bufsz, NULL);
258
if (status != EFI_BUFFER_TOO_SMALL)
259
return (NULL);
260
buf = malloc(bufsz);
261
status = BS->LocateHandle(ByProtocol, &uga_guid, NULL, &bufsz, buf);
262
if (status != EFI_SUCCESS) {
263
free(buf);
264
return (NULL);
265
}
266
bufsz /= sizeof(EFI_HANDLE);
267
268
/* Get the PCI I/O interface of the first handle that supports it. */
269
pciio = NULL;
270
for (hp = buf; hp < buf + bufsz; hp++) {
271
status = OpenProtocolByHandle(*hp, &pciio_guid,
272
(void **)&pciio);
273
if (status == EFI_SUCCESS) {
274
free(buf);
275
return (pciio);
276
}
277
}
278
free(buf);
279
return (NULL);
280
}
281
282
static EFI_STATUS
283
efifb_uga_locate_framebuffer(EFI_PCI_IO_PROTOCOL *pciio, uint64_t *addrp,
284
uint64_t *sizep)
285
{
286
uint8_t *resattr;
287
uint64_t addr, size;
288
EFI_STATUS status;
289
u_int bar;
290
291
if (pciio == NULL)
292
return (EFI_DEVICE_ERROR);
293
294
/* Attempt to get the frame buffer address (imprecise). */
295
*addrp = 0;
296
*sizep = 0;
297
for (bar = 0; bar < 6; bar++) {
298
status = pciio->GetBarAttributes(pciio, bar, NULL,
299
(void **)&resattr);
300
if (status != EFI_SUCCESS)
301
continue;
302
/* XXX magic offsets and constants. */
303
if (resattr[0] == 0x87 && resattr[3] == 0) {
304
/* 32-bit address space descriptor (MEMIO) */
305
addr = le32dec(resattr + 10);
306
size = le32dec(resattr + 22);
307
} else if (resattr[0] == 0x8a && resattr[3] == 0) {
308
/* 64-bit address space descriptor (MEMIO) */
309
addr = le64dec(resattr + 14);
310
size = le64dec(resattr + 38);
311
} else {
312
addr = 0;
313
size = 0;
314
}
315
BS->FreePool(resattr);
316
if (addr == 0 || size == 0)
317
continue;
318
319
/* We assume the largest BAR is the frame buffer. */
320
if (size > *sizep) {
321
*addrp = addr;
322
*sizep = size;
323
}
324
}
325
return ((*addrp == 0 || *sizep == 0) ? EFI_DEVICE_ERROR : 0);
326
}
327
328
static int
329
efifb_from_uga(struct efi_fb *efifb)
330
{
331
EFI_PCI_IO_PROTOCOL *pciio;
332
char *ev, *p;
333
EFI_STATUS status;
334
ssize_t offset;
335
uint64_t fbaddr;
336
uint32_t horiz, vert, stride;
337
uint32_t np, depth, refresh;
338
339
status = uga->GetMode(uga, &horiz, &vert, &depth, &refresh);
340
if (EFI_ERROR(status))
341
return (1);
342
efifb->fb_height = vert;
343
efifb->fb_width = horiz;
344
/* Paranoia... */
345
if (efifb->fb_height == 0 || efifb->fb_width == 0)
346
return (1);
347
348
/* The color masks are fixed AFAICT. */
349
efifb_mask_from_pixfmt(efifb, PixelBlueGreenRedReserved8BitPerColor,
350
NULL);
351
352
/* pciio can be NULL on return! */
353
pciio = efifb_uga_get_pciio();
354
355
/* Try to find the frame buffer. */
356
status = efifb_uga_locate_framebuffer(pciio, &efifb->fb_addr,
357
&efifb->fb_size);
358
if (EFI_ERROR(status)) {
359
efifb->fb_addr = 0;
360
efifb->fb_size = 0;
361
}
362
363
/*
364
* There's no reliable way to detect the frame buffer or the
365
* offset within the frame buffer of the visible region, nor
366
* the stride. Our only option is to look at the system and
367
* fill in the blanks based on that. Luckily, UGA was mostly
368
* only used on Apple hardware.
369
*/
370
offset = -1;
371
ev = getenv("smbios.system.maker");
372
if (ev != NULL && !strcmp(ev, "Apple Inc.")) {
373
ev = getenv("smbios.system.product");
374
if (ev != NULL && !strcmp(ev, "iMac7,1")) {
375
/* These are the expected values we should have. */
376
horiz = 1680;
377
vert = 1050;
378
fbaddr = 0xc0000000;
379
/* These are the missing bits. */
380
offset = 0x10000;
381
stride = 1728;
382
} else if (ev != NULL && !strcmp(ev, "MacBook3,1")) {
383
/* These are the expected values we should have. */
384
horiz = 1280;
385
vert = 800;
386
fbaddr = 0xc0000000;
387
/* These are the missing bits. */
388
offset = 0x0;
389
stride = 2048;
390
}
391
}
392
393
/*
394
* If this is hardware we know, make sure that it looks familiar
395
* before we accept our hardcoded values.
396
*/
397
if (offset >= 0 && efifb->fb_width == horiz &&
398
efifb->fb_height == vert && efifb->fb_addr == fbaddr) {
399
efifb->fb_addr += offset;
400
efifb->fb_size -= offset;
401
efifb->fb_stride = stride;
402
return (0);
403
} else if (offset >= 0) {
404
printf("Hardware make/model known, but graphics not "
405
"as expected.\n");
406
printf("Console may not work!\n");
407
}
408
409
/*
410
* The stride is equal or larger to the width. Often it's the
411
* next larger power of two. We'll start with that...
412
*/
413
efifb->fb_stride = efifb->fb_width;
414
do {
415
np = efifb->fb_stride & (efifb->fb_stride - 1);
416
if (np) {
417
efifb->fb_stride |= (np - 1);
418
efifb->fb_stride++;
419
}
420
} while (np);
421
422
ev = getenv("hw.efifb.address");
423
if (ev == NULL) {
424
if (efifb->fb_addr == 0) {
425
printf("Please set hw.efifb.address and "
426
"hw.efifb.stride.\n");
427
return (1);
428
}
429
430
/*
431
* The visible part of the frame buffer may not start at
432
* offset 0, so try to detect it. Note that we may not
433
* always be able to read from the frame buffer, which
434
* means that we may not be able to detect anything. In
435
* that case, we would take a long time scanning for a
436
* pixel change in the frame buffer, which would have it
437
* appear that we're hanging, so we limit the scan to
438
* 1/256th of the frame buffer. This number is mostly
439
* based on PR 202730 and the fact that on a MacBoook,
440
* where we can't read from the frame buffer the offset
441
* of the visible region is 0. In short: we want to scan
442
* enough to handle all adapters that have an offset
443
* larger than 0 and we want to scan as little as we can
444
* to not appear to hang when we can't read from the
445
* frame buffer.
446
*/
447
offset = efifb_uga_find_pixel(uga, 0, pciio, efifb->fb_addr,
448
efifb->fb_size >> 8);
449
if (offset == -1) {
450
printf("Unable to reliably detect frame buffer.\n");
451
} else if (offset > 0) {
452
efifb->fb_addr += offset;
453
efifb->fb_size -= offset;
454
}
455
} else {
456
offset = 0;
457
efifb->fb_size = efifb->fb_height * efifb->fb_stride * 4;
458
efifb->fb_addr = strtoul(ev, &p, 0);
459
if (*p != '\0')
460
return (1);
461
}
462
463
ev = getenv("hw.efifb.stride");
464
if (ev == NULL) {
465
if (pciio != NULL && offset != -1) {
466
/* Determine the stride. */
467
offset = efifb_uga_find_pixel(uga, 1, pciio,
468
efifb->fb_addr, horiz * 8);
469
if (offset != -1)
470
efifb->fb_stride = offset >> 2;
471
} else {
472
printf("Unable to reliably detect the stride.\n");
473
}
474
} else {
475
efifb->fb_stride = strtoul(ev, &p, 0);
476
if (*p != '\0')
477
return (1);
478
}
479
480
/*
481
* We finalized on the stride, so recalculate the size of the
482
* frame buffer.
483
*/
484
efifb->fb_size = efifb->fb_height * efifb->fb_stride * 4;
485
return (0);
486
}
487
488
/*
489
* Fetch EDID info. Caller must free the buffer.
490
*/
491
static struct vesa_edid_info *
492
efifb_gop_get_edid(EFI_HANDLE h)
493
{
494
const uint8_t magic[] = EDID_MAGIC;
495
EFI_EDID_ACTIVE_PROTOCOL *edid;
496
struct vesa_edid_info *edid_infop;
497
EFI_GUID *guid;
498
EFI_STATUS status;
499
size_t size;
500
501
guid = &active_edid_guid;
502
status = BS->OpenProtocol(h, guid, (void **)&edid, IH, NULL,
503
EFI_OPEN_PROTOCOL_GET_PROTOCOL);
504
if (status != EFI_SUCCESS ||
505
edid->SizeOfEdid == 0) {
506
guid = &discovered_edid_guid;
507
status = BS->OpenProtocol(h, guid, (void **)&edid, IH, NULL,
508
EFI_OPEN_PROTOCOL_GET_PROTOCOL);
509
if (status != EFI_SUCCESS ||
510
edid->SizeOfEdid == 0)
511
return (NULL);
512
}
513
514
size = MAX(sizeof(*edid_infop), edid->SizeOfEdid);
515
516
edid_infop = calloc(1, size);
517
if (edid_infop == NULL)
518
return (NULL);
519
520
memcpy(edid_infop, edid->Edid, edid->SizeOfEdid);
521
522
/* Validate EDID */
523
if (memcmp(edid_infop, magic, sizeof (magic)) != 0)
524
goto error;
525
526
if (edid_infop->header.version != 1)
527
goto error;
528
529
return (edid_infop);
530
error:
531
free(edid_infop);
532
return (NULL);
533
}
534
535
static bool
536
efifb_get_edid(edid_res_list_t *res)
537
{
538
bool rv = false;
539
540
if (edid_info == NULL)
541
edid_info = efifb_gop_get_edid(gop_handle);
542
543
if (edid_info != NULL)
544
rv = gfx_get_edid_resolution(edid_info, res);
545
546
return (rv);
547
}
548
549
bool
550
efi_has_gop(void)
551
{
552
EFI_STATUS status;
553
EFI_HANDLE *hlist;
554
UINTN hsize;
555
556
hsize = 0;
557
hlist = NULL;
558
status = BS->LocateHandle(ByProtocol, &gop_guid, NULL, &hsize, hlist);
559
560
return (status == EFI_BUFFER_TOO_SMALL);
561
}
562
563
564
int
565
efi_find_framebuffer(teken_gfx_t *gfx_state)
566
{
567
EFI_PHYSICAL_ADDRESS ptr;
568
EFI_HANDLE *hlist;
569
UINTN nhandles, i, hsize;
570
struct efi_fb efifb;
571
EFI_STATUS status;
572
int rv;
573
574
gfx_state->tg_fb_type = FB_TEXT;
575
576
hsize = 0;
577
hlist = NULL;
578
status = BS->LocateHandle(ByProtocol, &gop_guid, NULL, &hsize, hlist);
579
if (status == EFI_BUFFER_TOO_SMALL) {
580
hlist = malloc(hsize);
581
if (hlist == NULL)
582
return (ENOMEM);
583
status = BS->LocateHandle(ByProtocol, &gop_guid, NULL, &hsize,
584
hlist);
585
if (EFI_ERROR(status))
586
free(hlist);
587
}
588
if (EFI_ERROR(status))
589
return (efi_status_to_errno(status));
590
591
nhandles = hsize / sizeof(*hlist);
592
593
/*
594
* Search for ConOut protocol, if not found, use first handle.
595
*/
596
gop_handle = NULL;
597
for (i = 0; i < nhandles; i++) {
598
EFI_GRAPHICS_OUTPUT *tgop;
599
void *dummy;
600
601
status = OpenProtocolByHandle(hlist[i], &gop_guid, (void **)&tgop);
602
if (status != EFI_SUCCESS)
603
continue;
604
605
if (tgop->Mode->Info->PixelFormat == PixelBltOnly ||
606
tgop->Mode->Info->PixelFormat >= PixelFormatMax)
607
continue;
608
609
status = OpenProtocolByHandle(hlist[i], &conout_guid, &dummy);
610
if (status == EFI_SUCCESS) {
611
gop_handle = hlist[i];
612
gop = tgop;
613
break;
614
} else if (gop_handle == NULL) {
615
gop_handle = hlist[i];
616
gop = tgop;
617
}
618
}
619
620
free(hlist);
621
622
if (gop_handle != NULL) {
623
gfx_state->tg_fb_type = FB_GOP;
624
gfx_state->tg_private = gop;
625
if (edid_info == NULL)
626
edid_info = efifb_gop_get_edid(gop_handle);
627
} else {
628
status = BS->LocateProtocol(&uga_guid, NULL, (VOID **)&uga);
629
if (status == EFI_SUCCESS) {
630
gfx_state->tg_fb_type = FB_UGA;
631
gfx_state->tg_private = uga;
632
} else {
633
return (1);
634
}
635
}
636
637
switch (gfx_state->tg_fb_type) {
638
case FB_GOP:
639
rv = efifb_from_gop(&efifb, gop->Mode, gop->Mode->Info);
640
break;
641
642
case FB_UGA:
643
rv = efifb_from_uga(&efifb);
644
break;
645
646
default:
647
return (1);
648
}
649
650
gfx_state->tg_fb.fb_addr = efifb.fb_addr;
651
gfx_state->tg_fb.fb_size = efifb.fb_size;
652
gfx_state->tg_fb.fb_height = efifb.fb_height;
653
gfx_state->tg_fb.fb_width = efifb.fb_width;
654
gfx_state->tg_fb.fb_stride = efifb.fb_stride;
655
gfx_state->tg_fb.fb_mask_red = efifb.fb_mask_red;
656
gfx_state->tg_fb.fb_mask_green = efifb.fb_mask_green;
657
gfx_state->tg_fb.fb_mask_blue = efifb.fb_mask_blue;
658
gfx_state->tg_fb.fb_mask_reserved = efifb.fb_mask_reserved;
659
660
gfx_state->tg_fb.fb_bpp = fls(efifb.fb_mask_red | efifb.fb_mask_green |
661
efifb.fb_mask_blue | efifb.fb_mask_reserved);
662
663
if (gfx_state->tg_shadow_fb != NULL)
664
BS->FreePages((uintptr_t)gfx_state->tg_shadow_fb,
665
gfx_state->tg_shadow_sz);
666
gfx_state->tg_shadow_sz =
667
EFI_SIZE_TO_PAGES(efifb.fb_height * efifb.fb_width *
668
sizeof(EFI_GRAPHICS_OUTPUT_BLT_PIXEL));
669
status = BS->AllocatePages(AllocateAnyPages, EfiLoaderData,
670
gfx_state->tg_shadow_sz, &ptr);
671
gfx_state->tg_shadow_fb = status == EFI_SUCCESS ?
672
(uint32_t *)(uintptr_t)ptr : NULL;
673
674
return (0);
675
}
676
677
static void
678
print_efifb(int mode, struct efi_fb *efifb, int verbose)
679
{
680
u_int depth;
681
682
if (mode >= 0)
683
printf("mode %d: ", mode);
684
depth = efifb_color_depth(efifb);
685
printf("%ux%ux%u, stride=%u", efifb->fb_width, efifb->fb_height,
686
depth, efifb->fb_stride);
687
if (verbose) {
688
printf("\n frame buffer: address=%jx, size=%jx",
689
(uintmax_t)efifb->fb_addr, (uintmax_t)efifb->fb_size);
690
printf("\n color mask: R=%08x, G=%08x, B=%08x\n",
691
efifb->fb_mask_red, efifb->fb_mask_green,
692
efifb->fb_mask_blue);
693
}
694
}
695
696
static bool
697
efi_resolution_compare(struct named_resolution *res, const char *cmp)
698
{
699
700
if (strcasecmp(res->name, cmp) == 0)
701
return (true);
702
if (res->alias != NULL && strcasecmp(res->alias, cmp) == 0)
703
return (true);
704
return (false);
705
}
706
707
708
static void
709
efi_get_max_resolution(int *width, int *height)
710
{
711
struct named_resolution *res;
712
char *maxres;
713
char *height_start, *width_start;
714
int idx;
715
716
*width = *height = 0;
717
maxres = getenv("efi_max_resolution");
718
/* No max_resolution set? Bail out; choose highest resolution */
719
if (maxres == NULL)
720
return;
721
/* See if it matches one of our known resolutions */
722
for (idx = 0; idx < nitems(resolutions); ++idx) {
723
res = &resolutions[idx];
724
if (efi_resolution_compare(res, maxres)) {
725
*width = res->width;
726
*height = res->height;
727
return;
728
}
729
}
730
/* Not a known resolution, try to parse it; make a copy we can modify */
731
maxres = strdup(maxres);
732
if (maxres == NULL)
733
return;
734
height_start = strchr(maxres, 'x');
735
if (height_start == NULL) {
736
free(maxres);
737
return;
738
}
739
width_start = maxres;
740
*height_start++ = 0;
741
/* Errors from this will effectively mean "no max" */
742
*width = (int)strtol(width_start, NULL, 0);
743
*height = (int)strtol(height_start, NULL, 0);
744
free(maxres);
745
}
746
747
static int
748
gop_autoresize(void)
749
{
750
struct efi_fb efifb;
751
EFI_GRAPHICS_OUTPUT_MODE_INFORMATION *info;
752
EFI_STATUS status;
753
UINTN infosz;
754
UINT32 best_mode, currdim, maxdim, mode;
755
int height, max_height, max_width, width;
756
757
best_mode = maxdim = 0;
758
efi_get_max_resolution(&max_width, &max_height);
759
for (mode = 0; mode < gop->Mode->MaxMode; mode++) {
760
status = gop->QueryMode(gop, mode, &infosz, &info);
761
if (EFI_ERROR(status))
762
continue;
763
efifb_from_gop(&efifb, gop->Mode, info);
764
width = info->HorizontalResolution;
765
height = info->VerticalResolution;
766
currdim = width * height;
767
if (currdim > maxdim) {
768
if ((max_width != 0 && width > max_width) ||
769
(max_height != 0 && height > max_height))
770
continue;
771
maxdim = currdim;
772
best_mode = mode;
773
}
774
}
775
776
if (maxdim != 0) {
777
status = gop->SetMode(gop, best_mode);
778
if (EFI_ERROR(status)) {
779
snprintf(command_errbuf, sizeof(command_errbuf),
780
"gop_autoresize: Unable to set mode to %u (error=%lu)",
781
mode, EFI_ERROR_CODE(status));
782
return (CMD_ERROR);
783
}
784
(void) cons_update_mode(true);
785
}
786
return (CMD_OK);
787
}
788
789
static int
790
text_autoresize()
791
{
792
SIMPLE_TEXT_OUTPUT_INTERFACE *conout;
793
EFI_STATUS status;
794
UINTN i, max_dim, best_mode, cols, rows;
795
796
conout = ST->ConOut;
797
max_dim = best_mode = 0;
798
for (i = 0; i < conout->Mode->MaxMode; i++) {
799
status = conout->QueryMode(conout, i, &cols, &rows);
800
if (EFI_ERROR(status))
801
continue;
802
if (cols * rows > max_dim) {
803
max_dim = cols * rows;
804
best_mode = i;
805
}
806
}
807
if (max_dim > 0)
808
conout->SetMode(conout, best_mode);
809
(void) cons_update_mode(true);
810
return (CMD_OK);
811
}
812
813
static int
814
uga_autoresize(void)
815
{
816
817
return (text_autoresize());
818
}
819
820
COMMAND_SET(efi_autoresize, "efi-autoresizecons", "EFI Auto-resize Console", command_autoresize);
821
822
static int
823
command_autoresize(int argc, char *argv[])
824
{
825
char *textmode;
826
827
textmode = getenv("hw.vga.textmode");
828
/* If it's set and non-zero, we'll select a console mode instead */
829
if (textmode != NULL && strcmp(textmode, "0") != 0)
830
return (text_autoresize());
831
832
if (gop != NULL)
833
return (gop_autoresize());
834
835
if (uga != NULL)
836
return (uga_autoresize());
837
838
snprintf(command_errbuf, sizeof(command_errbuf),
839
"%s: Neither Graphics Output Protocol nor Universal Graphics Adapter present",
840
argv[0]);
841
842
/*
843
* Default to text_autoresize if we have neither GOP or UGA. This won't
844
* give us the most ideal resolution, but it will at least leave us
845
* functional rather than failing the boot for an objectively bad
846
* reason.
847
*/
848
return (text_autoresize());
849
}
850
851
COMMAND_SET(gop, "gop", "graphics output protocol", command_gop);
852
853
static int
854
command_gop(int argc, char *argv[])
855
{
856
struct efi_fb efifb;
857
EFI_STATUS status;
858
u_int mode;
859
extern bool ignore_gop_blt;
860
861
if (gop == NULL) {
862
snprintf(command_errbuf, sizeof(command_errbuf),
863
"%s: Graphics Output Protocol not present", argv[0]);
864
return (CMD_ERROR);
865
}
866
867
if (argc < 2)
868
goto usage;
869
870
if (strcmp(argv[1], "set") == 0) {
871
char *cp;
872
873
if (argc != 3)
874
goto usage;
875
mode = strtol(argv[2], &cp, 0);
876
if (cp[0] != '\0') {
877
sprintf(command_errbuf, "mode is an integer");
878
return (CMD_ERROR);
879
}
880
status = gop->SetMode(gop, mode);
881
if (EFI_ERROR(status)) {
882
snprintf(command_errbuf, sizeof(command_errbuf),
883
"%s: Unable to set mode to %u (error=%lu)",
884
argv[0], mode, EFI_ERROR_CODE(status));
885
return (CMD_ERROR);
886
}
887
(void) cons_update_mode(true);
888
} else if (strcmp(argv[1], "blt") == 0) {
889
/*
890
* "blt on" does allow gop->Blt() to be used (default).
891
* "blt off" does block gop->Blt() to be used and use
892
* software rendering instead.
893
*/
894
if (argc != 3)
895
goto usage;
896
if (strcmp(argv[2], "on") == 0)
897
ignore_gop_blt = false;
898
else if (strcmp(argv[2], "off") == 0)
899
ignore_gop_blt = true;
900
else
901
goto usage;
902
} else if (strcmp(argv[1], "off") == 0) {
903
/*
904
* Tell console to use SimpleTextOutput protocol.
905
* This means that we do not render the glyphs, but rely on
906
* UEFI firmware to draw on ConsOut device(s).
907
*/
908
(void) cons_update_mode(false);
909
} else if (strcmp(argv[1], "get") == 0) {
910
edid_res_list_t res;
911
912
if (argc != 2)
913
goto usage;
914
TAILQ_INIT(&res);
915
efifb_from_gop(&efifb, gop->Mode, gop->Mode->Info);
916
if (efifb_get_edid(&res)) {
917
struct resolution *rp;
918
919
printf("EDID");
920
while ((rp = TAILQ_FIRST(&res)) != NULL) {
921
printf(" %dx%d", rp->width, rp->height);
922
TAILQ_REMOVE(&res, rp, next);
923
free(rp);
924
}
925
printf("\n");
926
} else {
927
printf("no EDID information\n");
928
}
929
print_efifb(gop->Mode->Mode, &efifb, 1);
930
printf("\n");
931
} else if (strcmp(argv[1], "list") == 0) {
932
EFI_GRAPHICS_OUTPUT_MODE_INFORMATION *info;
933
UINTN infosz;
934
935
if (argc != 2)
936
goto usage;
937
938
pager_open();
939
for (mode = 0; mode < gop->Mode->MaxMode; mode++) {
940
status = gop->QueryMode(gop, mode, &infosz, &info);
941
if (EFI_ERROR(status))
942
continue;
943
efifb_from_gop(&efifb, gop->Mode, info);
944
print_efifb(mode, &efifb, 0);
945
if (pager_output("\n"))
946
break;
947
}
948
pager_close();
949
}
950
return (CMD_OK);
951
952
usage:
953
snprintf(command_errbuf, sizeof(command_errbuf),
954
"usage: %s [list | get | set <mode> | off | blt <on|off>]", argv[0]);
955
return (CMD_ERROR);
956
}
957
958
COMMAND_SET(uga, "uga", "universal graphics adapter", command_uga);
959
960
static int
961
command_uga(int argc, char *argv[])
962
{
963
struct efi_fb efifb;
964
965
if (uga == NULL) {
966
snprintf(command_errbuf, sizeof(command_errbuf),
967
"%s: UGA Protocol not present", argv[0]);
968
return (CMD_ERROR);
969
}
970
971
if (argc != 1)
972
goto usage;
973
974
if (efifb_from_uga(&efifb) != CMD_OK) {
975
snprintf(command_errbuf, sizeof(command_errbuf),
976
"%s: Unable to get UGA information", argv[0]);
977
return (CMD_ERROR);
978
}
979
980
print_efifb(-1, &efifb, 1);
981
printf("\n");
982
return (CMD_OK);
983
984
usage:
985
snprintf(command_errbuf, sizeof(command_errbuf), "usage: %s", argv[0]);
986
return (CMD_ERROR);
987
}
988
989