1 // SPDX-License-Identifier: GPL-2.0 2 /* ----------------------------------------------------------------------- 3 * 4 * Copyright 2011 Intel Corporation; author Matt Fleming 5 * 6 * ----------------------------------------------------------------------- */ 7 8 #include <linux/efi.h> 9 #include <linux/screen_info.h> 10 #include <asm/efi.h> 11 #include <asm/setup.h> 12 13 static void find_bits(unsigned long mask, u8 *pos, u8 *size) 14 { 15 u8 first, len; 16 17 first = 0; 18 len = 0; 19 20 if (mask) { 21 while (!(mask & 0x1)) { 22 mask = mask >> 1; 23 first++; 24 } 25 26 while (mask & 0x1) { 27 mask = mask >> 1; 28 len++; 29 } 30 } 31 32 *pos = first; 33 *size = len; 34 } 35 36 static void 37 setup_pixel_info(struct screen_info *si, u32 pixels_per_scan_line, 38 struct efi_pixel_bitmask pixel_info, int pixel_format) 39 { 40 if (pixel_format == PIXEL_RGB_RESERVED_8BIT_PER_COLOR) { 41 si->lfb_depth = 32; 42 si->lfb_linelength = pixels_per_scan_line * 4; 43 si->red_size = 8; 44 si->red_pos = 0; 45 si->green_size = 8; 46 si->green_pos = 8; 47 si->blue_size = 8; 48 si->blue_pos = 16; 49 si->rsvd_size = 8; 50 si->rsvd_pos = 24; 51 } else if (pixel_format == PIXEL_BGR_RESERVED_8BIT_PER_COLOR) { 52 si->lfb_depth = 32; 53 si->lfb_linelength = pixels_per_scan_line * 4; 54 si->red_size = 8; 55 si->red_pos = 16; 56 si->green_size = 8; 57 si->green_pos = 8; 58 si->blue_size = 8; 59 si->blue_pos = 0; 60 si->rsvd_size = 8; 61 si->rsvd_pos = 24; 62 } else if (pixel_format == PIXEL_BIT_MASK) { 63 find_bits(pixel_info.red_mask, &si->red_pos, &si->red_size); 64 find_bits(pixel_info.green_mask, &si->green_pos, 65 &si->green_size); 66 find_bits(pixel_info.blue_mask, &si->blue_pos, &si->blue_size); 67 find_bits(pixel_info.reserved_mask, &si->rsvd_pos, 68 &si->rsvd_size); 69 si->lfb_depth = si->red_size + si->green_size + 70 si->blue_size + si->rsvd_size; 71 si->lfb_linelength = (pixels_per_scan_line * si->lfb_depth) / 8; 72 } else { 73 si->lfb_depth = 4; 74 si->lfb_linelength = si->lfb_width / 2; 75 si->red_size = 0; 76 si->red_pos = 0; 77 si->green_size = 0; 78 si->green_pos = 0; 79 si->blue_size = 0; 80 si->blue_pos = 0; 81 si->rsvd_size = 0; 82 si->rsvd_pos = 0; 83 } 84 } 85 86 static efi_status_t 87 __gop_query32(efi_system_table_t *sys_table_arg, 88 struct efi_graphics_output_protocol_32 *gop32, 89 struct efi_graphics_output_mode_info **info, 90 unsigned long *size, u64 *fb_base) 91 { 92 struct efi_graphics_output_protocol_mode_32 *mode; 93 efi_graphics_output_protocol_query_mode query_mode; 94 efi_status_t status; 95 unsigned long m; 96 97 m = gop32->mode; 98 mode = (struct efi_graphics_output_protocol_mode_32 *)m; 99 query_mode = (void *)(unsigned long)gop32->query_mode; 100 101 status = __efi_call_early(query_mode, (void *)gop32, mode->mode, size, 102 info); 103 if (status != EFI_SUCCESS) 104 return status; 105 106 *fb_base = mode->frame_buffer_base; 107 return status; 108 } 109 110 static efi_status_t 111 setup_gop32(efi_system_table_t *sys_table_arg, struct screen_info *si, 112 efi_guid_t *proto, unsigned long size, void **gop_handle) 113 { 114 struct efi_graphics_output_protocol_32 *gop32, *first_gop; 115 unsigned long nr_gops; 116 u16 width, height; 117 u32 pixels_per_scan_line; 118 u32 ext_lfb_base; 119 u64 fb_base; 120 struct efi_pixel_bitmask pixel_info; 121 int pixel_format; 122 efi_status_t status = EFI_NOT_FOUND; 123 u32 *handles = (u32 *)(unsigned long)gop_handle; 124 int i; 125 126 first_gop = NULL; 127 gop32 = NULL; 128 129 nr_gops = size / sizeof(u32); 130 for (i = 0; i < nr_gops; i++) { 131 struct efi_graphics_output_mode_info *info = NULL; 132 efi_guid_t conout_proto = EFI_CONSOLE_OUT_DEVICE_GUID; 133 bool conout_found = false; 134 void *dummy = NULL; 135 efi_handle_t h = (efi_handle_t)(unsigned long)handles[i]; 136 u64 current_fb_base; 137 138 status = efi_call_early(handle_protocol, h, 139 proto, (void **)&gop32); 140 if (status != EFI_SUCCESS) 141 continue; 142 143 status = efi_call_early(handle_protocol, h, 144 &conout_proto, &dummy); 145 if (status == EFI_SUCCESS) 146 conout_found = true; 147 148 status = __gop_query32(sys_table_arg, gop32, &info, &size, 149 ¤t_fb_base); 150 if (status == EFI_SUCCESS && (!first_gop || conout_found) && 151 info->pixel_format != PIXEL_BLT_ONLY) { 152 /* 153 * Systems that use the UEFI Console Splitter may 154 * provide multiple GOP devices, not all of which are 155 * backed by real hardware. The workaround is to search 156 * for a GOP implementing the ConOut protocol, and if 157 * one isn't found, to just fall back to the first GOP. 158 */ 159 width = info->horizontal_resolution; 160 height = info->vertical_resolution; 161 pixel_format = info->pixel_format; 162 pixel_info = info->pixel_information; 163 pixels_per_scan_line = info->pixels_per_scan_line; 164 fb_base = current_fb_base; 165 166 /* 167 * Once we've found a GOP supporting ConOut, 168 * don't bother looking any further. 169 */ 170 first_gop = gop32; 171 if (conout_found) 172 break; 173 } 174 } 175 176 /* Did we find any GOPs? */ 177 if (!first_gop) 178 goto out; 179 180 /* EFI framebuffer */ 181 si->orig_video_isVGA = VIDEO_TYPE_EFI; 182 183 si->lfb_width = width; 184 si->lfb_height = height; 185 si->lfb_base = fb_base; 186 187 ext_lfb_base = (u64)(unsigned long)fb_base >> 32; 188 if (ext_lfb_base) { 189 si->capabilities |= VIDEO_CAPABILITY_64BIT_BASE; 190 si->ext_lfb_base = ext_lfb_base; 191 } 192 193 si->pages = 1; 194 195 setup_pixel_info(si, pixels_per_scan_line, pixel_info, pixel_format); 196 197 si->lfb_size = si->lfb_linelength * si->lfb_height; 198 199 si->capabilities |= VIDEO_CAPABILITY_SKIP_QUIRKS; 200 out: 201 return status; 202 } 203 204 static efi_status_t 205 __gop_query64(efi_system_table_t *sys_table_arg, 206 struct efi_graphics_output_protocol_64 *gop64, 207 struct efi_graphics_output_mode_info **info, 208 unsigned long *size, u64 *fb_base) 209 { 210 struct efi_graphics_output_protocol_mode_64 *mode; 211 efi_graphics_output_protocol_query_mode query_mode; 212 efi_status_t status; 213 unsigned long m; 214 215 m = gop64->mode; 216 mode = (struct efi_graphics_output_protocol_mode_64 *)m; 217 query_mode = (void *)(unsigned long)gop64->query_mode; 218 219 status = __efi_call_early(query_mode, (void *)gop64, mode->mode, size, 220 info); 221 if (status != EFI_SUCCESS) 222 return status; 223 224 *fb_base = mode->frame_buffer_base; 225 return status; 226 } 227 228 static efi_status_t 229 setup_gop64(efi_system_table_t *sys_table_arg, struct screen_info *si, 230 efi_guid_t *proto, unsigned long size, void **gop_handle) 231 { 232 struct efi_graphics_output_protocol_64 *gop64, *first_gop; 233 unsigned long nr_gops; 234 u16 width, height; 235 u32 pixels_per_scan_line; 236 u32 ext_lfb_base; 237 u64 fb_base; 238 struct efi_pixel_bitmask pixel_info; 239 int pixel_format; 240 efi_status_t status = EFI_NOT_FOUND; 241 u64 *handles = (u64 *)(unsigned long)gop_handle; 242 int i; 243 244 first_gop = NULL; 245 gop64 = NULL; 246 247 nr_gops = size / sizeof(u64); 248 for (i = 0; i < nr_gops; i++) { 249 struct efi_graphics_output_mode_info *info = NULL; 250 efi_guid_t conout_proto = EFI_CONSOLE_OUT_DEVICE_GUID; 251 bool conout_found = false; 252 void *dummy = NULL; 253 efi_handle_t h = (efi_handle_t)(unsigned long)handles[i]; 254 u64 current_fb_base; 255 256 status = efi_call_early(handle_protocol, h, 257 proto, (void **)&gop64); 258 if (status != EFI_SUCCESS) 259 continue; 260 261 status = efi_call_early(handle_protocol, h, 262 &conout_proto, &dummy); 263 if (status == EFI_SUCCESS) 264 conout_found = true; 265 266 status = __gop_query64(sys_table_arg, gop64, &info, &size, 267 ¤t_fb_base); 268 if (status == EFI_SUCCESS && (!first_gop || conout_found) && 269 info->pixel_format != PIXEL_BLT_ONLY) { 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