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 */ 24 struct 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 31 static struct efi_event *event_notify; 32 static struct efi_event *event_wait; 33 static struct efi_boot_services *boottime; 34 static struct notify_context notification_context; 35 static bool watchdog_reset; 36 37 /* 38 * Notification function, increments the notfication count if parameter 39 * context is provided. 40 * 41 * @event notified event 42 * @context pointer to the timeout 43 */ 44 static 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 */ 70 static 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 *)¬ification_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 */ 103 static 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 */ 117 static 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 */ 131 static 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 */ 171 static 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 215 EFI_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 223 EFI_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