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 *event_notify;
  32static struct efi_event *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                                     &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, &event_wait);
  89        if (ret != EFI_SUCCESS) {
  90                efi_st_error("could not create event\n");
  91                return EFI_ST_FAILURE;
  92        }
  93        return EFI_ST_SUCCESS;
  94}
  95
  96/*
  97 * Execute the test resetting the watchdog in a timely manner. No reboot occurs.
  98 *
  99 * @handle:     handle of the loaded image
 100 * @systable:   system table
 101 * @return:     EFI_ST_SUCCESS for success
 102 */
 103static int setup_timer(const efi_handle_t handle,
 104                       const struct efi_system_table *systable)
 105{
 106        watchdog_reset = true;
 107        return setup(handle, systable);
 108}
 109
 110/*
 111 * Execute the test without resetting the watchdog. A system reboot occurs.
 112 *
 113 * @handle:     handle of the loaded image
 114 * @systable:   system table
 115 * @return:     EFI_ST_SUCCESS for success
 116 */
 117static int setup_reboot(const efi_handle_t handle,
 118                        const struct efi_system_table *systable)
 119{
 120        watchdog_reset = false;
 121        return setup(handle, systable);
 122}
 123
 124/*
 125 * Tear down unit test.
 126 *
 127 * Close the events created in setup.
 128 *
 129 * @return:     EFI_ST_SUCCESS for success
 130 */
 131static int teardown(void)
 132{
 133        efi_status_t ret;
 134
 135        /* Set the watchdog timer to the five minute default value */
 136        ret = boottime->set_watchdog_timer(300, 0, 0, NULL);
 137        if (ret != EFI_SUCCESS) {
 138                efi_st_error("Setting watchdog timer failed\n");
 139                return EFI_ST_FAILURE;
 140        }
 141        if (event_notify) {
 142                ret = boottime->close_event(event_notify);
 143                event_notify = NULL;
 144                if (ret != EFI_SUCCESS) {
 145                        efi_st_error("Could not close event\n");
 146                        return EFI_ST_FAILURE;
 147                }
 148        }
 149        if (event_wait) {
 150                ret = boottime->close_event(event_wait);
 151                event_wait = NULL;
 152                if (ret != EFI_SUCCESS) {
 153                        efi_st_error("Could not close event\n");
 154                        return EFI_ST_FAILURE;
 155                }
 156        }
 157        return EFI_ST_SUCCESS;
 158}
 159
 160/*
 161 * Execute unit test.
 162 *
 163 * Run a 600 ms periodic timer that resets the watchdog to one second
 164 * on every timer tick.
 165 *
 166 * Run a 1350 ms single shot timer and check that the 600ms timer has
 167 * been called 2 times.
 168 *
 169 * @return:     EFI_ST_SUCCESS for success
 170 */
 171static int execute(void)
 172{
 173        size_t index;
 174        efi_status_t ret;
 175
 176        /* Set the watchdog timeout to one second */
 177        ret = boottime->set_watchdog_timer(1, 0, 0, NULL);
 178        if (ret != EFI_SUCCESS) {
 179                efi_st_error("Setting watchdog timer failed\n");
 180                return EFI_ST_FAILURE;
 181        }
 182        if (watchdog_reset) {
 183                /* Set 600 ms timer */
 184                ret = boottime->set_timer(event_notify, EFI_TIMER_PERIODIC,
 185                                          6000000);
 186                if (ret != EFI_SUCCESS) {
 187                        efi_st_error("Could not set timer\n");
 188                        return EFI_ST_FAILURE;
 189                }
 190        }
 191        /* Set 1350 ms timer */
 192        ret = boottime->set_timer(event_wait, EFI_TIMER_RELATIVE, 13500000);
 193        if (ret != EFI_SUCCESS) {
 194                efi_st_error("Could not set timer\n");
 195                return EFI_ST_FAILURE;
 196        }
 197
 198        ret = boottime->wait_for_event(1, &event_wait, &index);
 199        if (ret != EFI_SUCCESS) {
 200                efi_st_error("Could not wait for event\n");
 201                return EFI_ST_FAILURE;
 202        }
 203        if (notification_context.status != EFI_SUCCESS) {
 204                efi_st_error("Setting watchdog timer failed\n");
 205                return EFI_ST_FAILURE;
 206        }
 207        if (notification_context.timer_ticks != 2) {
 208                efi_st_error("The timer was called %u times, expected 2.\n",
 209                             notification_context.timer_ticks);
 210                return EFI_ST_FAILURE;
 211        }
 212        return EFI_ST_SUCCESS;
 213}
 214
 215EFI_UNIT_TEST(watchdog1) = {
 216        .name = "watchdog timer",
 217        .phase = EFI_EXECUTE_BEFORE_BOOTTIME_EXIT,
 218        .setup = setup_timer,
 219        .execute = execute,
 220        .teardown = teardown,
 221};
 222
 223EFI_UNIT_TEST(watchdog2) = {
 224        .name = "watchdog reboot",
 225        .phase = EFI_EXECUTE_BEFORE_BOOTTIME_EXIT,
 226        .setup = setup_reboot,
 227        .execute = execute,
 228        .teardown = teardown,
 229        .on_request = true,
 230};
 231