All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v2] kern/efi: Adding efi-watchdog command
@ 2021-09-02 16:50 Erwan Velu
  2021-09-03  2:09 ` Michael Chang
  2021-09-09 15:46 ` Daniel Kiper
  0 siblings, 2 replies; 9+ messages in thread
From: Erwan Velu @ 2021-09-02 16:50 UTC (permalink / raw)
  To: grub-devel
  Cc: dkiper, daniel.kiper, alexander.burmashev, phcoder, Erwan Velu,
	Arthur Mesh

This patch got written by Arthur Mesh from Juniper (now at Apple Sec team).
It was extracted from https://lists.gnu.org/archive/html/grub-devel/2015-09/msg00065.html

Since this email, the this patch was :
- rebased against the current tree
- added a default timeout value
- reworked to be controlled via mkimage
- reworked to simplify the command line

This commit adds a new command efi-watchdog to manage efi watchdogs.

On server platforms, this allows GRUB to reset the host automatically
if it wasn't able to succeed at booting in a given timeframe.
This usually covers the following issues :
- net boot is too slow and never ends
- the GRUB is unable to find a proper configuration and fails

Using --efi-watchdog-timeout option at mknetdir time allow users
controlling the default value of the timer.
This set the watchdog behavior at the early init of GRUB meaning that
even if grub is not able to load its configuration file,
the watchdog will be triggered automatically.

Please note that watchdog only covers GRUB itself.
As per the UEFI specification :
	The watchdog timer is only used during boot services. On successful completion of
	EFI_BOOT_SERVICES.ExitBootServices() the watchdog timer is disabled.

This implies this watchdog doesn't cover the the OS loading.
If you want to cover this phase of the boot,
an additional hardware watchdog is required (usually set in the BIOS).

On the command line, a single argument is enough to control the watchdog.
This argument defines the timeout of the watchdog (in seconds).
	- If 0 > argument < 0xFFFF, the watchdog is armed with the associate timeout.
	- If argument is set to 0, the watchdog is disarmed.

By default GRUB disable the watchdog and so this patch.
Therefore, this commit have no impact on the default GRUB's behavior.

This patch is used in production for month on a 50K server platform with success.

Signed-off-by: Erwan Velu <e.velu@criteo.com>
Signed-off-by: Arthur Mesh <amesh@juniper.net>
---
 docs/grub.texi              | 13 ++++++
 grub-core/kern/efi/init.c   | 85 ++++++++++++++++++++++++++++++++++++-
 include/grub/efi/efi.h      |  2 +
 include/grub/kernel.h       |  3 +-
 include/grub/util/install.h |  8 +++-
 util/grub-install-common.c  | 12 +++++-
 util/grub-mkimage.c         | 11 ++++-
 util/mkimage.c              | 23 +++++++++-
 8 files changed, 147 insertions(+), 10 deletions(-)

diff --git a/docs/grub.texi b/docs/grub.texi
index f8b4b3b21a7f..f012e9537306 100644
--- a/docs/grub.texi
+++ b/docs/grub.texi
@@ -3991,6 +3991,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
+* efi-watchdog::                Manipulate EFI watchdog
 * eval::                        Evaluate agruments as GRUB commands
 * export::                      Export an environment variable
 * false::                       Do nothing, unsuccessfully
@@ -4442,6 +4443,18 @@ When interpreting backslash escapes, backslash followed by any other
 character will print that character.
 @end deffn
 
+@node efi-watchdog
+@subsection efi-watchdog
+
+@deffn Command efi-watchdog @var{timeout}
+Control the EFI system's watchdog timer.
+Only available in EFI targeted GRUB.
+
+When the watchdog exceed @var{timeout}, the system is reset.
+
+If @var{timeout} is set to 0, the watchdog is disarmed.
+
+@end deffn
 
 @node eval
 @subsection eval
diff --git a/grub-core/kern/efi/init.c b/grub-core/kern/efi/init.c
index 7facacf09c7b..532e8533426e 100644
--- a/grub-core/kern/efi/init.c
+++ b/grub-core/kern/efi/init.c
@@ -28,6 +28,8 @@
 #include <grub/mm.h>
 #include <grub/kernel.h>
 #include <grub/stack_protector.h>
+#include <grub/extcmd.h>
+#include <grub/command.h>
 
 #ifdef GRUB_STACK_PROTECTOR
 
@@ -82,6 +84,82 @@ stack_protector_init (void)
 
 grub_addr_t grub_modbase;
 
+static grub_command_t cmd_list;
+
+
+static grub_err_t grub_efi_set_watchdog_timer(unsigned long timeout)
+{
+  /* User defined code, set to BOOT (B007) for GRUB
+  UEFI SPEC 2.6 reports :
+    The numeric code to log on a watchdog timer timeout event.
+    The firmware reserves codes 0x0000 to 0xFFFF.
+    Loaders and operating systems may use other timeout codes.
+  */
+  grub_efi_uint64_t code = 0xB007;
+  grub_efi_status_t status = 0;
+
+  if (timeout >= 0xFFFF)
+    return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("efi-watchdog: timeout must be lower than 65535"));
+
+  if (timeout > 0)
+    grub_printf("grub %s: Arming EFI watchdog at %lu seconds\n", PACKAGE_VERSION, timeout);
+  else {
+    grub_printf("grub %s: Disarming EFI watchdog\n", PACKAGE_VERSION);
+    code = 0;
+  }
+
+  status = efi_call_4 (grub_efi_system_table->boot_services->set_watchdog_timer, timeout, code, sizeof(L"GRUB"), L"GRUB");
+
+  if (status != GRUB_EFI_SUCCESS)
+    return grub_error (GRUB_ERR_BUG, N_("Unexpected UEFI SetWatchdogTimer() error"));
+  else
+    return GRUB_ERR_NONE;
+}
+
+static grub_err_t
+grub_cmd_efi_watchdog (grub_command_t cmd  __attribute__ ((unused)),
+                      int argc, char **args)
+{
+  unsigned long timeout = 0;
+
+
+  if (argc < 1)
+    return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("usage: efi-watchdog <timeout>"));
+
+  const char *ptr = args[0];
+  timeout = grub_strtoul (ptr, &ptr, 0);
+  if (grub_errno)
+    return grub_errno;
+
+  return grub_efi_set_watchdog_timer(timeout);
+}
+
+static void
+grub_efi_watchdog_timer_init(void)
+{
+  struct grub_module_header *header;
+  unsigned long efi_watchdog_timeout = 0;
+
+  FOR_MODULES (header)
+  if (header->type == OBJ_TYPE_EFI_WATCHDOG_TIMEOUT)
+    {
+      char *efi_wdt_orig_addr;
+      static char *efi_wdt_addr;
+      static grub_off_t efi_wdt_size = 0;
+
+      efi_wdt_orig_addr = (char *) header + sizeof (struct grub_module_header);
+      efi_wdt_size = header->size - sizeof (struct grub_module_header);
+      efi_wdt_addr = grub_malloc (efi_wdt_size);
+
+      grub_memmove (efi_wdt_addr, efi_wdt_orig_addr, efi_wdt_size);
+      efi_watchdog_timeout = (unsigned long) *efi_wdt_addr;
+      break;
+    }
+
+  grub_efi_set_watchdog_timer(efi_watchdog_timeout);
+}
+
+
 void
 grub_efi_init (void)
 {
@@ -105,10 +183,12 @@ grub_efi_init (void)
       grub_shim_lock_verifier_setup ();
     }
 
-  efi_call_4 (grub_efi_system_table->boot_services->set_watchdog_timer,
-	      0, 0, 0, NULL);
+  grub_efi_watchdog_timer_init();
 
   grub_efidisk_init ();
+
+  cmd_list = grub_register_command ("efi-watchdog", grub_cmd_efi_watchdog, 0,
+     N_("Enable/Disable system's watchdog timer."));
 }
 
 void (*grub_efi_net_config) (grub_efi_handle_t hnd, 
@@ -146,4 +226,5 @@ grub_efi_fini (void)
 {
   grub_efidisk_fini ();
   grub_console_fini ();
+  grub_unregister_command (cmd_list);
 }
diff --git a/include/grub/efi/efi.h b/include/grub/efi/efi.h
index 83d958f9945e..372f995b74f4 100644
--- a/include/grub/efi/efi.h
+++ b/include/grub/efi/efi.h
@@ -124,4 +124,6 @@ struct grub_net_card;
 grub_efi_handle_t
 grub_efinet_get_device_handle (struct grub_net_card *card);
 
+/* EFI Watchdog armed by grub, in minutes */
+#define EFI_WATCHDOG_MINUTES 0
 #endif /* ! GRUB_EFI_EFI_HEADER */
diff --git a/include/grub/kernel.h b/include/grub/kernel.h
index abbca5ea3359..172599c58b23 100644
--- a/include/grub/kernel.h
+++ b/include/grub/kernel.h
@@ -30,7 +30,8 @@ enum
   OBJ_TYPE_PREFIX,
   OBJ_TYPE_PUBKEY,
   OBJ_TYPE_DTB,
-  OBJ_TYPE_DISABLE_SHIM_LOCK
+  OBJ_TYPE_DISABLE_SHIM_LOCK,
+  OBJ_TYPE_EFI_WATCHDOG_TIMEOUT,
 };
 
 /* The module header.  */
