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