Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/drivers/firmware/efi/libstub/gop.c
26483 views
1
// SPDX-License-Identifier: GPL-2.0
2
/* -----------------------------------------------------------------------
3
*
4
* Copyright 2011 Intel Corporation; author Matt Fleming
5
*
6
* ----------------------------------------------------------------------- */
7
8
#include <linux/bitops.h>
9
#include <linux/ctype.h>
10
#include <linux/efi.h>
11
#include <linux/screen_info.h>
12
#include <linux/string.h>
13
#include <asm/efi.h>
14
#include <asm/setup.h>
15
16
#include "efistub.h"
17
18
enum efi_cmdline_option {
19
EFI_CMDLINE_NONE,
20
EFI_CMDLINE_MODE_NUM,
21
EFI_CMDLINE_RES,
22
EFI_CMDLINE_AUTO,
23
EFI_CMDLINE_LIST
24
};
25
26
static struct {
27
enum efi_cmdline_option option;
28
union {
29
u32 mode;
30
struct {
31
u32 width, height;
32
int format;
33
u8 depth;
34
} res;
35
};
36
} cmdline = { .option = EFI_CMDLINE_NONE };
37
38
static bool parse_modenum(char *option, char **next)
39
{
40
u32 m;
41
42
if (!strstarts(option, "mode="))
43
return false;
44
option += strlen("mode=");
45
m = simple_strtoull(option, &option, 0);
46
if (*option && *option++ != ',')
47
return false;
48
cmdline.option = EFI_CMDLINE_MODE_NUM;
49
cmdline.mode = m;
50
51
*next = option;
52
return true;
53
}
54
55
static bool parse_res(char *option, char **next)
56
{
57
u32 w, h, d = 0;
58
int pf = -1;
59
60
if (!isdigit(*option))
61
return false;
62
w = simple_strtoull(option, &option, 10);
63
if (*option++ != 'x' || !isdigit(*option))
64
return false;
65
h = simple_strtoull(option, &option, 10);
66
if (*option == '-') {
67
option++;
68
if (strstarts(option, "rgb")) {
69
option += strlen("rgb");
70
pf = PIXEL_RGB_RESERVED_8BIT_PER_COLOR;
71
} else if (strstarts(option, "bgr")) {
72
option += strlen("bgr");
73
pf = PIXEL_BGR_RESERVED_8BIT_PER_COLOR;
74
} else if (isdigit(*option))
75
d = simple_strtoull(option, &option, 10);
76
else
77
return false;
78
}
79
if (*option && *option++ != ',')
80
return false;
81
cmdline.option = EFI_CMDLINE_RES;
82
cmdline.res.width = w;
83
cmdline.res.height = h;
84
cmdline.res.format = pf;
85
cmdline.res.depth = d;
86
87
*next = option;
88
return true;
89
}
90
91
static bool parse_auto(char *option, char **next)
92
{
93
if (!strstarts(option, "auto"))
94
return false;
95
option += strlen("auto");
96
if (*option && *option++ != ',')
97
return false;
98
cmdline.option = EFI_CMDLINE_AUTO;
99
100
*next = option;
101
return true;
102
}
103
104
static bool parse_list(char *option, char **next)
105
{
106
if (!strstarts(option, "list"))
107
return false;
108
option += strlen("list");
109
if (*option && *option++ != ',')
110
return false;
111
cmdline.option = EFI_CMDLINE_LIST;
112
113
*next = option;
114
return true;
115
}
116
117
void efi_parse_option_graphics(char *option)
118
{
119
while (*option) {
120
if (parse_modenum(option, &option))
121
continue;
122
if (parse_res(option, &option))
123
continue;
124
if (parse_auto(option, &option))
125
continue;
126
if (parse_list(option, &option))
127
continue;
128
129
while (*option && *option++ != ',')
130
;
131
}
132
}
133
134
static u32 choose_mode_modenum(efi_graphics_output_protocol_t *gop)
135
{
136
efi_graphics_output_mode_info_t *info __free(efi_pool) = NULL;
137
efi_graphics_output_protocol_mode_t *mode;
138
unsigned long info_size;
139
u32 max_mode, cur_mode;
140
efi_status_t status;
141
int pf;
142
143
mode = efi_table_attr(gop, mode);
144
145
cur_mode = efi_table_attr(mode, mode);
146
if (cmdline.mode == cur_mode)
147
return cur_mode;
148
149
max_mode = efi_table_attr(mode, max_mode);
150
if (cmdline.mode >= max_mode) {
151
efi_err("Requested mode is invalid\n");
152
return cur_mode;
153
}
154
155
status = efi_call_proto(gop, query_mode, cmdline.mode, &info_size, &info);
156
if (status != EFI_SUCCESS) {
157
efi_err("Couldn't get mode information\n");
158
return cur_mode;
159
}
160
161
pf = info->pixel_format;
162
if (pf == PIXEL_BLT_ONLY || pf >= PIXEL_FORMAT_MAX) {
163
efi_err("Invalid PixelFormat\n");
164
return cur_mode;
165
}
166
167
return cmdline.mode;
168
}
169
170
static u32 choose_mode(efi_graphics_output_protocol_t *gop,
171
bool (*match)(const efi_graphics_output_mode_info_t *, u32, void *),
172
void *ctx)
173
{
174
efi_graphics_output_protocol_mode_t *mode = efi_table_attr(gop, mode);
175
u32 max_mode = efi_table_attr(mode, max_mode);
176
177
for (u32 m = 0; m < max_mode; m++) {
178
efi_graphics_output_mode_info_t *info __free(efi_pool) = NULL;
179
unsigned long info_size;
180
efi_status_t status;
181
182
status = efi_call_proto(gop, query_mode, m, &info_size, &info);
183
if (status != EFI_SUCCESS)
184
continue;
185
186
if (match(info, m, ctx))
187
return m;
188
}
189
return (unsigned long)ctx;
190
}
191
192
static u8 pixel_bpp(int pixel_format, efi_pixel_bitmask_t pixel_info)
193
{
194
if (pixel_format == PIXEL_BIT_MASK) {
195
u32 mask = pixel_info.red_mask | pixel_info.green_mask |
196
pixel_info.blue_mask | pixel_info.reserved_mask;
197
if (!mask)
198
return 0;
199
return __fls(mask) - __ffs(mask) + 1;
200
} else
201
return 32;
202
}
203
204
static bool match_res(const efi_graphics_output_mode_info_t *info, u32 mode, void *ctx)
205
{
206
efi_pixel_bitmask_t pi = info->pixel_information;
207
int pf = info->pixel_format;
208
209
if (pf == PIXEL_BLT_ONLY || pf >= PIXEL_FORMAT_MAX)
210
return false;
211
212
return cmdline.res.width == info->horizontal_resolution &&
213
cmdline.res.height == info->vertical_resolution &&
214
(cmdline.res.format < 0 || cmdline.res.format == pf) &&
215
(!cmdline.res.depth || cmdline.res.depth == pixel_bpp(pf, pi));
216
}
217
218
static u32 choose_mode_res(efi_graphics_output_protocol_t *gop)
219
{
220
efi_graphics_output_protocol_mode_t *mode = efi_table_attr(gop, mode);
221
unsigned long cur_mode = efi_table_attr(mode, mode);
222
223
if (match_res(efi_table_attr(mode, info), cur_mode, NULL))
224
return cur_mode;
225
226
return choose_mode(gop, match_res, (void *)cur_mode);
227
}
228
229
struct match {
230
u32 mode;
231
u32 area;
232
u8 depth;
233
};
234
235
static bool match_auto(const efi_graphics_output_mode_info_t *info, u32 mode, void *ctx)
236
{
237
u32 area = info->horizontal_resolution * info->vertical_resolution;
238
efi_pixel_bitmask_t pi = info->pixel_information;
239
int pf = info->pixel_format;
240
u8 depth = pixel_bpp(pf, pi);
241
struct match *m = ctx;
242
243
if (pf == PIXEL_BLT_ONLY || pf >= PIXEL_FORMAT_MAX)
244
return false;
245
246
if (area > m->area || (area == m->area && depth > m->depth))
247
*m = (struct match){ mode, area, depth };
248
249
return false;
250
}
251
252
static u32 choose_mode_auto(efi_graphics_output_protocol_t *gop)
253
{
254
struct match match = {};
255
256
choose_mode(gop, match_auto, &match);
257
258
return match.mode;
259
}
260
261
static bool match_list(const efi_graphics_output_mode_info_t *info, u32 mode, void *ctx)
262
{
263
efi_pixel_bitmask_t pi = info->pixel_information;
264
u32 cur_mode = (unsigned long)ctx;
265
int pf = info->pixel_format;
266
const char *dstr;
267
u8 depth = 0;
268
bool valid;
269
270
valid = !(pf == PIXEL_BLT_ONLY || pf >= PIXEL_FORMAT_MAX);
271
272
switch (pf) {
273
case PIXEL_RGB_RESERVED_8BIT_PER_COLOR:
274
dstr = "rgb";
275
break;
276
case PIXEL_BGR_RESERVED_8BIT_PER_COLOR:
277
dstr = "bgr";
278
break;
279
case PIXEL_BIT_MASK:
280
dstr = "";
281
depth = pixel_bpp(pf, pi);
282
break;
283
case PIXEL_BLT_ONLY:
284
dstr = "blt";
285
break;
286
default:
287
dstr = "xxx";
288
break;
289
}
290
291
efi_printk("Mode %3u %c%c: Resolution %ux%u-%s%.0hhu\n",
292
mode,
293
(mode == cur_mode) ? '*' : ' ',
294
!valid ? '-' : ' ',
295
info->horizontal_resolution,
296
info->vertical_resolution,
297
dstr, depth);
298
299
return false;
300
}
301
302
static u32 choose_mode_list(efi_graphics_output_protocol_t *gop)
303
{
304
efi_graphics_output_protocol_mode_t *mode = efi_table_attr(gop, mode);
305
unsigned long cur_mode = efi_table_attr(mode, mode);
306
u32 max_mode = efi_table_attr(mode, max_mode);
307
efi_input_key_t key;
308
efi_status_t status;
309
310
efi_printk("Available graphics modes are 0-%u\n", max_mode-1);
311
efi_puts(" * = current mode\n"
312
" - = unusable mode\n");
313
314
choose_mode(gop, match_list, (void *)cur_mode);
315
316
efi_puts("\nPress any key to continue (or wait 10 seconds)\n");
317
status = efi_wait_for_key(10 * EFI_USEC_PER_SEC, &key);
318
if (status != EFI_SUCCESS && status != EFI_TIMEOUT) {
319
efi_err("Unable to read key, continuing in 10 seconds\n");
320
efi_bs_call(stall, 10 * EFI_USEC_PER_SEC);
321
}
322
323
return cur_mode;
324
}
325
326
static void set_mode(efi_graphics_output_protocol_t *gop)
327
{
328
efi_graphics_output_protocol_mode_t *mode;
329
u32 cur_mode, new_mode;
330
331
switch (cmdline.option) {
332
case EFI_CMDLINE_MODE_NUM:
333
new_mode = choose_mode_modenum(gop);
334
break;
335
case EFI_CMDLINE_RES:
336
new_mode = choose_mode_res(gop);
337
break;
338
case EFI_CMDLINE_AUTO:
339
new_mode = choose_mode_auto(gop);
340
break;
341
case EFI_CMDLINE_LIST:
342
new_mode = choose_mode_list(gop);
343
break;
344
default:
345
return;
346
}
347
348
mode = efi_table_attr(gop, mode);
349
cur_mode = efi_table_attr(mode, mode);
350
351
if (new_mode == cur_mode)
352
return;
353
354
if (efi_call_proto(gop, set_mode, new_mode) != EFI_SUCCESS)
355
efi_err("Failed to set requested mode\n");
356
}
357
358
static void find_bits(u32 mask, u8 *pos, u8 *size)
359
{
360
if (!mask) {
361
*pos = *size = 0;
362
return;
363
}
364
365
/* UEFI spec guarantees that the set bits are contiguous */
366
*pos = __ffs(mask);
367
*size = __fls(mask) - *pos + 1;
368
}
369
370
static void
371
setup_pixel_info(struct screen_info *si, u32 pixels_per_scan_line,
372
efi_pixel_bitmask_t pixel_info, int pixel_format)
373
{
374
if (pixel_format == PIXEL_BIT_MASK) {
375
find_bits(pixel_info.red_mask,
376
&si->red_pos, &si->red_size);
377
find_bits(pixel_info.green_mask,
378
&si->green_pos, &si->green_size);
379
find_bits(pixel_info.blue_mask,
380
&si->blue_pos, &si->blue_size);
381
find_bits(pixel_info.reserved_mask,
382
&si->rsvd_pos, &si->rsvd_size);
383
si->lfb_depth = si->red_size + si->green_size +
384
si->blue_size + si->rsvd_size;
385
si->lfb_linelength = (pixels_per_scan_line * si->lfb_depth) / 8;
386
} else {
387
if (pixel_format == PIXEL_RGB_RESERVED_8BIT_PER_COLOR) {
388
si->red_pos = 0;
389
si->blue_pos = 16;
390
} else /* PIXEL_BGR_RESERVED_8BIT_PER_COLOR */ {
391
si->blue_pos = 0;
392
si->red_pos = 16;
393
}
394
395
si->green_pos = 8;
396
si->rsvd_pos = 24;
397
si->red_size = si->green_size =
398
si->blue_size = si->rsvd_size = 8;
399
400
si->lfb_depth = 32;
401
si->lfb_linelength = pixels_per_scan_line * 4;
402
}
403
}
404
405
static efi_graphics_output_protocol_t *find_gop(unsigned long num,
406
const efi_handle_t handles[])
407
{
408
efi_graphics_output_protocol_t *first_gop;
409
efi_handle_t h;
410
411
first_gop = NULL;
412
413
for_each_efi_handle(h, handles, num) {
414
efi_status_t status;
415
416
efi_graphics_output_protocol_t *gop;
417
efi_graphics_output_protocol_mode_t *mode;
418
efi_graphics_output_mode_info_t *info;
419
void *dummy = NULL;
420
421
status = efi_bs_call(handle_protocol, h,
422
&EFI_GRAPHICS_OUTPUT_PROTOCOL_GUID,
423
(void **)&gop);
424
if (status != EFI_SUCCESS)
425
continue;
426
427
mode = efi_table_attr(gop, mode);
428
info = efi_table_attr(mode, info);
429
if (info->pixel_format == PIXEL_BLT_ONLY ||
430
info->pixel_format >= PIXEL_FORMAT_MAX)
431
continue;
432
433
/*
434
* Systems that use the UEFI Console Splitter may
435
* provide multiple GOP devices, not all of which are
436
* backed by real hardware. The workaround is to search
437
* for a GOP implementing the ConOut protocol, and if
438
* one isn't found, to just fall back to the first GOP.
439
*
440
* Once we've found a GOP supporting ConOut,
441
* don't bother looking any further.
442
*/
443
status = efi_bs_call(handle_protocol, h,
444
&EFI_CONSOLE_OUT_DEVICE_GUID, &dummy);
445
if (status == EFI_SUCCESS)
446
return gop;
447
448
if (!first_gop)
449
first_gop = gop;
450
}
451
452
return first_gop;
453
}
454
455
efi_status_t efi_setup_gop(struct screen_info *si)
456
{
457
efi_handle_t *handles __free(efi_pool) = NULL;
458
efi_graphics_output_protocol_mode_t *mode;
459
efi_graphics_output_mode_info_t *info;
460
efi_graphics_output_protocol_t *gop;
461
efi_status_t status;
462
unsigned long num;
463
464
status = efi_bs_call(locate_handle_buffer, EFI_LOCATE_BY_PROTOCOL,
465
&EFI_GRAPHICS_OUTPUT_PROTOCOL_GUID, NULL, &num,
466
&handles);
467
if (status != EFI_SUCCESS)
468
return status;
469
470
gop = find_gop(num, handles);
471
if (!gop)
472
return EFI_NOT_FOUND;
473
474
/* Change mode if requested */
475
set_mode(gop);
476
477
/* EFI framebuffer */
478
mode = efi_table_attr(gop, mode);
479
info = efi_table_attr(mode, info);
480
481
si->orig_video_isVGA = VIDEO_TYPE_EFI;
482
483
si->lfb_width = info->horizontal_resolution;
484
si->lfb_height = info->vertical_resolution;
485
486
efi_set_u64_split(efi_table_attr(mode, frame_buffer_base),
487
&si->lfb_base, &si->ext_lfb_base);
488
if (si->ext_lfb_base)
489
si->capabilities |= VIDEO_CAPABILITY_64BIT_BASE;
490
491
si->pages = 1;
492
493
setup_pixel_info(si, info->pixels_per_scan_line,
494
info->pixel_information, info->pixel_format);
495
496
si->lfb_size = si->lfb_linelength * si->lfb_height;
497
498
si->capabilities |= VIDEO_CAPABILITY_SKIP_QUIRKS;
499
500
return EFI_SUCCESS;
501
}
502
503