| // SPDX-License-Identifier: GPL-2.0+ |
| /* |
| * efi_selftest_devicepath_util |
| * |
| * Copyright (c) 2018 Heinrich Schuchardt <xypron.glpk@gmx.de> |
| * |
| * This unit test checks the device path utilities protocol. |
| */ |
| |
| #include <efi_selftest.h> |
| |
| static struct efi_boot_services *boottime; |
| |
| static efi_guid_t guid_device_path_utilities_protocol = |
| EFI_DEVICE_PATH_UTILITIES_PROTOCOL_GUID; |
| |
| struct efi_device_path_utilities_protocol *dpu; |
| |
| /* |
| * Setup unit test. |
| * |
| * Locate the device path utilities protocol. |
| * |
| * @handle: handle of the loaded image |
| * @systable: system table |
| */ |
| static int setup(const efi_handle_t img_handle, |
| const struct efi_system_table *systable) |
| { |
| int ret; |
| |
| boottime = systable->boottime; |
| |
| ret = boottime->locate_protocol(&guid_device_path_utilities_protocol, |
| NULL, (void **)&dpu); |
| if (ret != EFI_SUCCESS) { |
| dpu = NULL; |
| efi_st_error( |
| "Device path to text protocol is not available.\n"); |
| return EFI_ST_FAILURE; |
| } |
| |
| return EFI_ST_SUCCESS; |
| } |
| |
| /* |
| * Create a device path consisting of a single media device node followed by an |
| * end node. |
| * |
| * @length: length of the media device node |
| * @dp: device path |
| * @return: status code |
| */ |
| static int create_single_node_device_path(unsigned int length, |
| struct efi_device_path **dp) |
| { |
| struct efi_device_path *node; |
| efi_uintn_t len; |
| int ret; |
| |
| node = dpu->create_device_node(DEVICE_PATH_TYPE_MEDIA_DEVICE, |
| DEVICE_PATH_SUB_TYPE_FILE_PATH, length); |
| if (!node) { |
| efi_st_error("CreateDeviceNode failed\n"); |
| return EFI_ST_FAILURE; |
| } |
| *dp = dpu->append_device_node(NULL, node); |
| if (!*dp) { |
| efi_st_error("AppendDeviceNode failed\n"); |
| return EFI_ST_FAILURE; |
| } |
| ret = boottime->free_pool(node); |
| if (ret != EFI_ST_SUCCESS) { |
| efi_st_error("FreePool failed\n"); |
| return EFI_ST_FAILURE; |
| } |
| len = dpu->get_device_path_size(*dp); |
| if (len != length + 4) { |
| efi_st_error("Wrong device path length %u, expected %u\n", |
| (unsigned int)len, length); |
| return EFI_ST_FAILURE; |
| } |
| return EFI_ST_SUCCESS; |
| } |
| |
| /* |
| * Execute unit test. |
| * |
| * In the test device paths are created, copied, and concatenated. The device |
| * path length is used as a measure of success. |
| */ |
| static int execute(void) |
| { |
| struct efi_device_path *dp1; |
| struct efi_device_path *dp2; |
| struct efi_device_path *dp3; |
| |
| efi_uintn_t len; |
| int ret; |
| |
| /* IsDevicePathMultiInstance(NULL) */ |
| if (dpu->is_device_path_multi_instance(NULL)) { |
| efi_st_error("IsDevicePathMultiInstance(NULL) returned true\n"); |
| return EFI_ST_FAILURE; |
| } |
| /* GetDevicePathSize(NULL) */ |
| len = dpu->get_device_path_size(NULL); |
| if (len) { |
| efi_st_error("Wrong device path length %u, expected 0\n", |
| (unsigned int)len); |
| return EFI_ST_FAILURE; |
| } |
| /* DuplicateDevicePath(NULL) */ |
| dp1 = dpu->duplicate_device_path(NULL); |
| if (dp1) { |
| efi_st_error("DuplicateDevicePath(NULL) failed\n"); |
| return EFI_ST_FAILURE; |
| } |
| /* AppendDevicePath(NULL, NULL) */ |
| dp1 = dpu->append_device_path(NULL, NULL); |
| if (!dp1) { |
| efi_st_error("AppendDevicePath(NULL, NULL) failed\n"); |
| return EFI_ST_FAILURE; |
| } |
| len = dpu->get_device_path_size(dp1); |
| if (len != 4) { |
| efi_st_error("Wrong device path length %u, expected 4\n", |
| (unsigned int)len); |
| return EFI_ST_FAILURE; |
| } |
| ret = boottime->free_pool(dp1); |
| if (ret != EFI_ST_SUCCESS) { |
| efi_st_error("FreePool failed\n"); |
| return EFI_ST_FAILURE; |
| } |
| /* CreateDeviceNode */ |
| ret = create_single_node_device_path(21, &dp1); |
| if (ret != EFI_ST_SUCCESS) |
| return ret; |
| ret = create_single_node_device_path(17, &dp2); |
| if (ret != EFI_ST_SUCCESS) |
| return ret; |
| /* AppendDevicePath */ |
| dp3 = dpu->append_device_path(dp1, dp2); |
| if (!dp3) { |
| efi_st_error("AppendDevicePath failed\n"); |
| return EFI_ST_FAILURE; |
| } |
| if (dp3 == dp1 || dp3 == dp2) { |
| efi_st_error("AppendDevicePath reused buffer\n"); |
| return EFI_ST_FAILURE; |
| } |
| len = dpu->get_device_path_size(dp3); |
| /* 21 + 17 + 4 */ |
| if (len != 42) { |
| efi_st_error("Wrong device path length %u, expected 42\n", |
| (unsigned int)len); |
| return EFI_ST_FAILURE; |
| } |
| ret = boottime->free_pool(dp2); |
| if (ret != EFI_ST_SUCCESS) { |
| efi_st_error("FreePool failed\n"); |
| return EFI_ST_FAILURE; |
| } |
| /* AppendDeviceNode */ |
| dp2 = dpu->append_device_node(dp1, dp3); |
| if (!dp2) { |
| efi_st_error("AppendDevicePath failed\n"); |
| return EFI_ST_FAILURE; |
| } |
| len = dpu->get_device_path_size(dp2); |
| /* 21 + 21 + 4 */ |
| if (len != 46) { |
| printf("%s(%d) %s\n", __FILE__, __LINE__, __func__); |
| efi_st_error("Wrong device path length %u, expected 46\n", |
| (unsigned int)len); |
| return EFI_ST_FAILURE; |
| } |
| ret = boottime->free_pool(dp1); |
| if (ret != EFI_ST_SUCCESS) { |
| efi_st_error("FreePool failed\n"); |
| return EFI_ST_FAILURE; |
| } |
| /* IsDevicePathMultiInstance */ |
| if (dpu->is_device_path_multi_instance(dp2)) { |
| printf("%s(%d) %s\n", __FILE__, __LINE__, __func__); |
| efi_st_error("IsDevicePathMultiInstance returned true\n"); |
| return EFI_ST_FAILURE; |
| } |
| /* AppendDevicePathInstance */ |
| dp1 = dpu->append_device_path_instance(dp2, dp3); |
| if (!dp1) { |
| efi_st_error("AppendDevicePathInstance failed\n"); |
| return EFI_ST_FAILURE; |
| } |
| len = dpu->get_device_path_size(dp1); |
| /* 46 + 42 */ |
| if (len != 88) { |
| efi_st_error("Wrong device path length %u, expected 88\n", |
| (unsigned int)len); |
| return EFI_ST_FAILURE; |
| } |
| /* IsDevicePathMultiInstance */ |
| if (!dpu->is_device_path_multi_instance(dp1)) { |
| efi_st_error("IsDevicePathMultiInstance returned false\n"); |
| return EFI_ST_FAILURE; |
| } |
| ret = boottime->free_pool(dp2); |
| if (ret != EFI_ST_SUCCESS) { |
| efi_st_error("FreePool failed\n"); |
| return EFI_ST_FAILURE; |
| } |
| ret = boottime->free_pool(dp3); |
| if (ret != EFI_ST_SUCCESS) { |
| efi_st_error("FreePool failed\n"); |
| return EFI_ST_FAILURE; |
| } |
| /* GetNextDevicePathInstance */ |
| dp3 = dp1; |
| dp2 = dpu->get_next_device_path_instance(&dp1, &len); |
| if (!dp2) { |
| efi_st_error("GetNextDevicePathInstance failed\n"); |
| return EFI_ST_FAILURE; |
| } |
| if (!dp1) { |
| efi_st_error("GetNextDevicePathInstance no 2nd instance\n"); |
| return EFI_ST_FAILURE; |
| } |
| if (len != 46) { |
| efi_st_error("Wrong device path length %u, expected 46\n", |
| (unsigned int)len); |
| return EFI_ST_FAILURE; |
| } |
| len = dpu->get_device_path_size(dp1); |
| if (len != 42) { |
| efi_st_error("Wrong device path length %u, expected 42\n", |
| (unsigned int)len); |
| return EFI_ST_FAILURE; |
| } |
| ret = boottime->free_pool(dp2); |
| if (ret != EFI_ST_SUCCESS) { |
| efi_st_error("FreePool failed\n"); |
| return EFI_ST_FAILURE; |
| } |
| dp2 = dpu->get_next_device_path_instance(&dp1, &len); |
| if (!dp2) { |
| efi_st_error("GetNextDevicePathInstance failed\n"); |
| return EFI_ST_FAILURE; |
| } |
| if (len != 42) { |
| efi_st_error("Wrong device path length %u, expected 46\n", |
| (unsigned int)len); |
| return EFI_ST_FAILURE; |
| } |
| if (dp1) { |
| efi_st_error("GetNextDevicePathInstance did not signal end\n"); |
| return EFI_ST_FAILURE; |
| } |
| ret = boottime->free_pool(dp2); |
| if (ret != EFI_ST_SUCCESS) { |
| efi_st_error("FreePool failed\n"); |
| return EFI_ST_FAILURE; |
| } |
| |
| /* Clean up */ |
| ret = boottime->free_pool(dp2); |
| if (ret != EFI_ST_SUCCESS) { |
| efi_st_error("FreePool failed\n"); |
| return EFI_ST_FAILURE; |
| } |
| ret = boottime->free_pool(dp3); |
| if (ret != EFI_ST_SUCCESS) { |
| efi_st_error("FreePool failed\n"); |
| return EFI_ST_FAILURE; |
| } |
| |
| return EFI_ST_SUCCESS; |
| } |
| |
| EFI_UNIT_TEST(dputil) = { |
| .name = "device path utilities protocol", |
| .phase = EFI_EXECUTE_BEFORE_BOOTTIME_EXIT, |
| .setup = setup, |
| .execute = execute, |
| }; |