blob: 9f23117449c3f67042a714b84ba111e2f0cc9413 [file] [log] [blame]
Tom Rini10e47792018-05-06 17:58:06 -04001// SPDX-License-Identifier: GPL-2.0+
Heinrich Schuchardt22703d32018-01-11 08:16:08 +01002/*
3 * efi_selftest_controllers
4 *
5 * Copyright (c) 2017 Heinrich Schuchardt <xypron.glpk@gmx.de>
6 *
Heinrich Schuchardt22703d32018-01-11 08:16:08 +01007 * This unit test checks the following protocol services:
8 * ConnectController, DisconnectController,
Heinrich Schuchardt3fba0cd2018-05-11 12:09:23 +02009 * InstallProtocol, ReinstallProtocol, UninstallProtocol,
Heinrich Schuchardt22703d32018-01-11 08:16:08 +010010 * OpenProtocol, CloseProtcol, OpenProtocolInformation
11 */
12
13#include <efi_selftest.h>
14
15#define NUMBER_OF_CHILD_CONTROLLERS 4
16
Heinrich Schuchardt3fba0cd2018-05-11 12:09:23 +020017static int interface1 = 1;
18static int interface2 = 2;
Heinrich Schuchardt22703d32018-01-11 08:16:08 +010019static struct efi_boot_services *boottime;
20const efi_guid_t guid_driver_binding_protocol =
21 EFI_DRIVER_BINDING_PROTOCOL_GUID;
22static efi_guid_t guid_controller =
23 EFI_GUID(0xe6ab1d96, 0x6bff, 0xdb42,
24 0xaa, 0x05, 0xc8, 0x1f, 0x7f, 0x45, 0x26, 0x34);
25static efi_guid_t guid_child_controller =
26 EFI_GUID(0x1d41f6f5, 0x2c41, 0xddfb,
27 0xe2, 0x9b, 0xb8, 0x0e, 0x2e, 0xe8, 0x3a, 0x85);
28static efi_handle_t handle_controller;
29static efi_handle_t handle_child_controller[NUMBER_OF_CHILD_CONTROLLERS];
30static efi_handle_t handle_driver;
Ilias Apalodimase1ad1082023-06-20 09:19:31 +030031static bool allow_removal;
Heinrich Schuchardt22703d32018-01-11 08:16:08 +010032
33/*
34 * Count child controllers
35 *
36 * @handle handle on which child controllers are installed
Heinrich Schuchardt702e7782018-09-27 20:44:40 +020037 * @protocol protocol for which the child controllers were installed
Heinrich Schuchardt22703d32018-01-11 08:16:08 +010038 * @count number of child controllers
Heinrich Schuchardt47b4c022022-01-19 18:05:50 +010039 * Return: status code
Heinrich Schuchardt22703d32018-01-11 08:16:08 +010040 */
41static efi_status_t count_child_controllers(efi_handle_t handle,
42 efi_guid_t *protocol,
43 efi_uintn_t *count)
44{
45 efi_status_t ret;
46 efi_uintn_t entry_count;
47 struct efi_open_protocol_info_entry *entry_buffer;
48
49 *count = 0;
50 ret = boottime->open_protocol_information(handle, protocol,
51 &entry_buffer, &entry_count);
52 if (ret != EFI_SUCCESS)
53 return ret;
54 if (!entry_count)
55 return EFI_SUCCESS;
56 while (entry_count) {
57 if (entry_buffer[--entry_count].attributes &
58 EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER)
59 ++*count;
60 }
61 ret = boottime->free_pool(entry_buffer);
62 if (ret != EFI_SUCCESS)
63 efi_st_error("Cannot free buffer\n");
64 return ret;
65}
66
67/*
68 * Check if the driver supports the controller.
69 *
70 * @this driver binding protocol
71 * @controller_handle handle of the controller
72 * @remaining_device_path path specifying the child controller
Heinrich Schuchardt47b4c022022-01-19 18:05:50 +010073 * Return: status code
Heinrich Schuchardt22703d32018-01-11 08:16:08 +010074 */
75static efi_status_t EFIAPI supported(
76 struct efi_driver_binding_protocol *this,
77 efi_handle_t controller_handle,
78 struct efi_device_path *remaining_device_path)
79{
80 efi_status_t ret;
81 void *interface;
82
83 ret = boottime->open_protocol(
84 controller_handle, &guid_controller,
85 &interface, handle_driver,
86 controller_handle, EFI_OPEN_PROTOCOL_BY_DRIVER);
87 switch (ret) {
88 case EFI_ACCESS_DENIED:
Heinrich Schuchardt22703d32018-01-11 08:16:08 +010089 return ret;
Ilias Apalodimase1ad1082023-06-20 09:19:31 +030090 case EFI_ALREADY_STARTED:
Heinrich Schuchardt22703d32018-01-11 08:16:08 +010091 case EFI_SUCCESS:
92 break;
93 default:
94 return EFI_UNSUPPORTED;
95 }
96 ret = boottime->close_protocol(
97 controller_handle, &guid_controller,
98 handle_driver, controller_handle);
99 if (ret != EFI_SUCCESS)
100 ret = EFI_UNSUPPORTED;
101 return ret;
102}
103
104/*
105 * Create child controllers and attach driver.
106 *
107 * @this driver binding protocol
108 * @controller_handle handle of the controller
109 * @remaining_device_path path specifying the child controller
Heinrich Schuchardt47b4c022022-01-19 18:05:50 +0100110 * Return: status code
Heinrich Schuchardt22703d32018-01-11 08:16:08 +0100111 */
112static efi_status_t EFIAPI start(
113 struct efi_driver_binding_protocol *this,
114 efi_handle_t controller_handle,
115 struct efi_device_path *remaining_device_path)
116{
117 size_t i;
118 efi_status_t ret;
119 void *interface;
120
121 /* Attach driver to controller */
122 ret = boottime->open_protocol(
123 controller_handle, &guid_controller,
124 &interface, handle_driver,
125 controller_handle, EFI_OPEN_PROTOCOL_BY_DRIVER);
126 switch (ret) {
127 case EFI_ACCESS_DENIED:
Heinrich Schuchardt22703d32018-01-11 08:16:08 +0100128 return ret;
Ilias Apalodimase1ad1082023-06-20 09:19:31 +0300129 case EFI_ALREADY_STARTED:
Heinrich Schuchardt22703d32018-01-11 08:16:08 +0100130 case EFI_SUCCESS:
131 break;
132 default:
133 return EFI_UNSUPPORTED;
134 }
135
136 /* Create child controllers */
137 for (i = 0; i < NUMBER_OF_CHILD_CONTROLLERS; ++i) {
Heinrich Schuchardtb906abf2018-09-28 22:14:16 +0200138 /* Creating a new handle for the child controller */
139 handle_child_controller[i] = 0;
Heinrich Schuchardt22703d32018-01-11 08:16:08 +0100140 ret = boottime->install_protocol_interface(
141 &handle_child_controller[i], &guid_child_controller,
142 EFI_NATIVE_INTERFACE, NULL);
143 if (ret != EFI_SUCCESS) {
144 efi_st_error("InstallProtocolInterface failed\n");
145 return EFI_ST_FAILURE;
146 }
147 ret = boottime->open_protocol(
148 controller_handle, &guid_controller,
149 &interface, handle_child_controller[i],
150 handle_child_controller[i],
151 EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER);
152 if (ret != EFI_SUCCESS) {
153 efi_st_error("OpenProtocol failed\n");
154 return EFI_ST_FAILURE;
155 }
156 }
157 return ret;
158}
159
160/*
161 * Remove a single child controller from the parent controller.
162 *
163 * @controller_handle parent controller
164 * @child_handle child controller
Heinrich Schuchardt47b4c022022-01-19 18:05:50 +0100165 * Return: status code
Heinrich Schuchardt22703d32018-01-11 08:16:08 +0100166 */
167static efi_status_t disconnect_child(efi_handle_t controller_handle,
168 efi_handle_t child_handle)
169{
170 efi_status_t ret;
171
172 ret = boottime->close_protocol(
173 controller_handle, &guid_controller,
174 child_handle, child_handle);
175 if (ret != EFI_SUCCESS) {
176 efi_st_error("Cannot close protocol\n");
177 return ret;
178 }
179 ret = boottime->uninstall_protocol_interface(
180 child_handle, &guid_child_controller, NULL);
181 if (ret != EFI_SUCCESS) {
182 efi_st_error("Cannot uninstall protocol interface\n");
183 return ret;
184 }
185 return ret;
186}
187
188/*
189 * Remove child controllers and disconnect the controller.
190 *
191 * @this driver binding protocol
192 * @controller_handle handle of the controller
193 * @number_of_children number of child controllers to remove
194 * @child_handle_buffer handles of the child controllers to remove
Heinrich Schuchardt47b4c022022-01-19 18:05:50 +0100195 * Return: status code
Heinrich Schuchardt22703d32018-01-11 08:16:08 +0100196 */
197static efi_status_t EFIAPI stop(
198 struct efi_driver_binding_protocol *this,
199 efi_handle_t controller_handle,
200 size_t number_of_children,
201 efi_handle_t *child_handle_buffer)
202{
203 efi_status_t ret;
204 efi_uintn_t count;
205 struct efi_open_protocol_info_entry *entry_buffer;
206
207 /* Destroy provided child controllers */
208 if (number_of_children) {
209 efi_uintn_t i;
210
211 for (i = 0; i < number_of_children; ++i) {
212 ret = disconnect_child(controller_handle,
213 child_handle_buffer[i]);
214 if (ret != EFI_SUCCESS)
215 return ret;
216 }
217 return EFI_SUCCESS;
218 }
219
220 /* Destroy all children */
221 ret = boottime->open_protocol_information(
222 controller_handle, &guid_controller,
223 &entry_buffer, &count);
224 if (ret != EFI_SUCCESS) {
225 efi_st_error("OpenProtocolInformation failed\n");
226 return ret;
227 }
228 while (count) {
229 if (entry_buffer[--count].attributes &
230 EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER) {
231 ret = disconnect_child(
232 controller_handle,
233 entry_buffer[count].agent_handle);
234 if (ret != EFI_SUCCESS)
235 return ret;
236 }
237 }
238 ret = boottime->free_pool(entry_buffer);
239 if (ret != EFI_SUCCESS)
240 efi_st_error("Cannot free buffer\n");
241
Ilias Apalodimase1ad1082023-06-20 09:19:31 +0300242 if (!allow_removal)
243 return EFI_DEVICE_ERROR;
244
Heinrich Schuchardt22703d32018-01-11 08:16:08 +0100245 /* Detach driver from controller */
246 ret = boottime->close_protocol(
247 controller_handle, &guid_controller,
248 handle_driver, controller_handle);
249 if (ret != EFI_SUCCESS) {
250 efi_st_error("Cannot close protocol\n");
251 return ret;
252 }
253 return EFI_SUCCESS;
254}
255
256/* Driver binding protocol interface */
257static struct efi_driver_binding_protocol binding_interface = {
258 supported,
259 start,
260 stop,
261 0xffffffff,
262 NULL,
263 NULL,
264 };
265
266/*
267 * Setup unit test.
268 *
269 * @handle handle of the loaded image
270 * @systable system table
271 */
272static int setup(const efi_handle_t img_handle,
273 const struct efi_system_table *systable)
274{
275 efi_status_t ret;
276
277 boottime = systable->boottime;
Ilias Apalodimasa467c272023-06-14 09:55:48 +0300278 handle_controller = NULL;
279 handle_driver = NULL;
Heinrich Schuchardt22703d32018-01-11 08:16:08 +0100280
281 /* Create controller handle */
282 ret = boottime->install_protocol_interface(
283 &handle_controller, &guid_controller,
Heinrich Schuchardt3fba0cd2018-05-11 12:09:23 +0200284 EFI_NATIVE_INTERFACE, &interface1);
Heinrich Schuchardt22703d32018-01-11 08:16:08 +0100285 if (ret != EFI_SUCCESS) {
286 efi_st_error("InstallProtocolInterface failed\n");
287 return EFI_ST_FAILURE;
288 }
289 /* Create driver handle */
290 ret = boottime->install_protocol_interface(
291 &handle_driver, &guid_driver_binding_protocol,
292 EFI_NATIVE_INTERFACE, &binding_interface);
293 if (ret != EFI_SUCCESS) {
294 efi_st_error("InstallProtocolInterface failed\n");
295 return EFI_ST_FAILURE;
296 }
297
298 return EFI_ST_SUCCESS;
299}
300
301/*
302 * Execute unit test.
303 *
304 * The number of child controllers is checked after each of the following
305 * actions:
306 *
307 * Connect a controller to a driver.
308 * Disconnect and destroy a child controller.
309 * Disconnect and destroy the remaining child controllers.
310 *
311 * Connect a controller to a driver.
Heinrich Schuchardt3fba0cd2018-05-11 12:09:23 +0200312 * Reinstall the driver protocol on the controller.
Heinrich Schuchardt22703d32018-01-11 08:16:08 +0100313 * Uninstall the driver protocol from the controller.
314 */
315static int execute(void)
316{
317 efi_status_t ret;
318 efi_uintn_t count;
319
320 /* Connect controller to driver */
321 ret = boottime->connect_controller(handle_controller, NULL, NULL, 1);
322 if (ret != EFI_SUCCESS) {
323 efi_st_error("Failed to connect controller\n");
324 return EFI_ST_FAILURE;
325 }
326 /* Check number of child controllers */
327 ret = count_child_controllers(handle_controller, &guid_controller,
328 &count);
329 if (ret != EFI_SUCCESS || count != NUMBER_OF_CHILD_CONTROLLERS) {
330 efi_st_error("Number of children %u != %u\n",
331 (unsigned int)count, NUMBER_OF_CHILD_CONTROLLERS);
332 }
333 /* Destroy second child controller */
334 ret = boottime->disconnect_controller(handle_controller,
335 handle_driver,
336 handle_child_controller[1]);
337 if (ret != EFI_SUCCESS) {
338 efi_st_error("Failed to disconnect child controller\n");
339 return EFI_ST_FAILURE;
340 }
341 /* Check number of child controllers */
342 ret = count_child_controllers(handle_controller, &guid_controller,
343 &count);
344 if (ret != EFI_SUCCESS || count != NUMBER_OF_CHILD_CONTROLLERS - 1) {
345 efi_st_error("Destroying single child controller failed\n");
346 return EFI_ST_FAILURE;
347 }
348 /* Destroy remaining child controllers and disconnect controller */
Ilias Apalodimase1ad1082023-06-20 09:19:31 +0300349 allow_removal = true;
Heinrich Schuchardt22703d32018-01-11 08:16:08 +0100350 ret = boottime->disconnect_controller(handle_controller, NULL, NULL);
351 if (ret != EFI_SUCCESS) {
352 efi_st_error("Failed to disconnect controller\n");
353 return EFI_ST_FAILURE;
354 }
355 /* Check number of child controllers */
356 ret = count_child_controllers(handle_controller, &guid_controller,
357 &count);
358 if (ret != EFI_SUCCESS || count) {
359 efi_st_error("Destroying child controllers failed\n");
360 return EFI_ST_FAILURE;
361 }
362
363 /* Connect controller to driver */
364 ret = boottime->connect_controller(handle_controller, NULL, NULL, 1);
365 if (ret != EFI_SUCCESS) {
366 efi_st_error("Failed to connect controller\n");
367 return EFI_ST_FAILURE;
368 }
369 /* Check number of child controllers */
370 ret = count_child_controllers(handle_controller, &guid_controller,
371 &count);
372 if (ret != EFI_SUCCESS || count != NUMBER_OF_CHILD_CONTROLLERS) {
373 efi_st_error("Number of children %u != %u\n",
374 (unsigned int)count, NUMBER_OF_CHILD_CONTROLLERS);
375 }
Heinrich Schuchardt3fba0cd2018-05-11 12:09:23 +0200376 /* Try to uninstall controller protocol using the wrong interface */
377 ret = boottime->uninstall_protocol_interface(handle_controller,
378 &guid_controller,
379 &interface2);
380 if (ret == EFI_SUCCESS) {
381 efi_st_error(
382 "Interface not checked when uninstalling protocol\n");
383 return EFI_ST_FAILURE;
384 }
385 /* Reinstall controller protocol */
386 ret = boottime->reinstall_protocol_interface(handle_controller,
387 &guid_controller,
388 &interface1,
389 &interface2);
390 if (ret != EFI_SUCCESS) {
391 efi_st_error("Failed to reinstall protocols\n");
392 return EFI_ST_FAILURE;
393 }
394 /* Check number of child controllers */
395 ret = count_child_controllers(handle_controller, &guid_controller,
396 &count);
397 if (ret != EFI_SUCCESS || count != NUMBER_OF_CHILD_CONTROLLERS) {
398 efi_st_error("Number of children %u != %u\n",
399 (unsigned int)count, NUMBER_OF_CHILD_CONTROLLERS);
400 }
Ilias Apalodimase1ad1082023-06-20 09:19:31 +0300401
402 allow_removal = false;
403 /* Try to uninstall controller protocol using the wrong interface */
404 ret = boottime->uninstall_protocol_interface(handle_controller,
405 &guid_controller,
406 &interface1);
407 if (ret != EFI_NOT_FOUND) {
408 efi_st_error("Interface not checked when uninstalling protocol\n");
409 return EFI_ST_FAILURE;
410 }
411
412 /*
413 * Uninstall a protocol while Disconnect controller won't
414 * allow it.
415 */
416 ret = boottime->uninstall_protocol_interface(handle_controller,
417 &guid_controller,
418 &interface2);
419 if (ret != EFI_ACCESS_DENIED) {
420 efi_st_error("Uninstall protocol interface failed\n");
421 return EFI_ST_FAILURE;
422 }
423 /*
424 * Check number of child controllers and make sure children have
425 * been reconnected
426 */
427 ret = count_child_controllers(handle_controller, &guid_controller,
428 &count);
429 if (ret != EFI_SUCCESS || count != NUMBER_OF_CHILD_CONTROLLERS) {
430 efi_st_error("Number of children %u != %u\n",
431 (unsigned int)count, NUMBER_OF_CHILD_CONTROLLERS);
432 }
433
434 allow_removal = true;
Heinrich Schuchardt22703d32018-01-11 08:16:08 +0100435 ret = boottime->uninstall_protocol_interface(handle_controller,
Heinrich Schuchardt3fba0cd2018-05-11 12:09:23 +0200436 &guid_controller,
437 &interface2);
Heinrich Schuchardt22703d32018-01-11 08:16:08 +0100438 if (ret != EFI_SUCCESS) {
439 efi_st_error("Failed to uninstall protocols\n");
440 return EFI_ST_FAILURE;
441 }
442 /* Check number of child controllers */
443 ret = count_child_controllers(handle_controller, &guid_controller,
444 &count);
Ilias Apalodimasa467c272023-06-14 09:55:48 +0300445 if (ret == EFI_SUCCESS || count) {
Heinrich Schuchardt22703d32018-01-11 08:16:08 +0100446 efi_st_error("Uninstall failed\n");
Ilias Apalodimasa467c272023-06-14 09:55:48 +0300447 return EFI_ST_FAILURE;
448 }
449
Heinrich Schuchardt22703d32018-01-11 08:16:08 +0100450 return EFI_ST_SUCCESS;
451}
452
Ilias Apalodimasa467c272023-06-14 09:55:48 +0300453 /*
454 * Tear down unit test.
455 *
456 */
457static int teardown(void)
458{
459 efi_status_t ret;
460 /* Uninstall binding protocol */
461 ret = boottime->uninstall_protocol_interface(handle_driver,
462 &guid_driver_binding_protocol,
463 &binding_interface);
464 if (ret != EFI_SUCCESS)
465 efi_st_error("Failed to uninstall protocols\n");
466
467 return ret;
468}
469
Heinrich Schuchardt22703d32018-01-11 08:16:08 +0100470EFI_UNIT_TEST(controllers) = {
471 .name = "controllers",
472 .phase = EFI_EXECUTE_BEFORE_BOOTTIME_EXIT,
473 .setup = setup,
474 .execute = execute,
Ilias Apalodimasa467c272023-06-14 09:55:48 +0300475 .teardown = teardown,
Heinrich Schuchardt22703d32018-01-11 08:16:08 +0100476};