]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
efi/libstub: Refactor and clean up GOP resolution picker code
authorArd Biesheuvel <ardb@kernel.org>
Fri, 20 Dec 2024 11:00:28 +0000 (12:00 +0100)
committerArd Biesheuvel <ardb@kernel.org>
Tue, 14 Jan 2025 07:35:27 +0000 (08:35 +0100)
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>
drivers/firmware/efi/libstub/gop.c

index a4b3d18f91da82b8958f97ac371080c0600c2db8..3785fb4986b4390ae44a34465ee26db9ee8adc1f 100644 (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 u32 choose_mode_res(efi_graphics_output_protocol_t *gop)
+static bool match_res(const efi_graphics_output_mode_info_t *info, u32 mode, void *ctx)
 {
-       efi_status_t status;
+       efi_pixel_bitmask_t pi = info->pixel_information;
+       int pf = info->pixel_format;
 
-       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;
+       if (pf == PIXEL_BLT_ONLY || pf >= PIXEL_FORMAT_MAX)
+               return false;
 
-       mode = efi_table_attr(gop, mode);
+       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));
+}
 
-       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;
+static u32 choose_mode_res(efi_graphics_output_protocol_t *gop)
+{
+       efi_graphics_output_protocol_mode_t *mode = efi_table_attr(gop, mode);
+       unsigned long cur_mode = efi_table_attr(mode, mode);
 
-       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);
-
-       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;
+       return choose_mode(gop, match_res, (void *)cur_mode);
+}
 
-               pf = info->pixel_format;
-               pi = info->pixel_information;
-               w  = info->horizontal_resolution;
-               h  = info->vertical_resolution;
+struct match {
+       u32     mode;
+       u32     area;
+       u8      depth;
+};
 
-               efi_bs_call(free_pool, info);
+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;
 
-               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;
-       }
+       if (pf == PIXEL_BLT_ONLY || pf >= PIXEL_FORMAT_MAX)
+               return false;
 
-       efi_err("Couldn't find requested mode\n");
+       if (area > m->area || (area == m->area && depth > m->depth))
+               *m = (struct match){ mode, area, depth };
 
-       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;
-
-       mode = efi_table_attr(gop, mode);
-
-       cur_mode = efi_table_attr(mode, mode);
-       max_mode = efi_table_attr(mode, max_mode);
-
-       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;
+       return match.mode;
+}
 
-               pf = info->pixel_format;
-               pi = info->pixel_information;
-               w  = info->horizontal_resolution;
-               h  = info->vertical_resolution;
+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;
 
-               efi_bs_call(free_pool, info);
+       valid = !(pf == PIXEL_BLT_ONLY || pf >= PIXEL_FORMAT_MAX);
 
-               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);