grub-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[PATCH v3] efi: Add efitextmode command for getting/setting the text mod


From: Glenn Washburn
Subject: [PATCH v3] efi: Add efitextmode command for getting/setting the text mode resolution
Date: Fri, 22 Jul 2022 02:16:33 -0500

This command is meant to behave similarly to the 'mode' command of the EFI
Shell application. In addition to allowing mode selection by giving the
number of columns and rows as arguments, the command allows specifying the
mode number to select the mode. Also supported are the arguments "min" and
"max", which set the mode to the minimum and maximum mode respectively as
calculated by the columns * rows of that mode.

Signed-off-by: Glenn Washburn <development@efficientek.com>
---
Updates since v2:
* Merge docs patch with this patch
* Allow specifying cols and rows to select mode
* Refactor
* Other changes suggested by Daniel

Glenn
---
 docs/grub.texi                       |  42 ++++++++
 grub-core/Makefile.core.def          |   6 ++
 grub-core/commands/efi/efitextmode.c | 156 +++++++++++++++++++++++++++
 include/grub/efi/api.h               |   6 ++
 include/grub/err.h                   |   3 +-
 5 files changed, 212 insertions(+), 1 deletion(-)
 create mode 100644 grub-core/commands/efi/efitextmode.c

diff --git a/docs/grub.texi b/docs/grub.texi
index af119dea3..31194a0c7 100644
--- a/docs/grub.texi
+++ b/docs/grub.texi
@@ -4238,6 +4238,7 @@ you forget a command, you can run the command 
@command{help}
 * distrust::                    Remove a pubkey from trusted keys
 * drivemap::                    Map a drive to another
 * echo::                        Display a line of text
+* efitextmode::                 Set/Get text output mode resolution
 * eval::                        Evaluate agruments as GRUB commands
 * export::                      Export an environment variable
 * false::                       Do nothing, unsuccessfully
@@ -4685,6 +4686,47 @@ character will print that character.
 @end deffn
 
 
+@node efitextmode
+@subsection efitextmode
+
+@deffn Command efitextmode [min | max | <mode_num> | <cols> <rows>]
+When used with no arguments displays all available text output modes. The
+set mode determines the columns and rows of the text display when in
+text mode. An asterisk, @samp{*}, will be at the end of the line of the
+currently set mode.
+
+If given a single parameter, it must be @samp{min}, @samp{max}, or a mode
+number given by the listing when run with no arguments. These arguments set
+the mode to the minimum, maximum, and particular mode respectively.
+
+Otherwise, the command must be given two numerical arguments specifying the
+columns and rows of the desired mode. Specifying a columns and rows
+combination that corresponds to no supported mode, will return error, but
+otherwise have no effect.
+
+By default GRUB will start in whatever mode the EFI firmware defaults to.
+There are firmwares known to set up the default mode such that output
+behaves strangely, for example the cursor in the grub shell never reaches
+the bottom of the screen or, when typing characters at the prompt,
+characters from previous command output are overwritten. Setting the mode
+may fix this.
+
+The EFI specification says that mode 0 is must be available and have
+columns and rows of 80 and 25 respectively. Mode 1 may be defined and if
+so must have columns and rows of 80 and 50 respectively. Any other modes
+may have columns and rows arbitrarily defined by the firmware. This means
+that a mode with columns and rows of 100 and 31 on one firmware may be a
+different mode number on a different firmware or not exist at all.
+Likewise, mode number 2 on one firmware may have a different number of
+columns and rows than mode 2 on a different firmware. So one should not
+rely on a particular mode number or a mode of a certain number of columns
+and rows existing on all firmwares, except for mode 0.
+
+Note: This command is only available on EFI platforms and is similar to
+EFI shell "mode" command.
+@end deffn
+
+
 @node eval
 @subsection eval
 
diff --git a/grub-core/Makefile.core.def b/grub-core/Makefile.core.def
index 715994872..5212dfab1 100644
--- a/grub-core/Makefile.core.def
+++ b/grub-core/Makefile.core.def
@@ -813,6 +813,12 @@ module = {
   enable = efi;
 };
 
