1// SPDX-License-Identifier: GPL-2.0+
2/*
3 * efi_selftest_devicepath_util
4 *
5 * Copyright (c) 2018 Heinrich Schuchardt <xypron.glpk@gmx.de>
6 *
7 * This unit test checks the device path utilities protocol.
8 */
9
10#include <efi_selftest.h>
11
12static struct efi_boot_services *boottime;
13
14static efi_guid_t guid_device_path_utilities_protocol =
15	EFI_DEVICE_PATH_UTILITIES_PROTOCOL_GUID;
16
17struct efi_device_path_utilities_protocol *dpu;
18
19/*
20 * Setup unit test.
21 *
22 * Locate the device path utilities protocol.
23 *
24 * @handle:	handle of the loaded image
25 * @systable:	system table
26 */
27static int setup(const efi_handle_t img_handle,
28		 const struct efi_system_table *systable)
29{
30	int ret;
31
32	boottime = systable->boottime;
33
34	ret = boottime->locate_protocol(&guid_device_path_utilities_protocol,
35					NULL, (void **)&dpu);
36	if (ret != EFI_SUCCESS) {
37		dpu = NULL;
38		efi_st_error(
39			"Device path to text protocol is not available.\n");
40		return EFI_ST_FAILURE;
41	}
42
43	return EFI_ST_SUCCESS;
44}
45
46/*
47 * Create a device path consisting of a single media device node followed by an
48 * end node.
49 *
50 * @length:	length of the media device node
51 * @dp:		device path
52 * Return:	status code
53 */
54static int create_single_node_device_path(unsigned int length,
55					  struct efi_device_path **dp)
56{
57	struct efi_device_path *node;
58	efi_uintn_t len;
59	int ret;
60
61	node = dpu->create_device_node(DEVICE_PATH_TYPE_MEDIA_DEVICE,
62				       DEVICE_PATH_SUB_TYPE_FILE_PATH, length);
63	if (!node) {
64		efi_st_error("CreateDeviceNode failed\n");
65		return EFI_ST_FAILURE;
66	}
67	*dp = dpu->append_device_node(NULL, node);
68	if (!*dp) {
69		efi_st_error("AppendDeviceNode failed\n");
70		return EFI_ST_FAILURE;
71	}
72	ret = boottime->free_pool(node);
73	if (ret != EFI_ST_SUCCESS) {
74		efi_st_error("FreePool failed\n");
75		return EFI_ST_FAILURE;
76	}
77	len = dpu->get_device_path_size(*dp);
78	if (len != length + 4) {
79		efi_st_error("Wrong device path length %u, expected %u\n",
80			     (unsigned int)len, length);
81		return EFI_ST_FAILURE;
82	}
83	return EFI_ST_SUCCESS;
84}
85
86/*
87 * Execute unit test.
88 *
89 * In the test device paths are created, copied, and concatenated. The device
90 * path length is used as a measure of success.
91 */
92static int execute(void)
93{
94	struct efi_device_path *dp1;
95	struct efi_device_path *dp2;
96	struct efi_device_path *dp3;
97
98	efi_uintn_t len;
99	int ret;
100
101	/* IsDevicePathMultiInstance(NULL) */
102	if (dpu->is_device_path_multi_instance(NULL)) {
103		efi_st_error("IsDevicePathMultiInstance(NULL) returned true\n");
104		return EFI_ST_FAILURE;
105	}
106	/* GetDevicePathSize(NULL) */
107	len = dpu->get_device_path_size(NULL);
108	if (len) {
109		efi_st_error("Wrong device path length %u, expected 0\n",
110			     (unsigned int)len);
111		return EFI_ST_FAILURE;
112	}
113	/* DuplicateDevicePath(NULL) */
114	dp1 = dpu->duplicate_device_path(NULL);
115	if (dp1) {
116		efi_st_error("DuplicateDevicePath(NULL) failed\n");
117		return EFI_ST_FAILURE;
118	}
119	/* AppendDevicePath(NULL, NULL) */
120	dp1 = dpu->append_device_path(NULL, NULL);
121	if (!dp1) {
122		efi_st_error("AppendDevicePath(NULL, NULL) failed\n");
123		return EFI_ST_FAILURE;
124	}
125	len = dpu->get_device_path_size(dp1);
126	if (len != 4) {
127		efi_st_error("Wrong device path length %u, expected 4\n",
128			     (unsigned int)len);
129		return EFI_ST_FAILURE;
130	}
131	ret = boottime->free_pool(dp1);
132	if (ret != EFI_ST_SUCCESS) {
133		efi_st_error("FreePool failed\n");
134		return EFI_ST_FAILURE;
135	}
136	/* CreateDeviceNode */
137	ret = create_single_node_device_path(21, &dp1);
138	if (ret != EFI_ST_SUCCESS)
139		return ret;
140	ret = create_single_node_device_path(17, &dp2);
141	if (ret != EFI_ST_SUCCESS)
142		return ret;
143	/* AppendDevicePath */
144	dp3 = dpu->append_device_path(dp1, dp2);
145	if (!dp3) {
146		efi_st_error("AppendDevicePath failed\n");
147		return EFI_ST_FAILURE;
148	}
149	if (dp3 == dp1 || dp3 == dp2) {
150		efi_st_error("AppendDevicePath reused buffer\n");
151		return EFI_ST_FAILURE;
152	}
153	len = dpu->get_device_path_size(dp3);
154	/* 21 + 17 + 4 */
155	if (len != 42) {
156		efi_st_error("Wrong device path length %u, expected 42\n",
157			     (unsigned int)len);
158		return EFI_ST_FAILURE;
159	}
160	ret = boottime->free_pool(dp2);
161	if (ret != EFI_ST_SUCCESS) {
162		efi_st_error("FreePool failed\n");
163		return EFI_ST_FAILURE;
164	}
165	/* AppendDeviceNode */
166	dp2 = dpu->append_device_node(dp1, dp3);
167	if (!dp2) {
168		efi_st_error("AppendDevicePath failed\n");
169		return EFI_ST_FAILURE;
170	}
171	len = dpu->get_device_path_size(dp2);
172	/* 21 + 21 + 4 */
173	if (len != 46) {
174		printf("%s(%d) %s\n", __FILE__, __LINE__, __func__);
175		efi_st_error("Wrong device path length %u, expected 46\n",
176			     (unsigned int)len);
177		return EFI_ST_FAILURE;
178	}
179	ret = boottime->free_pool(dp1);
180	if (ret != EFI_ST_SUCCESS) {
181		efi_st_error("FreePool failed\n");
182		return EFI_ST_FAILURE;
183	}
184	/* IsDevicePathMultiInstance */
185	if (dpu->is_device_path_multi_instance(dp2)) {
186		printf("%s(%d) %s\n", __FILE__, __LINE__, __func__);
187		efi_st_error("IsDevicePathMultiInstance returned true\n");
188		return EFI_ST_FAILURE;
189	}
190	/* AppendDevicePathInstance */
191	dp1 = dpu->append_device_path_instance(dp2, dp3);
192	if (!dp1) {
193		efi_st_error("AppendDevicePathInstance failed\n");
194		return EFI_ST_FAILURE;
195	}
196	len = dpu->get_device_path_size(dp1);
197	/* 46 + 42 */
198	if (len != 88) {
199		efi_st_error("Wrong device path length %u, expected 88\n",
200			     (unsigned int)len);
201		return EFI_ST_FAILURE;
202	}
203	/* IsDevicePathMultiInstance */
204	if (!dpu->is_device_path_multi_instance(dp1)) {
205		efi_st_error("IsDevicePathMultiInstance returned false\n");
206		return EFI_ST_FAILURE;
207	}
208	ret = boottime->free_pool(dp2);
209	if (ret != EFI_ST_SUCCESS) {
210		efi_st_error("FreePool failed\n");
211		return EFI_ST_FAILURE;
212	}
213	ret = boottime->free_pool(dp3);
214	if (ret != EFI_ST_SUCCESS) {
215		efi_st_error("FreePool failed\n");
216		return EFI_ST_FAILURE;
217	}
218	/* GetNextDevicePathInstance */
219	dp3 = dp1;
220	dp2 = dpu->get_next_device_path_instance(&dp1, &len);
221	if (!dp2) {
222		efi_st_error("GetNextDevicePathInstance failed\n");
223		return EFI_ST_FAILURE;
224	}
225	if (!dp1) {
226		efi_st_error("GetNextDevicePathInstance no 2nd instance\n");
227		return EFI_ST_FAILURE;
228	}
229	if (len != 46) {
230		efi_st_error("Wrong device path length %u, expected 46\n",
231			     (unsigned int)len);
232		return EFI_ST_FAILURE;
233	}
234	len = dpu->get_device_path_size(dp1);
235	if (len != 42) {
236		efi_st_error("Wrong device path length %u, expected 42\n",
237			     (unsigned int)len);
238		return EFI_ST_FAILURE;
239	}
240	ret = boottime->free_pool(dp2);
241	if (ret != EFI_ST_SUCCESS) {
242		efi_st_error("FreePool failed\n");
243		return EFI_ST_FAILURE;
244	}
245	dp2 = dpu->get_next_device_path_instance(&dp1, &len);
246	if (!dp2) {
247		efi_st_error("GetNextDevicePathInstance failed\n");
248		return EFI_ST_FAILURE;
249	}
250	if (len != 42) {
251		efi_st_error("Wrong device path length %u, expected 46\n",
252			     (unsigned int)len);
253		return EFI_ST_FAILURE;
254	}
255	if (dp1) {
256		efi_st_error("GetNextDevicePathInstance did not signal end\n");
257		return EFI_ST_FAILURE;
258	}
259
260	/* Clean up */
261	ret = boottime->free_pool(dp2);
262	if (ret != EFI_ST_SUCCESS) {
263		efi_st_error("FreePool failed\n");
264		return EFI_ST_FAILURE;
265	}
266	ret = boottime->free_pool(dp3);
267	if (ret != EFI_ST_SUCCESS) {
268		efi_st_error("FreePool failed\n");
269		return EFI_ST_FAILURE;
270	}
271
272	return EFI_ST_SUCCESS;
273}
274
275EFI_UNIT_TEST(dputil) = {
276	.name = "device path utilities protocol",
277	.phase = EFI_EXECUTE_BEFORE_BOOTTIME_EXIT,
278	.setup = setup,
279	.execute = execute,
280};
281