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