0001
0002
0003
0004
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
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 {
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
0495
0496
0497
0498
0499
0500
0501
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
0524 if (!gop)
0525 return EFI_NOT_FOUND;
0526
0527
0528 set_mode(gop);
0529
0530
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
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 }