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