diff --git a/include/grub/util/install.h b/include/grub/util/install.h
index 7df3191f47ef..1276742d09e6 100644
--- a/include/grub/util/install.h
+++ b/include/grub/util/install.h
@@ -67,6 +67,8 @@
       N_("SBAT metadata"), 0 },						\
   { "disable-shim-lock", GRUB_INSTALL_OPTIONS_DISABLE_SHIM_LOCK, 0, 0,	\
       N_("disable shim_lock verifier"), 0 },				\
+  { "efi-watchdog-timeout", GRUB_INSTALL_OPTIONS_EFI_WATCHDOG_TIMEOUT, N_("EFI_WATCHDOG_TIMEOUT"), 0, \
+      N_("arm efi watchdog at EFI_WATCHDOG_TIMEOUT seconds"), 0 }, \
   { "verbose", 'v', 0, 0,						\
     N_("print verbose messages."), 1 }
 
@@ -128,7 +130,8 @@ enum grub_install_options {
   GRUB_INSTALL_OPTIONS_INSTALL_CORE_COMPRESS,
   GRUB_INSTALL_OPTIONS_DTB,
   GRUB_INSTALL_OPTIONS_SBAT,
-  GRUB_INSTALL_OPTIONS_DISABLE_SHIM_LOCK
+  GRUB_INSTALL_OPTIONS_DISABLE_SHIM_LOCK,
+  GRUB_INSTALL_OPTIONS_EFI_WATCHDOG_TIMEOUT
 };
 
 extern char *grub_install_source_directory;
@@ -190,7 +193,8 @@ grub_install_generate_image (const char *dir, const char *prefix,
 			     const struct grub_install_image_target_desc *image_target,
 			     int note,
 			     grub_compression_t comp, const char *dtb_file,
-			     const char *sbat_path, const int disable_shim_lock);
+			     const char *sbat_path, const int disable_shim_lock,
+			     unsigned long efi_watchdog_timeout);
 
 const struct grub_install_image_target_desc *
 grub_install_get_image_target (const char *arg);
diff --git a/util/grub-install-common.c b/util/grub-install-common.c
index 4e212e690c52..dab94799a6c3 100644
--- a/util/grub-install-common.c
+++ b/util/grub-install-common.c
@@ -460,11 +460,13 @@ static char **pubkeys;
 static size_t npubkeys;
 static char *sbat;
 static int disable_shim_lock;
+static unsigned long efi_watchdog_timeout;
 static grub_compression_t compression;
 
 int
 grub_install_parse (int key, char *arg)
 {
+  const char *const_arg = arg;
   switch (key)
     {
     case 'C':
@@ -562,6 +564,11 @@ grub_install_parse (int key, char *arg)
       grub_util_error (_("Unrecognized compression `%s'"), arg);
     case GRUB_INSTALL_OPTIONS_GRUB_MKIMAGE:
       return 1;
+    case GRUB_INSTALL_OPTIONS_EFI_WATCHDOG_TIMEOUT:
+      efi_watchdog_timeout = grub_strtoul (arg, &const_arg, 0);
+      if (grub_errno)
+        grub_util_error (_("timeout argument must be an integer : %s"), arg);
+      return 1;
     default:
       return 0;
     }
@@ -661,9 +668,10 @@ grub_install_make_image_wrap_file (const char *dir, const char *prefix,
 		  " --output '%s' "
 		  " --dtb '%s' "
 		  "--sbat '%s' "
+		  "--efi-watchdog-timeout '%lu' "
 		  "--format '%s' --compression '%s' %s %s %s\n",
 		  dir, prefix,
-		  outname, dtb ? : "", sbat ? : "", mkimage_target,
+		  outname, dtb ? : "", sbat ? : "", efi_watchdog_timeout, mkimage_target,
 		  compnames[compression], note ? "--note" : "",
 		  disable_shim_lock ? "--disable-shim-lock" : "", s);
   free (s);
@@ -676,7 +684,7 @@ grub_install_make_image_wrap_file (const char *dir, const char *prefix,
 			       modules.entries, memdisk_path,
 			       pubkeys, npubkeys, config_path, tgt,
 			       note, compression, dtb, sbat,
-			       disable_shim_lock);
+			       disable_shim_lock, efi_watchdog_timeout);
   while (dc--)
     grub_install_pop_module ();
 }
diff --git a/util/grub-mkimage.c b/util/grub-mkimage.c
index c0d559937020..fce7941a58fe 100644
--- a/util/grub-mkimage.c
+++ b/util/grub-mkimage.c
@@ -83,6 +83,7 @@ static struct argp_option options[] = {
   {"compression",  'C', "(xz|none|auto)", 0, N_("choose the compression to use for core image"), 0},
   {"sbat", 's', N_("FILE"), 0, N_("SBAT metadata"), 0},
   {"disable-shim-lock", GRUB_INSTALL_OPTIONS_DISABLE_SHIM_LOCK, 0, 0, N_("disable shim_lock verifier"), 0},
+  {"efi-watchdog-timeout", GRUB_INSTALL_OPTIONS_EFI_WATCHDOG_TIMEOUT, 0, 0, N_("efi watchdog timeout"), 0},
   {"verbose",     'v', 0,      0, N_("print verbose messages."), 0},
   { 0, 0, 0, 0, 0, 0 }
 };
@@ -128,6 +129,7 @@ struct arguments
   char *sbat;
   int note;
   int disable_shim_lock;
+  unsigned long efi_watchdog_timeout;
   const struct grub_install_image_target_desc *image_target;
   grub_compression_t comp;
 };
@@ -138,6 +140,7 @@ argp_parser (int key, char *arg, struct argp_state *state)
   /* Get the input argument from argp_parse, which we
      know is a pointer to our arguments structure. */
   struct arguments *arguments = state->input;
