Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /* -----------------------------------------------------------------------
0003  *
0004  *   Copyright 2011 Intel Corporation; author Matt Fleming
0005  *
0006  * ----------------------------------------------------------------------- */
0007 
0008 #include <linux/bitops.h>
0009 #include <linux/ctype.h>
0010 #include <linux/efi.h>
0011 #include <linux/screen_info.h>
0012 #include <linux/string.h>
0013 #include <asm/efi.h>
0014 #include <asm/setup.h>
0015 
0016 #include "efistub.h"
0017 
0018 enum efi_cmdline_option {
0019     EFI_CMDLINE_NONE,
0020     EFI_CMDLINE_MODE_NUM,
0021     EFI_CMDLINE_RES,
0022     EFI_CMDLINE_AUTO,
0023     EFI_CMDLINE_LIST
0024 };
0025 
0026 static struct {
0027     enum efi_cmdline_option option;
0028     union {
0029         u32 mode;
0030         struct {
0031             u32 width, height;
0032             int format;
0033             u8 depth;
0034         } res;
0035     };
0036 } cmdline = { .option = EFI_CMDLINE_NONE };
0037 
0038 static bool parse_modenum(char *option, char **next)
0039 {
0040     u32 m;
0041 
0042     if (!strstarts(option, "mode="))
0043         return false;
0044     option += strlen("mode=");
0045     m = simple_strtoull(option, &option, 0);
0046     if (*option && *option++ != ',')
0047         return false;
0048     cmdline.option = EFI_CMDLINE_MODE_NUM;
0049     cmdline.mode   = m;
0050 
0051     *next = option;
0052     return true;
0053 }
0054 
0055 static bool parse_res(char *option, char **next)
0056 {
0057     u32 w, h, d = 0;
0058     int pf = -1;
0059 
0060     if (!isdigit(*option))
0061         return false;
0062     w = simple_strtoull(option, &option, 10);
0063     if (*option++ != 'x' || !isdigit(*option))
0064         return false;
0065     h = simple_strtoull(option, &option, 10);
0066     if (*option == '-') {
0067         option++;
0068         if (strstarts(option, "rgb")) {
0069             option += strlen("rgb");
0070             pf = PIXEL_RGB_RESERVED_8BIT_PER_COLOR;
0071         } else if (strstarts(option, "bgr")) {
0072             option += strlen("bgr");
0073             pf = PIXEL_BGR_RESERVED_8BIT_PER_COLOR;
0074         } else if (isdigit(*option))
0075             d = simple_strtoull(option, &option, 10);
0076         else
0077             return false;
0078     }
0079     if (*option && *option++ != ',')
0080         return false;
0081     cmdline.option     = EFI_CMDLINE_RES;
0082     cmdline.res.width  = w;
0083     cmdline.res.height = h;
0084     cmdline.res.format = pf;
0085     cmdline.res.depth  = d;
0086 
0087     *next = option;
0088     return true;
0089 }
0090 
0091 static bool parse_auto(char *option, char **next)
0092 {
0093     if (!strstarts(option, "auto"))
0094         return false;
0095     option += strlen("auto");
0096     if (*option && *option++ != ',')
0097         return false;
0098     cmdline.option = EFI_CMDLINE_AUTO;
0099 
0100     *next = option;
0101     return true;
0102 }
0103 
0104 static bool parse_list(char *option, char **next)
0105 {
0106     if (!strstarts(option, "list"))
0107         return false;
0108     option += strlen("list");
0109     if (*option && *option++ != ',')
0110         return false;
0111     cmdline.option = EFI_CMDLINE_LIST;
0112 
0113     *next = option;
0114     return true;
0115 }
0116 
0117 void efi_parse_option_graphics(char *option)
0118 {
0119     while (*option) {
0120         if (parse_modenum(option, &option))
0121             continue;
0122         if (parse_res(option, &option))
0123             continue;
0124         if (parse_auto(option, &option))
0125             continue;
0126         if (parse_list(option, &option))
0127             continue;
0128 
0129         while (*option && *option++ != ',')
0130             ;
0131     }
0132 }
0133 
0134 static u32 choose_mode_modenum(efi_graphics_output_protocol_t *gop)
0135 {
0136     efi_status_t status;
0137 
0138     efi_graphics_output_protocol_mode_t *mode;
0139     efi_graphics_output_mode_info_t *info;
0140     unsigned long info_size;
0141 
0142     u32 max_mode, cur_mode;
0143     int pf;
0144 
0145     mode = efi_table_attr(gop, mode);
0146 
0147     cur_mode = efi_table_attr(mode, mode);
0148     if (cmdline.mode == cur_mode)
0149         return cur_mode;
0150 
0151     max_mode = efi_table_attr(mode, max_mode);
0152     if (cmdline.mode >= max_mode) {
0153         efi_err("Requested mode is invalid\n");
0154         return cur_mode;
0155     }
0156 
0157     status = efi_call_proto(gop, query_mode, cmdline.mode,
0158                 &info_size, &info);
0159     if (status != EFI_SUCCESS) {
0160         efi_err("Couldn't get mode information\n");
0161         return cur_mode;
0162     }
0163 
0164     pf = info->pixel_format;
0165 
0166     efi_bs_call(free_pool, info);
0167 
0168     if (pf == PIXEL_BLT_ONLY || pf >= PIXEL_FORMAT_MAX) {
0169         efi_err("Invalid PixelFormat\n");
0170         return cur_mode;
0171     }
0172 
0173     return cmdline.mode;
0174 }
0175 
0176 static u8 pixel_bpp(int pixel_format, efi_pixel_bitmask_t pixel_info)
0177 {
0178     if (pixel_format == PIXEL_BIT_MASK) {
0179         u32 mask = pixel_info.red_mask | pixel_info.green_mask |
0180                pixel_info.blue_mask | pixel_info.reserved_mask;
0181         if (!mask)
0182             return 0;
0183         return __fls(mask) - __ffs(mask) + 1;
0184     } else
0185         return 32;
0186 }
0187 
0188 static u32 choose_mode_res(efi_graphics_output_protocol_t *gop)
0189 {
0190     efi_status_t status;
0191 
0192     efi_graphics_output_protocol_mode_t *mode;
0193     efi_graphics_output_mode_info_t *info;
0194     unsigned long info_size;
0195 
0196     u32 max_mode, cur_mode;
0197     int pf;
0198     efi_pixel_bitmask_t pi;
0199     u32 m, w, h;
0200 
0201     mode = efi_table_attr(gop, mode);
0202 
0203     cur_mode = efi_table_attr(mode, mode);
0204     info = efi_table_attr(mode, info);
0205     pf = info->pixel_format;
0206     pi = info->pixel_information;
0207     w  = info->horizontal_resolution;
0208     h  = info->vertical_resolution;
0209 
0210     if (w == cmdline.res.width && h == cmdline.res.height &&
0211         (cmdline.res.format < 0 || cmdline.res.format == pf) &&
0212         (!cmdline.res.depth || cmdline.res.depth == pixel_bpp(pf, pi)))
0213         return cur_mode;
0214 
0215     max_mode = efi_table_attr(mode, max_mode);
0216 
0217     for (m = 0; m < max_mode; m++) {
0218         if (m == cur_mode)
0219             continue;
0220 
0221         status = efi_call_proto(gop, query_mode, m,
0222                     &info_size, &info);
0223         if (status != EFI_SUCCESS)
0224             continue;
0225 
0226         pf = info->pixel_format;
0227         pi = info->pixel_information;
0228         w  = info->horizontal_resolution;
0229         h  = info->vertical_resolution;
0230 
0231         efi_bs_call(free_pool, info);
0232 
0233         if (pf == PIXEL_BLT_ONLY || pf >= PIXEL_FORMAT_MAX)
0234             continue;
0235         if (w == cmdline.res.width && h == cmdline.res.height &&
0236             (cmdline.res.format < 0 || cmdline.res.format == pf) &&
0237             (!cmdline.res.depth || cmdline.res.depth == pixel_bpp(pf, pi)))
0238             return m;
0239     }
0240 
0241     efi_err("Couldn't find requested mode\n");
0242 
0243     return cur_mode;
0244 }
0245 
0246 static u32 choose_mode_auto(efi_graphics_output_protocol_t *gop)
0247 {
0248     efi_status_t status;
0249 
0250     efi_graphics_output_protocol_mode_t *mode;
0251     efi_graphics_output_mode_info_t *info;
0252     unsigned long info_size;
0253 
0254     u32 max_mode, cur_mode, best_mode, area;
0255     u8 depth;
0256     int pf;
0257     efi_pixel_bitmask_t pi;
0258     u32 m, w, h, a;
0259     u8 d;
0260 
0261     mode = efi_table_attr(gop, mode);
0262 
0263     cur_mode = efi_table_attr(mode, mode);
0264     max_mode = efi_table_attr(mode, max_mode);
0265 
0266     info = efi_table_attr(mode, info);
0267 
0268     pf = info->pixel_format;
0269     pi = info->pixel_information;
0270     w  = info->horizontal_resolution;
0271     h  = info->vertical_resolution;
0272 
0273     best_mode = cur_mode;
0274     area = w * h;
0275     depth = pixel_bpp(pf, pi);
0276 
0277     for (m = 0; m < max_mode; m++) {
0278         if (m == cur_mode)
0279             continue;
0280 
0281         status = efi_call_proto(gop, query_mode, m,
0282                     &info_size, &info);
0283         if (status != EFI_SUCCESS)
0284             continue;
0285 
0286         pf = info->pixel_format;
0287         pi = info->pixel_information;
0288         w  = info->horizontal_resolution;
0289         h  = info->vertical_resolution;
0290 
0291         efi_bs_call(free_pool, info);
0292 
0293         if (pf == PIXEL_BLT_ONLY || pf >= PIXEL_FORMAT_MAX)
0294             continue;
0295         a = w * h;
0296         if (a < area)
0297             continue;
0298         d = pixel_bpp(pf, pi);
0299         if (a > area || d > depth) {
0300             best_mode = m;
0301             area = a;
0302             depth = d;
0303         }
0304     }
0305 
0306     return best_mode;
0307 }
0308 
0309 static u32 choose_mode_list(efi_graphics_output_protocol_t *gop)
0310 {
0311     efi_status_t status;
0312 
0313     efi_graphics_output_protocol_mode_t *mode;
0314     efi_graphics_output_mode_info_t *info;
0315     unsigned long info_size;
0316 
0317     u32 max_mode, cur_mode;
0318     int pf;
0319     efi_pixel_bitmask_t pi;
0320     u32 m, w, h;
0321     u8 d;
0322     const char *dstr;
0323     bool valid;
0324     efi_input_key_t key;
0325 
0326     mode = efi_table_attr(gop, mode);
0327 
0328     cur_mode = efi_table_attr(mode, mode);
0329     max_mode = efi_table_attr(mode, max_mode);
0330 
0331     efi_printk("Available graphics modes are 0-%u\n", max_mode-1);
0332     efi_puts("  * = current mode\n"
0333          "  - = unusable mode\n");
0334     for (m = 0; m < max_mode; m++) {
0335         status = efi_call_proto(gop, query_mode, m,
0336                     &info_size, &info);
0337         if (status != EFI_SUCCESS)
0338             continue;
0339 
0340         pf = info->pixel_format;
0341         pi = info->pixel_information;
0342         w  = info->horizontal_resolution;
0343         h  = info->vertical_resolution;
0344 
0345         efi_bs_call(free_pool, info);
0346 
0347         valid = !(pf == PIXEL_BLT_ONLY || pf >= PIXEL_FORMAT_MAX);
0348         d = 0;
0349         switch (pf) {
0350         case PIXEL_RGB_RESERVED_8BIT_PER_COLOR:
0351             dstr = "rgb";
0352             break;
0353         case PIXEL_BGR_RESERVED_8BIT_PER_COLOR:
0354             dstr = "bgr";
0355             break;
0356         case PIXEL_BIT_MASK:
0357             dstr = "";
0358             d = pixel_bpp(pf, pi);
0359             break;
0360         case PIXEL_BLT_ONLY:
0361             dstr = "blt";
0362             break;
0363         default:
0364             dstr = "xxx";
0365             break;
0366         }
0367 
0368         efi_printk("Mode %3u %c%c: Resolution %ux%u-%s%.0hhu\n",
0369                m,
0370                m == cur_mode ? '*' : ' ',
0371                !valid ? '-' : ' ',
0372                w, h, dstr, d);
0373     }
0374 
0375     efi_puts("\nPress any key to continue (or wait 10 seconds)\n");
0376     status = efi_wait_for_key(10 * EFI_USEC_PER_SEC, &key);
0377     if (status != EFI_SUCCESS && status != EFI_TIMEOUT) {
0378         efi_err("Unable to read key, continuing in 10 seconds\n");
0379         efi_bs_call(stall, 10 * EFI_USEC_PER_SEC);
0380     }
0381 
0382     return cur_mode;
0383 }
0384 
0385 static void set_mode(efi_graphics_output_protocol_t *gop)
0386 {
0387     efi_graphics_output_protocol_mode_t *mode;
0388     u32 cur_mode, new_mode;
0389 
0390     switch (cmdline.option) {
0391     case EFI_CMDLINE_MODE_NUM:
0392         new_mode = choose_mode_modenum(gop);
0393         break;
0394     case EFI_CMDLINE_RES:
0395         new_mode = choose_mode_res(gop);
0396         break;
0397     case EFI_CMDLINE_AUTO:
0398         new_mode = choose_mode_auto(gop);
0399         break;
0400     case EFI_CMDLINE_LIST:
0401         new_mode = choose_mode_list(gop);
0402         break;
0403     default:
0404         return;
0405     }
0406 
0407     mode = efi_table_attr(gop, mode);
0408     cur_mode = efi_table_attr(mode, mode);
0409 
0410     if (new_mode == cur_mode)
0411         return;
0412 
0413     if (efi_call_proto(gop, set_mode, new_mode) != EFI_SUCCESS)
0414         efi_err("Failed to set requested mode\n");
0415 }
0416 
0417 static void find_bits(u32 mask, u8 *pos, u8 *size)
0418 {
0419     if (!mask) {
0420         *pos = *size = 0;
0421         return;
0422     }
0423 
0424     /* UEFI spec guarantees that the set bits are contiguous */
0425     *pos  = __ffs(mask);
0426     *size = __fls(mask) - *pos + 1;
0427 }
0428 
0429 static void
0430 setup_pixel_info(struct screen_info *si, u32 pixels_per_scan_line,
0431          efi_pixel_bitmask_t pixel_info, int pixel_format)
0432 {
0433     if (pixel_format == PIXEL_BIT_MASK) {
0434         find_bits(pixel_info.red_mask,
0435               &si->red_pos, &si->red_size);
0436         find_bits(pixel_info.green_mask,
0437               &si->green_pos, &si->green_size);
0438         find_bits(pixel_info.blue_mask,
0439               &si->blue_pos, &si->blue_size);
0440         find_bits(pixel_info.reserved_mask,
0441               &si->rsvd_pos, &si->rsvd_size);
0442         si->lfb_depth = si->red_size + si->green_size +
0443             si->blue_size + si->rsvd_size;
0444         si->lfb_linelength = (pixels_per_scan_line * si->lfb_depth) / 8;
0445     } else {
0446         if (pixel_format == PIXEL_RGB_RESERVED_8BIT_PER_COLOR) {
0447             si->red_pos   = 0;
0448             si->blue_pos  = 16;
0449         } else /* PIXEL_BGR_RESERVED_8BIT_PER_COLOR */ {
0450             si->blue_pos  = 0;
0451             si->red_pos   = 16;
0452         }
0453 
0454         si->green_pos = 8;
0455         si->rsvd_pos  = 24;
0456         si->red_size = si->green_size =
0457             si->blue_size = si->rsvd_size = 8;
0458 
0459         si->lfb_depth = 32;
0460         si->lfb_linelength = pixels_per_scan_line * 4;
0461     }
0462 }
0463 
0464 static efi_graphics_output_protocol_t *
0465 find_gop(efi_guid_t *proto, unsigned long size, void **handles)
0466 {
0467     efi_graphics_output_protocol_t *first_gop;
0468     efi_handle_t h;
0469     int i;
0470 
0471     first_gop = NULL;
0472 
0473     for_each_efi_handle(h, handles, size, i) {
0474         efi_status_t status;
0475 
0476         efi_graphics_output_protocol_t *gop;
0477         efi_graphics_output_protocol_mode_t *mode;
0478         efi_graphics_output_mode_info_t *info;
0479 
0480         efi_guid_t conout_proto = EFI_CONSOLE_OUT_DEVICE_GUID;
0481         void *dummy = NULL;
0482 
0483         status = efi_bs_call(handle_protocol, h, proto, (void **)&gop);
0484         if (status != EFI_SUCCESS)
0485             continue;
0486 
0487         mode = efi_table_attr(gop, mode);
0488         info = efi_table_attr(mode, info);
0489         if (info->pixel_format == PIXEL_BLT_ONLY ||
0490             info->pixel_format >= PIXEL_FORMAT_MAX)
0491             continue;
0492 
0493         /*
0494          * Systems that use the UEFI Console Splitter may
0495          * provide multiple GOP devices, not all of which are
0496          * backed by real hardware. The workaround is to search
0497          * for a GOP implementing the ConOut protocol, and if
0498          * one isn't found, to just fall back to the first GOP.
0499          *
0500          * Once we've found a GOP supporting ConOut,
0501          * don't bother looking any further.
0502          */
0503         status = efi_bs_call(handle_protocol, h, &conout_proto, &dummy);
0504         if (status == EFI_SUCCESS)
0505             return gop;
0506 
0507         if (!first_gop)
0508             first_gop = gop;
0509     }
0510 
0511     return first_gop;
0512 }
0513 
0514 static efi_status_t setup_gop(struct screen_info *si, efi_guid_t *proto,
0515                   unsigned long size, void **handles)
0516 {
0517     efi_graphics_output_protocol_t *gop;
0518     efi_graphics_output_protocol_mode_t *mode;
0519     efi_graphics_output_mode_info_t *info;
0520 
0521     gop = find_gop(proto, size, handles);
0522 
0523     /* Did we find any GOPs? */
0524     if (!gop)
0525         return EFI_NOT_FOUND;
0526 
0527     /* Change mode if requested */
0528     set_mode(gop);
0529 
0530     /* EFI framebuffer */
0531     mode = efi_table_attr(gop, mode);
0532     info = efi_table_attr(mode, info);
0533 
0534     si->orig_video_isVGA = VIDEO_TYPE_EFI;
0535 
0536     si->lfb_width  = info->horizontal_resolution;
0537     si->lfb_height = info->vertical_resolution;
0538 
0539     efi_set_u64_split(efi_table_attr(mode, frame_buffer_base),
0540               &si->lfb_base, &si->ext_lfb_base);
0541     if (si->ext_lfb_base)
0542         si->capabilities |= VIDEO_CAPABILITY_64BIT_BASE;
0543 
0544     si->pages = 1;
0545 
0546     setup_pixel_info(si, info->pixels_per_scan_line,
0547                  info->pixel_information, info->pixel_format);
0548 
0549     si->lfb_size = si->lfb_linelength * si->lfb_height;
0550 
0551     si->capabilities |= VIDEO_CAPABILITY_SKIP_QUIRKS;
0552 
0553     return EFI_SUCCESS;
0554 }
0555 
0556 /*
0557  * See if we have Graphics Output Protocol
0558  */
0559 efi_status_t efi_setup_gop(struct screen_info *si, efi_guid_t *proto,
0560                unsigned long size)
0561 {
0562     efi_status_t status;
0563     void **gop_handle = NULL;
0564 
0565     status = efi_bs_call(allocate_pool, EFI_LOADER_DATA, size,
0566                  (void **)&gop_handle);
0567     if (status != EFI_SUCCESS)
0568         return status;
0569 
0570     status = efi_bs_call(locate_handle, EFI_LOCATE_BY_PROTOCOL, proto, NULL,
0571                  &size, gop_handle);
0572     if (status != EFI_SUCCESS)
0573         goto free_handle;
0574 
0575     status = setup_gop(si, proto, size, gop_handle);
0576 
0577 free_handle:
0578     efi_bs_call(free_pool, gop_handle);
0579     return status;
0580 }