diff options
Diffstat (limited to 'lib/efi_selftest/efi_selftest_controllers.c')
-rw-r--r-- | lib/efi_selftest/efi_selftest_controllers.c | 385 |
1 files changed, 385 insertions, 0 deletions
diff --git a/lib/efi_selftest/efi_selftest_controllers.c b/lib/efi_selftest/efi_selftest_controllers.c new file mode 100644 index 0000000000..1a22aba6f4 --- /dev/null +++ b/lib/efi_selftest/efi_selftest_controllers.c @@ -0,0 +1,385 @@ +/* + * efi_selftest_controllers + * + * Copyright (c) 2017 Heinrich Schuchardt <xypron.glpk@gmx.de> + * + * SPDX-License-Identifier: GPL-2.0+ + * + * This unit test checks the following protocol services: + * ConnectController, DisconnectController, + * InstallProtocol, UninstallProtocol, + * OpenProtocol, CloseProtcol, OpenProtocolInformation + */ + +#include <efi_selftest.h> + +#define NUMBER_OF_CHILD_CONTROLLERS 4 + +static struct efi_boot_services *boottime; +const efi_guid_t guid_driver_binding_protocol = + EFI_DRIVER_BINDING_PROTOCOL_GUID; +static efi_guid_t guid_controller = + EFI_GUID(0xe6ab1d96, 0x6bff, 0xdb42, + 0xaa, 0x05, 0xc8, 0x1f, 0x7f, 0x45, 0x26, 0x34); +static efi_guid_t guid_child_controller = + EFI_GUID(0x1d41f6f5, 0x2c41, 0xddfb, + 0xe2, 0x9b, 0xb8, 0x0e, 0x2e, 0xe8, 0x3a, 0x85); +static efi_handle_t handle_controller; +static efi_handle_t handle_child_controller[NUMBER_OF_CHILD_CONTROLLERS]; +static efi_handle_t handle_driver; + +/* + * Count child controllers + * + * @handle handle on which child controllers are installed + * @protocol protocol for which the child controlles where installed + * @count number of child controllers + * @return status code + */ +static efi_status_t count_child_controllers(efi_handle_t handle, + efi_guid_t *protocol, + efi_uintn_t *count) +{ + efi_status_t ret; + efi_uintn_t entry_count; + struct efi_open_protocol_info_entry *entry_buffer; + + *count = 0; + ret = boottime->open_protocol_information(handle, protocol, + &entry_buffer, &entry_count); + if (ret != EFI_SUCCESS) + return ret; + if (!entry_count) + return EFI_SUCCESS; + while (entry_count) { + if (entry_buffer[--entry_count].attributes & + EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER) + ++*count; + } + ret = boottime->free_pool(entry_buffer); + if (ret != EFI_SUCCESS) + efi_st_error("Cannot free buffer\n"); + return ret; +} + +/* + * Check if the driver supports the controller. + * + * @this driver binding protocol + * @controller_handle handle of the controller + * @remaining_device_path path specifying the child controller + * @return status code + */ +static efi_status_t EFIAPI supported( + struct efi_driver_binding_protocol *this, + efi_handle_t controller_handle, + struct efi_device_path *remaining_device_path) +{ + efi_status_t ret; + void *interface; + + ret = boottime->open_protocol( + controller_handle, &guid_controller, + &interface, handle_driver, + controller_handle, EFI_OPEN_PROTOCOL_BY_DRIVER); + switch (ret) { + case EFI_ACCESS_DENIED: + case EFI_ALREADY_STARTED: + return ret; + case EFI_SUCCESS: + break; + default: + return EFI_UNSUPPORTED; + } + ret = boottime->close_protocol( + controller_handle, &guid_controller, + handle_driver, controller_handle); + if (ret != EFI_SUCCESS) + ret = EFI_UNSUPPORTED; + return ret; +} + +/* + * Create child controllers and attach driver. + * + * @this driver binding protocol + * @controller_handle handle of the controller + * @remaining_device_path path specifying the child controller + * @return status code + */ +static efi_status_t EFIAPI start( + struct efi_driver_binding_protocol *this, + efi_handle_t controller_handle, + struct efi_device_path *remaining_device_path) +{ + size_t i; + efi_status_t ret; + void *interface; + + /* Attach driver to controller */ + ret = boottime->open_protocol( + controller_handle, &guid_controller, + &interface, handle_driver, + controller_handle, EFI_OPEN_PROTOCOL_BY_DRIVER); + switch (ret) { + case EFI_ACCESS_DENIED: + case EFI_ALREADY_STARTED: + return ret; + case EFI_SUCCESS: + break; + default: + return EFI_UNSUPPORTED; + } + + /* Create child controllers */ + for (i = 0; i < NUMBER_OF_CHILD_CONTROLLERS; ++i) { + ret = boottime->install_protocol_interface( + &handle_child_controller[i], &guid_child_controller, + EFI_NATIVE_INTERFACE, NULL); + if (ret != EFI_SUCCESS) { + efi_st_error("InstallProtocolInterface failed\n"); + return EFI_ST_FAILURE; + } + ret = boottime->open_protocol( + controller_handle, &guid_controller, + &interface, handle_child_controller[i], + handle_child_controller[i], + EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER); + if (ret != EFI_SUCCESS) { + efi_st_error("OpenProtocol failed\n"); + return EFI_ST_FAILURE; + } + } + return ret; +} + +/* + * Remove a single child controller from the parent controller. + * + * @controller_handle parent controller + * @child_handle child controller + * @return status code + */ +static efi_status_t disconnect_child(efi_handle_t controller_handle, + efi_handle_t child_handle) +{ + efi_status_t ret; + + ret = boottime->close_protocol( + controller_handle, &guid_controller, + child_handle, child_handle); + if (ret != EFI_SUCCESS) { + efi_st_error("Cannot close protocol\n"); + return ret; + } + ret = boottime->uninstall_protocol_interface( + child_handle, &guid_child_controller, NULL); + if (ret != EFI_SUCCESS) { + efi_st_error("Cannot uninstall protocol interface\n"); + return ret; + } + return ret; +} + +/* + * Remove child controllers and disconnect the controller. + * + * @this driver binding protocol + * @controller_handle handle of the controller + * @number_of_children number of child controllers to remove + * @child_handle_buffer handles of the child controllers to remove + * @return status code + */ +static efi_status_t EFIAPI stop( + struct efi_driver_binding_protocol *this, + efi_handle_t controller_handle, + size_t number_of_children, + efi_handle_t *child_handle_buffer) +{ + efi_status_t ret; + efi_uintn_t count; + struct efi_open_protocol_info_entry *entry_buffer; + + /* Destroy provided child controllers */ + if (number_of_children) { + efi_uintn_t i; + + for (i = 0; i < number_of_children; ++i) { + ret = disconnect_child(controller_handle, + child_handle_buffer[i]); + if (ret != EFI_SUCCESS) + return ret; + } + return EFI_SUCCESS; + } + + /* Destroy all children */ + ret = boottime->open_protocol_information( + controller_handle, &guid_controller, + &entry_buffer, &count); + if (ret != EFI_SUCCESS) { + efi_st_error("OpenProtocolInformation failed\n"); + return ret; + } + while (count) { + if (entry_buffer[--count].attributes & + EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER) { + ret = disconnect_child( + controller_handle, + entry_buffer[count].agent_handle); + if (ret != EFI_SUCCESS) + return ret; + } + } + ret = boottime->free_pool(entry_buffer); + if (ret != EFI_SUCCESS) + efi_st_error("Cannot free buffer\n"); + + /* Detach driver from controller */ + ret = boottime->close_protocol( + controller_handle, &guid_controller, + handle_driver, controller_handle); + if (ret != EFI_SUCCESS) { + efi_st_error("Cannot close protocol\n"); + return ret; + } + return EFI_SUCCESS; +} + +/* Driver binding protocol interface */ +static struct efi_driver_binding_protocol binding_interface = { + supported, + start, + stop, + 0xffffffff, + NULL, + NULL, + }; + +/* + * Setup unit test. + * + * @handle handle of the loaded image + * @systable system table + */ +static int setup(const efi_handle_t img_handle, + const struct efi_system_table *systable) +{ + efi_status_t ret; + + boottime = systable->boottime; + + /* Create controller handle */ + ret = boottime->install_protocol_interface( + &handle_controller, &guid_controller, + EFI_NATIVE_INTERFACE, NULL); + if (ret != EFI_SUCCESS) { + efi_st_error("InstallProtocolInterface failed\n"); + return EFI_ST_FAILURE; + } + /* Create driver handle */ + ret = boottime->install_protocol_interface( + &handle_driver, &guid_driver_binding_protocol, + EFI_NATIVE_INTERFACE, &binding_interface); + if (ret != EFI_SUCCESS) { + efi_st_error("InstallProtocolInterface failed\n"); + return EFI_ST_FAILURE; + } + + return EFI_ST_SUCCESS; +} + +/* + * Execute unit test. + * + * The number of child controllers is checked after each of the following + * actions: + * + * Connect a controller to a driver. + * Disconnect and destroy a child controller. + * Disconnect and destroy the remaining child controllers. + * + * Connect a controller to a driver. + * Uninstall the driver protocol from the controller. + */ +static int execute(void) +{ + efi_status_t ret; + efi_uintn_t count; + + /* Connect controller to driver */ + ret = boottime->connect_controller(handle_controller, NULL, NULL, 1); + if (ret != EFI_SUCCESS) { + efi_st_error("Failed to connect controller\n"); + return EFI_ST_FAILURE; + } + /* Check number of child controllers */ + ret = count_child_controllers(handle_controller, &guid_controller, + &count); + if (ret != EFI_SUCCESS || count != NUMBER_OF_CHILD_CONTROLLERS) { + efi_st_error("Number of children %u != %u\n", + (unsigned int)count, NUMBER_OF_CHILD_CONTROLLERS); + } + /* Destroy second child controller */ + ret = boottime->disconnect_controller(handle_controller, + handle_driver, + handle_child_controller[1]); + if (ret != EFI_SUCCESS) { + efi_st_error("Failed to disconnect child controller\n"); + return EFI_ST_FAILURE; + } + /* Check number of child controllers */ + ret = count_child_controllers(handle_controller, &guid_controller, + &count); + if (ret != EFI_SUCCESS || count != NUMBER_OF_CHILD_CONTROLLERS - 1) { + efi_st_error("Destroying single child controller failed\n"); + return EFI_ST_FAILURE; + } + /* Destroy remaining child controllers and disconnect controller */ + ret = boottime->disconnect_controller(handle_controller, NULL, NULL); + if (ret != EFI_SUCCESS) { + efi_st_error("Failed to disconnect controller\n"); + return EFI_ST_FAILURE; + } + /* Check number of child controllers */ + ret = count_child_controllers(handle_controller, &guid_controller, + &count); + if (ret != EFI_SUCCESS || count) { + efi_st_error("Destroying child controllers failed\n"); + return EFI_ST_FAILURE; + } + + /* Connect controller to driver */ + ret = boottime->connect_controller(handle_controller, NULL, NULL, 1); + if (ret != EFI_SUCCESS) { + efi_st_error("Failed to connect controller\n"); + return EFI_ST_FAILURE; + } + /* Check number of child controllers */ + ret = count_child_controllers(handle_controller, &guid_controller, + &count); + if (ret != EFI_SUCCESS || count != NUMBER_OF_CHILD_CONTROLLERS) { + efi_st_error("Number of children %u != %u\n", + (unsigned int)count, NUMBER_OF_CHILD_CONTROLLERS); + } + /* Uninstall controller protocol */ + ret = boottime->uninstall_protocol_interface(handle_controller, + &guid_controller, NULL); + if (ret != EFI_SUCCESS) { + efi_st_error("Failed to uninstall protocols\n"); + return EFI_ST_FAILURE; + } + /* Check number of child controllers */ + ret = count_child_controllers(handle_controller, &guid_controller, + &count); + if (ret == EFI_SUCCESS) + efi_st_error("Uninstall failed\n"); + return EFI_ST_SUCCESS; +} + +EFI_UNIT_TEST(controllers) = { + .name = "controllers", + .phase = EFI_EXECUTE_BEFORE_BOOTTIME_EXIT, + .setup = setup, + .execute = execute, +}; |