+module = {
+  name = efitextmode;
+  efi = commands/efi/efitextmode.c;
+  enable = efi;
+};
+
 module = {
   name = blocklist;
   common = commands/blocklist.c;
diff --git a/grub-core/commands/efi/efitextmode.c 
b/grub-core/commands/efi/efitextmode.c
new file mode 100644
index 000000000..71d0fe625
--- /dev/null
+++ b/grub-core/commands/efi/efitextmode.c
@@ -0,0 +1,156 @@
+/* efitextmode.c - command to get/set text mode resolution */
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 2022  Free Software Foundation, Inc.
+ *
+ *  GRUB is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  GRUB is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with GRUB.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ *  Set/Get UEFI text output mode resolution
+ */
+
+#include <grub/dl.h>
+#include <grub/misc.h>
+#include <grub/mm.h>
+#include <grub/command.h>
+#include <grub/i18n.h>
+#include <grub/efi/efi.h>
+#include <grub/efi/api.h>
+
+GRUB_MOD_LICENSE ("GPLv3+");
+
+static grub_err_t
+grub_efi_set_mode (grub_efi_simple_text_output_interface_t *o,
+                  grub_efi_int32_t mode)
+{
+  grub_efi_status_t status;
+
+  if (mode != o->mode->mode)
+    {
+      status = efi_call_2 (o->set_mode, o, mode);
+      if (status == GRUB_EFI_SUCCESS)
+       ;
+      else if (status == GRUB_EFI_DEVICE_ERROR)
+       return grub_error (GRUB_ERR_BAD_DEVICE,
+                          N_("device error: could not set requested mode"));
+      else if (status == GRUB_EFI_UNSUPPORTED)
+       return grub_error (GRUB_ERR_OUT_OF_RANGE,
+                          N_("invalid mode: number not valid"));
+      else
+       return grub_error (GRUB_ERR_BAD_FIRMWARE,
+                          N_("unexpected EFI error number: `%u'"),
+                          (unsigned) status);
+    }
+
+  return GRUB_ERR_NONE;
+}
+
+static grub_err_t
+grub_cmd_efitextmode (grub_command_t cmd __attribute__ ((unused)),
+                     int argc, char **args)
+{
+  grub_efi_simple_text_output_interface_t *o = grub_efi_system_table->con_out;
+  unsigned long mode;
+  const char *p = NULL;
+  grub_err_t err;
+  grub_efi_uintn_t columns, rows;
+  grub_efi_int32_t i;
+
+  if (o == NULL)
+    return grub_error (GRUB_ERR_BAD_DEVICE, N_("no UEFI output console 
interface"));
+
+  if (o->mode == NULL)
+    return grub_error (GRUB_ERR_BUG, N_("no mode struct for UEFI output 
console"));
+
+  if (argc > 2)
+    return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("at most two arguments 
expected"));
+
+  if (argc == 0)
+    {
+      grub_printf_ (N_("Available modes for console output device.\n"));
+
+      for (i = 0; i < o->mode->max_mode; i++)
+       if (GRUB_EFI_SUCCESS == efi_call_4 (o->query_mode, o, i,
+                                           &columns, &rows))
+         grub_printf_ (N_(" [%" PRIuGRUB_EFI_UINT32_T "]  Col %5"
+                          PRIuGRUB_EFI_UINTN_T " Row %5" PRIuGRUB_EFI_UINTN_T
+                          " %c\n"),
+                       i, columns, rows, (i == o->mode->mode) ? '*' : ' ');
+    }
+  else if (argc == 1)
+    {
+      if (grub_strcmp (args[0], "min") == 0)
+       mode = 0;
+      else if (grub_strcmp (args[0], "max") == 0)
+       mode = o->mode->max_mode - 1;
+      else
+       {
+         mode = grub_strtoul (args[0], &p, 0);
+
+         if (*args[0] == '\0' || *p != '\0')
+           return grub_error (GRUB_ERR_BAD_ARGUMENT,
+                              N_("non-numeric or invalid mode `%s'"), args[0]);
+       }
+
+      if (mode < (unsigned long) o->mode->max_mode)
+       {
+         err = grub_efi_set_mode (o, (grub_efi_int32_t) mode);
+         if (err != GRUB_ERR_NONE)
+           return err;
+       }
+      else
+       return grub_error (GRUB_ERR_BAD_ARGUMENT,
+                          N_("invalid mode: `%lu' is greater than maximum mode 
`%lu'"),
+                          mode, (unsigned long) o->mode->max_mode);
+    }
+  else if (argc == 2)
+    {
+      grub_efi_uintn_t u_columns, u_rows;
+
+      u_columns = (grub_efi_uintn_t) grub_strtoul (args[0], &p, 0);
+
+      if (*args[0] == '\0' || *p != '\0')
+       return grub_error (GRUB_ERR_BAD_ARGUMENT,
+                          N_("non-numeric or invalid columns number `%s'"), 
args[0]);
+
+      u_rows = (grub_efi_uintn_t) grub_strtoul (args[1], &p, 0);
+
+      if (*args[1] == '\0' || *p != '\0')
+       return grub_error (GRUB_ERR_BAD_ARGUMENT,
+                          N_("non-numeric or invalid rows number `%s'"), 
args[1]);
+
+      for (i = 0; i < o->mode->max_mode; i++)
+       if (GRUB_EFI_SUCCESS == efi_call_4 (o->query_mode, o, i,
+                                           &columns, &rows))
+         if (u_columns == columns && u_rows == rows)
+           return grub_efi_set_mode (o, (grub_efi_int32_t) i);
+
+      return grub_error (GRUB_ERR_BAD_ARGUMENT,
+                        N_("no mode found with requested columns and rows"));
+    }
+
+  return GRUB_ERR_NONE;
+}
+
+static grub_command_t cmd;
+GRUB_MOD_INIT (efitextmode)
+{
+  cmd = grub_register_command ("efitextmode", grub_cmd_efitextmode,
+                              N_("[min | max | <mode_num> | <cols> <rows>]"),
+                              N_("Get or set EFI text mode."));
+}
+
+GRUB_MOD_FINI (efitextmode)
+{
+  grub_unregister_command (cmd);
+}
diff --git a/include/grub/efi/api.h b/include/grub/efi/api.h
index d4cadd8b5..b1cd036f4 100644
--- a/include/grub/efi/api.h
+++ b/include/grub/efi/api.h
@@ -536,9 +536,13 @@ typedef char grub_efi_boolean_t;
 #if GRUB_CPU_SIZEOF_VOID_P == 8
 typedef grub_int64_t grub_efi_intn_t;
 typedef grub_uint64_t grub_efi_uintn_t;
+#define PRIxGRUB_EFI_UINTN_T "lx"
+#define PRIuGRUB_EFI_UINTN_T "lu"
 #else
 typedef grub_int32_t grub_efi_intn_t;
 typedef grub_uint32_t grub_efi_uintn_t;
+#define PRIxGRUB_EFI_UINTN_T "x"
+#define PRIuGRUB_EFI_UINTN_T "u"
 #endif
 typedef grub_int8_t grub_efi_int8_t;
 typedef grub_uint8_t grub_efi_uint8_t;
@@ -546,6 +550,8 @@ typedef grub_int16_t grub_efi_int16_t;
 typedef grub_uint16_t grub_efi_uint16_t;
 typedef grub_int32_t grub_efi_int32_t;
 typedef grub_uint32_t grub_efi_uint32_t;
+#define PRIxGRUB_EFI_UINT32_T "x"
+#define PRIuGRUB_EFI_UINT32_T "u"
 typedef grub_int64_t grub_efi_int64_t;
 typedef grub_uint64_t grub_efi_uint64_t;
 typedef grub_uint8_t grub_efi_char8_t;
diff --git a/include/grub/err.h b/include/grub/err.h
index b08d5d0de..1c07034cd 100644
--- a/include/grub/err.h
+++ b/include/grub/err.h
@@ -72,7 +72,8 @@ typedef enum
     GRUB_ERR_NET_PACKET_TOO_BIG,
     GRUB_ERR_NET_NO_DOMAIN,
     GRUB_ERR_EOF,
-    GRUB_ERR_BAD_SIGNATURE
+    GRUB_ERR_BAD_SIGNATURE,
+    GRUB_ERR_BAD_FIRMWARE
   }
 grub_err_t;
 
-- 
2.34.1




reply via email to

[Prev in Thread] Current Thread [Next in Thread]