efi: Add efitextmode command for getting/setting the text mode resolution

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>
Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
This commit is contained in:
Glenn Washburn 2022-07-22 02:16:33 -05:00 committed by Daniel Kiper
parent e43f3d93b2
commit 294c0501e9
5 changed files with 211 additions and 1 deletions

View File

@ -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 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

View File

@ -813,6 +813,12 @@ module = {
enable = efi;
};
module = {
name = efitextmode;
efi = commands/efi/efitextmode.c;
enable = efi;
};
module = {
name = blocklist;
common = commands/blocklist.c;

View File

@ -0,0 +1,155 @@
/*
* 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);
}

View File

@ -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 PRIxGRUB_UINT64_T
#define PRIuGRUB_EFI_UINTN_T PRIuGRUB_UINT64_T
#else
typedef grub_int32_t grub_efi_intn_t;
typedef grub_uint32_t grub_efi_uintn_t;
#define PRIxGRUB_EFI_UINTN_T PRIxGRUB_UINT32_T
#define PRIuGRUB_EFI_UINTN_T PRIuGRUB_UINT32_T
#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 PRIxGRUB_UINT32_T
#define PRIuGRUB_EFI_UINT32_T PRIuGRUB_UINT32_T
typedef grub_int64_t grub_efi_int64_t;
typedef grub_uint64_t grub_efi_uint64_t;
typedef grub_uint8_t grub_efi_char8_t;

View File

@ -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;