cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

gop.c (13303B)


      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}