1 /* 2 * efi_selftest_watchdog 3 * 4 * Copyright (c) 2017 Heinrich Schuchardt <xypron.glpk@gmx.de> 5 * 6 * SPDX-License-Identifier: GPL-2.0+ 7 * 8 * The 'watchdog timer' unit test checks that the watchdog timer 9 * will not cause a system restart during the timeout period after 10 * a timer reset. 11 * 12 * The 'watchdog reboot' unit test checks that the watchdog timer 13 * actually reboots the system after a timeout. The test is only 14 * executed on explicit request. Use the following commands: 15 * 16 * setenv efi_selftest watchdog reboot 17 * bootefi selftest 18 */ 19 20 #include <efi_selftest.h> 21 22 /* 23 * This is the communication structure for the notification function. 24 */ 25 struct notify_context { 26 /* Status code returned when resetting watchdog */ 27 efi_status_t status; 28 /* Number of invocations of the notification function */ 29 unsigned int timer_ticks; 30 }; 31 32 static struct efi_event *event_notify; 33 static struct efi_event *event_wait; 34 static struct efi_boot_services *boottime; 35 static struct notify_context notification_context; 36 static bool watchdog_reset; 37 38 /* 39 * Notification function, increments the notfication count if parameter 40 * context is provided. 41 * 42 * @event notified event 43 * @context pointer to the timeout 44 */ 45 static void EFIAPI notify(struct efi_event *event, void *context) 46 { 47 struct notify_context *notify_context = context; 48 efi_status_t ret = EFI_SUCCESS; 49 50 if (!notify_context) 51 return; 52 53 /* Reset watchdog timer to one second */ 54 ret = boottime->set_watchdog_timer(1, 0, 0, NULL); 55 if (ret != EFI_SUCCESS) 56 notify_context->status = ret; 57 /* Count number of calls */ 58 notify_context->timer_ticks++; 59 } 60 61 /* 62 * Setup unit test. 63 * 64 * Create two timer events. 65 * One with EVT_NOTIFY_SIGNAL, the other with EVT_NOTIFY_WAIT. 66 * 67 * @handle: handle of the loaded image 68 * @systable: system table 69 * @return: EFI_ST_SUCCESS for success 70 */ 71 static int setup(const efi_handle_t handle, 72 const struct efi_system_table *systable) 73 { 74 efi_status_t ret; 75 76 boottime = systable->boottime; 77 78 notification_context.status = EFI_SUCCESS; 79 notification_context.timer_ticks = 0; 80 ret = boottime->create_event(EVT_TIMER | EVT_NOTIFY_SIGNAL, 81 TPL_CALLBACK, notify, 82 (void *)¬ification_context, 83 &event_notify); 84 if (ret != EFI_SUCCESS) { 85 efi_st_error("could not create event\n"); 86 return EFI_ST_FAILURE; 87 } 88 ret = boottime->create_event(EVT_TIMER | EVT_NOTIFY_WAIT, 89 TPL_CALLBACK, notify, NULL, &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 */ 104 static 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 */ 118 static 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 */ 132 static 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 (event_notify) { 143 ret = boottime->close_event(event_notify); 144 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 (event_wait) { 151 ret = boottime->close_event(event_wait); 152 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 */ 172 static 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(event_notify, EFI_TIMER_PERIODIC, 186 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(event_wait, EFI_TIMER_RELATIVE, 13500000); 194 if (ret != EFI_SUCCESS) { 195 efi_st_error("Could not set timer\n"); 196 return EFI_ST_FAILURE; 197 } 198 199 ret = boottime->wait_for_event(1, &event_wait, &index); 200 if (ret != EFI_SUCCESS) { 201 efi_st_error("Could not wait for event\n"); 202 return EFI_ST_FAILURE; 203 } 204 if (notification_context.status != EFI_SUCCESS) { 205 efi_st_error("Setting watchdog timer failed\n"); 206 return EFI_ST_FAILURE; 207 } 208 if (notification_context.timer_ticks != 2) { 209 efi_st_error("The timer was called %u times, expected 2.\n", 210 notification_context.timer_ticks); 211 return EFI_ST_FAILURE; 212 } 213 return EFI_ST_SUCCESS; 214 } 215 216 EFI_UNIT_TEST(watchdog1) = { 217 .name = "watchdog timer", 218 .phase = EFI_EXECUTE_BEFORE_BOOTTIME_EXIT, 219 .setup = setup_timer, 220 .execute = execute, 221 .teardown = teardown, 222 }; 223 224 EFI_UNIT_TEST(watchdog2) = { 225 .name = "watchdog reboot", 226 .phase = EFI_EXECUTE_BEFORE_BOOTTIME_EXIT, 227 .setup = setup_reboot, 228 .execute = execute, 229 .teardown = teardown, 230 .on_request = true, 231 }; 232