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 18 enum efi_cmdline_option { 19 EFI_CMDLINE_NONE, 20 EFI_CMDLINE_MODE_NUM, 21 EFI_CMDLINE_RES 22 }; 23 24 static struct { 25 enum efi_cmdline_option option; 26 union { 27 u32 mode; 28 struct { 29 u32 width, height; 30 } res; 31 }; 32 } cmdline __efistub_global = { .option = EFI_CMDLINE_NONE }; 33 34 static bool parse_modenum(char *option, char **next) 35 { 36 u32 m; 37 38 if (!strstarts(option, "mode=")) 39 return false; 40 option += strlen("mode="); 41 m = simple_strtoull(option, &option, 0); 42 if (*option && *option++ != ',') 43 return false; 44 cmdline.option = EFI_CMDLINE_MODE_NUM; 45 cmdline.mode = m; 46 47 *next = option; 48 return true; 49 } 50 51 static bool parse_res(char *option, char **next) 52 { 53 u32 w, h; 54 55 if (!isdigit(*option)) 56 return false; 57 w = simple_strtoull(option, &option, 10); 58 if (*option++ != 'x' || !isdigit(*option)) 59 return false; 60 h = simple_strtoull(option, &option, 10); 61 if (*option && *option++ != ',') 62 return false; 63 cmdline.option = EFI_CMDLINE_RES; 64 cmdline.res.width = w; 65 cmdline.res.height = h; 66 67 *next = option; 68 return true; 69 } 70 71 void efi_parse_option_graphics(char *option) 72 { 73 while (*option) { 74 if (parse_modenum(option, &option)) 75 continue; 76 if (parse_res(option, &option)) 77 continue; 78 79 while (*option && *option++ != ',') 80 ; 81 } 82 } 83 84 static u32 choose_mode_modenum(efi_graphics_output_protocol_t *gop) 85 { 86 efi_status_t status; 87 88 efi_graphics_output_protocol_mode_t *mode; 89 efi_graphics_output_mode_info_t *info; 90 unsigned long info_size; 91 92 u32 max_mode, cur_mode; 93 int pf; 94 95 mode = efi_table_attr(gop, mode); 96 97 cur_mode = efi_table_attr(mode, mode); 98 if (cmdline.mode == cur_mode) 99 return cur_mode; 100 101 max_mode = efi_table_attr(mode, max_mode); 102 if (cmdline.mode >= max_mode) { 103 efi_printk("Requested mode is invalid\n"); 104 return cur_mode; 105 } 106 107 status = efi_call_proto(gop, query_mode, cmdline.mode, 108 &info_size, &info); 109 if (status != EFI_SUCCESS) { 110 efi_printk("Couldn't get mode information\n"); 111 return cur_mode; 112 } 113 114 pf = info->pixel_format; 115 116 efi_bs_call(free_pool, info); 117 118 if (pf == PIXEL_BLT_ONLY || pf >= PIXEL_FORMAT_MAX) { 119 efi_printk("Invalid PixelFormat\n"); 120 return cur_mode; 121 } 122 123 return cmdline.mode; 124 } 125 126 static u32 choose_mode_res(efi_graphics_output_protocol_t *gop) 127 { 128 efi_status_t status; 129 130 efi_graphics_output_protocol_mode_t *mode; 131 efi_graphics_output_mode_info_t *info; 132 unsigned long info_size; 133 134 u32 max_mode, cur_mode; 135 int pf; 136 u32 m, w, h; 137 138 mode = efi_table_attr(gop, mode); 139 140 cur_mode = efi_table_attr(mode, mode); 141 info = efi_table_attr(mode, info); 142 w = info->horizontal_resolution; 143 h = info->vertical_resolution; 144 145 if (w == cmdline.res.width && h == cmdline.res.height) 146 return cur_mode; 147 148 max_mode = efi_table_attr(mode, max_mode); 149 150 for (m = 0; m < max_mode; m++) { 151 if (m == cur_mode) 152 continue; 153 154 status = efi_call_proto(gop, query_mode, m, 155 &info_size, &info); 156 if (status != EFI_SUCCESS) 157 continue; 158 159 pf = info->pixel_format; 160 w = info->horizontal_resolution; 161 h = info->vertical_resolution; 162 163 efi_bs_call(free_pool, info); 164 165 if (pf == PIXEL_BLT_ONLY || pf >= PIXEL_FORMAT_MAX) 166 continue; 167 if (w == cmdline.res.width && h == cmdline.res.height) 168 return m; 169 } 170 171 efi_printk("Couldn't find requested mode\n"); 172 173 return cur_mode; 174 } 175 176 static void set_mode(efi_graphics_output_protocol_t *gop) 177 { 178 efi_graphics_output_protocol_mode_t *mode; 179 u32 cur_mode, new_mode; 180 181 switch (cmdline.option) { 182 case EFI_CMDLINE_MODE_NUM: 183 new_mode = choose_mode_modenum(gop); 184 break; 185 case EFI_CMDLINE_RES: 186 new_mode = choose_mode_res(gop); 187 break; 188 default: 189 return; 190 } 191 192 mode = efi_table_attr(gop, mode); 193 cur_mode = efi_table_attr(mode, mode); 194 195 if (new_mode == cur_mode) 196 return; 197 198 if (efi_call_proto(gop, set_mode, new_mode) != EFI_SUCCESS) 199 efi_printk("Failed to set requested mode\n"); 200 } 201 202 static void find_bits(u32 mask, u8 *pos, u8 *size) 203 { 204 if (!mask) { 205 *pos = *size = 0; 206 return; 207 } 208 209 /* UEFI spec guarantees that the set bits are contiguous */ 210 *pos = __ffs(mask); 211 *size = __fls(mask) - *pos + 1; 212 } 213 214 static void 215 setup_pixel_info(struct screen_info *si, u32 pixels_per_scan_line, 216 efi_pixel_bitmask_t pixel_info, int pixel_format) 217 { 218 if (pixel_format == PIXEL_BIT_MASK) { 219 find_bits(pixel_info.red_mask, 220 &si->red_pos, &si->red_size); 221 find_bits(pixel_info.green_mask, 222 &si->green_pos, &si->green_size); 223 find_bits(pixel_info.blue_mask, 224 &si->blue_pos, &si->blue_size); 225 find_bits(pixel_info.reserved_mask, 226 &si->rsvd_pos, &si->rsvd_size); 227 si->lfb_depth = si->red_size + si->green_size + 228 si->blue_size + si->rsvd_size; 229 si->lfb_linelength = (pixels_per_scan_line * si->lfb_depth) / 8; 230 } else { 231 if (pixel_format == PIXEL_RGB_RESERVED_8BIT_PER_COLOR) { 232 si->red_pos = 0; 233 si->blue_pos = 16; 234 } else /* PIXEL_BGR_RESERVED_8BIT_PER_COLOR */ { 235 si->blue_pos = 0; 236 si->red_pos = 16; 237 } 238 239 si->green_pos = 8; 240 si->rsvd_pos = 24; 241 si->red_size = si->green_size = 242 si->blue_size = si->rsvd_size = 8; 243 244 si->lfb_depth = 32; 245 si->lfb_linelength = pixels_per_scan_line * 4; 246 } 247 } 248 249 static efi_graphics_output_protocol_t * 250 find_gop(efi_guid_t *proto, unsigned long size, void **handles) 251 { 252 efi_graphics_output_protocol_t *first_gop; 253 efi_handle_t h; 254 int i; 255 256 first_gop = NULL; 257 258 for_each_efi_handle(h, handles, size, i) { 259 efi_status_t status; 260 261 efi_graphics_output_protocol_t *gop; 262 efi_graphics_output_protocol_mode_t *mode; 263 efi_graphics_output_mode_info_t *info; 264 265 efi_guid_t conout_proto = EFI_CONSOLE_OUT_DEVICE_GUID; 266 void *dummy = NULL; 267 268 status = efi_bs_call(handle_protocol, h, proto, (void **)&gop); 269 if (status != EFI_SUCCESS) 270 continue; 271 272 mode = efi_table_attr(gop, mode); 273 info = efi_table_attr(mode, info); 274 if (info->pixel_format == PIXEL_BLT_ONLY || 275 info->pixel_format >= PIXEL_FORMAT_MAX) 276 continue; 277 278 /* 279 * Systems that use the UEFI Console Splitter may 280 * provide multiple GOP devices, not all of which are 281 * backed by real hardware. The workaround is to search 282 * for a GOP implementing the ConOut protocol, and if 283 * one isn't found, to just fall back to the first GOP. 284 * 285 * Once we've found a GOP supporting ConOut, 286 * don't bother looking any further. 287 */ 288 status = efi_bs_call(handle_protocol, h, &conout_proto, &dummy); 289 if (status == EFI_SUCCESS) 290 return gop; 291 292 if (!first_gop) 293 first_gop = gop; 294 } 295 296 return first_gop; 297 } 298 299 static efi_status_t setup_gop(struct screen_info *si, efi_guid_t *proto, 300 unsigned long size, void **handles) 301 { 302 efi_graphics_output_protocol_t *gop; 303 efi_graphics_output_protocol_mode_t *mode; 304 efi_graphics_output_mode_info_t *info; 305 efi_physical_addr_t fb_base; 306 307 gop = find_gop(proto, size, handles); 308 309 /* Did we find any GOPs? */ 310 if (!gop) 311 return EFI_NOT_FOUND; 312 313 /* Change mode if requested */ 314 set_mode(gop); 315 316 /* EFI framebuffer */ 317 mode = efi_table_attr(gop, mode); 318 info = efi_table_attr(mode, info); 319 320 si->orig_video_isVGA = VIDEO_TYPE_EFI; 321 322 si->lfb_width = info->horizontal_resolution; 323 si->lfb_height = info->vertical_resolution; 324 325 fb_base = efi_table_attr(mode, frame_buffer_base); 326 si->lfb_base = lower_32_bits(fb_base); 327 si->ext_lfb_base = upper_32_bits(fb_base); 328 if (si->ext_lfb_base) 329 si->capabilities |= VIDEO_CAPABILITY_64BIT_BASE; 330 331 si->pages = 1; 332 333 setup_pixel_info(si, info->pixels_per_scan_line, 334 info->pixel_information, info->pixel_format); 335 336 si->lfb_size = si->lfb_linelength * si->lfb_height; 337 338 si->capabilities |= VIDEO_CAPABILITY_SKIP_QUIRKS; 339 340 return EFI_SUCCESS; 341 } 342 343 /* 344 * See if we have Graphics Output Protocol 345 */ 346 efi_status_t efi_setup_gop(struct screen_info *si, efi_guid_t *proto, 347 unsigned long size) 348 { 349 efi_status_t status; 350 void **gop_handle = NULL; 351 352 status = efi_bs_call(allocate_pool, EFI_LOADER_DATA, size, 353 (void **)&gop_handle); 354 if (status != EFI_SUCCESS) 355 return status; 356 357 status = efi_bs_call(locate_handle, EFI_LOCATE_BY_PROTOCOL, proto, NULL, 358 &size, gop_handle); 359 if (status != EFI_SUCCESS) 360 goto free_handle; 361 362 status = setup_gop(si, proto, size, gop_handle); 363 364 free_handle: 365 efi_bs_call(free_pool, gop_handle); 366 return status; 367 } 368