efi/libstub: Refactor and clean up GOP resolution picker code

The EFI stub implements various ways of setting the resolution of the
EFI framebuffer at boot, and this duplicates a lot of boilerplate for
iterating over the supported modes and extracting the resolution and
color depth.

Refactor this into a single helper that takes a callback, and use it for
the 'auto', 'list' and 'res' selection methods.

Signed-off-by: Ard Biesheuvel <ardb@kernel.org>
This commit is contained in:
Ard Biesheuvel 2024-12-20 12:00:28 +01:00
parent 60a34085c3
commit b52587c5e8

View File

@ -133,13 +133,11 @@ void efi_parse_option_graphics(char *option)
static u32 choose_mode_modenum(efi_graphics_output_protocol_t *gop)
{
efi_status_t status;
efi_graphics_output_mode_info_t *info __free(efi_pool) = NULL;
efi_graphics_output_protocol_mode_t *mode;
efi_graphics_output_mode_info_t *info;
unsigned long info_size;
u32 max_mode, cur_mode;
efi_status_t status;
int pf;
mode = efi_table_attr(gop, mode);
@ -154,17 +152,13 @@ static u32 choose_mode_modenum(efi_graphics_output_protocol_t *gop)
return cur_mode;
}
status = efi_call_proto(gop, query_mode, cmdline.mode,
&info_size, &info);
status = efi_call_proto(gop, query_mode, cmdline.mode, &info_size, &info);
if (status != EFI_SUCCESS) {
efi_err("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_err("Invalid PixelFormat\n");
return cur_mode;
@ -173,6 +167,28 @@ static u32 choose_mode_modenum(efi_graphics_output_protocol_t *gop)
return cmdline.mode;
}
static u32 choose_mode(efi_graphics_output_protocol_t *gop,
bool (*match)(const efi_graphics_output_mode_info_t *, u32, void *),
void *ctx)
{
efi_graphics_output_protocol_mode_t *mode = efi_table_attr(gop, mode);
u32 max_mode = efi_table_attr(mode, max_mode);
for (u32 m = 0; m < max_mode; m++) {
efi_graphics_output_mode_info_t *info __free(efi_pool) = NULL;
unsigned long info_size;
efi_status_t status;
status = efi_call_proto(gop, query_mode, m, &info_size, &info);
if (status != EFI_SUCCESS)
continue;
if (match(info, m, ctx))
return m;
}
return (unsigned long)ctx;
}
static u8 pixel_bpp(int pixel_format, efi_pixel_bitmask_t pixel_info)
{
if (pixel_format == PIXEL_BIT_MASK) {
@ -185,192 +201,117 @@ static u8 pixel_bpp(int pixel_format, efi_pixel_bitmask_t pixel_info)
return 32;
}
static bool match_res(const efi_graphics_output_mode_info_t *info, u32 mode, void *ctx)
{
efi_pixel_bitmask_t pi = info->pixel_information;
int pf = info->pixel_format;
if (pf == PIXEL_BLT_ONLY || pf >= PIXEL_FORMAT_MAX)
return false;
return cmdline.res.width == info->horizontal_resolution &&
cmdline.res.height == info->vertical_resolution &&
(cmdline.res.format < 0 || cmdline.res.format == pf) &&
(!cmdline.res.depth || cmdline.res.depth == pixel_bpp(pf, pi));
}
static u32 choose_mode_res(efi_graphics_output_protocol_t *gop)
{
efi_status_t status;
efi_graphics_output_protocol_mode_t *mode = efi_table_attr(gop, mode);
unsigned long cur_mode = efi_table_attr(mode, mode);
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;
efi_pixel_bitmask_t pi;
u32 m, w, h;
mode = efi_table_attr(gop, mode);
cur_mode = efi_table_attr(mode, mode);
info = efi_table_attr(mode, info);
pf = info->pixel_format;
pi = info->pixel_information;
w = info->horizontal_resolution;
h = info->vertical_resolution;
if (w == cmdline.res.width && h == cmdline.res.height &&
(cmdline.res.format < 0 || cmdline.res.format == pf) &&
(!cmdline.res.depth || cmdline.res.depth == pixel_bpp(pf, pi)))
if (match_res(efi_table_attr(mode, info), cur_mode, NULL))
return cur_mode;
max_mode = efi_table_attr(mode, max_mode);
return choose_mode(gop, match_res, (void *)cur_mode);
}
for (m = 0; m < max_mode; m++) {
if (m == cur_mode)
continue;
struct match {
u32 mode;
u32 area;
u8 depth;
};
status = efi_call_proto(gop, query_mode, m,
&info_size, &info);
if (status != EFI_SUCCESS)
continue;
static bool match_auto(const efi_graphics_output_mode_info_t *info, u32 mode, void *ctx)
{
u32 area = info->horizontal_resolution * info->vertical_resolution;
efi_pixel_bitmask_t pi = info->pixel_information;
int pf = info->pixel_format;
u8 depth = pixel_bpp(pf, pi);
struct match *m = ctx;
pf = info->pixel_format;
pi = info->pixel_information;
w = info->horizontal_resolution;
h = info->vertical_resolution;
if (pf == PIXEL_BLT_ONLY || pf >= PIXEL_FORMAT_MAX)
return false;
efi_bs_call(free_pool, info);
if (area > m->area || (area == m->area && depth > m->depth))
*m = (struct match){ mode, area, depth };
if (pf == PIXEL_BLT_ONLY || pf >= PIXEL_FORMAT_MAX)
continue;
if (w == cmdline.res.width && h == cmdline.res.height &&
(cmdline.res.format < 0 || cmdline.res.format == pf) &&
(!cmdline.res.depth || cmdline.res.depth == pixel_bpp(pf, pi)))
return m;
}
efi_err("Couldn't find requested mode\n");
return cur_mode;
return false;
}
static u32 choose_mode_auto(efi_graphics_output_protocol_t *gop)
{
efi_status_t status;
struct match match = {};
efi_graphics_output_protocol_mode_t *mode;
efi_graphics_output_mode_info_t *info;
unsigned long info_size;
choose_mode(gop, match_auto, &match);
u32 max_mode, cur_mode, best_mode, area;
u8 depth;
int pf;
efi_pixel_bitmask_t pi;
u32 m, w, h, a;
u8 d;
return match.mode;
}
mode = efi_table_attr(gop, mode);
static bool match_list(const efi_graphics_output_mode_info_t *info, u32 mode, void *ctx)
{
efi_pixel_bitmask_t pi = info->pixel_information;
u32 cur_mode = (unsigned long)ctx;
int pf = info->pixel_format;
const char *dstr;
u8 depth = 0;
bool valid;
cur_mode = efi_table_attr(mode, mode);
max_mode = efi_table_attr(mode, max_mode);
valid = !(pf == PIXEL_BLT_ONLY || pf >= PIXEL_FORMAT_MAX);
info = efi_table_attr(mode, info);
pf = info->pixel_format;
pi = info->pixel_information;
w = info->horizontal_resolution;
h = info->vertical_resolution;
best_mode = cur_mode;
area = w * h;
depth = pixel_bpp(pf, pi);
for (m = 0; m < max_mode; m++) {
if (m == cur_mode)
continue;
status = efi_call_proto(gop, query_mode, m,
&info_size, &info);
if (status != EFI_SUCCESS)
continue;
pf = info->pixel_format;
pi = info->pixel_information;
w = info->horizontal_resolution;
h = info->vertical_resolution;
efi_bs_call(free_pool, info);
if (pf == PIXEL_BLT_ONLY || pf >= PIXEL_FORMAT_MAX)
continue;
a = w * h;
if (a < area)
continue;
d = pixel_bpp(pf, pi);
if (a > area || d > depth) {
best_mode = m;
area = a;
depth = d;
}
switch (pf) {
case PIXEL_RGB_RESERVED_8BIT_PER_COLOR:
dstr = "rgb";
break;
case PIXEL_BGR_RESERVED_8BIT_PER_COLOR:
dstr = "bgr";
break;
case PIXEL_BIT_MASK:
dstr = "";
depth = pixel_bpp(pf, pi);
break;
case PIXEL_BLT_ONLY:
dstr = "blt";
break;
default:
dstr = "xxx";
break;
}
return best_mode;
efi_printk("Mode %3u %c%c: Resolution %ux%u-%s%.0hhu\n",
mode,
(mode == cur_mode) ? '*' : ' ',
!valid ? '-' : ' ',
info->horizontal_resolution,
info->vertical_resolution,
dstr, depth);
return false;
}
static u32 choose_mode_list(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;
efi_pixel_bitmask_t pi;
u32 m, w, h;
u8 d;
const char *dstr;
bool valid;
efi_graphics_output_protocol_mode_t *mode = efi_table_attr(gop, mode);
unsigned long cur_mode = efi_table_attr(mode, mode);
u32 max_mode = efi_table_attr(mode, max_mode);
efi_input_key_t key;
mode = efi_table_attr(gop, mode);
cur_mode = efi_table_attr(mode, mode);
max_mode = efi_table_attr(mode, max_mode);
efi_status_t status;
efi_printk("Available graphics modes are 0-%u\n", max_mode-1);
efi_puts(" * = current mode\n"
" - = unusable mode\n");
for (m = 0; m < max_mode; m++) {
status = efi_call_proto(gop, query_mode, m,
&info_size, &info);
if (status != EFI_SUCCESS)
continue;
pf = info->pixel_format;
pi = info->pixel_information;
w = info->horizontal_resolution;
h = info->vertical_resolution;
efi_bs_call(free_pool, info);
valid = !(pf == PIXEL_BLT_ONLY || pf >= PIXEL_FORMAT_MAX);
d = 0;
switch (pf) {
case PIXEL_RGB_RESERVED_8BIT_PER_COLOR:
dstr = "rgb";
break;
case PIXEL_BGR_RESERVED_8BIT_PER_COLOR:
dstr = "bgr";
break;
case PIXEL_BIT_MASK:
dstr = "";
d = pixel_bpp(pf, pi);
break;
case PIXEL_BLT_ONLY:
dstr = "blt";
break;
default:
dstr = "xxx";
break;
}
efi_printk("Mode %3u %c%c: Resolution %ux%u-%s%.0hhu\n",
m,
m == cur_mode ? '*' : ' ',
!valid ? '-' : ' ',
w, h, dstr, d);
}
choose_mode(gop, match_list, (void *)cur_mode);
efi_puts("\nPress any key to continue (or wait 10 seconds)\n");
status = efi_wait_for_key(10 * EFI_USEC_PER_SEC, &key);