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