xref: /linux/drivers/firmware/efi/libstub/gop.c (revision c31f4aa8fed048fa70e742c4bb49bb48dc489ab3)
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 = active_edid->size_of_edid;
517 			gop_edid = 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 = discovered_edid->size_of_edid;
524 				gop_edid = 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