uboot/lib/efi_selftest/efi_selftest_watchdog.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0+
   2/*
   3 * efi_selftest_watchdog
   4 *
   5 * Copyright (c) 2017 Heinrich Schuchardt <xypron.glpk@gmx.de>
   6 *
   7 * The 'watchdog timer' unit test checks that the watchdog timer
   8 * will not cause a system restart during the timeout period after
   9 * a timer reset.
  10 *
  11 * The 'watchdog reboot' unit test checks that the watchdog timer
  12 * actually reboots the system after a timeout. The test is only
  13 * executed on explicit request. Use the following commands:
  14 *
  15 *      setenv efi_selftest watchdog reboot
  16 *      bootefi selftest
  17 */
  18
  19#include <efi_selftest.h>
  20
  21/*
  22 * This is the communication structure for the notification function.
  23 */
  24struct notify_context {
  25        /* Status code returned when resetting watchdog */
  26        efi_status_t status;
  27        /* Number of invocations of the notification function */
  28        unsigned int timer_ticks;
  29};
  30
  31static struct efi_event *efi_st_event_notify;
  32static struct efi_event *efi_st_event_wait;
  33static struct efi_boot_services *boottime;
  34static struct notify_context notification_context;
  35static bool watchdog_reset;
  36
  37/*
  38 * Notification function, increments the notification count if parameter
  39 * context is provided.
  40 *
  41 * @event       notified event
  42 * @context     pointer to the timeout
  43 */
  44static void EFIAPI notify(struct efi_event *event, void *context)
  45{
  46        struct notify_context *notify_context = context;
  47        efi_status_t ret = EFI_SUCCESS;
  48
  49        if (!notify_context)
  50                return;
  51
  52        /* Reset watchdog timer to one second */
  53        ret = boottime->set_watchdog_timer(1, 0, 0, NULL);
  54        if (ret != EFI_SUCCESS)
  55                notify_context->status = ret;
  56        /* Count number of calls */
  57        notify_context->timer_ticks++;
  58}
  59
  60/*
  61 * Setup unit test.
  62 *
  63 * Create two timer events.
  64 * One with EVT_NOTIFY_SIGNAL, the other with EVT_NOTIFY_WAIT.
  65 *
  66 * @handle:     handle of the loaded image
  67 * @systable:   system table
  68 * Return:      EFI_ST_SUCCESS for success
  69 */
  70static int setup(const efi_handle_t handle,
  71                 const struct efi_system_table *systable)
  72{
  73        efi_status_t ret;
  74
  75        boottime = systable->boottime;
  76
  77        notification_context.status = EFI_SUCCESS;
  78        notification_context.timer_ticks = 0;
  79        ret = boottime->create_event(EVT_TIMER | EVT_NOTIFY_SIGNAL,
  80                                     TPL_CALLBACK, notify,
  81                                     (void *)&notification_context,
  82                                     &efi_st_event_notify);
  83        if (ret != EFI_SUCCESS) {
  84                efi_st_error("could not create event\n");
  85                return EFI_ST_FAILURE;
  86        }
  87        ret = boottime->create_event(EVT_TIMER | EVT_NOTIFY_WAIT,
  88                                     TPL_CALLBACK, notify, NULL,
  89                                     &efi_st_event_wait);
  90        if (ret != EFI_SUCCESS) {
  91                efi_st_error("could not create event\n");
  92                return EFI_ST_FAILURE;
  93        }
  94        return EFI_ST_SUCCESS;
  95}
  96
  97/*
  98 * Execute the test resetting the watchdog in a timely manner. No reboot occurs.
  99 *
 100 * @handle:     handle of the loaded image
 101 * @systable:   system table
 102 * Return:      EFI_ST_SUCCESS for success
 103 */
 104static int setup_timer(const efi_handle_t handle,
 105                       const struct efi_system_table *systable)
 106{
 107        watchdog_reset = true;
 108        return setup(handle, systable);
 109}
 110
 111/*
 112 * Execute the test without resetting the watchdog. A system reboot occurs.
 113 *
 114 * @handle:     handle of the loaded image
 115 * @systable:   system table
 116 * Return:      EFI_ST_SUCCESS for success
 117 */
 118static int setup_reboot(const efi_handle_t handle,
 119                        const struct efi_system_table *systable)
 120{
 121        watchdog_reset = false;
 122        return setup(handle, systable);
 123}
 124
 125/*
 126 * Tear down unit test.
 127 *
 128 * Close the events created in setup.
 129 *
 130 * Return:      EFI_ST_SUCCESS for success
 131 */
 132static int teardown(void)
 133{
 134        efi_status_t ret;
 135
 136        /* Set the watchdog timer to the five minute default value */
 137        ret = boottime->set_watchdog_timer(300, 0, 0, NULL);
 138        if (ret != EFI_SUCCESS) {
 139                efi_st_error("Setting watchdog timer failed\n");
 140                return EFI_ST_FAILURE;
 141        }
 142        if (efi_st_event_notify) {
 143                ret = boottime->close_event(efi_st_event_notify);
 144                efi_st_event_notify = NULL;
 145                if (ret != EFI_SUCCESS) {
 146                        efi_st_error("Could not close event\n");
 147                        return EFI_ST_FAILURE;
 148                }
 149        }
 150        if (efi_st_event_wait) {
 151                ret = boottime->close_event(efi_st_event_wait);
 152                efi_st_event_wait = NULL;
 153                if (ret != EFI_SUCCESS) {
 154                        efi_st_error("Could not close event\n");
 155                        return EFI_ST_FAILURE;
 156                }
 157        }
 158        return EFI_ST_SUCCESS;
 159}
 160
 161/*
 162 * Execute unit test.
 163 *
 164 * Run a 600 ms periodic timer that resets the watchdog to one second
 165 * on every timer tick.
 166 *
 167 * Run a 1350 ms single shot timer and check that the 600ms timer has
 168 * been called 2 times.
 169 *
 170 * Return:      EFI_ST_SUCCESS for success
 171 */
 172static int execute(void)
 173{
 174        size_t index;
 175        efi_status_t ret;
 176
 177        /* Set the watchdog timeout to one second */
 178        ret = boottime->set_watchdog_timer(1, 0, 0, NULL);
 179        if (ret != EFI_SUCCESS) {
 180                efi_st_error("Setting watchdog timer failed\n");
 181                return EFI_ST_FAILURE;
 182        }
 183        if (watchdog_reset) {
 184                /* Set 600 ms timer */
 185                ret = boottime->set_timer(efi_st_event_notify,
 186                                          EFI_TIMER_PERIODIC, 6000000);
 187                if (ret != EFI_SUCCESS) {
 188                        efi_st_error("Could not set timer\n");
 189                        return EFI_ST_FAILURE;
 190                }
 191        }
 192        /* Set 1350 ms timer */
 193        ret = boottime->set_timer(efi_st_event_wait, EFI_TIMER_RELATIVE,
 194                                  13500000);
 195        if (ret != EFI_SUCCESS) {
 196                efi_st_error("Could not set timer\n");
 197                return EFI_ST_FAILURE;
 198        }
 199
 200        ret = boottime->wait_for_event(1, &efi_st_event_wait, &index);
 201        if (ret != EFI_SUCCESS) {
 202                efi_st_error("Could not wait for event\n");
 203                return EFI_ST_FAILURE;
 204        }
 205        if (notification_context.status != EFI_SUCCESS) {
 206                efi_st_error("Setting watchdog timer failed\n");
 207                return EFI_ST_FAILURE;
 208        }
 209        if (notification_context.timer_ticks != 2) {
 210                efi_st_error("The timer was called %u times, expected 2.\n",
 211                             notification_context.timer_ticks);
 212                return EFI_ST_FAILURE;
 213        }
 214        return EFI_ST_SUCCESS;
 215}
 216
 217EFI_UNIT_TEST(watchdog1) = {
 218        .name = "watchdog timer",
 219        .phase = EFI_EXECUTE_BEFORE_BOOTTIME_EXIT,
 220        .setup = setup_timer,
 221        .execute = execute,
 222        .teardown = teardown,
 223};
 224
 225EFI_UNIT_TEST(watchdog2) = {
 226        .name = "watchdog reboot",
 227        .phase = EFI_EXECUTE_BEFORE_BOOTTIME_EXIT,
 228        .setup = setup_reboot,
 229        .execute = execute,
 230        .teardown = teardown,
 231        .on_request = true,
 232};
 233