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
parse_modenum(char * option,char ** next)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
parse_res(char * option,char ** next)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
parse_auto(char * option,char ** next)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
parse_list(char * option,char ** next)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
efi_parse_option_graphics(char * option)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
choose_mode_modenum(efi_graphics_output_protocol_t * gop)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
choose_mode(efi_graphics_output_protocol_t * gop,bool (* match)(const efi_graphics_output_mode_info_t *,u32,void *),void * ctx)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
pixel_bpp(int pixel_format,efi_pixel_bitmask_t pixel_info)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
match_res(const efi_graphics_output_mode_info_t * info,u32 mode,void * ctx)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
choose_mode_res(efi_graphics_output_protocol_t * gop)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
match_auto(const efi_graphics_output_mode_info_t * info,u32 mode,void * ctx)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
choose_mode_auto(efi_graphics_output_protocol_t * gop)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
match_list(const efi_graphics_output_mode_info_t * info,u32 mode,void * ctx)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
choose_mode_list(efi_graphics_output_protocol_t * gop)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
set_mode(efi_graphics_output_protocol_t * gop)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
find_bits(u32 mask,u8 * pos,u8 * size)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
setup_pixel_info(struct screen_info * si,u32 pixels_per_scan_line,efi_pixel_bitmask_t pixel_info,int pixel_format)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
find_gop(unsigned long num,const efi_handle_t handles[])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
efi_setup_gop(struct screen_info * si)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