efi_selftest_watchdog.c 5.7 KB

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