]>
Commit | Line | Data |
---|---|---|
30a0045a HS |
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 | * | |
7bbae6f2 HS |
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. | |
30a0045a | 11 | * |
7bbae6f2 HS |
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 | |
30a0045a HS |
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; | |
7bbae6f2 | 36 | static bool watchdog_reset; |
30a0045a HS |
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 | ||
7bbae6f2 HS |
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 | ||
30a0045a HS |
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 | } | |
7bbae6f2 HS |
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 | } | |
30a0045a HS |
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 | ||
7bbae6f2 | 216 | EFI_UNIT_TEST(watchdog1) = { |
30a0045a HS |
217 | .name = "watchdog timer", |
218 | .phase = EFI_EXECUTE_BEFORE_BOOTTIME_EXIT, | |
7bbae6f2 HS |
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, | |
30a0045a HS |
228 | .execute = execute, |
229 | .teardown = teardown, | |
7bbae6f2 | 230 | .on_request = true, |
30a0045a | 231 | }; |