efi_selftest: provide test for watchdog timer
[platform/kernel/u-boot.git] / lib / efi_selftest / efi_selftest_watchdog.c
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  * This unit test checks that the watchdog timer will not cause
9  * a system restart during the timeout period after a timer reset.
10  *
11  * Testing that the watchdog timer actually will reset the system
12  * after a timeout is not possible within the used framework.
13  */
14
15 #include <efi_selftest.h>
16
17 /*
18  * This is the communication structure for the notification function.
19  */
20 struct notify_context {
21         /* Status code returned when resetting watchdog */
22         efi_status_t status;
23         /* Number of invocations of the notification function */
24         unsigned int timer_ticks;
25 };
26
27 static struct efi_event *event_notify;
28 static struct efi_event *event_wait;
29 static struct efi_boot_services *boottime;
30 static struct notify_context notification_context;
31
32 /*
33  * Notification function, increments the notfication count if parameter
34  * context is provided.
35  *
36  * @event       notified event
37  * @context     pointer to the timeout
38  */
39 static void EFIAPI notify(struct efi_event *event, void *context)
40 {
41         struct notify_context *notify_context = context;
42         efi_status_t ret = EFI_SUCCESS;
43
44         if (!notify_context)
45                 return;
46
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 /*
56  * Setup unit test.
57  *
58  * Create two timer events.
59  * One with EVT_NOTIFY_SIGNAL, the other with EVT_NOTIFY_WAIT.
60  *
61  * @handle:     handle of the loaded image
62  * @systable:   system table
63  * @return:     EFI_ST_SUCCESS for success
64  */
65 static int setup(const efi_handle_t handle,
66                  const struct efi_system_table *systable)
67 {
68         efi_status_t ret;
69
70         boottime = systable->boottime;
71
72         notification_context.status = EFI_SUCCESS;
73         notification_context.timer_ticks = 0;
74         ret = boottime->create_event(EVT_TIMER | EVT_NOTIFY_SIGNAL,
75                                      TPL_CALLBACK, notify,
76                                      (void *)&notification_context,
77                                      &event_notify);
78         if (ret != EFI_SUCCESS) {
79                 efi_st_error("could not create event\n");
80                 return EFI_ST_FAILURE;
81         }
82         ret = boottime->create_event(EVT_TIMER | EVT_NOTIFY_WAIT,
83                                      TPL_CALLBACK, notify, NULL, &event_wait);
84         if (ret != EFI_SUCCESS) {
85                 efi_st_error("could not create event\n");
86                 return EFI_ST_FAILURE;
87         }
88         return EFI_ST_SUCCESS;
89 }
90
91 /*
92  * Tear down unit test.
93  *
94  * Close the events created in setup.
95  *
96  * @return:     EFI_ST_SUCCESS for success
97  */
98 static int teardown(void)
99 {
100         efi_status_t ret;
101
102         /* Set the watchdog timer to the five minute default value */
103         ret = boottime->set_watchdog_timer(300, 0, 0, NULL);
104         if (ret != EFI_SUCCESS) {
105                 efi_st_error("Setting watchdog timer failed\n");
106                 return EFI_ST_FAILURE;
107         }
108         if (event_notify) {
109                 ret = boottime->close_event(event_notify);
110                 event_notify = NULL;
111                 if (ret != EFI_SUCCESS) {
112                         efi_st_error("Could not close event\n");
113                         return EFI_ST_FAILURE;
114                 }
115         }
116         if (event_wait) {
117                 ret = boottime->close_event(event_wait);
118                 event_wait = NULL;
119                 if (ret != EFI_SUCCESS) {
120                         efi_st_error("Could not close event\n");
121                         return EFI_ST_FAILURE;
122                 }
123         }
124         return EFI_ST_SUCCESS;
125 }
126
127 /*
128  * Execute unit test.
129  *
130  * Run a 600 ms periodic timer that resets the watchdog to one second
131  * on every timer tick.
132  *
133  * Run a 1350 ms single shot timer and check that the 600ms timer has
134  * been called 2 times.
135  *
136  * @return:     EFI_ST_SUCCESS for success
137  */
138 static int execute(void)
139 {
140         size_t index;
141         efi_status_t ret;
142
143         /* Set the watchdog timeout to one second */
144         ret = boottime->set_watchdog_timer(1, 0, 0, NULL);
145         if (ret != EFI_SUCCESS) {
146                 efi_st_error("Setting watchdog timer failed\n");
147                 return EFI_ST_FAILURE;
148         }
149         /* Set 600 ms timer */
150         ret = boottime->set_timer(event_notify, EFI_TIMER_PERIODIC, 6000000);
151         if (ret != EFI_SUCCESS) {
152                 efi_st_error("Could not set timer\n");
153                 return EFI_ST_FAILURE;
154         }
155         /* Set 1350 ms timer */
156         ret = boottime->set_timer(event_wait, EFI_TIMER_RELATIVE, 13500000);
157         if (ret != EFI_SUCCESS) {
158                 efi_st_error("Could not set timer\n");
159                 return EFI_ST_FAILURE;
160         }
161
162         ret = boottime->wait_for_event(1, &event_wait, &index);
163         if (ret != EFI_SUCCESS) {
164                 efi_st_error("Could not wait for event\n");
165                 return EFI_ST_FAILURE;
166         }
167         if (notification_context.status != EFI_SUCCESS) {
168                 efi_st_error("Setting watchdog timer failed\n");
169                 return EFI_ST_FAILURE;
170         }
171         if (notification_context.timer_ticks != 2) {
172                 efi_st_error("The timer was called %u times, expected 2.\n",
173                              notification_context.timer_ticks);
174                 return EFI_ST_FAILURE;
175         }
176         return EFI_ST_SUCCESS;
177 }
178
179 EFI_UNIT_TEST(watchdog) = {
180         .name = "watchdog timer",
181         .phase = EFI_EXECUTE_BEFORE_BOOTTIME_EXIT,
182         .setup = setup,
183         .execute = execute,
184         .teardown = teardown,
185 };