+  const char *const_arg = arg;
 
   switch (key)
     {
@@ -239,6 +242,12 @@ argp_parser (int key, char *arg, struct argp_state *state)
       arguments->disable_shim_lock = 1;
       break;
 
+    case GRUB_INSTALL_OPTIONS_EFI_WATCHDOG_TIMEOUT:
+      arguments->efi_watchdog_timeout = grub_strtoul (arg, &const_arg, 0);
+      if (grub_errno)
+        grub_util_error (_("timeout argument must be an integer : %s"), arg);
+      break;
+
     case 'v':
       verbosity++;
       break;
@@ -325,7 +334,7 @@ main (int argc, char *argv[])
 			       arguments.npubkeys, arguments.config,
 			       arguments.image_target, arguments.note,
 			       arguments.comp, arguments.dtb,
-			       arguments.sbat, arguments.disable_shim_lock);
+			       arguments.sbat, arguments.disable_shim_lock, arguments.efi_watchdog_timeout);
 
   if (grub_util_file_sync (fp) < 0)
     grub_util_error (_("cannot sync `%s': %s"), arguments.output ? : "stdout",
diff --git a/util/mkimage.c b/util/mkimage.c
index a26cf76f72f2..91b03801e628 100644
--- a/util/mkimage.c
+++ b/util/mkimage.c
@@ -870,12 +870,12 @@ grub_install_generate_image (const char *dir, const char *prefix,
 			     size_t npubkeys, char *config_path,
 			     const struct grub_install_image_target_desc *image_target,
 			     int note, grub_compression_t comp, const char *dtb_path,
-			     const char *sbat_path, int disable_shim_lock)
+			     const char *sbat_path, int disable_shim_lock, unsigned long efi_watchdog_timeout)
 {
   char *kernel_img, *core_img;
   size_t total_module_size, core_size;
   size_t memdisk_size = 0, config_size = 0;
-  size_t prefix_size = 0, dtb_size = 0, sbat_size = 0;
+  size_t prefix_size = 0, dtb_size = 0, sbat_size = 0, efi_watchdog_timeout_size = 0;
   char *kernel_path;
   size_t offset;
   struct grub_util_path_list *path_list, *p;
@@ -929,6 +929,12 @@ grub_install_generate_image (const char *dir, const char *prefix,
   if (sbat_path != NULL && image_target->id != IMAGE_EFI)
     grub_util_error (_(".sbat section can be embedded into EFI images only"));
 
+  if (efi_watchdog_timeout)
+    {
+      efi_watchdog_timeout_size = ALIGN_ADDR(sizeof (efi_watchdog_timeout));
+      total_module_size += efi_watchdog_timeout_size + sizeof (struct grub_module_header);
+    }
+
   if (disable_shim_lock)
     total_module_size += sizeof (struct grub_module_header);
 
@@ -1078,6 +1084,19 @@ grub_install_generate_image (const char *dir, const char *prefix,
       offset += sizeof (*header);
     }
 
+  if (efi_watchdog_timeout)
+    {
+      struct grub_module_header *header;
+
+      header = (struct grub_module_header *) (kernel_img + offset);
+      header->type = grub_host_to_target32 (OBJ_TYPE_EFI_WATCHDOG_TIMEOUT);
+      header->size = grub_host_to_target32 (efi_watchdog_timeout_size + sizeof (*header));
+      offset += sizeof (*header);
+
+      grub_memcpy(kernel_img + offset, &efi_watchdog_timeout, sizeof(efi_watchdog_timeout));
+      offset += efi_watchdog_timeout_size;
+    }
+
   if (config_path)
     {
       struct grub_module_header *header;
-- 
2.25.1



^ permalink raw reply related	[flat|nested] 9+ messages in thread

* Re: [PATCH v2] kern/efi: Adding efi-watchdog command
  2021-09-02 16:50 [PATCH v2] kern/efi: Adding efi-watchdog command Erwan Velu
@ 2021-09-03  2:09 ` Michael Chang
  2021-09-03  6:33   ` Erwan Velu
  2021-09-09 15:46 ` Daniel Kiper
  1 sibling, 1 reply; 9+ messages in thread
From: Michael Chang @ 2021-09-03  2:09 UTC (permalink / raw)
  To: The development of GNU GRUB
  Cc: dkiper, daniel.kiper, alexander.burmashev, phcoder, Erwan Velu,
	Arthur Mesh

On Thu, Sep 02, 2021 at 06:50:35PM +0200, Erwan Velu wrote:
> This patch got written by Arthur Mesh from Juniper (now at Apple Sec team).
> It was extracted from https://lists.gnu.org/archive/html/grub-devel/2015-09/msg00065.html
> 
> Since this email, the this patch was :
> - rebased against the current tree
> - added a default timeout value
> - reworked to be controlled via mkimage
> - reworked to simplify the command line
> 
> This commit adds a new command efi-watchdog to manage efi watchdogs.
> 
> On server platforms, this allows GRUB to reset the host automatically
> if it wasn't able to succeed at booting in a given timeframe.
> This usually covers the following issues :
> - net boot is too slow and never ends
> - the GRUB is unable to find a proper configuration and fails
> 
> Using --efi-watchdog-timeout option at mknetdir time allow users
> controlling the default value of the timer.
> This set the watchdog behavior at the early init of GRUB meaning that
> even if grub is not able to load its configuration file,
> the watchdog will be triggered automatically.
> 
> Please note that watchdog only covers GRUB itself.
> As per the UEFI specification :
> 	The watchdog timer is only used during boot services. On successful completion of
> 	EFI_BOOT_SERVICES.ExitBootServices() the watchdog timer is disabled.
> 
> This implies this watchdog doesn't cover the the OS loading.
> If you want to cover this phase of the boot,
> an additional hardware watchdog is required (usually set in the BIOS).
> 
> On the command line, a single argument is enough to control the watchdog.
> This argument defines the timeout of the watchdog (in seconds).
> 	- If 0 > argument < 0xFFFF, the watchdog is armed with the associate timeout.
> 	- If argument is set to 0, the watchdog is disarmed.
> 
> By default GRUB disable the watchdog and so this patch.
> Therefore, this commit have no impact on the default GRUB's behavior.
> 
> This patch is used in production for month on a 50K server platform with success.
> 
> Signed-off-by: Erwan Velu <e.velu@criteo.com>
> Signed-off-by: Arthur Mesh <amesh@juniper.net>
> ---
>  docs/grub.texi              | 13 ++++++
>  grub-core/kern/efi/init.c   | 85 ++++++++++++++++++++++++++++++++++++-
>  include/grub/efi/efi.h      |  2 +
>  include/grub/kernel.h       |  3 +-
>  include/grub/util/install.h |  8 +++-
>  util/grub-install-common.c  | 12 +++++-
>  util/grub-mkimage.c         | 11 ++++-
>  util/mkimage.c              | 23 +++++++++-
>  8 files changed, 147 insertions(+), 10 deletions(-)
> 
> diff --git a/docs/grub.texi b/docs/grub.texi
> index f8b4b3b21a7f..f012e9537306 100644
> --- a/docs/grub.texi
> +++ b/docs/grub.texi
> @@ -3991,6 +3991,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
> +* efi-watchdog::                Manipulate EFI watchdog
>  * eval::                        Evaluate agruments as GRUB commands
>  * export::                      Export an environment variable
>  * false::                       Do nothing, unsuccessfully
> @@ -4442,6 +4443,18 @@ When interpreting backslash escapes, backslash followed by any other
>  character will print that character.
>  @end deffn
>  
> +@node efi-watchdog
> +@subsection efi-watchdog
> +
> +@deffn Command efi-watchdog @var{timeout}
> +Control the EFI system's watchdog timer.
> +Only available in EFI targeted GRUB.
> +
> +When the watchdog exceed @var{timeout}, the system is reset.
> +
> +If @var{timeout} is set to 0, the watchdog is disarmed.
> +
> +@end deffn
>  
>  @node eval
>  @subsection eval
> diff --git a/grub-core/kern/efi/init.c b/grub-core/kern/efi/init.c
> index 7facacf09c7b..532e8533426e 100644
> --- a/grub-core/kern/efi/init.c
> +++ b/grub-core/kern/efi/init.c
> @@ -28,6 +28,8 @@
>  #include <grub/mm.h>
>  #include <grub/kernel.h>
>  #include <grub/stack_protector.h>
> +#include <grub/extcmd.h>
> +#include <grub/command.h>
>  
>  #ifdef GRUB_STACK_PROTECTOR
>  
> @@ -82,6 +84,82 @@ stack_protector_init (void)
>  
>  grub_addr_t grub_modbase;
>  
> +static grub_command_t cmd_list;
> +
> +
> +static grub_err_t grub_efi_set_watchdog_timer(unsigned long timeout)
> +{
> +  /* User defined code, set to BOOT (B007) for GRUB
> +  UEFI SPEC 2.6 reports :
> +    The numeric code to log on a watchdog timer timeout event.
> +    The firmware reserves codes 0x0000 to 0xFFFF.
> +    Loaders and operating systems may use other timeout codes.
> +  */
> +  grub_efi_uint64_t code = 0xB007;
> +  grub_efi_status_t status = 0;
> +
> +  if (timeout >= 0xFFFF)
> +    return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("efi-watchdog: timeout must be lower than 65535"));
> +
> +  if (timeout > 0)
> +    grub_printf("grub %s: Arming EFI watchdog at %lu seconds\n", PACKAGE_VERSION, timeout);
> +  else {
> +    grub_printf("grub %s: Disarming EFI watchdog\n", PACKAGE_VERSION);
> +    code = 0;
> +  }
> +
> +  status = efi_call_4 (grub_efi_system_table->boot_services->set_watchdog_timer, timeout, code, sizeof(L"GRUB"), L"GRUB");
> +
> +  if (status != GRUB_EFI_SUCCESS)
> +    return grub_error (GRUB_ERR_BUG, N_("Unexpected UEFI SetWatchdogTimer() error"));
> +  else
> +    return GRUB_ERR_NONE;
> +}
> +
> +static grub_err_t
> +grub_cmd_efi_watchdog (grub_command_t cmd  __attribute__ ((unused)),
> +                      int argc, char **args)
> +{
> +  unsigned long timeout = 0;
> +
> +
> +  if (argc < 1)
> +    return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("usage: efi-watchdog <timeout>"));
> +
> +  const char *ptr = args[0];
> +  timeout = grub_strtoul (ptr, &ptr, 0);
> +  if (grub_errno)
> +    return grub_errno;
> +
> +  return grub_efi_set_watchdog_timer(timeout);
> +}
> +
> +static void
> +grub_efi_watchdog_timer_init(void)
> +{
> +  struct grub_module_header *header;
> +  unsigned long efi_watchdog_timeout = 0;
> +
> +  FOR_MODULES (header)
> +  if (header->type == OBJ_TYPE_EFI_WATCHDOG_TIMEOUT)
> +    {
> +      char *efi_wdt_orig_addr;
> +      static char *efi_wdt_addr;
> +      static grub_off_t efi_wdt_size = 0;
> +
> +      efi_wdt_orig_addr = (char *) header + sizeof (struct grub_module_header);
> +      efi_wdt_size = header->size - sizeof (struct grub_module_header);
> +      efi_wdt_addr = grub_malloc (efi_wdt_size);
> +
> +      grub_memmove (efi_wdt_addr, efi_wdt_orig_addr, efi_wdt_size);
> +      efi_watchdog_timeout = (unsigned long) *efi_wdt_addr;
> +      break;
> +    }
> +
> +  grub_efi_set_watchdog_timer(efi_watchdog_timeout);
> +}
> +
> +
>  void
>  grub_efi_init (void)
>  {
> @@ -105,10 +183,12 @@ grub_efi_init (void)
>        grub_shim_lock_verifier_setup ();
>      }
>  
> -  efi_call_4 (grub_efi_system_table->boot_services->set_watchdog_timer,
> -	      0, 0, 0, NULL);
> +  grub_efi_watchdog_timer_init();
>  
>    grub_efidisk_init ();
> +
> +  cmd_list = grub_register_command ("efi-watchdog", grub_cmd_efi_watchdog, 0,
> +     N_("Enable/Disable system's watchdog timer."));

I'd suggest to move this efi-watchdog command registration to
grub_register_core_commands() in grub-core/kern/corecmd.c as that helps
us tracing or knowing available commands in grub's rescue mode.

Also it would be great to see the explaination why this command needs to
be in the core. To my understandng it could be that users may want to
disarm the watchdog to not interrupt their work in rescue mode during a
troubleshooting or debug session, but I am not fully clear about the
purpose.

Thanks,
Michael

>  }
>  
>  void (*grub_efi_net_config) (grub_efi_handle_t hnd, 
> @@ -146,4 +226,5 @@ grub_efi_fini (void)
>  {
>    grub_efidisk_fini ();
>    grub_console_fini ();
> +  grub_unregister_command (cmd_list);
>  }
> diff --git a/include/grub/efi/efi.h b/include/grub/efi/efi.h
> index 83d958f9945e..372f995b74f4 100644
> --- a/include/grub/efi/efi.h
> +++ b/include/grub/efi/efi.h
> @@ -124,4 +124,6 @@ struct grub_net_card;
>  grub_efi_handle_t
>  grub_efinet_get_device_handle (struct grub_net_card *card);
>  
> +/* EFI Watchdog armed by grub, in minutes */
> +#define EFI_WATCHDOG_MINUTES 0
>  #endif /* ! GRUB_EFI_EFI_HEADER */
> diff --git a/include/grub/kernel.h b/include/grub/kernel.h
> index abbca5ea3359..172599c58b23 100644
> --- a/include/grub/kernel.h
> +++ b/include/grub/kernel.h
> @@ -30,7 +30,8 @@ enum
>    OBJ_TYPE_PREFIX,
>    OBJ_TYPE_PUBKEY,
>    OBJ_TYPE_DTB,
> -  OBJ_TYPE_DISABLE_SHIM_LOCK
> +  OBJ_TYPE_DISABLE_SHIM_LOCK,
> +  OBJ_TYPE_EFI_WATCHDOG_TIMEOUT,
>  };
>  
>  /* The module header.  */
> diff --git a/include/grub/util/install.h b/include/grub/util/install.h
> index 7df3191f47ef..1276742d09e6 100644
> --- a/include/grub/util/install.h
> +++ b/include/grub/util/install.h
> @@ -67,6 +67,8 @@
>        N_("SBAT metadata"), 0 },						\
>    { "disable-shim-lock", GRUB_INSTALL_OPTIONS_DISABLE_SHIM_LOCK, 0, 0,	\
>        N_("disable shim_lock verifier"), 0 },				\
> +  { "efi-watchdog-timeout", GRUB_INSTALL_OPTIONS_EFI_WATCHDOG_TIMEOUT, N_("EFI_WATCHDOG_TIMEOUT"), 0, \
> +      N_("arm efi watchdog at EFI_WATCHDOG_TIMEOUT seconds"), 0 }, \
>    { "verbose", 'v', 0, 0,						\
>      N_("print verbose messages."), 1 }
>  
> @@ -128,7 +130,8 @@ enum grub_install_options {
>    GRUB_INSTALL_OPTIONS_INSTALL_CORE_COMPRESS,
>    GRUB_INSTALL_OPTIONS_DTB,
>    GRUB_INSTALL_OPTIONS_SBAT,
> -  GRUB_INSTALL_OPTIONS_DISABLE_SHIM_LOCK
> +  GRUB_INSTALL_OPTIONS_DISABLE_SHIM_LOCK,
> +  GRUB_INSTALL_OPTIONS_EFI_WATCHDOG_TIMEOUT
>  };
>  
>  extern char *grub_install_source_directory;
> @@ -190,7 +193,8 @@ grub_install_generate_image (const char *dir, const char *prefix,
>  			     const struct grub_install_image_target_desc *image_target,
>  			     int note,
>  			     grub_compression_t comp, const char *dtb_file,
> -			     const char *sbat_path, const int disable_shim_lock);
> +			     const char *sbat_path, const int disable_shim_lock,
> +			     unsigned long efi_watchdog_timeout);
>  
>  const struct grub_install_image_target_desc *
>  grub_install_get_image_target (const char *arg);
> diff --git a/util/grub-install-common.c b/util/grub-install-common.c
> index 4e212e690c52..dab94799a6c3 100644
> --- a/util/grub-install-common.c
> +++ b/util/grub-install-common.c
> @@ -460,11 +460,13 @@ static char **pubkeys;
>  static size_t npubkeys;
>  static char *sbat;
>  static int disable_shim_lock;
> +static unsigned long efi_watchdog_timeout;
>  static grub_compression_t compression;
>  
>  int
>  grub_install_parse (int key, char *arg)
>  {
> +  const char *const_arg = arg;
>    switch (key)
>      {
>      case 'C':
> @@ -562,6 +564,11 @@ grub_install_parse (int key, char *arg)
>        grub_util_error (_("Unrecognized compression `%s'"), arg);
>      case GRUB_INSTALL_OPTIONS_GRUB_MKIMAGE:
>        return 1;
> +    case GRUB_INSTALL_OPTIONS_EFI_WATCHDOG_TIMEOUT:
> +      efi_watchdog_timeout = grub_strtoul (arg, &const_arg, 0);
> +      if (grub_errno)
> +        grub_util_error (_("timeout argument must be an integer : %s"), arg);
> +      return 1;
>      default:
>        return 0;
>      }
> @@ -661,9 +668,10 @@ grub_install_make_image_wrap_file (const char *dir, const char *prefix,
>  		  " --output '%s' "
>  		  " --dtb '%s' "
>  		  "--sbat '%s' "
> +		  "--efi-watchdog-timeout '%lu' "
>  		  "--format '%s' --compression '%s' %s %s %s\n",
>  		  dir, prefix,
> -		  outname, dtb ? : "", sbat ? : "", mkimage_target,
> +		  outname, dtb ? : "", sbat ? : "", efi_watchdog_timeout, mkimage_target,
>  		  compnames[compression], note ? "--note" : "",
>  		  disable_shim_lock ? "--disable-shim-lock" : "", s);
>    free (s);
> @@ -676,7 +684,7 @@ grub_install_make_image_wrap_file (const char *dir, const char *prefix,
>  			       modules.entries, memdisk_path,
>  			       pubkeys, npubkeys, config_path, tgt,
>  			       note, compression, dtb, sbat,
> -			       disable_shim_lock);
> +			       disable_shim_lock, efi_watchdog_timeout);
>    while (dc--)
>      grub_install_pop_module ();
>  }
> diff --git a/util/grub-mkimage.c b/util/grub-mkimage.c
> index c0d559937020..fce7941a58fe 100644
> --- a/util/grub-mkimage.c
> +++ b/util/grub-mkimage.c
> @@ -83,6 +83,7 @@ static struct argp_option options[] = {
>    {"compression",  'C', "(xz|none|auto)", 0, N_("choose the compression to use for core image"), 0},
>    {"sbat", 's', N_("FILE"), 0, N_("SBAT metadata"), 0},
>    {"disable-shim-lock", GRUB_INSTALL_OPTIONS_DISABLE_SHIM_LOCK, 0, 0, N_("disable shim_lock verifier"), 0},
> +  {"efi-watchdog-timeout", GRUB_INSTALL_OPTIONS_EFI_WATCHDOG_TIMEOUT, 0, 0, N_("efi watchdog timeout"), 0},
>    {"verbose",     'v', 0,      0, N_("print verbose messages."), 0},
>    { 0, 0, 0, 0, 0, 0 }
>  };
> @@ -128,6 +129,7 @@ struct arguments
>    char *sbat;
>    int note;
>    int disable_shim_lock;
> +  unsigned long efi_watchdog_timeout;
>    const struct grub_install_image_target_desc *image_target;
>    grub_compression_t comp;
>  };
> @@ -138,6 +140,7 @@ argp_parser (int key, char *arg, struct argp_state *state)
>    /* Get the input argument from argp_parse, which we
>       know is a pointer to our arguments structure. */
>    struct arguments *arguments = state->input;
> +  const char *const_arg = arg;
>  
>    switch (key)
>      {
> @@ -239,6 +242,12 @@ argp_parser (int key, char *arg, struct argp_state *state)
>        arguments->disable_shim_lock = 1;
>        break;
>  
> +    case GRUB_INSTALL_OPTIONS_EFI_WATCHDOG_TIMEOUT:
> +      arguments->efi_watchdog_timeout = grub_strtoul (arg, &const_arg, 0);
> +      if (grub_errno)
> +        grub_util_error (_("timeout argument must be an integer : %s"), arg);
> +      break;
> +
>      case 'v':
>        verbosity++;
>        break;
> @@ -325,7 +334,7 @@ main (int argc, char *argv[])
>  			       arguments.npubkeys, arguments.config,
>  			       arguments.image_target, arguments.note,
>  			       arguments.comp, arguments.dtb,
> -			       arguments.sbat, arguments.disable_shim_lock);
> +			       arguments.sbat, arguments.disable_shim_lock, arguments.efi_watchdog_timeout);
>  
>    if (grub_util_file_sync (fp) < 0)
>      grub_util_error (_("cannot sync `%s': %s"), arguments.output ? : "stdout",
> diff --git a/util/mkimage.c b/util/mkimage.c
> index a26cf76f72f2..91b03801e628 100644
> --- a/util/mkimage.c
> +++ b/util/mkimage.c
> @@ -870,12 +870,12 @@ grub_install_generate_image (const char *dir, const char *prefix,
>  			     size_t npubkeys, char *config_path,
>  			     const struct grub_install_image_target_desc *image_target,
>  			     int note, grub_compression_t comp, const char *dtb_path,
> -			     const char *sbat_path, int disable_shim_lock)
> +			     const char *sbat_path, int disable_shim_lock, unsigned long efi_watchdog_timeout)
>  {
>    char *kernel_img, *core_img;
>    size_t total_module_size, core_size;
>    size_t memdisk_size = 0, config_size = 0;
> -  size_t prefix_size = 0, dtb_size = 0, sbat_size = 0;
> +  size_t prefix_size = 0, dtb_size = 0, sbat_size = 0, efi_watchdog_timeout_size = 0;
>    char *kernel_path;
>    size_t offset;
>    struct grub_util_path_list *path_list, *p;
> @@ -929,6 +929,12 @@ grub_install_generate_image (const char *dir, const char *prefix,
>    if (sbat_path != NULL && image_target->id != IMAGE_EFI)
>      grub_util_error (_(".sbat section can be embedded into EFI images only"));
>  
> +  if (efi_watchdog_timeout)
> +    {
> +      efi_watchdog_timeout_size = ALIGN_ADDR(sizeof (efi_watchdog_timeout));
> +      total_module_size += efi_watchdog_timeout_size + sizeof (struct grub_module_header);
> +    }
> +
>    if (disable_shim_lock)
>      total_module_size += sizeof (struct grub_module_header);
>  
> @@ -1078,6 +1084,19 @@ grub_install_generate_image (const char *dir, const char *prefix,
>        offset += sizeof (*header);
>      }
>  
> +  if (efi_watchdog_timeout)
> +    {
> +      struct grub_module_header *header;
> +
> +      header = (struct grub_module_header *) (kernel_img + offset);
> +      header->type = grub_host_to_target32 (OBJ_TYPE_EFI_WATCHDOG_TIMEOUT);
> +      header->size = grub_host_to_target32 (efi_watchdog_timeout_size + sizeof (*header));
> +      offset += sizeof (*header);
> +
> +      grub_memcpy(kernel_img + offset, &efi_watchdog_timeout, sizeof(efi_watchdog_timeout));
> +      offset += efi_watchdog_timeout_size;
> +    }
> +
>    if (config_path)
>      {
>        struct grub_module_header *header;
> -- 
> 2.25.1
> 
> 
> _______________________________________________
> Grub-devel mailing list
> Grub-devel@gnu.org
> https://lists.gnu.org/mailman/listinfo/grub-devel



^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH v2] kern/efi: Adding efi-watchdog command
  2021-09-03  2:09 ` Michael Chang
@ 2021-09-03  6:33   ` Erwan Velu
  2021-09-07 11:20     ` Erwan Velu
  0 siblings, 1 reply; 9+ messages in thread
From: Erwan Velu @ 2021-09-03  6:33 UTC (permalink / raw)
  To: Michael Chang, The development of GNU GRUB
  Cc: dkiper, daniel.kiper, alexander.burmashev, phcoder, Arthur Mesh

Le 03/09/2021 à 04:09, Michael Chang a écrit :
[...]
> I'd suggest to move this efi-watchdog command registration to
> grub_register_core_commands() in grub-core/kern/corecmd.c as that helps
> us tracing or knowing available commands in grub's rescue mode.
>
> Also it would be great to see the explaination why this command needs to
> be in the core. To my understandng it could be that users may want to
> disarm the watchdog to not interrupt their work in rescue mode during a
> troubleshooting or debug session, but I am not fully clear about the
> purpose.

Hey Michael,

I'm waiting for other reviews and will add your points into a V3.


You are perfectly right on the aim.

To be efficient, the watchdog must be enabled at soon as possible and so

cannot be set into the configuration file as we are far too late and issues

could have already hit GRUB (aka unable to read the filesystem).

So, once the option is set, every-time GRUB will boots the watchdog is 
enabled.


And yes, if you need to perform a debug session, you need being in a 
position to disable it.

That's why we need this commands in the low-level command sets of grub.

I'm not very familiar with GRUB internals, so if the corecmd is a better 
place and other agree on that,

I'll make the move in V3.


Erwan,



^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH v2] kern/efi: Adding efi-watchdog command
  2021-09-03  6:33   ` Erwan Velu
@ 2021-09-07 11:20     ` Erwan Velu
  2021-09-07 18:01       ` Daniel Kiper
  0 siblings, 1 reply; 9+ messages in thread
From: Erwan Velu @ 2021-09-07 11:20 UTC (permalink / raw)
  To: The development of GNU GRUB
  Cc: Michael Chang, dkiper, daniel.kiper, alexander.burmashev,
	phcoder, Arthur Mesh

[-- Attachment #1: Type: text/plain, Size: 1701 bytes --]

Daniel, anything more about this V2 before I prepare the V3 ?

Le ven. 3 sept. 2021 à 08:34, Erwan Velu <e.velu@criteo.com> a écrit :

> Le 03/09/2021 à 04:09, Michael Chang a écrit :
> [...]
> > I'd suggest to move this efi-watchdog command registration to
> > grub_register_core_commands() in grub-core/kern/corecmd.c as that helps
> > us tracing or knowing available commands in grub's rescue mode.
> >
> > Also it would be great to see the explaination why this command needs to
> > be in the core. To my understandng it could be that users may want to
> > disarm the watchdog to not interrupt their work in rescue mode during a
> > troubleshooting or debug session, but I am not fully clear about the
> > purpose.
>
> Hey Michael,
>
> I'm waiting for other reviews and will add your points into a V3.
>
>
> You are perfectly right on the aim.
>
> To be efficient, the watchdog must be enabled at soon as possible and so
>
> cannot be set into the configuration file as we are far too late and issues
>
> could have already hit GRUB (aka unable to read the filesystem).
>
> So, once the option is set, every-time GRUB will boots the watchdog is
> enabled.
>
>
> And yes, if you need to perform a debug session, you need being in a
> position to disable it.
>
> That's why we need this commands in the low-level command sets of grub.
>
> I'm not very familiar with GRUB internals, so if the corecmd is a better
> place and other agree on that,
>
> I'll make the move in V3.
>
>
> Erwan,
>
>
> _______________________________________________
> Grub-devel mailing list
> Grub-devel@gnu.org
> https://lists.gnu.org/mailman/listinfo/grub-devel
>

[-- Attachment #2: Type: text/html, Size: 2298 bytes --]

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH v2] kern/efi: Adding efi-watchdog command
  2021-09-07 11:20     ` Erwan Velu
@ 2021-09-07 18:01       ` Daniel Kiper
  2021-09-07 18:28         ` Erwan Velu
  0 siblings, 1 reply; 9+ messages in thread
From: Daniel Kiper @ 2021-09-07 18:01 UTC (permalink / raw)
  To: Erwan Velu
  Cc: The development of GNU GRUB, Michael Chang, alexander.burmashev,
	phcoder, Arthur Mesh

On Tue, Sep 07, 2021 at 01:20:05PM +0200, Erwan Velu wrote:
> Daniel, anything more about this V2 before I prepare the V3 ?

I will be looking at it this week.

Daniel


^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH v2] kern/efi: Adding efi-watchdog command
  2021-09-07 18:01       ` Daniel Kiper
@ 2021-09-07 18:28         ` Erwan Velu
  0 siblings, 0 replies; 9+ messages in thread
From: Erwan Velu @ 2021-09-07 18:28 UTC (permalink / raw)
  To: Daniel Kiper
  Cc: The development of GNU GRUB, Michael Chang, alexander.burmashev,
	phcoder, Arthur Mesh

[-- Attachment #1: Type: text/plain, Size: 279 bytes --]

Thanks !

Le mar. 7 sept. 2021 à 20:01, Daniel Kiper <dkiper@net-space.pl> a écrit :

> On Tue, Sep 07, 2021 at 01:20:05PM +0200, Erwan Velu wrote:
> > Daniel, anything more about this V2 before I prepare the V3 ?
>
> I will be looking at it this week.
>
> Daniel
>

[-- Attachment #2: Type: text/html, Size: 582 bytes --]

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH v2] kern/efi: Adding efi-watchdog command
  2021-09-02 16:50 [PATCH v2] kern/efi: Adding efi-watchdog command Erwan Velu
  2021-09-03  2:09 ` Michael Chang
@ 2021-09-09 15:46 ` Daniel Kiper
  2021-09-10 10:34   ` Erwan Velu
  1 sibling, 1 reply; 9+ messages in thread
From: Daniel Kiper @ 2021-09-09 15:46 UTC (permalink / raw)
  To: Erwan Velu; +Cc: grub-devel, alexander.burmashev, phcoder, Erwan Velu

On Thu, Sep 02, 2021 at 06:50:35PM +0200, Erwan Velu wrote:
> This patch got written by Arthur Mesh from Juniper (now at Apple Sec team).
> It was extracted from https://lists.gnu.org/archive/html/grub-devel/2015-09/msg00065.html
>
> Since this email, the this patch was :
> - rebased against the current tree
> - added a default timeout value
> - reworked to be controlled via mkimage
> - reworked to simplify the command line

All lines above should go after SOB and "---". Good example what I mean
is here [1].

> This commit adds a new command efi-watchdog to manage efi watchdogs.

I do not see any reason to name the command "efi-watchdog". It should be
simply named as "watchdog". If in the future we will support more
watchdog devices we can add a command to switch between them.

> On server platforms, this allows GRUB to reset the host automatically

Please drop "On server platforms".

> if it wasn't able to succeed at booting in a given timeframe.
> This usually covers the following issues :
> - net boot is too slow and never ends
> - the GRUB is unable to find a proper configuration and fails
>
> Using --efi-watchdog-timeout option at mknetdir time allow users

s/efi-watchdog-timeout/watchdog-timeout/

And I am not sure what you mean by "Using ... option at mknetdir time"...

> controlling the default value of the timer.
> This set the watchdog behavior at the early init of GRUB meaning that
> even if grub is not able to load its configuration file,
> the watchdog will be triggered automatically.
>
> Please note that watchdog only covers GRUB itself.

The GRUB not always calls ExitBootServices(). So, this is not entirely true.
E.g Xen calls ExitBootServices() instead of GRUB on UEFI platforms.

> As per the UEFI specification :
> 	The watchdog timer is only used during boot services. On successful completion of
> 	EFI_BOOT_SERVICES.ExitBootServices() the watchdog timer is disabled.
>
> This implies this watchdog doesn't cover the the OS loading.
> If you want to cover this phase of the boot,
> an additional hardware watchdog is required (usually set in the BIOS).

Ditto. Please update these two paragraphs taking into account my comments above.

> On the command line, a single argument is enough to control the watchdog.
> This argument defines the timeout of the watchdog (in seconds).
> 	- If 0 > argument < 0xFFFF, the watchdog is armed with the associate timeout.
> 	- If argument is set to 0, the watchdog is disarmed.
>
> By default GRUB disable the watchdog and so this patch.
> Therefore, this commit have no impact on the default GRUB's behavior.
>
> This patch is used in production for month on a 50K server platform with success.

I think you are missing a description why this functionality must be in
the GRUB core.img.

> Signed-off-by: Erwan Velu <e.velu@criteo.com>
> Signed-off-by: Arthur Mesh <amesh@juniper.net>

Arthur Mesh SOB should be before yours.

> ---
>  docs/grub.texi              | 13 ++++++
>  grub-core/kern/efi/init.c   | 85 ++++++++++++++++++++++++++++++++++++-
>  include/grub/efi/efi.h      |  2 +
>  include/grub/kernel.h       |  3 +-
>  include/grub/util/install.h |  8 +++-
>  util/grub-install-common.c  | 12 +++++-
>  util/grub-mkimage.c         | 11 ++++-
>  util/mkimage.c              | 23 +++++++++-
>  8 files changed, 147 insertions(+), 10 deletions(-)
>
> diff --git a/docs/grub.texi b/docs/grub.texi
> index f8b4b3b21a7f..f012e9537306 100644
> --- a/docs/grub.texi
> +++ b/docs/grub.texi
> @@ -3991,6 +3991,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
> +* efi-watchdog::                Manipulate EFI watchdog
>  * eval::                        Evaluate agruments as GRUB commands
>  * export::                      Export an environment variable
>  * false::                       Do nothing, unsuccessfully
> @@ -4442,6 +4443,18 @@ When interpreting backslash escapes, backslash followed by any other
>  character will print that character.
>  @end deffn
>
> +@node efi-watchdog
> +@subsection efi-watchdog

s/efi-watchdog/watchdog/ and below please...

> +@deffn Command efi-watchdog @var{timeout}
> +Control the EFI system's watchdog timer.
> +Only available in EFI targeted GRUB.

I do not see any reason to put this in two lines.

> +
> +When the watchdog exceed @var{timeout}, the system is reset.
> +
> +If @var{timeout} is set to 0, the watchdog is disarmed.
> +

Please drop this empty line.

> +@end deffn
>
>  @node eval
>  @subsection eval
> diff --git a/grub-core/kern/efi/init.c b/grub-core/kern/efi/init.c
> index 7facacf09c7b..532e8533426e 100644
> --- a/grub-core/kern/efi/init.c
> +++ b/grub-core/kern/efi/init.c
> @@ -28,6 +28,8 @@
>  #include <grub/mm.h>
>  #include <grub/kernel.h>
>  #include <grub/stack_protector.h>
> +#include <grub/extcmd.h>

I think this include is not needed.

> +#include <grub/command.h>
>
>  #ifdef GRUB_STACK_PROTECTOR
>
> @@ -82,6 +84,82 @@ stack_protector_init (void)
>
>  grub_addr_t grub_modbase;
>
> +static grub_command_t cmd_list;
> +
> +

Please drop this empty line.

> +static grub_err_t grub_efi_set_watchdog_timer(unsigned long timeout)

Please use grub_efi_uintn_t instead of unsigned long. However, please
remember that its size depends on architecture. So, it can be 32-bits or
64-bits...

> +{
> +  /* User defined code, set to BOOT (B007) for GRUB
> +  UEFI SPEC 2.6 reports :

Please refer to the latest UEFI spec.

> +    The numeric code to log on a watchdog timer timeout event.
> +    The firmware reserves codes 0x0000 to 0xFFFF.
> +    Loaders and operating systems may use other timeout codes.

This comment says you cannot use 0xb007. I would use 0xdeadb007deadb007
by default. However, I would give an option to change the default by user.

> +  */

Please format comments according to [2].

> +  grub_efi_uint64_t code = 0xB007;
> +  grub_efi_status_t status = 0;
> +
> +  if (timeout >= 0xFFFF)
> +    return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("efi-watchdog: timeout must be lower than 65535"));

Why do you artificially limit the timeout value? Please do not do that.

> +  if (timeout > 0)
> +    grub_printf("grub %s: Arming EFI watchdog at %lu seconds\n", PACKAGE_VERSION, timeout);

If you want debug messages please use grub_dprintf() instead of grub_printf().

> +  else {
> +    grub_printf("grub %s: Disarming EFI watchdog\n", PACKAGE_VERSION);
> +    code = 0;
> +  }
> +
> +  status = efi_call_4 (grub_efi_system_table->boot_services->set_watchdog_timer, timeout, code, sizeof(L"GRUB"), L"GRUB");
> +
> +  if (status != GRUB_EFI_SUCCESS)
> +    return grub_error (GRUB_ERR_BUG, N_("Unexpected UEFI SetWatchdogTimer() error"));

Please print status value in case of an error, e.g. "UEFI SetWatchdogTimer() error: %..."

> +  else
> +    return GRUB_ERR_NONE;
> +}
> +
> +static grub_err_t
> +grub_cmd_efi_watchdog (grub_command_t cmd  __attribute__ ((unused)),
> +                      int argc, char **args)
> +{
> +  unsigned long timeout = 0;

s/unsigned long/grub_efi_uintn_t/ Please use types according to the UEFI
specification and convert properly between them.

> +
> +

Please drop this redundant empty line.

> +  if (argc < 1)
> +    return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("usage: efi-watchdog <timeout>"));

As I said earlier. I think "watchdog" command should take two arguments:
-t <timeout> and -c <code>.

> +  const char *ptr = args[0];
> +  timeout = grub_strtoul (ptr, &ptr, 0);

Please read strtoul() documentation and handle all errors properly.

> +  if (grub_errno)

if (grub_errno != GRUB_ERR_NONE)

> +    return grub_errno;
> +
> +  return grub_efi_set_watchdog_timer(timeout);

Please do not forget about spaces between function names and "(".

> +}
> +
> +static void
> +grub_efi_watchdog_timer_init(void)
> +{
> +  struct grub_module_header *header;
> +  unsigned long efi_watchdog_timeout = 0;

Again, please use correct UEFI type...

> +
> +  FOR_MODULES (header)

Missing "{}" for FOR_MODULES() and...

> +  if (header->type == OBJ_TYPE_EFI_WATCHDOG_TIMEOUT)

... incorrect "if" indention. Please take a look at
grub-core/kern/efi/sb.c for more details...

> +    {
> +      char *efi_wdt_orig_addr;
> +      static char *efi_wdt_addr;
> +      static grub_off_t efi_wdt_size = 0;
> +
> +      efi_wdt_orig_addr = (char *) header + sizeof (struct grub_module_header);
> +      efi_wdt_size = header->size - sizeof (struct grub_module_header);
> +      efi_wdt_addr = grub_malloc (efi_wdt_size);

Please do not ignore grub_malloc() errors. In general you need proper
error handling for every function which may fail.

> +      grub_memmove (efi_wdt_addr, efi_wdt_orig_addr, efi_wdt_size);
> +      efi_watchdog_timeout = (unsigned long) *efi_wdt_addr;
> +      break;
> +    }
> +
> +  grub_efi_set_watchdog_timer(efi_watchdog_timeout);
> +}
> +
> +

Please drop this redundant empty line...

>  void
>  grub_efi_init (void)
>  {
> @@ -105,10 +183,12 @@ grub_efi_init (void)
>        grub_shim_lock_verifier_setup ();
>      }
>
> -  efi_call_4 (grub_efi_system_table->boot_services->set_watchdog_timer,
> -	      0, 0, 0, NULL);
> +  grub_efi_watchdog_timer_init();
>
>    grub_efidisk_init ();
> +
> +  cmd_list = grub_register_command ("efi-watchdog", grub_cmd_efi_watchdog, 0,
> +     N_("Enable/Disable system's watchdog timer."));
>  }
>
>  void (*grub_efi_net_config) (grub_efi_handle_t hnd,
> @@ -146,4 +226,5 @@ grub_efi_fini (void)
>  {
>    grub_efidisk_fini ();
>    grub_console_fini ();
> +  grub_unregister_command (cmd_list);
>  }
> diff --git a/include/grub/efi/efi.h b/include/grub/efi/efi.h
> index 83d958f9945e..372f995b74f4 100644
> --- a/include/grub/efi/efi.h
> +++ b/include/grub/efi/efi.h
> @@ -124,4 +124,6 @@ struct grub_net_card;
>  grub_efi_handle_t
>  grub_efinet_get_device_handle (struct grub_net_card *card);
>
> +/* EFI Watchdog armed by grub, in minutes */
> +#define EFI_WATCHDOG_MINUTES 0
>  #endif /* ! GRUB_EFI_EFI_HEADER */
> diff --git a/include/grub/kernel.h b/include/grub/kernel.h
> index abbca5ea3359..172599c58b23 100644
> --- a/include/grub/kernel.h
> +++ b/include/grub/kernel.h
> @@ -30,7 +30,8 @@ enum
>    OBJ_TYPE_PREFIX,
>    OBJ_TYPE_PUBKEY,
>    OBJ_TYPE_DTB,
> -  OBJ_TYPE_DISABLE_SHIM_LOCK
> +  OBJ_TYPE_DISABLE_SHIM_LOCK,
> +  OBJ_TYPE_EFI_WATCHDOG_TIMEOUT,
>  };
>
>  /* The module header.  */
> diff --git a/include/grub/util/install.h b/include/grub/util/install.h
> index 7df3191f47ef..1276742d09e6 100644
> --- a/include/grub/util/install.h
> +++ b/include/grub/util/install.h
> @@ -67,6 +67,8 @@
>        N_("SBAT metadata"), 0 },						\
>    { "disable-shim-lock", GRUB_INSTALL_OPTIONS_DISABLE_SHIM_LOCK, 0, 0,	\
>        N_("disable shim_lock verifier"), 0 },				\
> +  { "efi-watchdog-timeout", GRUB_INSTALL_OPTIONS_EFI_WATCHDOG_TIMEOUT, N_("EFI_WATCHDOG_TIMEOUT"), 0, \
> +      N_("arm efi watchdog at EFI_WATCHDOG_TIMEOUT seconds"), 0 }, \
>    { "verbose", 'v', 0, 0,						\
>      N_("print verbose messages."), 1 }
>
> @@ -128,7 +130,8 @@ enum grub_install_options {
>    GRUB_INSTALL_OPTIONS_INSTALL_CORE_COMPRESS,
>    GRUB_INSTALL_OPTIONS_DTB,
>    GRUB_INSTALL_OPTIONS_SBAT,
> -  GRUB_INSTALL_OPTIONS_DISABLE_SHIM_LOCK
> +  GRUB_INSTALL_OPTIONS_DISABLE_SHIM_LOCK,
> +  GRUB_INSTALL_OPTIONS_EFI_WATCHDOG_TIMEOUT
>  };
>
>  extern char *grub_install_source_directory;
> @@ -190,7 +193,8 @@ grub_install_generate_image (const char *dir, const char *prefix,
>  			     const struct grub_install_image_target_desc *image_target,
>  			     int note,
>  			     grub_compression_t comp, const char *dtb_file,
> -			     const char *sbat_path, const int disable_shim_lock);
> +			     const char *sbat_path, const int disable_shim_lock,
> +			     unsigned long efi_watchdog_timeout);
>
>  const struct grub_install_image_target_desc *
>  grub_install_get_image_target (const char *arg);
> diff --git a/util/grub-install-common.c b/util/grub-install-common.c
> index 4e212e690c52..dab94799a6c3 100644
> --- a/util/grub-install-common.c
> +++ b/util/grub-install-common.c
> @@ -460,11 +460,13 @@ static char **pubkeys;
>  static size_t npubkeys;
>  static char *sbat;
>  static int disable_shim_lock;
> +static unsigned long efi_watchdog_timeout;
>  static grub_compression_t compression;
>
>  int
>  grub_install_parse (int key, char *arg)
>  {
> +  const char *const_arg = arg;
>    switch (key)
>      {
>      case 'C':
> @@ -562,6 +564,11 @@ grub_install_parse (int key, char *arg)
>        grub_util_error (_("Unrecognized compression `%s'"), arg);
>      case GRUB_INSTALL_OPTIONS_GRUB_MKIMAGE:
>        return 1;
> +    case GRUB_INSTALL_OPTIONS_EFI_WATCHDOG_TIMEOUT:
> +      efi_watchdog_timeout = grub_strtoul (arg, &const_arg, 0);

Please do not ignore errors...

> +      if (grub_errno)
> +        grub_util_error (_("timeout argument must be an integer : %s"), arg);
> +      return 1;
>      default:
>        return 0;
>      }
> @@ -661,9 +668,10 @@ grub_install_make_image_wrap_file (const char *dir, const char *prefix,
>  		  " --output '%s' "
>  		  " --dtb '%s' "
>  		  "--sbat '%s' "
> +		  "--efi-watchdog-timeout '%lu' "
>  		  "--format '%s' --compression '%s' %s %s %s\n",
>  		  dir, prefix,
> -		  outname, dtb ? : "", sbat ? : "", mkimage_target,
> +		  outname, dtb ? : "", sbat ? : "", efi_watchdog_timeout, mkimage_target,
>  		  compnames[compression], note ? "--note" : "",
>  		  disable_shim_lock ? "--disable-shim-lock" : "", s);

Could you be more consistent and add your argument behind the
--disable-shim-lock one?

>    free (s);
> @@ -676,7 +684,7 @@ grub_install_make_image_wrap_file (const char *dir, const char *prefix,
>  			       modules.entries, memdisk_path,
>  			       pubkeys, npubkeys, config_path, tgt,
>  			       note, compression, dtb, sbat,
> -			       disable_shim_lock);
> +			       disable_shim_lock, efi_watchdog_timeout);
>    while (dc--)
>      grub_install_pop_module ();
>  }
> diff --git a/util/grub-mkimage.c b/util/grub-mkimage.c
> index c0d559937020..fce7941a58fe 100644
> --- a/util/grub-mkimage.c
> +++ b/util/grub-mkimage.c
> @@ -83,6 +83,7 @@ static struct argp_option options[] = {
>    {"compression",  'C', "(xz|none|auto)", 0, N_("choose the compression to use for core image"), 0},
>    {"sbat", 's', N_("FILE"), 0, N_("SBAT metadata"), 0},
>    {"disable-shim-lock", GRUB_INSTALL_OPTIONS_DISABLE_SHIM_LOCK, 0, 0, N_("disable shim_lock verifier"), 0},
> +  {"efi-watchdog-timeout", GRUB_INSTALL_OPTIONS_EFI_WATCHDOG_TIMEOUT, 0, 0, N_("efi watchdog timeout"), 0},
>    {"verbose",     'v', 0,      0, N_("print verbose messages."), 0},
>    { 0, 0, 0, 0, 0, 0 }
>  };
> @@ -128,6 +129,7 @@ struct arguments
>    char *sbat;
>    int note;
>    int disable_shim_lock;
> +  unsigned long efi_watchdog_timeout;
>    const struct grub_install_image_target_desc *image_target;
>    grub_compression_t comp;
>  };
> @@ -138,6 +140,7 @@ argp_parser (int key, char *arg, struct argp_state *state)
>    /* Get the input argument from argp_parse, which we
>       know is a pointer to our arguments structure. */
>    struct arguments *arguments = state->input;
> +  const char *const_arg = arg;
>
>    switch (key)
>      {
> @@ -239,6 +242,12 @@ argp_parser (int key, char *arg, struct argp_state *state)
>        arguments->disable_shim_lock = 1;
>        break;
>
> +    case GRUB_INSTALL_OPTIONS_EFI_WATCHDOG_TIMEOUT:
> +      arguments->efi_watchdog_timeout = grub_strtoul (arg, &const_arg, 0);
> +      if (grub_errno)
> +        grub_util_error (_("timeout argument must be an integer : %s"), arg);
> +      break;
> +
>      case 'v':
>        verbosity++;
>        break;
> @@ -325,7 +334,7 @@ main (int argc, char *argv[])
>  			       arguments.npubkeys, arguments.config,
>  			       arguments.image_target, arguments.note,
>  			       arguments.comp, arguments.dtb,
> -			       arguments.sbat, arguments.disable_shim_lock);
> +			       arguments.sbat, arguments.disable_shim_lock, arguments.efi_watchdog_timeout);
>
>    if (grub_util_file_sync (fp) < 0)
>      grub_util_error (_("cannot sync `%s': %s"), arguments.output ? : "stdout",
> diff --git a/util/mkimage.c b/util/mkimage.c
> index a26cf76f72f2..91b03801e628 100644
> --- a/util/mkimage.c
> +++ b/util/mkimage.c
> @@ -870,12 +870,12 @@ grub_install_generate_image (const char *dir, const char *prefix,
>  			     size_t npubkeys, char *config_path,
>  			     const struct grub_install_image_target_desc *image_target,
>  			     int note, grub_compression_t comp, const char *dtb_path,
> -			     const char *sbat_path, int disable_shim_lock)
> +			     const char *sbat_path, int disable_shim_lock, unsigned long efi_watchdog_timeout)
>  {
>    char *kernel_img, *core_img;
>    size_t total_module_size, core_size;
>    size_t memdisk_size = 0, config_size = 0;
> -  size_t prefix_size = 0, dtb_size = 0, sbat_size = 0;
> +  size_t prefix_size = 0, dtb_size = 0, sbat_size = 0, efi_watchdog_timeout_size = 0;
>    char *kernel_path;
>    size_t offset;
>    struct grub_util_path_list *path_list, *p;
> @@ -929,6 +929,12 @@ grub_install_generate_image (const char *dir, const char *prefix,
>    if (sbat_path != NULL && image_target->id != IMAGE_EFI)
>      grub_util_error (_(".sbat section can be embedded into EFI images only"));
>
> +  if (efi_watchdog_timeout)
> +    {
> +      efi_watchdog_timeout_size = ALIGN_ADDR(sizeof (efi_watchdog_timeout));
> +      total_module_size += efi_watchdog_timeout_size + sizeof (struct grub_module_header);
> +    }
> +
>    if (disable_shim_lock)
>      total_module_size += sizeof (struct grub_module_header);
>
> @@ -1078,6 +1084,19 @@ grub_install_generate_image (const char *dir, const char *prefix,
>        offset += sizeof (*header);
>      }
>
> +  if (efi_watchdog_timeout)
> +    {
> +      struct grub_module_header *header;
> +
> +      header = (struct grub_module_header *) (kernel_img + offset);
> +      header->type = grub_host_to_target32 (OBJ_TYPE_EFI_WATCHDOG_TIMEOUT);
> +      header->size = grub_host_to_target32 (efi_watchdog_timeout_size + sizeof (*header));

After looking at this it seems to me the timeout should be u32 thing
internally in the GRUB.

Daniel

[1] https://lists.gnu.org/archive/html/grub-devel/2021-06/msg00017.html
[2] https://www.gnu.org/software/grub/manual/grub-dev/grub-dev.html#Comments


^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH v2] kern/efi: Adding efi-watchdog command
  2021-09-09 15:46 ` Daniel Kiper
@ 2021-09-10 10:34   ` Erwan Velu
  2021-09-13 14:12     ` Daniel Kiper
  0 siblings, 1 reply; 9+ messages in thread
From: Erwan Velu @ 2021-09-10 10:34 UTC (permalink / raw)
  To: Daniel Kiper, Erwan Velu; +Cc: grub-devel, alexander.burmashev, phcoder

Thanks for your review, I'll rework my patch according to your comments.
I just kept the open points in this thread.

Le 09/09/2021 à 17:46, Daniel Kiper a écrit :
>
> +static grub_err_t grub_efi_set_watchdog_timer(unsigned long timeout)
> Please use grub_efi_uintn_t instead of unsigned long. However, please
> remember that its size depends on architecture. So, it can be 32-bits or
> 64-bits...
I'll follow the uefi spec here so grub_efi_uintn_ is fine.
>> +    The numeric code to log on a watchdog timer timeout event.
>> +    The firmware reserves codes 0x0000 to 0xFFFF.
>> +    Loaders and operating systems may use other timeout codes.
> This comment says you cannot use 0xb007. I would use 0xdeadb007deadb007
> by default. However, I would give an option to change the default by user.

Stupid me ....

I was wondering if the code should be configurable.

In the initial patch it was the case but though this would give an 
easier command line interface to users.

I mean, the aim of this code is to report mostly 'who' manipulated the 
watchdog, so sound good to me to get a value that is more grub depend 
than user centric.

>
>> +  */
> Please format comments according to [2].
>
>> +  grub_efi_uint64_t code = 0xB007;
>> +  grub_efi_status_t status = 0;
>> +
>> +  if (timeout >= 0xFFFF)
>> +    return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("efi-watchdog: timeout must be lower than 65535"));
> Why do you artificially limit the timeout value? Please do not do that.

Mistake of my own, make sense to remove that.


>
>> +  if (timeout > 0)
>> +    grub_printf("grub %s: Arming EFI watchdog at %lu seconds\n", PACKAGE_VERSION, timeout);
> If you want debug messages please use grub_dprintf() instead of grub_printf().

Maybe my print isn't ideal but I found important to warn the user a 
watchdog is armed.

If the user is not informed and the watchdog fires, the user could think 
there is a hardware issue on the host.

So to me, this message is mandatory to say users : "beware, the system 
will be reset within x seconds if you don't boot"





^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH v2] kern/efi: Adding efi-watchdog command
  2021-09-10 10:34   ` Erwan Velu
@ 2021-09-13 14:12     ` Daniel Kiper
  0 siblings, 0 replies; 9+ messages in thread
From: Daniel Kiper @ 2021-09-13 14:12 UTC (permalink / raw)
  To: Erwan Velu; +Cc: Erwan Velu, grub-devel, alexander.burmashev, phcoder

On Fri, Sep 10, 2021 at 12:34:54PM +0200, Erwan Velu wrote:
> Thanks for your review, I'll rework my patch according to your comments.
> I just kept the open points in this thread.
>
> Le 09/09/2021 à 17:46, Daniel Kiper a écrit :
> >
> > +static grub_err_t grub_efi_set_watchdog_timer(unsigned long timeout)
> > Please use grub_efi_uintn_t instead of unsigned long. However, please
> > remember that its size depends on architecture. So, it can be 32-bits or
> > 64-bits...
> I'll follow the uefi spec here so grub_efi_uintn_ is fine.

At the end of my previous email I suggested it should be u32, i.e. grub_efi_uint32_t.
I think we should stick with it and only do conversion during UEFI watchdog call.
Of course it means you have to be very careful what you get from grub_strtoul().

> > > +    The numeric code to log on a watchdog timer timeout event.
> > > +    The firmware reserves codes 0x0000 to 0xFFFF.
> > > +    Loaders and operating systems may use other timeout codes.
> > This comment says you cannot use 0xb007. I would use 0xdeadb007deadb007
> > by default. However, I would give an option to change the default by user.
>
> Stupid me ....
>
> I was wondering if the code should be configurable.
>
> In the initial patch it was the case but though this would give an easier
> command line interface to users.
>
> I mean, the aim of this code is to report mostly 'who' manipulated the
> watchdog, so sound good to me to get a value that is more grub depend than
> user centric.

The GRUB should provide default but user should be able to change it.

> > > +  */
> > Please format comments according to [2].
> >
> > > +  grub_efi_uint64_t code = 0xB007;
> > > +  grub_efi_status_t status = 0;
> > > +
> > > +  if (timeout >= 0xFFFF)
> > > +    return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("efi-watchdog: timeout must be lower than 65535"));
> > Why do you artificially limit the timeout value? Please do not do that.
>
> Mistake of my own, make sense to remove that.

Please be aware of my note WRT grub_efi_uint32_t type above.

> > > +  if (timeout > 0)
> > > +    grub_printf("grub %s: Arming EFI watchdog at %lu seconds\n", PACKAGE_VERSION, timeout);
> > If you want debug messages please use grub_dprintf() instead of grub_printf().
>
> Maybe my print isn't ideal but I found important to warn the user a watchdog
> is armed.
>
> If the user is not informed and the watchdog fires, the user could think
> there is a hardware issue on the host.
>
> So to me, this message is mandatory to say users : "beware, the system will
> be reset within x seconds if you don't boot"

If user have problems with watchdog they can enable debugging and see what
is going on. That is why I suggested grub_dprintf() usage.

Daniel


^ permalink raw reply	[flat|nested] 9+ messages in thread

end of thread, other threads:[~2021-09-13 14:12 UTC | newest]

Thread overview: 9+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-09-02 16:50 [PATCH v2] kern/efi: Adding efi-watchdog command Erwan Velu
2021-09-03  2:09 ` Michael Chang
2021-09-03  6:33   ` Erwan Velu
2021-09-07 11:20     ` Erwan Velu
2021-09-07 18:01       ` Daniel Kiper
2021-09-07 18:28         ` Erwan Velu
2021-09-09 15:46 ` Daniel Kiper
2021-09-10 10:34   ` Erwan Velu
2021-09-13 14:12     ` Daniel Kiper

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.