source: src/linux/universal/linux-4.9/drivers/firmware/efi/libstub/gop.c @ 31885

Last change on this file since 31885 was 31885, checked in by brainslayer, 5 weeks ago

update

File size: 9.2 KB
Line 
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
15static 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
38static void
39setup_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
88static 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
112static efi_status_t
113setup_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                    info->pixel_format != PIXEL_BLT_ONLY) {
154                        /*
155                         * Systems that use the UEFI Console Splitter may
156                         * provide multiple GOP devices, not all of which are
157                         * backed by real hardware. The workaround is to search
158                         * for a GOP implementing the ConOut protocol, and if
159                         * one isn't found, to just fall back to the first GOP.
160                         */
161                        width = info->horizontal_resolution;
162                        height = info->vertical_resolution;
163                        pixel_format = info->pixel_format;
164                        pixel_info = info->pixel_information;
165                        pixels_per_scan_line = info->pixels_per_scan_line;
166                        fb_base = current_fb_base;
167
168                        /*
169                         * Once we've found a GOP supporting ConOut,
170                         * don't bother looking any further.
171                         */
172                        first_gop = gop32;
173                        if (conout_found)
174                                break;
175                }
176        }
177
178        /* Did we find any GOPs? */
179        if (!first_gop)
180                goto out;
181
182        /* EFI framebuffer */
183        si->orig_video_isVGA = VIDEO_TYPE_EFI;
184
185        si->lfb_width = width;
186        si->lfb_height = height;
187        si->lfb_base = fb_base;
188
189        ext_lfb_base = (u64)(unsigned long)fb_base >> 32;
190        if (ext_lfb_base) {
191                si->capabilities |= VIDEO_CAPABILITY_64BIT_BASE;
192                si->ext_lfb_base = ext_lfb_base;
193        }
194
195        si->pages = 1;
196
197        setup_pixel_info(si, pixels_per_scan_line, pixel_info, pixel_format);
198
199        si->lfb_size = si->lfb_linelength * si->lfb_height;
200
201        si->capabilities |= VIDEO_CAPABILITY_SKIP_QUIRKS;
202out:
203        return status;
204}
205
206static efi_status_t
207__gop_query64(efi_system_table_t *sys_table_arg,
208              struct efi_graphics_output_protocol_64 *gop64,
209              struct efi_graphics_output_mode_info **info,
210              unsigned long *size, u64 *fb_base)
211{
212        struct efi_graphics_output_protocol_mode_64 *mode;
213        efi_graphics_output_protocol_query_mode query_mode;
214        efi_status_t status;
215        unsigned long m;
216
217        m = gop64->mode;
218        mode = (struct efi_graphics_output_protocol_mode_64 *)m;
219        query_mode = (void *)(unsigned long)gop64->query_mode;
220
221        status = __efi_call_early(query_mode, (void *)gop64, mode->mode, size,
222                                  info);
223        if (status != EFI_SUCCESS)
224                return status;
225
226        *fb_base = mode->frame_buffer_base;
227        return status;
228}
229
230static efi_status_t
231setup_gop64(efi_system_table_t *sys_table_arg, struct screen_info *si,
232            efi_guid_t *proto, unsigned long size, void **gop_handle)
233{
234        struct efi_graphics_output_protocol_64 *gop64, *first_gop;
235        unsigned long nr_gops;
236        u16 width, height;
237        u32 pixels_per_scan_line;
238        u32 ext_lfb_base;
239        u64 fb_base;
240        struct efi_pixel_bitmask pixel_info;
241        int pixel_format;
242        efi_status_t status = EFI_NOT_FOUND;
243        u64 *handles = (u64 *)(unsigned long)gop_handle;
244        int i;
245
246        first_gop = NULL;
247        gop64 = NULL;
248
249        nr_gops = size / sizeof(u64);
250        for (i = 0; i < nr_gops; i++) {
251                struct efi_graphics_output_mode_info *info = NULL;
252                efi_guid_t conout_proto = EFI_CONSOLE_OUT_DEVICE_GUID;
253                bool conout_found = false;
254                void *dummy = NULL;
255                efi_handle_t h = (efi_handle_t)(unsigned long)handles[i];
256                u64 current_fb_base;
257
258                status = efi_call_early(handle_protocol, h,
259                                        proto, (void **)&gop64);
260                if (status != EFI_SUCCESS)
261                        continue;
262
263                status = efi_call_early(handle_protocol, h,
264                                        &conout_proto, &dummy);
265                if (status == EFI_SUCCESS)
266                        conout_found = true;
267
268                status = __gop_query64(sys_table_arg, gop64, &info, &size,
269                                       &current_fb_base);
270                if (status == EFI_SUCCESS && (!first_gop || conout_found) &&
271                    info->pixel_format != PIXEL_BLT_ONLY) {
272                        /*
273                         * Systems that use the UEFI Console Splitter may
274                         * provide multiple GOP devices, not all of which are
275                         * backed by real hardware. The workaround is to search
276                         * for a GOP implementing the ConOut protocol, and if
277                         * one isn't found, to just fall back to the first GOP.
278                         */
279                        width = info->horizontal_resolution;
280                        height = info->vertical_resolution;
281                        pixel_format = info->pixel_format;
282                        pixel_info = info->pixel_information;
283                        pixels_per_scan_line = info->pixels_per_scan_line;
284                        fb_base = current_fb_base;
285
286                        /*
287                         * Once we've found a GOP supporting ConOut,
288                         * don't bother looking any further.
289                         */
290                        first_gop = gop64;
291                        if (conout_found)
292                                break;
293                }
294        }
295
296        /* Did we find any GOPs? */
297        if (!first_gop)
298                goto out;
299
300        /* EFI framebuffer */
301        si->orig_video_isVGA = VIDEO_TYPE_EFI;
302
303        si->lfb_width = width;
304        si->lfb_height = height;
305        si->lfb_base = fb_base;
306
307        ext_lfb_base = (u64)(unsigned long)fb_base >> 32;
308        if (ext_lfb_base) {
309                si->capabilities |= VIDEO_CAPABILITY_64BIT_BASE;
310                si->ext_lfb_base = ext_lfb_base;
311        }
312
313        si->pages = 1;
314
315        setup_pixel_info(si, pixels_per_scan_line, pixel_info, pixel_format);
316
317        si->lfb_size = si->lfb_linelength * si->lfb_height;
318
319        si->capabilities |= VIDEO_CAPABILITY_SKIP_QUIRKS;
320out:
321        return status;
322}
323
324/*
325 * See if we have Graphics Output Protocol
326 */
327efi_status_t efi_setup_gop(efi_system_table_t *sys_table_arg,
328                           struct screen_info *si, efi_guid_t *proto,
329                           unsigned long size)
330{
331        efi_status_t status;
332        void **gop_handle = NULL;
333
334        status = efi_call_early(allocate_pool, EFI_LOADER_DATA,
335                                size, (void **)&gop_handle);
336        if (status != EFI_SUCCESS)
337                return status;
338
339        status = efi_call_early(locate_handle,
340                                EFI_LOCATE_BY_PROTOCOL,
341                                proto, NULL, &size, gop_handle);
342        if (status != EFI_SUCCESS)
343                goto free_handle;
344
345        if (efi_is_64bit()) {
346                status = setup_gop64(sys_table_arg, si, proto, size,
347                                     gop_handle);
348        } else {
349                status = setup_gop32(sys_table_arg, si, proto, size,
350                                     gop_handle);
351        }
352
353free_handle:
354        efi_call_early(free_pool, gop_handle);
355        return status;
356}
Note: See TracBrowser for help on using the repository browser.