diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2016-05-16 16:06:27 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2016-05-16 16:06:27 -0400 |
commit | 49817c33433a3cd6f320b13699e6746cc39b453b (patch) | |
tree | 1eb8c4a4d585e648b0783741c02ab611149971d9 /drivers/firmware/efi/libstub/gop.c | |
parent | 230e51f21101e49c8d73018d414adbd0d57459a1 (diff) | |
parent | 6c5450ef66816216e574885cf8d3ddb31ef77428 (diff) |
Merge branch 'efi-core-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip
Pull EFI updates from Ingo Molnar:
"The main changes in this cycle were:
- Drop the unused EFI_SYSTEM_TABLES efi.flags bit and ensure the
ARM/arm64 EFI System Table mapping is read-only (Ard Biesheuvel)
- Add a comment to explain that one of the code paths in the x86/pat
code is only executed for EFI boot (Matt Fleming)
- Improve Secure Boot status checks on arm64 and handle unexpected
errors (Linn Crosetto)
- Remove the global EFI memory map variable 'memmap' as the same
information is already available in efi::memmap (Matt Fleming)
- Add EFI Memory Attribute table support for ARM/arm64 (Ard
Biesheuvel)
- Add EFI GOP framebuffer support for ARM/arm64 (Ard Biesheuvel)
- Add EFI Bootloader Control driver for storing reboot(2) data in EFI
variables for consumption by bootloaders (Jeremy Compostella)
- Add Core EFI capsule support (Matt Fleming)
- Add EFI capsule char driver (Kweh, Hock Leong)
- Unify EFI memory map code for ARM and arm64 (Ard Biesheuvel)
- Add generic EFI support for detecting when firmware corrupts CPU
status register bits (like IRQ flags) when performing EFI runtime
service calls (Mark Rutland)
... and other misc cleanups"
* 'efi-core-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip: (46 commits)
efivarfs: Make efivarfs_file_ioctl() static
efi: Merge boolean flag arguments
efi/capsule: Move 'capsule' to the stack in efi_capsule_supported()
efibc: Fix excessive stack footprint warning
efi/capsule: Make efi_capsule_pending() lockless
efi: Remove unnecessary (and buggy) .memmap initialization from the Xen EFI driver
efi/runtime-wrappers: Remove ARCH_EFI_IRQ_FLAGS_MASK #ifdef
x86/efi: Enable runtime call flag checking
arm/efi: Enable runtime call flag checking
arm64/efi: Enable runtime call flag checking
efi/runtime-wrappers: Detect firmware IRQ flag corruption
efi/runtime-wrappers: Remove redundant #ifdefs
x86/efi: Move to generic {__,}efi_call_virt()
arm/efi: Move to generic {__,}efi_call_virt()
arm64/efi: Move to generic {__,}efi_call_virt()
efi/runtime-wrappers: Add {__,}efi_call_virt() templates
efi/arm-init: Reserve rather than unmap the memory map for ARM as well
efi: Add misc char driver interface to update EFI firmware
x86/efi: Force EFI reboot to process pending capsules
efi: Add 'capsule' update support
...
Diffstat (limited to 'drivers/firmware/efi/libstub/gop.c')
-rw-r--r-- | drivers/firmware/efi/libstub/gop.c | 354 |
1 files changed, 354 insertions, 0 deletions
diff --git a/drivers/firmware/efi/libstub/gop.c b/drivers/firmware/efi/libstub/gop.c new file mode 100644 index 000000000000..932742e4cf23 --- /dev/null +++ b/drivers/firmware/efi/libstub/gop.c | |||
@@ -0,0 +1,354 @@ | |||
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 | ¤t_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 | ¤t_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 | } | ||