From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from list by lists.gnu.org with archive (Exim 4.90_1) id 1mLpw0-0002ud-5e for mharc-grub-devel@gnu.org; Thu, 02 Sep 2021 12:51:29 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]:49896) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1mLpvw-0002rR-TN for grub-devel@gnu.org; Thu, 02 Sep 2021 12:51:25 -0400 Received: from mail-wm1-x335.google.com ([2a00:1450:4864:20::335]:42857) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from ) id 1mLpvu-0000un-Fw for grub-devel@gnu.org; Thu, 02 Sep 2021 12:51:24 -0400 Received: by mail-wm1-x335.google.com with SMTP id k20-20020a05600c0b5400b002e87ad6956eso1821582wmr.1 for ; Thu, 02 Sep 2021 09:51:22 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112; h=from:to:cc:subject:date:message-id:mime-version :content-transfer-encoding; bh=YUneAXfcBo81zhxlQBzM2DqYBIMQU11RCCBc43P232M=; b=I+y6V21+zLPUTnRCE8EFC1RSbDZNos0KGP17Ygil2X1WXfuRxwpG9JhWGW8r9Li0V9 gxZ/aQTTDF00qUpxtFInr7a5AUK7gY0PcIg4Oicx5m9dCufThlEmyLioJbYanYrW+fsA rTxVwm/921AnuQR/MqXRqt4m/NlA4PjGeCTYsDYoeowFCy5AogRFqslQi4DIUSKH9nI5 ldwVAgNFvusfkw8GHSwucWAXtgzpnUhyswGlsr45ei57wY0vH2GQjDLrT1mypQmd9sm1 ooDym+bsdDOPa+IhkdSa+taNZB+Ll92/03NbK5ytTFuJraMQ5s6AgfX6xVKzV2JTxszY 1cXw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:mime-version :content-transfer-encoding; bh=YUneAXfcBo81zhxlQBzM2DqYBIMQU11RCCBc43P232M=; b=XJZaVJGQIoMxf+UmScYiyJqb54fIhAXw36qgiDNcxndtDA5jvasYmNjSPvQqcP6KJj lf8WfF/x8sXyp1dnYMHsSk3Pp9eqGEW50+P5A0eTzdGu0GqVquJHBvX/4vxtsHWV1lyY fDBKKY4XzDmdVZQDkuBB4k9H7oyZxzoR43JcWGRgSGnYp+/zcC8BHnXubKEL9LMx48tq IzNDuGpWJwfElM+PCZqgSBXYnS8muORY6hba5jZhoqVJ5Ivyc9RZGnKAekgciu1BGegD 9y4thZk0RXfbnLZ+xYWLcSDAcd/2CkGAUYrf5LVVLIz9tGiyBZDwSXuK6cR045KU0VL7 j65A== X-Gm-Message-State: AOAM532StEH3zz4sg+wh/6RWvFCSqChS06CdDjrizX+u2uPxPFBtkjZY 0T70gG6LYwYKFmVtk4t1jkkwFdBl59OeCw== X-Google-Smtp-Source: ABdhPJxEcUgClroQSjK6NKT2wTSWpv89kVZ6Yd3kAxh042+an0uaW4+lK8Z5tf0IkCVRzSNiJqDQyA== X-Received: by 2002:a05:600c:4ece:: with SMTP id g14mr4175132wmq.6.1630601480800; Thu, 02 Sep 2021 09:51:20 -0700 (PDT) Received: from R90YT7WC.crto.in (2a01cb000de88800931d396877b644c6.ipv6.abo.wanadoo.fr. [2a01:cb00:de8:8800:931d:3968:77b6:44c6]) by smtp.gmail.com with ESMTPSA id r20sm2425484wrr.47.2021.09.02.09.51.19 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 02 Sep 2021 09:51:20 -0700 (PDT) From: Erwan Velu X-Google-Original-From: Erwan Velu To: grub-devel@gnu.org Cc: dkiper@net-space.pl, daniel.kiper@oracle.com, alexander.burmashev@oracle.com, phcoder@gmail.com, Erwan Velu , Arthur Mesh Subject: [PATCH v2] kern/efi: Adding efi-watchdog command Date: Thu, 2 Sep 2021 18:50:35 +0200 Message-Id: <20210902165035.1986540-1-e.velu@criteo.com> X-Mailer: git-send-email 2.25.1 MIME-Version: 1.0 Content-Transfer-Encoding: 8bit Received-SPF: pass client-ip=2a00:1450:4864:20::335; envelope-from=erwanaliasr1@gmail.com; helo=mail-wm1-x335.google.com X-Spam_score_int: -17 X-Spam_score: -1.8 X-Spam_bar: - X-Spam_report: (-1.8 / 5.0 requ) BAYES_00=-1.9, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, FREEMAIL_ENVFROM_END_DIGIT=0.25, FREEMAIL_FROM=0.001, RCVD_IN_DNSWL_NONE=-0.0001, SPF_HELO_NONE=0.001, SPF_PASS=-0.001 autolearn=ham autolearn_force=no X-Spam_action: no action X-BeenThere: grub-devel@gnu.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: The development of GNU GRUB List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Thu, 02 Sep 2021 16:51:26 -0000 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 Signed-off-by: Arthur Mesh --- 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 #include #include +#include +#include #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 ")); + + 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