efi_loader: implement SetWatchdogTimer
authorHeinrich Schuchardt <xypron.glpk@gmx.de>
Wed, 18 Oct 2017 16:13:04 +0000 (18:13 +0200)
committerAlexander Graf <agraf@suse.de>
Fri, 1 Dec 2017 12:22:55 +0000 (13:22 +0100)
The watchdog is initialized with a 5 minute timeout period.
It can be reset by SetWatchdogTimer.
It is stopped by ExitBoottimeServices.

Signed-off-by: Heinrich Schuchardt <xypron.glpk@gmx.de>
Signed-off-by: Alexander Graf <agraf@suse.de>
cmd/bootefi.c
include/efi_loader.h
lib/efi_loader/Makefile
lib/efi_loader/efi_boottime.c
lib/efi_loader/efi_watchdog.c [new file with mode: 0644]

index 478bc11..18176a1 100644 (file)
@@ -43,6 +43,7 @@ static void efi_init_obj_list(void)
 #ifdef CONFIG_GENERATE_SMBIOS_TABLE
        efi_smbios_register();
 #endif
+       efi_watchdog_register();
 
        /* Initialize EFI runtime services */
        efi_reset_system_init();
index 1b92edb..af64b11 100644 (file)
@@ -163,6 +163,8 @@ int efi_disk_register(void);
 int efi_gop_register(void);
 /* Called by bootefi to make the network interface available */
 int efi_net_register(void);
+/* Called by bootefi to make the watchdog available */
+int efi_watchdog_register(void);
 /* Called by bootefi to make SMBIOS tables available */
 void efi_smbios_register(void);
 
@@ -171,6 +173,8 @@ efi_fs_from_path(struct efi_device_path *fp);
 
 /* Called by networking code to memorize the dhcp ack package */
 void efi_net_set_dhcp_ack(void *pkt, int len);
+/* Called by efi_set_watchdog_timer to reset the timer */
+efi_status_t efi_set_watchdog(unsigned long timeout);
 
 /* Called from places to check whether a timer expired */
 void efi_timer_check(void);
index ddb978f..83d879b 100644 (file)
@@ -17,7 +17,7 @@ endif
 obj-$(CONFIG_CMD_BOOTEFI_HELLO) += helloworld_efi.o
 obj-y += efi_image_loader.o efi_boottime.o efi_runtime.o efi_console.o
 obj-y += efi_memory.o efi_device_path_to_text.o efi_device_path.o
-obj-y += efi_file.o efi_variable.o efi_bootmgr.o
+obj-y += efi_file.o efi_variable.o efi_bootmgr.o efi_watchdog.o
 obj-$(CONFIG_LCD) += efi_gop.o
 obj-$(CONFIG_DM_VIDEO) += efi_gop.o
 obj-$(CONFIG_PARTITIONS) += efi_disk.o
index 04a7859..c77da8a 100644 (file)
@@ -156,18 +156,6 @@ void efi_signal_event(struct efi_event *event)
 }
 
 /*
- * Write a debug message for an EPI API service that is not implemented yet.
- *
- * @funcname   function that is not yet implemented
- * @return     EFI_UNSUPPORTED
- */
-static efi_status_t efi_unsupported(const char *funcname)
-{
-       debug("EFI: App called into unimplemented function %s\n", funcname);
-       return EFI_EXIT(EFI_UNSUPPORTED);
-}
-
-/*
  * Raise the task priority level.
  *
  * This function implements the RaiseTpl service.
@@ -1451,6 +1439,7 @@ static efi_status_t EFIAPI efi_exit_boot_services(void *image_handle,
        bootm_disable_interrupts();
 
        /* Give the payload some time to boot */
+       efi_set_watchdog(0);
        WATCHDOG_RESET();
 
        return EFI_EXIT(EFI_SUCCESS);
