brintos

brintos / linux-shallow public Read only

0
0
Text · 13.0 KiB · ea5da30 Raw
581 lines · c
1// SPDX-License-Identifier: GPL-2.02/* -----------------------------------------------------------------------3 *4 *   Copyright 2011 Intel Corporation; author Matt Fleming5 *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_LIST24};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		else77			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	} else185		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 void430setup_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 may495		 * provide multiple GOP devices, not all of which are496		 * backed by real hardware. The workaround is to search497		 * for a GOP implementing the ConOut protocol, and if498		 * 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 Protocol558 */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