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