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/ctype.h> 10 #include <linux/efi.h> 11 #include <linux/screen_info.h> 12 #include <linux/string.h> 13 #include <asm/efi.h> 14 #include <asm/setup.h> 15 16 #include "efistub.h" 17 18 enum efi_cmdline_option { 19 EFI_CMDLINE_NONE, 20 EFI_CMDLINE_MODE_NUM, 21 EFI_CMDLINE_RES, 22 EFI_CMDLINE_AUTO 23 }; 24 25 static struct { 26 enum efi_cmdline_option option; 27 union { 28 u32 mode; 29 struct { 30 u32 width, height; 31 int format; 32 u8 depth; 33 } res; 34 }; 35 } cmdline = { .option = EFI_CMDLINE_NONE }; 36 37 static bool parse_modenum(char *option, char **next) 38 { 39 u32 m; 40 41 if (!strstarts(option, "mode=")) 42 return false; 43 option += strlen("mode="); 44 m = simple_strtoull(option, &option, 0); 45 if (*option && *option++ != ',') 46 return false; 47 cmdline.option = EFI_CMDLINE_MODE_NUM; 48 cmdline.mode = m; 49 50 *next = option; 51 return true; 52 } 53 54 static bool parse_res(char *option, char **next) 55 { 56 u32 w, h, d = 0; 57 int pf = -1; 58 59 if (!isdigit(*option)) 60 return false; 61 w = simple_strtoull(option, &option, 10); 62 if (*option++ != 'x' || !isdigit(*option)) 63 return false; 64 h = simple_strtoull(option, &option, 10); 65 if (*option == '-') { 66 option++; 67 if (strstarts(option, "rgb")) { 68 option += strlen("rgb"); 69 pf = PIXEL_RGB_RESERVED_8BIT_PER_COLOR; 70 } else if (strstarts(option, "bgr")) { 71 option += strlen("bgr"); 72 pf = PIXEL_BGR_RESERVED_8BIT_PER_COLOR; 73 } else if (isdigit(*option)) 74 d = simple_strtoull(option, &option, 10); 75 else 76 return false; 77 } 78 if (*option && *option++ != ',') 79 return false; 80 cmdline.option = EFI_CMDLINE_RES; 81 cmdline.res.width = w; 82 cmdline.res.height = h; 83 cmdline.res.format = pf; 84 cmdline.res.depth = d; 85 86 *next = option; 87 return true; 88 } 89 90 static bool parse_auto(char *option, char **next) 91 { 92 if (!strstarts(option, "auto")) 93 return false; 94 option += strlen("auto"); 95 if (*option && *option++ != ',') 96 return false; 97 cmdline.option = EFI_CMDLINE_AUTO; 98 99 *next = option; 100 return true; 101 } 102 103 void efi_parse_option_graphics(char *option) 104 { 105 while (*option) { 106 if (parse_modenum(option, &option)) 107 continue; 108 if (parse_res(option, &option)) 109 continue; 110 if (parse_auto(option, &option)) 111 continue; 112 113 while (*option && *option++ != ',') 114 ; 115 } 116 } 117 118 static u32 choose_mode_modenum(efi_graphics_output_protocol_t *gop) 119 { 120 efi_status_t status; 121 122 efi_graphics_output_protocol_mode_t *mode; 123 efi_graphics_output_mode_info_t *info; 124 unsigned long info_size; 125 126 u32 max_mode, cur_mode; 127 int pf; 128 129 mode = efi_table_attr(gop, mode); 130 131 cur_mode = efi_table_attr(mode, mode); 132 if (cmdline.mode == cur_mode) 133 return cur_mode; 134 135 max_mode = efi_table_attr(mode, max_mode); 136 if (cmdline.mode >= max_mode) { 137 efi_err("Requested mode is invalid\n"); 138 return cur_mode; 139 } 140 141 status = efi_call_proto(gop, query_mode, cmdline.mode, 142 &info_size, &info); 143 if (status != EFI_SUCCESS) { 144 efi_err("Couldn't get mode information\n"); 145 return cur_mode; 146 } 147 148 pf = info->pixel_format; 149 150 efi_bs_call(free_pool, info); 151 152 if (pf == PIXEL_BLT_ONLY || pf >= PIXEL_FORMAT_MAX) { 153 efi_err("Invalid PixelFormat\n"); 154 return cur_mode; 155 } 156 157 return cmdline.mode; 158 } 159 160 static u8 pixel_bpp(int pixel_format, efi_pixel_bitmask_t pixel_info) 161 { 162 if (pixel_format == PIXEL_BIT_MASK) { 163 u32 mask = pixel_info.red_mask | pixel_info.green_mask | 164 pixel_info.blue_mask | pixel_info.reserved_mask; 165 if (!mask) 166 return 0; 167 return __fls(mask) - __ffs(mask) + 1; 168 } else 169 return 32; 170 } 171 172 static u32 choose_mode_res(efi_graphics_output_protocol_t *gop) 173 { 174 efi_status_t status; 175 176 efi_graphics_output_protocol_mode_t *mode; 177 efi_graphics_output_mode_info_t *info; 178 unsigned long info_size; 179 180 u32 max_mode, cur_mode; 181 int pf; 182 efi_pixel_bitmask_t pi; 183 u32 m, w, h; 184 185 mode = efi_table_attr(gop, mode); 186 187 cur_mode = efi_table_attr(mode, mode); 188 info = efi_table_attr(mode, info); 189 pf = info->pixel_format; 190 pi = info->pixel_information; 191 w = info->horizontal_resolution; 192 h = info->vertical_resolution; 193 194 if (w == cmdline.res.width && h == cmdline.res.height && 195 (cmdline.res.format < 0 || cmdline.res.format == pf) && 196 (!cmdline.res.depth || cmdline.res.depth == pixel_bpp(pf, pi))) 197 return cur_mode; 198 199 max_mode = efi_table_attr(mode, max_mode); 200 201 for (m = 0; m < max_mode; m++) { 202 if (m == cur_mode) 203 continue; 204 205 status = efi_call_proto(gop, query_mode, m, 206 &info_size, &info); 207 if (status != EFI_SUCCESS) 208 continue; 209 210 pf = info->pixel_format; 211 pi = info->pixel_information; 212 w = info->horizontal_resolution; 213 h = info->vertical_resolution; 214 215 efi_bs_call(free_pool, info); 216 217 if (pf == PIXEL_BLT_ONLY || pf >= PIXEL_FORMAT_MAX) 218 continue; 219 if (w == cmdline.res.width && h == cmdline.res.height && 220 (cmdline.res.format < 0 || cmdline.res.format == pf) && 221 (!cmdline.res.depth || cmdline.res.depth == pixel_bpp(pf, pi))) 222 return m; 223 } 224 225 efi_err("Couldn't find requested mode\n"); 226 227 return cur_mode; 228 } 229 230 static u32 choose_mode_auto(efi_graphics_output_protocol_t *gop) 231 { 232 efi_status_t status; 233 234 efi_graphics_output_protocol_mode_t *mode; 235 efi_graphics_output_mode_info_t *info; 236 unsigned long info_size; 237 238 u32 max_mode, cur_mode, best_mode, area; 239 u8 depth; 240 int pf; 241 efi_pixel_bitmask_t pi; 242 u32 m, w, h, a; 243 u8 d; 244 245 mode = efi_table_attr(gop, mode); 246 247 cur_mode = efi_table_attr(mode, mode); 248 max_mode = efi_table_attr(mode, max_mode); 249 250 info = efi_table_attr(mode, info); 251 252 pf = info->pixel_format; 253 pi = info->pixel_information; 254 w = info->horizontal_resolution; 255 h = info->vertical_resolution; 256 257 best_mode = cur_mode; 258 area = w * h; 259 depth = pixel_bpp(pf, pi); 260 261 for (m = 0; m < max_mode; m++) { 262 if (m == cur_mode) 263 continue; 264 265 status = efi_call_proto(gop, query_mode, m, 266 &info_size, &info); 267 if (status != EFI_SUCCESS) 268 continue; 269 270 pf = info->pixel_format; 271 pi = info->pixel_information; 272 w = info->horizontal_resolution; 273 h = info->vertical_resolution; 274 275 efi_bs_call(free_pool, info); 276 277 if (pf == PIXEL_BLT_ONLY || pf >= PIXEL_FORMAT_MAX) 278 continue; 279 a = w * h; 280 if (a < area) 281 continue; 282 d = pixel_bpp(pf, pi); 283 if (a > area || d > depth) { 284 best_mode = m; 285 area = a; 286 depth = d; 287 } 288 } 289 290 return best_mode; 291 } 292 293 static void set_mode(efi_graphics_output_protocol_t *gop) 294 { 295 efi_graphics_output_protocol_mode_t *mode; 296 u32 cur_mode, new_mode; 297 298 switch (cmdline.option) { 299 case EFI_CMDLINE_MODE_NUM: 300 new_mode = choose_mode_modenum(gop); 301 break; 302 case EFI_CMDLINE_RES: 303 new_mode = choose_mode_res(gop); 304 break; 305 case EFI_CMDLINE_AUTO: 306 new_mode = choose_mode_auto(gop); 307 break; 308 default: 309 return; 310 } 311 312 mode = efi_table_attr(gop, mode); 313 cur_mode = efi_table_attr(mode, mode); 314 315 if (new_mode == cur_mode) 316 return; 317 318 if (efi_call_proto(gop, set_mode, new_mode) != EFI_SUCCESS) 319 efi_err("Failed to set requested mode\n"); 320 } 321 322 static void find_bits(u32 mask, u8 *pos, u8 *size) 323 { 324 if (!mask) { 325 *pos = *size = 0; 326 return; 327 } 328 329 /* UEFI spec guarantees that the set bits are contiguous */ 330 *pos = __ffs(mask); 331 *size = __fls(mask) - *pos + 1; 332 } 333 334 static void 335 setup_pixel_info(struct screen_info *si, u32 pixels_per_scan_line, 336 efi_pixel_bitmask_t pixel_info, int pixel_format) 337 { 338 if (pixel_format == PIXEL_BIT_MASK) { 339 find_bits(pixel_info.red_mask, 340 &si->red_pos, &si->red_size); 341 find_bits(pixel_info.green_mask, 342 &si->green_pos, &si->green_size); 343 find_bits(pixel_info.blue_mask, 344 &si->blue_pos, &si->blue_size); 345 find_bits(pixel_info.reserved_mask, 346 &si->rsvd_pos, &si->rsvd_size); 347 si->lfb_depth = si->red_size + si->green_size + 348 si->blue_size + si->rsvd_size; 349 si->lfb_linelength = (pixels_per_scan_line * si->lfb_depth) / 8; 350 } else { 351 if (pixel_format == PIXEL_RGB_RESERVED_8BIT_PER_COLOR) { 352 si->red_pos = 0; 353 si->blue_pos = 16; 354 } else /* PIXEL_BGR_RESERVED_8BIT_PER_COLOR */ { 355 si->blue_pos = 0; 356 si->red_pos = 16; 357 } 358 359 si->green_pos = 8; 360 si->rsvd_pos = 24; 361 si->red_size = si->green_size = 362 si->blue_size = si->rsvd_size = 8; 363 364 si->lfb_depth = 32; 365 si->lfb_linelength = pixels_per_scan_line * 4; 366 } 367 } 368 369 static efi_graphics_output_protocol_t * 370 find_gop(efi_guid_t *proto, unsigned long size, void **handles) 371 { 372 efi_graphics_output_protocol_t *first_gop; 373 efi_handle_t h; 374 int i; 375 376 first_gop = NULL; 377 378 for_each_efi_handle(h, handles, size, i) { 379 efi_status_t status; 380 381 efi_graphics_output_protocol_t *gop; 382 efi_graphics_output_protocol_mode_t *mode; 383 efi_graphics_output_mode_info_t *info; 384 385 efi_guid_t conout_proto = EFI_CONSOLE_OUT_DEVICE_GUID; 386 void *dummy = NULL; 387 388 status = efi_bs_call(handle_protocol, h, proto, (void **)&gop); 389 if (status != EFI_SUCCESS) 390 continue; 391 392 mode = efi_table_attr(gop, mode); 393 info = efi_table_attr(mode, info); 394 if (info->pixel_format == PIXEL_BLT_ONLY || 395 info->pixel_format >= PIXEL_FORMAT_MAX) 396 continue; 397 398 /* 399 * Systems that use the UEFI Console Splitter may 400 * provide multiple GOP devices, not all of which are 401 * backed by real hardware. The workaround is to search 402 * for a GOP implementing the ConOut protocol, and if 403 * one isn't found, to just fall back to the first GOP. 404 * 405 * Once we've found a GOP supporting ConOut, 406 * don't bother looking any further. 407 */ 408 status = efi_bs_call(handle_protocol, h, &conout_proto, &dummy); 409 if (status == EFI_SUCCESS) 410 return gop; 411 412 if (!first_gop) 413 first_gop = gop; 414 } 415 416 return first_gop; 417 } 418 419 static efi_status_t setup_gop(struct screen_info *si, efi_guid_t *proto, 420 unsigned long size, void **handles) 421 { 422 efi_graphics_output_protocol_t *gop; 423 efi_graphics_output_protocol_mode_t *mode; 424 efi_graphics_output_mode_info_t *info; 425 426 gop = find_gop(proto, size, handles); 427 428 /* Did we find any GOPs? */ 429 if (!gop) 430 return EFI_NOT_FOUND; 431 432 /* Change mode if requested */ 433 set_mode(gop); 434 435 /* EFI framebuffer */ 436 mode = efi_table_attr(gop, mode); 437 info = efi_table_attr(mode, info); 438 439 si->orig_video_isVGA = VIDEO_TYPE_EFI; 440 441 si->lfb_width = info->horizontal_resolution; 442 si->lfb_height = info->vertical_resolution; 443 444 efi_set_u64_split(efi_table_attr(mode, frame_buffer_base), 445 &si->lfb_base, &si->ext_lfb_base); 446 if (si->ext_lfb_base) 447 si->capabilities |= VIDEO_CAPABILITY_64BIT_BASE; 448 449 si->pages = 1; 450 451 setup_pixel_info(si, info->pixels_per_scan_line, 452 info->pixel_information, info->pixel_format); 453 454 si->lfb_size = si->lfb_linelength * si->lfb_height; 455 456 si->capabilities |= VIDEO_CAPABILITY_SKIP_QUIRKS; 457 458 return EFI_SUCCESS; 459 } 460 461 /* 462 * See if we have Graphics Output Protocol 463 */ 464 efi_status_t efi_setup_gop(struct screen_info *si, efi_guid_t *proto, 465 unsigned long size) 466 { 467 efi_status_t status; 468 void **gop_handle = NULL; 469 470 status = efi_bs_call(allocate_pool, EFI_LOADER_DATA, size, 471 (void **)&gop_handle); 472 if (status != EFI_SUCCESS) 473 return status; 474 475 status = efi_bs_call(locate_handle, EFI_LOCATE_BY_PROTOCOL, proto, NULL, 476 &size, gop_handle); 477 if (status != EFI_SUCCESS) 478 goto free_handle; 479 480 status = setup_gop(si, proto, size, gop_handle); 481 482 free_handle: 483 efi_bs_call(free_pool, gop_handle); 484 return status; 485 } 486