efi/gop: Allow specifying mode number on command line

Add the ability to choose a video mode for the selected gop by using a
command-line argument of the form
	video=efifb:mode=<n>

Signed-off-by: Arvind Sankar <nivedita@alum.mit.edu>
Link: https://lore.kernel.org/r/20200320020028.1936003-12-nivedita@alum.mit.edu
Signed-off-by: Ard Biesheuvel <ardb@kernel.org>
This commit is contained in:
Arvind Sankar
2020-03-19 22:00:25 -04:00
committed by Ard Biesheuvel
parent b4b89a0272
commit fffb68047e
4 changed files with 129 additions and 3 deletions

View File

@@ -105,6 +105,9 @@ efi_status_t efi_parse_options(char const *cmdline)
efi_disable_pci_dma = true;
if (parse_option_str(val, "no_disable_early_pci_dma"))
efi_disable_pci_dma = false;
} else if (!strcmp(param, "video") &&
val && strstarts(val, "efifb:")) {
efi_parse_option_graphics(val + strlen("efifb:"));
}
}
efi_bs_call(free_pool, buf);

View File

@@ -666,6 +666,8 @@ efi_status_t efi_relocate_kernel(unsigned long *image_addr,
efi_status_t efi_parse_options(char const *cmdline);
void efi_parse_option_graphics(char *option);
efi_status_t efi_setup_gop(struct screen_info *si, efi_guid_t *proto,
unsigned long size);

View File

@@ -8,11 +8,115 @@
#include <linux/bitops.h>
#include <linux/efi.h>
#include <linux/screen_info.h>
#include <linux/string.h>
#include <asm/efi.h>
#include <asm/setup.h>
#include "efistub.h"
enum efi_cmdline_option {
EFI_CMDLINE_NONE,
EFI_CMDLINE_MODE_NUM,
};
static struct {
enum efi_cmdline_option option;
u32 mode;
} cmdline __efistub_global = { .option = EFI_CMDLINE_NONE };
static bool parse_modenum(char *option, char **next)
{
u32 m;
if (!strstarts(option, "mode="))
return false;
option += strlen("mode=");
m = simple_strtoull(option, &option, 0);
if (*option && *option++ != ',')
return false;
cmdline.option = EFI_CMDLINE_MODE_NUM;
cmdline.mode = m;
*next = option;
return true;
}
void efi_parse_option_graphics(char *option)
{
while (*option) {
if (parse_modenum(option, &option))
continue;
while (*option && *option++ != ',')
;
}
}
static u32 choose_mode_modenum(efi_graphics_output_protocol_t *gop)
{
efi_status_t status;
efi_graphics_output_protocol_mode_t *mode;
efi_graphics_output_mode_info_t *info;
unsigned long info_size;
u32 max_mode, cur_mode;
int pf;
mode = efi_table_attr(gop, mode);
cur_mode = efi_table_attr(mode, mode);
if (cmdline.mode == cur_mode)
return cur_mode;
max_mode = efi_table_attr(mode, max_mode);
if (cmdline.mode >= max_mode) {
efi_printk("Requested mode is invalid\n");
return cur_mode;
}
status = efi_call_proto(gop, query_mode, cmdline.mode,
&info_size, &info);
if (status != EFI_SUCCESS) {
efi_printk("Couldn't get mode information\n");
return cur_mode;
}
pf = info->pixel_format;
efi_bs_call(free_pool, info);
if (pf == PIXEL_BLT_ONLY || pf >= PIXEL_FORMAT_MAX) {
efi_printk("Invalid PixelFormat\n");
return cur_mode;
}
return cmdline.mode;
}
static void set_mode(efi_graphics_output_protocol_t *gop)
{
efi_graphics_output_protocol_mode_t *mode;
u32 cur_mode, new_mode;
switch (cmdline.option) {
case EFI_CMDLINE_MODE_NUM:
new_mode = choose_mode_modenum(gop);
break;
default:
return;
}
mode = efi_table_attr(gop, mode);
cur_mode = efi_table_attr(mode, mode);
if (new_mode == cur_mode)
return;
if (efi_call_proto(gop, set_mode, new_mode) != EFI_SUCCESS)
efi_printk("Failed to set requested mode\n");
}
static void find_bits(u32 mask, u8 *pos, u8 *size)
{
if (!mask) {
@@ -124,6 +228,9 @@ static efi_status_t setup_gop(struct screen_info *si, efi_guid_t *proto,
if (!gop)
return EFI_NOT_FOUND;
/* Change mode if requested */
set_mode(gop);
/* EFI framebuffer */
mode = efi_table_attr(gop, mode);
info = efi_table_attr(mode, info);