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