xref: /openbmc/linux/drivers/firmware/efi/libstub/gop.c (revision 3cf3cdea)
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_status_t status;
137 
138 	efi_graphics_output_protocol_mode_t *mode;
139 	efi_graphics_output_mode_info_t *info;
140 	unsigned long info_size;
141 
142 	u32 max_mode, cur_mode;
143 	int pf;
144 
145 	mode = efi_table_attr(gop, mode);
146 
147 	cur_mode = efi_table_attr(mode, mode);
148 	if (cmdline.mode == cur_mode)
149 		return cur_mode;
150 
151 	max_mode = efi_table_attr(mode, max_mode);
152 	if (cmdline.mode >= max_mode) {
153 		efi_err("Requested mode is invalid\n");
154 		return cur_mode;
155 	}
156 
157 	status = efi_call_proto(gop, query_mode, cmdline.mode,
158 				&info_size, &info);
159 	if (status != EFI_SUCCESS) {
160 		efi_err("Couldn't get mode information\n");
161 		return cur_mode;
162 	}
163 
164 	pf = info->pixel_format;
165 
166 	efi_bs_call(free_pool, info);
167 
168 	if (pf == PIXEL_BLT_ONLY || pf >= PIXEL_FORMAT_MAX) {
169 		efi_err("Invalid PixelFormat\n");
170 		return cur_mode;
171 	}
172 
173 	return cmdline.mode;
174 }
175 
176 static u8 pixel_bpp(int pixel_format, efi_pixel_bitmask_t pixel_info)
177 {
178 	if (pixel_format == PIXEL_BIT_MASK) {
179 		u32 mask = pixel_info.red_mask | pixel_info.green_mask |
180 			   pixel_info.blue_mask | pixel_info.reserved_mask;
181 		if (!mask)
182 			return 0;
183 		return __fls(mask) - __ffs(mask) + 1;
184 	} else
185 		return 32;
186 }
187 
188 static u32 choose_mode_res(efi_graphics_output_protocol_t *gop)
189 {
190 	efi_status_t status;
191 
192 	efi_graphics_output_protocol_mode_t *mode;
193 	efi_graphics_output_mode_info_t *info;
194 	unsigned long info_size;
195 
196 	u32 max_mode, cur_mode;
197 	int pf;
198 	efi_pixel_bitmask_t pi;
199 	u32 m, w, h;
200 
201 	mode = efi_table_attr(gop, mode);
202 
203 	cur_mode = efi_table_attr(mode, mode);
204 	info = efi_table_attr(mode, info);
205 	pf = info->pixel_format;
206 	pi = info->pixel_information;
207 	w  = info->horizontal_resolution;
208 	h  = info->vertical_resolution;
209 
210 	if (w == cmdline.res.width && h == cmdline.res.height &&
211 	    (cmdline.res.format < 0 || cmdline.res.format == pf) &&
212 	    (!cmdline.res.depth || cmdline.res.depth == pixel_bpp(pf, pi)))
213 		return cur_mode;
214 
215 	max_mode = efi_table_attr(mode, max_mode);
216 
217 	for (m = 0; m < max_mode; m++) {
218 		if (m == cur_mode)
219 			continue;
220 
221 		status = efi_call_proto(gop, query_mode, m,
222 					&info_size, &info);
223 		if (status != EFI_SUCCESS)
224 			continue;
225 
226 		pf = info->pixel_format;
227 		pi = info->pixel_information;
228 		w  = info->horizontal_resolution;
229 		h  = info->vertical_resolution;
230 
231 		efi_bs_call(free_pool, info);
232 
233 		if (pf == PIXEL_BLT_ONLY || pf >= PIXEL_FORMAT_MAX)
234 			continue;
235 		if (w == cmdline.res.width && h == cmdline.res.height &&
236 		    (cmdline.res.format < 0 || cmdline.res.format == pf) &&
237 		    (!cmdline.res.depth || cmdline.res.depth == pixel_bpp(pf, pi)))
238 			return m;
239 	}
240 
241 	efi_err("Couldn't find requested mode\n");
242 
243 	return cur_mode;
244 }
245 
246 static u32 choose_mode_auto(efi_graphics_output_protocol_t *gop)
247 {
248 	efi_status_t status;
249 
250 	efi_graphics_output_protocol_mode_t *mode;
251 	efi_graphics_output_mode_info_t *info;
252 	unsigned long info_size;
253 
254 	u32 max_mode, cur_mode, best_mode, area;
255 	u8 depth;
256 	int pf;
257 	efi_pixel_bitmask_t pi;
258 	u32 m, w, h, a;
259 	u8 d;
260 
261 	mode = efi_table_attr(gop, mode);
262 
263 	cur_mode = efi_table_attr(mode, mode);
264 	max_mode = efi_table_attr(mode, max_mode);
265 
266 	info = efi_table_attr(mode, info);
267 
268 	pf = info->pixel_format;
269 	pi = info->pixel_information;
270 	w  = info->horizontal_resolution;
271 	h  = info->vertical_resolution;
272 
273 	best_mode = cur_mode;
274 	area = w * h;
275 	depth = pixel_bpp(pf, pi);
276 
277 	for (m = 0; m < max_mode; m++) {
278 		if (m == cur_mode)
279 			continue;
280 
281 		status = efi_call_proto(gop, query_mode, m,
282 					&info_size, &info);
283 		if (status != EFI_SUCCESS)
284 			continue;
285 
286 		pf = info->pixel_format;
287 		pi = info->pixel_information;
288 		w  = info->horizontal_resolution;
289 		h  = info->vertical_resolution;
290 
291 		efi_bs_call(free_pool, info);
292 
293 		if (pf == PIXEL_BLT_ONLY || pf >= PIXEL_FORMAT_MAX)
294 			continue;
295 		a = w * h;
296 		if (a < area)
297 			continue;
298 		d = pixel_bpp(pf, pi);
299 		if (a > area || d > depth) {
300 			best_mode = m;
301 			area = a;
302 			depth = d;
303 		}
304 	}
305 
306 	return best_mode;
307 }
308 
309 static u32 choose_mode_list(efi_graphics_output_protocol_t *gop)
310 {
311 	efi_status_t status;
312 
313 	efi_graphics_output_protocol_mode_t *mode;
314 	efi_graphics_output_mode_info_t *info;
315 	unsigned long info_size;
316 
317 	u32 max_mode, cur_mode;
318 	int pf;
319 	efi_pixel_bitmask_t pi;
320 	u32 m, w, h;
321 	u8 d;
322 	const char *dstr;
323 	bool valid;
324 	efi_input_key_t key;
325 
326 	mode = efi_table_attr(gop, mode);
327 
328 	cur_mode = efi_table_attr(mode, mode);
329 	max_mode = efi_table_attr(mode, max_mode);
330 
331 	efi_printk("Available graphics modes are 0-%u\n", max_mode-1);
332 	efi_puts("  * = current mode\n"
333 		 "  - = unusable mode\n");
334 	for (m = 0; m < max_mode; m++) {
335 		status = efi_call_proto(gop, query_mode, m,
336 					&info_size, &info);
337 		if (status != EFI_SUCCESS)
338 			continue;
339 
340 		pf = info->pixel_format;
341 		pi = info->pixel_information;
342 		w  = info->horizontal_resolution;
343 		h  = info->vertical_resolution;
344 
345 		efi_bs_call(free_pool, info);
346 
347 		valid = !(pf == PIXEL_BLT_ONLY || pf >= PIXEL_FORMAT_MAX);
348 		d = 0;
349 		switch (pf) {
350 		case PIXEL_RGB_RESERVED_8BIT_PER_COLOR:
351 			dstr = "rgb";
352 			break;
353 		case PIXEL_BGR_RESERVED_8BIT_PER_COLOR:
354 			dstr = "bgr";
355 			break;
356 		case PIXEL_BIT_MASK:
357 			dstr = "";
358 			d = pixel_bpp(pf, pi);
359 			break;
360 		case PIXEL_BLT_ONLY:
361 			dstr = "blt";
362 			break;
363 		default:
364 			dstr = "xxx";
365 			break;
366 		}
367 
368 		efi_printk("Mode %3u %c%c: Resolution %ux%u-%s%.0hhu\n",
369 			   m,
370 			   m == cur_mode ? '*' : ' ',
371 			   !valid ? '-' : ' ',
372 			   w, h, dstr, d);
373 	}
374 
375 	efi_puts("\nPress any key to continue (or wait 10 seconds)\n");
376 	status = efi_wait_for_key(10 * EFI_USEC_PER_SEC, &key);
377 	if (status != EFI_SUCCESS && status != EFI_TIMEOUT) {
378 		efi_err("Unable to read key, continuing in 10 seconds\n");
379 		efi_bs_call(stall, 10 * EFI_USEC_PER_SEC);
380 	}
381 
382 	return cur_mode;
383 }
384 
385 static void set_mode(efi_graphics_output_protocol_t *gop)
386 {
387 	efi_graphics_output_protocol_mode_t *mode;
388 	u32 cur_mode, new_mode;
389 
390 	switch (cmdline.option) {
391 	case EFI_CMDLINE_MODE_NUM:
392 		new_mode = choose_mode_modenum(gop);
393 		break;
394 	case EFI_CMDLINE_RES:
395 		new_mode = choose_mode_res(gop);
396 		break;
397 	case EFI_CMDLINE_AUTO:
398 		new_mode = choose_mode_auto(gop);
399 		break;
400 	case EFI_CMDLINE_LIST:
401 		new_mode = choose_mode_list(gop);
402 		break;
403 	default:
404 		return;
405 	}
406 
407 	mode = efi_table_attr(gop, mode);
408 	cur_mode = efi_table_attr(mode, mode);
409 
410 	if (new_mode == cur_mode)
411 		return;
412 
413 	if (efi_call_proto(gop, set_mode, new_mode) != EFI_SUCCESS)
414 		efi_err("Failed to set requested mode\n");
415 }
416 
417 static void find_bits(u32 mask, u8 *pos, u8 *size)
418 {
419 	if (!mask) {
420 		*pos = *size = 0;
421 		return;
422 	}
423 
424 	/* UEFI spec guarantees that the set bits are contiguous */
425 	*pos  = __ffs(mask);
426 	*size = __fls(mask) - *pos + 1;
427 }
428 
429 static void
430 setup_pixel_info(struct screen_info *si, u32 pixels_per_scan_line,
431 		 efi_pixel_bitmask_t pixel_info, int pixel_format)
432 {
433 	if (pixel_format == PIXEL_BIT_MASK) {
434 		find_bits(pixel_info.red_mask,
435 			  &si->red_pos, &si->red_size);
436 		find_bits(pixel_info.green_mask,
437 			  &si->green_pos, &si->green_size);
438 		find_bits(pixel_info.blue_mask,
439 			  &si->blue_pos, &si->blue_size);
440 		find_bits(pixel_info.reserved_mask,
441 			  &si->rsvd_pos, &si->rsvd_size);
442 		si->lfb_depth = si->red_size + si->green_size +
443 			si->blue_size + si->rsvd_size;
444 		si->lfb_linelength = (pixels_per_scan_line * si->lfb_depth) / 8;
445 	} else {
446 		if (pixel_format == PIXEL_RGB_RESERVED_8BIT_PER_COLOR) {
447 			si->red_pos   = 0;
448 			si->blue_pos  = 16;
449 		} else /* PIXEL_BGR_RESERVED_8BIT_PER_COLOR */ {
450 			si->blue_pos  = 0;
451 			si->red_pos   = 16;
452 		}
453 
454 		si->green_pos = 8;
455 		si->rsvd_pos  = 24;
456 		si->red_size = si->green_size =
457 			si->blue_size = si->rsvd_size = 8;
458 
459 		si->lfb_depth = 32;
460 		si->lfb_linelength = pixels_per_scan_line * 4;
461 	}
462 }
463 
464 static efi_graphics_output_protocol_t *
465 find_gop(efi_guid_t *proto, unsigned long size, void **handles)
466 {
467 	efi_graphics_output_protocol_t *first_gop;
468 	efi_handle_t h;
469 	int i;
470 
471 	first_gop = NULL;
472 
473 	for_each_efi_handle(h, handles, size, i) {
474 		efi_status_t status;
475 
476 		efi_graphics_output_protocol_t *gop;
477 		efi_graphics_output_protocol_mode_t *mode;
478 		efi_graphics_output_mode_info_t *info;
479 
480 		efi_guid_t conout_proto = EFI_CONSOLE_OUT_DEVICE_GUID;
481 		void *dummy = NULL;
482 
483 		status = efi_bs_call(handle_protocol, h, proto, (void **)&gop);
484 		if (status != EFI_SUCCESS)
485 			continue;
486 
487 		mode = efi_table_attr(gop, mode);
488 		info = efi_table_attr(mode, info);
489 		if (info->pixel_format == PIXEL_BLT_ONLY ||
490 		    info->pixel_format >= PIXEL_FORMAT_MAX)
491 			continue;
492 
493 		/*
494 		 * Systems that use the UEFI Console Splitter may
495 		 * provide multiple GOP devices, not all of which are
496 		 * backed by real hardware. The workaround is to search
497 		 * for a GOP implementing the ConOut protocol, and if
498 		 * one isn't found, to just fall back to the first GOP.
499 		 *
500 		 * Once we've found a GOP supporting ConOut,
501 		 * don't bother looking any further.
502 		 */
503 		status = efi_bs_call(handle_protocol, h, &conout_proto, &dummy);
504 		if (status == EFI_SUCCESS)
505 			return gop;
506 
507 		if (!first_gop)
508 			first_gop = gop;
509 	}
510 
511 	return first_gop;
512 }
513 
514 static efi_status_t setup_gop(struct screen_info *si, efi_guid_t *proto,
515 			      unsigned long size, void **handles)
516 {
517 	efi_graphics_output_protocol_t *gop;
518 	efi_graphics_output_protocol_mode_t *mode;
519 	efi_graphics_output_mode_info_t *info;
520 
521 	gop = find_gop(proto, size, handles);
522 
523 	/* Did we find any GOPs? */
524 	if (!gop)
525 		return EFI_NOT_FOUND;
526 
527 	/* Change mode if requested */
528 	set_mode(gop);
529 
530 	/* EFI framebuffer */
531 	mode = efi_table_attr(gop, mode);
532 	info = efi_table_attr(mode, info);
533 
534 	si->orig_video_isVGA = VIDEO_TYPE_EFI;
535 
536 	si->lfb_width  = info->horizontal_resolution;
537 	si->lfb_height = info->vertical_resolution;
538 
539 	efi_set_u64_split(efi_table_attr(mode, frame_buffer_base),
540 			  &si->lfb_base, &si->ext_lfb_base);
541 	if (si->ext_lfb_base)
542 		si->capabilities |= VIDEO_CAPABILITY_64BIT_BASE;
543 
544 	si->pages = 1;
545 
546 	setup_pixel_info(si, info->pixels_per_scan_line,
547 			     info->pixel_information, info->pixel_format);
548 
549 	si->lfb_size = si->lfb_linelength * si->lfb_height;
550 
551 	si->capabilities |= VIDEO_CAPABILITY_SKIP_QUIRKS;
552 
553 	return EFI_SUCCESS;
554 }
555 
556 /*
557  * See if we have Graphics Output Protocol
558  */
559 efi_status_t efi_setup_gop(struct screen_info *si, efi_guid_t *proto,
560 			   unsigned long size)
561 {
562 	efi_status_t status;
563 	void **gop_handle = NULL;
564 
565 	status = efi_bs_call(allocate_pool, EFI_LOADER_DATA, size,
566 			     (void **)&gop_handle);
567 	if (status != EFI_SUCCESS)
568 		return status;
569 
570 	status = efi_bs_call(locate_handle, EFI_LOCATE_BY_PROTOCOL, proto, NULL,
571 			     &size, gop_handle);
572 	if (status != EFI_SUCCESS)
573 		goto free_handle;
574 
575 	status = setup_gop(si, proto, size, gop_handle);
576 
577 free_handle:
578 	efi_bs_call(free_pool, gop_handle);
579 	return status;
580 }
581