]>
Commit | Line | Data |
---|---|---|
83d290c5 | 1 | // SPDX-License-Identifier: GPL-2.0+ |
30a0045a HS |
2 | /* |
3 | * efi_selftest_watchdog | |
4 | * | |
5 | * Copyright (c) 2017 Heinrich Schuchardt <[email protected]> | |
6 | * | |
7bbae6f2 HS |
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. | |
30a0045a | 10 | * |
7bbae6f2 HS |
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 | |
30a0045a HS |
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; | |
7bbae6f2 | 35 | static bool watchdog_reset; |
30a0045a HS |
36 | |
37 | /* | |
d8b2216c | 38 | * Notification function, increments the notification count if parameter |
30a0045a HS |
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 | ||
7bbae6f2 HS |
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 | ||
30a0045a HS |
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 | } | |
7bbae6f2 HS |
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 | } | |
30a0045a HS |
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 | ||
7bbae6f2 | 215 | EFI_UNIT_TEST(watchdog1) = { |
30a0045a HS |
216 | .name = "watchdog timer", |
217 | .phase = EFI_EXECUTE_BEFORE_BOOTTIME_EXIT, | |
7bbae6f2 HS |
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, | |
30a0045a HS |
227 | .execute = execute, |
228 | .teardown = teardown, | |
7bbae6f2 | 229 | .on_request = true, |
30a0045a | 230 | }; |