| // SPDX-License-Identifier: GPL-2.0+ |
| /* |
| * efi_selftest_manageprotocols |
| * |
| * Copyright (c) 2017 Heinrich Schuchardt <xypron.glpk@gmx.de> |
| * |
| * This unit test checks the following protocol services: |
| * InstallProtocolInterface, UninstallProtocolInterface, |
| * InstallMultipleProtocolsInterfaces, UninstallMultipleProtocolsInterfaces, |
| * HandleProtocol, ProtocolsPerHandle, |
| * LocateHandle, LocateHandleBuffer. |
| */ |
| |
| #include <efi_selftest.h> |
| |
| /* |
| * The test currently does not actually call the interface function. |
| * So this is just a dummy structure. |
| */ |
| struct interface { |
| void (EFIAPI * inc)(void); |
| }; |
| |
| static struct efi_boot_services *boottime; |
| static efi_guid_t guid1 = |
| EFI_GUID(0x2e7ca819, 0x21d3, 0x0a3a, |
| 0xf7, 0x91, 0x82, 0x1f, 0x7a, 0x83, 0x67, 0xaf); |
| static efi_guid_t guid2 = |
| EFI_GUID(0xf909f2bb, 0x90a8, 0x0d77, |
| 0x94, 0x0c, 0x3e, 0xa8, 0xea, 0x38, 0xd6, 0x6f); |
| static efi_guid_t guid3 = |
| EFI_GUID(0x06d641a3, 0xf4e7, 0xe0c9, |
| 0xe7, 0x8d, 0x41, 0x2d, 0x72, 0xa6, 0xb1, 0x24); |
| static efi_handle_t handle1; |
| static efi_handle_t handle2; |
| static struct interface interface1; |
| static struct interface interface2; |
| static struct interface interface3; |
| static struct interface interface4; |
| |
| /* |
| * Find a handle in an array. |
| * |
| * @handle: handle to find |
| * @count: number of entries in the array |
| * @buffer: array to search |
| */ |
| efi_status_t find_in_buffer(efi_handle_t handle, size_t count, |
| efi_handle_t *buffer) |
| { |
| size_t i; |
| |
| for (i = 0; i < count; ++i) { |
| if (buffer[i] == handle) |
| return EFI_SUCCESS; |
| } |
| return EFI_NOT_FOUND; |
| } |
| |
| /* |
| * Setup unit test. |
| * |
| * Create two handles and install two out of three protocol interfaces on each |
| * of them: |
| * |
| * handle1 |
| * guid1 interface1 |
| * guid3 interface3 |
| * handle2 |
| * guid1 interface4 |
| * guid2 interface2 |
| * |
| * @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; |
| efi_handle_t handle; |
| |
| boottime = systable->boottime; |
| |
| ret = boottime->install_protocol_interface(&handle1, &guid3, |
| EFI_NATIVE_INTERFACE, |
| &interface3); |
| if (ret != EFI_SUCCESS) { |
| efi_st_error("InstallProtocolInterface failed\n"); |
| return EFI_ST_FAILURE; |
| } |
| if (!handle1) { |
| efi_st_error("InstallProtocolInterface failed to create handle\n"); |
| return EFI_ST_FAILURE; |
| } |
| handle = handle1; |
| ret = boottime->install_protocol_interface(&handle1, &guid1, |
| EFI_NATIVE_INTERFACE, |
| &interface1); |
| if (ret != EFI_SUCCESS) { |
| efi_st_error("InstallProtocolInterface failed\n"); |
| return EFI_ST_FAILURE; |
| } |
| if (handle != handle1) { |
| efi_st_error("InstallProtocolInterface failed to use handle\n"); |
| return EFI_ST_FAILURE; |
| } |
| ret = boottime->install_multiple_protocol_interfaces(&handle2, |
| &guid1, &interface4, &guid2, &interface2, NULL); |
| if (ret != EFI_SUCCESS) { |
| efi_st_error("InstallMultipleProtocolInterfaces failed\n"); |
| return EFI_ST_FAILURE; |
| } |
| if (!handle2 || handle1 == handle2) { |
| efi_st_error("InstallMultipleProtocolInterfaces failed to create handle\n"); |
| return EFI_ST_FAILURE; |
| } |
| |
| return EFI_ST_SUCCESS; |
| } |
| |
| /* |
| * Tear down unit test. |
| * |
| */ |
| static int teardown(void) |
| { |
| return EFI_ST_SUCCESS; |
| } |
| |
| /* |
| * Execute unit test. |
| * |
| */ |
| static int execute(void) |
| { |
| struct interface *interface; |
| efi_status_t ret; |
| efi_handle_t *buffer; |
| size_t buffer_size; |
| efi_uintn_t count = 0; |
| efi_guid_t **prot_buffer; |
| efi_uintn_t prot_count; |
| |
| /* |
| * Test HandleProtocol |
| */ |
| ret = boottime->handle_protocol(handle1, &guid3, (void **)&interface); |
| if (ret != EFI_SUCCESS) { |
| efi_st_error("HandleProtocol failed to retrieve interface\n"); |
| return EFI_ST_FAILURE; |
| } |
| if (interface != &interface3) { |
| efi_st_error("HandleProtocol returned wrong interface\n"); |
| return EFI_ST_FAILURE; |
| } |
| ret = boottime->handle_protocol(handle1, &guid2, (void **)&interface); |
| if (ret == EFI_SUCCESS) { |
| efi_st_error("HandleProtocol returned not installed interface\n"); |
| return EFI_ST_FAILURE; |
| } |
| |
| /* |
| * Test LocateHandleBuffer with AllHandles |
| */ |
| ret = boottime->locate_handle_buffer(ALL_HANDLES, NULL, NULL, |
| &count, &buffer); |
| if (ret != EFI_SUCCESS) { |
| efi_st_error("LocateHandleBuffer with AllHandles failed\n"); |
| return EFI_ST_FAILURE; |
| } |
| buffer_size = count; |
| ret = find_in_buffer(handle1, count, buffer); |
| if (ret != EFI_SUCCESS) { |
| efi_st_error("LocateHandleBuffer failed to locate new handle\n"); |
| return EFI_ST_FAILURE; |
| } |
| ret = find_in_buffer(handle2, count, buffer); |
| if (ret != EFI_SUCCESS) { |
| efi_st_error("LocateHandleBuffer failed to locate new handle\n"); |
| return EFI_ST_FAILURE; |
| } |
| /* Release buffer */ |
| ret = boottime->free_pool(buffer); |
| if (ret != EFI_SUCCESS) { |
| efi_st_error("FreePool failed\n"); |
| return EFI_ST_FAILURE; |
| } |
| |
| /* |
| * Test error handling in UninstallMultipleProtocols |
| * |
| * These are the installed protocol interfaces on handle 2: |
| * |
| * guid1 interface4 |
| * guid2 interface2 |
| * |
| * Try to uninstall more protocols than there are installed. This |
| * should return an error EFI_INVALID_PARAMETER. All deleted protocols |
| * should be reinstalled. |
| */ |
| ret = boottime->uninstall_multiple_protocol_interfaces( |
| handle2, |
| &guid1, &interface4, |
| &guid2, &interface2, |
| &guid3, &interface3, |
| NULL); |
| if (ret != EFI_INVALID_PARAMETER) { |
| printf("%lx", ret); |
| efi_st_error("UninstallMultipleProtocolInterfaces did not catch error\n"); |
| return EFI_ST_FAILURE; |
| } |
| |
| /* |
| * Test LocateHandleBuffer with ByProtocol |
| * |
| * These are the handles with a guid1 protocol interface installed: |
| * |
| * handle1, handle2 |
| */ |
| count = buffer_size; |
| ret = boottime->locate_handle_buffer(BY_PROTOCOL, &guid1, NULL, |
| &count, &buffer); |
| if (ret != EFI_SUCCESS) { |
| efi_st_error("LocateHandleBuffer failed to locate new handles\n"); |
| return EFI_ST_FAILURE; |
| } |
| if (count != 2) { |
| efi_st_error("UninstallMultipleProtocolInterfaces deleted handle\n"); |
| return EFI_ST_FAILURE; |
| } |
| ret = find_in_buffer(handle1, count, buffer); |
| if (ret != EFI_SUCCESS) { |
| efi_st_error("LocateHandleBuffer failed to locate new handle\n"); |
| return EFI_ST_FAILURE; |
| } |
| ret = find_in_buffer(handle2, count, buffer); |
| if (ret != EFI_SUCCESS) { |
| efi_st_error("LocateHandleBuffer failed to locate new handle\n"); |
| return EFI_ST_FAILURE; |
| } |
| /* Clear the buffer, we are reusing it it the next step. */ |
| boottime->set_mem(buffer, sizeof(efi_handle_t) * buffer_size, 0); |
| |
| /* |
| * Test LocateHandle with ByProtocol |
| */ |
| count = buffer_size * sizeof(efi_handle_t); |
| ret = boottime->locate_handle(BY_PROTOCOL, &guid1, NULL, |
| &count, buffer); |
| if (ret != EFI_SUCCESS) { |
| efi_st_error("LocateHandle with ByProtocol failed\n"); |
| return EFI_ST_FAILURE; |
| } |
| if (count / sizeof(efi_handle_t) != 2) { |
| efi_st_error("LocateHandle failed to locate new handles\n"); |
| return EFI_ST_FAILURE; |
| } |
| buffer_size = count; |
| ret = find_in_buffer(handle1, count, buffer); |
| if (ret != EFI_SUCCESS) { |
| efi_st_error("LocateHandle failed to locate new handles\n"); |
| return EFI_ST_FAILURE; |
| } |
| ret = find_in_buffer(handle2, count, buffer); |
| if (ret != EFI_SUCCESS) { |
| efi_st_error("LocateHandle failed to locate new handles\n"); |
| return EFI_ST_FAILURE; |
| } |
| /* Release buffer */ |
| ret = boottime->free_pool(buffer); |
| if (ret != EFI_SUCCESS) { |
| efi_st_error("FreePool failed\n"); |
| return EFI_ST_FAILURE; |
| } |
| |
| /* |
| * Test LocateProtocol |
| */ |
| ret = boottime->locate_protocol(&guid1, NULL, (void **)&interface); |
| if (ret != EFI_SUCCESS) { |
| efi_st_error("LocateProtocol failed\n"); |
| return EFI_ST_FAILURE; |
| } |
| if (interface != &interface1 && interface != &interface4) { |
| efi_st_error("LocateProtocol failed to locate protocol\n"); |
| return EFI_ST_FAILURE; |
| } |
| |
| /* |
| * Test UninstallMultipleProtocols |
| */ |
| ret = boottime->uninstall_multiple_protocol_interfaces( |
| handle2, |
| &guid1, &interface4, |
| &guid2, &interface2, |
| NULL); |
| if (ret != EFI_SUCCESS) { |
| efi_st_error("UninstallMultipleProtocolInterfaces failed\n"); |
| return EFI_ST_FAILURE; |
| } |
| /* |
| * Check that the protocols are really uninstalled. |
| */ |
| count = buffer_size; |
| ret = boottime->locate_handle_buffer(BY_PROTOCOL, &guid1, NULL, |
| &count, &buffer); |
| if (ret != EFI_SUCCESS) { |
| efi_st_error("LocateHandleBuffer failed\n"); |
| return EFI_ST_FAILURE; |
| } |
| if (count != 1) { |
| efi_st_error("UninstallMultipleProtocolInterfaces failed to uninstall protocols\n"); |
| return EFI_ST_FAILURE; |
| } |
| ret = find_in_buffer(handle1, count, buffer); |
| if (ret != EFI_SUCCESS) { |
| efi_st_error("Failed to locate new handle\n"); |
| return EFI_ST_FAILURE; |
| } |
| boottime->set_mem(buffer, sizeof(efi_handle_t) * buffer_size, 0); |
| |
| /* |
| * Test ProtocolsPerHandle |
| */ |
| ret = boottime->protocols_per_handle(handle1, |
| &prot_buffer, &prot_count); |
| if (ret != EFI_SUCCESS) { |
| efi_st_error("Failed to get protocols per handle\n"); |
| return EFI_ST_FAILURE; |
| } |
| if (prot_count != 2) { |
| efi_st_error("Failed to get protocols per handle\n"); |
| return EFI_ST_FAILURE; |
| } |
| if (efi_st_memcmp(prot_buffer[0], &guid1, 16) && |
| efi_st_memcmp(prot_buffer[1], &guid1, 16)) { |
| efi_st_error("Failed to get protocols per handle\n"); |
| return EFI_ST_FAILURE; |
| } |
| if (efi_st_memcmp(prot_buffer[0], &guid3, 16) && |
| efi_st_memcmp(prot_buffer[1], &guid3, 16)) { |
| efi_st_error("Failed to get protocols per handle\n"); |
| return EFI_ST_FAILURE; |
| } |
| /* Release buffer */ |
| ret = boottime->free_pool(prot_buffer); |
| if (ret != EFI_SUCCESS) { |
| efi_st_error("FreePool failed\n"); |
| return EFI_ST_FAILURE; |
| } |
| |
| /* |
| * Uninstall remaining protocols |
| */ |
| ret = boottime->uninstall_protocol_interface(handle1, &guid1, |
| &interface1); |
| if (ret != EFI_SUCCESS) { |
| efi_st_error("UninstallProtocolInterface failed\n"); |
| return EFI_ST_FAILURE; |
| } |
| ret = boottime->handle_protocol(handle1, &guid1, (void **)&interface); |
| if (ret == EFI_SUCCESS) { |
| efi_st_error("UninstallProtocolInterface failed\n"); |
| return EFI_ST_FAILURE; |
| } |
| ret = boottime->uninstall_protocol_interface(handle1, &guid3, |
| &interface3); |
| if (ret != EFI_SUCCESS) { |
| efi_st_error("UninstallProtocolInterface failed\n"); |
| return EFI_ST_FAILURE; |
| } |
| |
| return EFI_ST_SUCCESS; |
| } |
| |
| EFI_UNIT_TEST(protserv) = { |
| .name = "manage protocols", |
| .phase = EFI_EXECUTE_BEFORE_BOOTTIME_EXIT, |
| .setup = setup, |
| .execute = execute, |
| .teardown = teardown, |
| }; |