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