@@ -1494,7 +1483,7 @@ static efi_status_t EFIAPI efi_stall(unsigned long microseconds)
 /*
  * Reset the watchdog timer.
  *
- * This function implements the WatchdogTimer service.
+ * This function implements the SetWatchdogTimer service.
  * See the Unified Extensible Firmware Interface (UEFI) specification
  * for details.
  *
@@ -1511,7 +1500,7 @@ static efi_status_t EFIAPI efi_set_watchdog_timer(unsigned long timeout,
 {
        EFI_ENTRY("%ld, 0x%"PRIx64", %ld, %p", timeout, watchdog_code,
                  data_size, watchdog_data);
-       return efi_unsupported(__func__);
+       return EFI_EXIT(efi_set_watchdog(timeout));
 }
 
 /*
diff --git a/lib/efi_loader/efi_watchdog.c b/lib/efi_loader/efi_watchdog.c
new file mode 100644 (file)
index 0000000..35a45de
--- /dev/null
@@ -0,0 +1,89 @@
+/*
+ *  EFI watchdog
+ *
+ *  Copyright (c) 2017 Heinrich Schuchardt
+ *
+ *  SPDX-License-Identifier:     GPL-2.0+
+ */
+
+#include <common.h>
+#include <efi_loader.h>
+
+/* Conversion factor from seconds to multiples of 100ns */
+#define EFI_SECONDS_TO_100NS 10000000ULL
+
+static struct efi_event *watchdog_timer_event;
+
+/*
+ * Reset the system when the watchdog event is notified.
+ *
+ * @event:     the watchdog event
+ * @context:   not used
+ */
+static void EFIAPI efi_watchdog_timer_notify(struct efi_event *event,
+                                            void *context)
+{
+       EFI_ENTRY("%p, %p", event, context);
+
+       printf("\nEFI: Watchdog timeout\n");
+       EFI_CALL_VOID(efi_runtime_services.reset_system(EFI_RESET_COLD,
+                                                       EFI_SUCCESS, 0, NULL));
+
+       EFI_EXIT(EFI_UNSUPPORTED);
+}
+
+/*
+ * Reset the watchdog timer.
+ *
+ * This function is used by the SetWatchdogTimer service.
+ *
+ * @timeout:           seconds before reset by watchdog
+ * @return:            status code
+ */
+efi_status_t efi_set_watchdog(unsigned long timeout)
+{
+       efi_status_t r;
+
+       if (timeout)
+               /* Reset watchdog */
+               r = efi_set_timer(watchdog_timer_event, EFI_TIMER_RELATIVE,
+                                 EFI_SECONDS_TO_100NS * timeout);
+       else
+               /* Deactivate watchdog */
+               r = efi_set_timer(watchdog_timer_event, EFI_TIMER_STOP, 0);
+       return r;
+}
+
+/*
+ * Initialize the EFI watchdog.
+ *
+ * This function is called by efi_init_obj_list()
+ */
+int efi_watchdog_register(void)
+{
+       efi_status_t r;
+
+       /*
+        * Create a timer event.
+        */
+       r = efi_create_event(EVT_TIMER | EVT_NOTIFY_SIGNAL, TPL_CALLBACK,
+                            efi_watchdog_timer_notify, NULL,
+                            &watchdog_timer_event);
+       if (r != EFI_SUCCESS) {
+               printf("ERROR: Failed to register watchdog event\n");
+               return r;
+       }
+       /*
+        * The UEFI standard requires that the watchdog timer is set to five
+        * minutes when invoking an EFI boot option.
+        *
+        * Unified Extensible Firmware Interface (UEFI), version 2.7 Errata A
+        * 7.5. Miscellaneous Boot Services - EFI_BOOT_SERVICES.SetWatchdogTimer
+        */
+       r = efi_set_watchdog(300);
+       if (r != EFI_SUCCESS) {
+               printf("ERROR: Failed to set watchdog timer\n");
+               return r;
+       }
+       return 0;
+}