efi/gop: Add an option to list out the available GOP modes
authorArvind Sankar <nivedita@alum.mit.edu>
Mon, 18 May 2020 19:07:11 +0000 (15:07 -0400)
committerArd Biesheuvel <ardb@kernel.org>
Wed, 20 May 2020 17:09:20 +0000 (19:09 +0200)
Add video=efifb:list option to list the modes that are available.

Signed-off-by: Arvind Sankar <nivedita@alum.mit.edu>
Link: https://lore.kernel.org/r/20200518190716.751506-20-nivedita@alum.mit.edu
Signed-off-by: Ard Biesheuvel <ardb@kernel.org>
Documentation/fb/efifb.rst
drivers/firmware/efi/libstub/efi-stub-helper.c
drivers/firmware/efi/libstub/efistub.h
drivers/firmware/efi/libstub/gop.c
include/linux/efi.h

index 5195505..6badff6 100644 (file)
@@ -63,4 +63,9 @@ auto
         with the highest resolution, it will choose one with the highest color
         depth.
 
+list
+        The EFI stub will list out all the display modes that are available. A
+        specific mode can then be chosen using one of the above options for the
+        next boot.
+
 Edgar Hucek <gimli@dark-green.com>
index 1f5a00b..f338d14 100644 (file)
@@ -463,3 +463,38 @@ efi_status_t efi_load_initrd(efi_loaded_image_t *image,
 
        return status;
 }
+
+efi_status_t efi_wait_for_key(unsigned long usec, efi_input_key_t *key)
+{
+       efi_event_t events[2], timer;
+       unsigned long index;
+       efi_simple_text_input_protocol_t *con_in;
+       efi_status_t status;
+
+       con_in = efi_table_attr(efi_system_table, con_in);
+       if (!con_in)
+               return EFI_UNSUPPORTED;
+       efi_set_event_at(events, 0, efi_table_attr(con_in, wait_for_key));
+
+       status = efi_bs_call(create_event, EFI_EVT_TIMER, 0, NULL, NULL, &timer);
+       if (status != EFI_SUCCESS)
+               return status;
+
+       status = efi_bs_call(set_timer, timer, EfiTimerRelative,
+                            EFI_100NSEC_PER_USEC * usec);
+       if (status != EFI_SUCCESS)
+               return status;
+       efi_set_event_at(events, 1, timer);
+
+       status = efi_bs_call(wait_for_event, 2, events, &index);
+       if (status == EFI_SUCCESS) {
+               if (index == 0)
+                       status = efi_call_proto(con_in, read_keystroke, key);
+               else
+                       status = EFI_TIMEOUT;
+       }
+
+       efi_bs_call(close_event, timer);
+
+       return status;
+}
index c7c0309..ad7e040 100644 (file)
@@ -323,6 +323,8 @@ union efi_simple_text_input_protocol {
        } mixed_mode;
 };
 
+efi_status_t efi_wait_for_key(unsigned long usec, efi_input_key_t *key);
+
 union efi_simple_text_output_protocol {
        struct {
                void *reset;
index 34c0cba..ea5da30 100644 (file)
@@ -19,7 +19,8 @@ enum efi_cmdline_option {
        EFI_CMDLINE_NONE,
        EFI_CMDLINE_MODE_NUM,
        EFI_CMDLINE_RES,
-       EFI_CMDLINE_AUTO
+       EFI_CMDLINE_AUTO,
+       EFI_CMDLINE_LIST
 };
 
 static struct {
@@ -100,6 +101,19 @@ static bool parse_auto(char *option, char **next)
        return true;
 }
 
+static bool parse_list(char *option, char **next)
+{
+       if (!strstarts(option, "list"))
+               return false;
+       option += strlen("list");
+       if (*option && *option++ != ',')
+               return false;
+       cmdline.option = EFI_CMDLINE_LIST;
+
+       *next = option;
+       return true;
+}
+
 void efi_parse_option_graphics(char *option)
 {
        while (*option) {
@@ -109,6 +123,8 @@ void efi_parse_option_graphics(char *option)
                        continue;
                if (parse_auto(option, &option))
                        continue;
+               if (parse_list(option, &option))
+                       continue;
 
                while (*option && *option++ != ',')
                        ;
@@ -290,6 +306,82 @@ static u32 choose_mode_auto(efi_graphics_output_protocol_t *gop)
        return best_mode;
 }
 
+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_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_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);
+       }
+
+       efi_puts("\nPress any key to continue (or wait 10 seconds)\n");
+       status = efi_wait_for_key(10 * EFI_USEC_PER_SEC, &key);
+       if (status != EFI_SUCCESS && status != EFI_TIMEOUT) {
+               efi_err("Unable to read key, continuing in 10 seconds\n");
+               efi_bs_call(stall, 10 * EFI_USEC_PER_SEC);
+       }
+
+       return cur_mode;
+}
+
 static void set_mode(efi_graphics_output_protocol_t *gop)
 {
        efi_graphics_output_protocol_mode_t *mode;
@@ -305,6 +397,9 @@ static void set_mode(efi_graphics_output_protocol_t *gop)
        case EFI_CMDLINE_AUTO:
                new_mode = choose_mode_auto(gop);
                break;
+       case EFI_CMDLINE_LIST:
+               new_mode = choose_mode_list(gop);
+               break;
        default:
                return;
        }
index 974648d..609201b 100644 (file)
@@ -39,6 +39,7 @@
 #define EFI_WRITE_PROTECTED    ( 8 | (1UL << (BITS_PER_LONG-1)))
 #define EFI_OUT_OF_RESOURCES   ( 9 | (1UL << (BITS_PER_LONG-1)))
 #define EFI_NOT_FOUND          (14 | (1UL << (BITS_PER_LONG-1)))
+#define EFI_TIMEOUT            (18 | (1UL << (BITS_PER_LONG-1)))
 #define EFI_ABORTED            (21 | (1UL << (BITS_PER_LONG-1)))
 #define EFI_SECURITY_VIOLATION (26 | (1UL << (BITS_PER_LONG-1)))