xref: /linux/drivers/firmware/efi/libstub/gop.c (revision 0883c2c06fb5bcf5b9e008270827e63c09a88c1e)
1 /* -----------------------------------------------------------------------
2  *
3  *   Copyright 2011 Intel Corporation; author Matt Fleming
4  *
5  *   This file is part of the Linux kernel, and is made available under
6  *   the terms of the GNU General Public License version 2.
7  *
8  * ----------------------------------------------------------------------- */
9 
10 #include <linux/efi.h>
11 #include <linux/screen_info.h>
12 #include <asm/efi.h>
13 #include <asm/setup.h>
14 
15 static void find_bits(unsigned long mask, u8 *pos, u8 *size)
16 {
17 	u8 first, len;
18 
19 	first = 0;
20 	len = 0;
21 
22 	if (mask) {
23 		while (!(mask & 0x1)) {
24 			mask = mask >> 1;
25 			first++;
26 		}
27 
28 		while (mask & 0x1) {
29 			mask = mask >> 1;
30 			len++;
31 		}
32 	}
33 
34 	*pos = first;
35 	*size = len;
36 }
37 
38 static void
39 setup_pixel_info(struct screen_info *si, u32 pixels_per_scan_line,
40 		 struct efi_pixel_bitmask pixel_info, int pixel_format)
41 {
42 	if (pixel_format == PIXEL_RGB_RESERVED_8BIT_PER_COLOR) {
43 		si->lfb_depth = 32;
44 		si->lfb_linelength = pixels_per_scan_line * 4;
45 		si->red_size = 8;
46 		si->red_pos = 0;
47 		si->green_size = 8;
48 		si->green_pos = 8;
49 		si->blue_size = 8;
50 		si->blue_pos = 16;
51 		si->rsvd_size = 8;
52 		si->rsvd_pos = 24;
53 	} else if (pixel_format == PIXEL_BGR_RESERVED_8BIT_PER_COLOR) {
54 		si->lfb_depth = 32;
55 		si->lfb_linelength = pixels_per_scan_line * 4;
56 		si->red_size = 8;
57 		si->red_pos = 16;
58 		si->green_size = 8;
59 		si->green_pos = 8;
60 		si->blue_size = 8;
61 		si->blue_pos = 0;
62 		si->rsvd_size = 8;
63 		si->rsvd_pos = 24;
64 	} else if (pixel_format == PIXEL_BIT_MASK) {
65 		find_bits(pixel_info.red_mask, &si->red_pos, &si->red_size);
66 		find_bits(pixel_info.green_mask, &si->green_pos,
67 			  &si->green_size);
68 		find_bits(pixel_info.blue_mask, &si->blue_pos, &si->blue_size);
69 		find_bits(pixel_info.reserved_mask, &si->rsvd_pos,
70 			  &si->rsvd_size);
71 		si->lfb_depth = si->red_size + si->green_size +
72 			si->blue_size + si->rsvd_size;
73 		si->lfb_linelength = (pixels_per_scan_line * si->lfb_depth) / 8;
74 	} else {
75 		si->lfb_depth = 4;
76 		si->lfb_linelength = si->lfb_width / 2;
77 		si->red_size = 0;
78 		si->red_pos = 0;
79 		si->green_size = 0;
80 		si->green_pos = 0;
81 		si->blue_size = 0;
82 		si->blue_pos = 0;
83 		si->rsvd_size = 0;
84 		si->rsvd_pos = 0;
85 	}
86 }
87 
88 static efi_status_t
89 __gop_query32(efi_system_table_t *sys_table_arg,
90 	      struct efi_graphics_output_protocol_32 *gop32,
91 	      struct efi_graphics_output_mode_info **info,
92 	      unsigned long *size, u64 *fb_base)
93 {
94 	struct efi_graphics_output_protocol_mode_32 *mode;
95 	efi_graphics_output_protocol_query_mode query_mode;
96 	efi_status_t status;
97 	unsigned long m;
98 
99 	m = gop32->mode;
100 	mode = (struct efi_graphics_output_protocol_mode_32 *)m;
101 	query_mode = (void *)(unsigned long)gop32->query_mode;
102 
103 	status = __efi_call_early(query_mode, (void *)gop32, mode->mode, size,
104 				  info);
105 	if (status != EFI_SUCCESS)
106 		return status;
107 
108 	*fb_base = mode->frame_buffer_base;
109 	return status;
110 }
111 
112 static efi_status_t
113 setup_gop32(efi_system_table_t *sys_table_arg, struct screen_info *si,
114             efi_guid_t *proto, unsigned long size, void **gop_handle)
115 {
116 	struct efi_graphics_output_protocol_32 *gop32, *first_gop;
117 	unsigned long nr_gops;
118 	u16 width, height;
119 	u32 pixels_per_scan_line;
120 	u32 ext_lfb_base;
121 	u64 fb_base;
122 	struct efi_pixel_bitmask pixel_info;
123 	int pixel_format;
124 	efi_status_t status = EFI_NOT_FOUND;
125 	u32 *handles = (u32 *)(unsigned long)gop_handle;
126 	int i;
127 
128 	first_gop = NULL;
129 	gop32 = NULL;
130 
131 	nr_gops = size / sizeof(u32);
132 	for (i = 0; i < nr_gops; i++) {
133 		struct efi_graphics_output_mode_info *info = NULL;
134 		efi_guid_t conout_proto = EFI_CONSOLE_OUT_DEVICE_GUID;
135 		bool conout_found = false;
136 		void *dummy = NULL;
137 		efi_handle_t h = (efi_handle_t)(unsigned long)handles[i];
138 		u64 current_fb_base;
139 
140 		status = efi_call_early(handle_protocol, h,
141 					proto, (void **)&gop32);
142 		if (status != EFI_SUCCESS)
143 			continue;
144 
145 		status = efi_call_early(handle_protocol, h,
146 					&conout_proto, &dummy);
147 		if (status == EFI_SUCCESS)
148 			conout_found = true;
149 
150 		status = __gop_query32(sys_table_arg, gop32, &info, &size,
151 				       &current_fb_base);
152 		if (status == EFI_SUCCESS && (!first_gop || conout_found)) {
153 			/*
154 			 * Systems that use the UEFI Console Splitter may
155 			 * provide multiple GOP devices, not all of which are
156 			 * backed by real hardware. The workaround is to search
157 			 * for a GOP implementing the ConOut protocol, and if
158 			 * one isn't found, to just fall back to the first GOP.
159 			 */
160 			width = info->horizontal_resolution;
161 			height = info->vertical_resolution;
162 			pixel_format = info->pixel_format;
163 			pixel_info = info->pixel_information;
164 			pixels_per_scan_line = info->pixels_per_scan_line;
165 			fb_base = current_fb_base;
166 
167 			/*
168 			 * Once we've found a GOP supporting ConOut,
169 			 * don't bother looking any further.
170 			 */
171 			first_gop = gop32;
172 			if (conout_found)
173 				break;
174 		}
175 	}
176 
177 	/* Did we find any GOPs? */
178 	if (!first_gop)
179 		goto out;
180 
181 	/* EFI framebuffer */
182 	si->orig_video_isVGA = VIDEO_TYPE_EFI;
183 
184 	si->lfb_width = width;
185 	si->lfb_height = height;
186 	si->lfb_base = fb_base;
187 
188 	ext_lfb_base = (u64)(unsigned long)fb_base >> 32;
189 	if (ext_lfb_base) {
190 		si->capabilities |= VIDEO_CAPABILITY_64BIT_BASE;
191 		si->ext_lfb_base = ext_lfb_base;
192 	}
193 
194 	si->pages = 1;
195 
196 	setup_pixel_info(si, pixels_per_scan_line, pixel_info, pixel_format);
197 
198 	si->lfb_size = si->lfb_linelength * si->lfb_height;
199 
200 	si->capabilities |= VIDEO_CAPABILITY_SKIP_QUIRKS;
201 out:
202 	return status;
203 }
204 
205 static efi_status_t
206 __gop_query64(efi_system_table_t *sys_table_arg,
207 	      struct efi_graphics_output_protocol_64 *gop64,
208 	      struct efi_graphics_output_mode_info **info,
209 	      unsigned long *size, u64 *fb_base)
210 {
211 	struct efi_graphics_output_protocol_mode_64 *mode;
212 	efi_graphics_output_protocol_query_mode query_mode;
213 	efi_status_t status;
214 	unsigned long m;
215 
216 	m = gop64->mode;
217 	mode = (struct efi_graphics_output_protocol_mode_64 *)m;
218 	query_mode = (void *)(unsigned long)gop64->query_mode;
219 
220 	status = __efi_call_early(query_mode, (void *)gop64, mode->mode, size,
221 				  info);
222 	if (status != EFI_SUCCESS)
223 		return status;
224 
225 	*fb_base = mode->frame_buffer_base;
226 	return status;
227 }
228 
229 static efi_status_t
230 setup_gop64(efi_system_table_t *sys_table_arg, struct screen_info *si,
231 	    efi_guid_t *proto, unsigned long size, void **gop_handle)
232 {
233 	struct efi_graphics_output_protocol_64 *gop64, *first_gop;
234 	unsigned long nr_gops;
235 	u16 width, height;
236 	u32 pixels_per_scan_line;
237 	u32 ext_lfb_base;
238 	u64 fb_base;
239 	struct efi_pixel_bitmask pixel_info;
240 	int pixel_format;
241 	efi_status_t status = EFI_NOT_FOUND;
242 	u64 *handles = (u64 *)(unsigned long)gop_handle;
243 	int i;
244 
245 	first_gop = NULL;
246 	gop64 = NULL;
247 
248 	nr_gops = size / sizeof(u64);
249 	for (i = 0; i < nr_gops; i++) {
250 		struct efi_graphics_output_mode_info *info = NULL;
251 		efi_guid_t conout_proto = EFI_CONSOLE_OUT_DEVICE_GUID;
252 		bool conout_found = false;
253 		void *dummy = NULL;
254 		efi_handle_t h = (efi_handle_t)(unsigned long)handles[i];
255 		u64 current_fb_base;
256 
257 		status = efi_call_early(handle_protocol, h,
258 					proto, (void **)&gop64);
259 		if (status != EFI_SUCCESS)
260 			continue;
261 
262 		status = efi_call_early(handle_protocol, h,
263 					&conout_proto, &dummy);
264 		if (status == EFI_SUCCESS)
265 			conout_found = true;
266 
267 		status = __gop_query64(sys_table_arg, gop64, &info, &size,
268 				       &current_fb_base);
269 		if (status == EFI_SUCCESS && (!first_gop || conout_found)) {
270 			/*
271 			 * Systems that use the UEFI Console Splitter may
272 			 * provide multiple GOP devices, not all of which are
273 			 * backed by real hardware. The workaround is to search
274 			 * for a GOP implementing the ConOut protocol, and if
275 			 * one isn't found, to just fall back to the first GOP.
276 			 */
277 			width = info->horizontal_resolution;
278 			height = info->vertical_resolution;
279 			pixel_format = info->pixel_format;
280 			pixel_info = info->pixel_information;
281 			pixels_per_scan_line = info->pixels_per_scan_line;
282 			fb_base = current_fb_base;
283 
284 			/*
285 			 * Once we've found a GOP supporting ConOut,
286 			 * don't bother looking any further.
287 			 */
288 			first_gop = gop64;
289 			if (conout_found)
290 				break;
291 		}
292 	}
293 
294 	/* Did we find any GOPs? */
295 	if (!first_gop)
296 		goto out;
297 
298 	/* EFI framebuffer */
299 	si->orig_video_isVGA = VIDEO_TYPE_EFI;
300 
301 	si->lfb_width = width;
302 	si->lfb_height = height;
303 	si->lfb_base = fb_base;
304 
305 	ext_lfb_base = (u64)(unsigned long)fb_base >> 32;
306 	if (ext_lfb_base) {
307 		si->capabilities |= VIDEO_CAPABILITY_64BIT_BASE;
308 		si->ext_lfb_base = ext_lfb_base;
309 	}
310 
311 	si->pages = 1;
312 
313 	setup_pixel_info(si, pixels_per_scan_line, pixel_info, pixel_format);
314 
315 	si->lfb_size = si->lfb_linelength * si->lfb_height;
316 
317 	si->capabilities |= VIDEO_CAPABILITY_SKIP_QUIRKS;
318 out:
319 	return status;
320 }
321 
322 /*
323  * See if we have Graphics Output Protocol
324  */
325 efi_status_t efi_setup_gop(efi_system_table_t *sys_table_arg,
326 			   struct screen_info *si, efi_guid_t *proto,
327 			   unsigned long size)
328 {
329 	efi_status_t status;
330 	void **gop_handle = NULL;
331 
332 	status = efi_call_early(allocate_pool, EFI_LOADER_DATA,
333 				size, (void **)&gop_handle);
334 	if (status != EFI_SUCCESS)
335 		return status;
336 
337 	status = efi_call_early(locate_handle,
338 				EFI_LOCATE_BY_PROTOCOL,
339 				proto, NULL, &size, gop_handle);
340 	if (status != EFI_SUCCESS)
341 		goto free_handle;
342 
343 	if (efi_is_64bit()) {
344 		status = setup_gop64(sys_table_arg, si, proto, size,
345 				     gop_handle);
346 	} else {
347 		status = setup_gop32(sys_table_arg, si, proto, size,
348 				     gop_handle);
349 	}
350 
351 free_handle:
352 	efi_call_early(free_pool, gop_handle);
353 	return status;
354 }
355