linux/drivers/firmware/efi/libstub/gop.c
<<
>>
Prefs
   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}
 581