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 
12 static struct efi_boot_services *boottime;
13 
14 static efi_guid_t guid_device_path_utilities_protocol =
15 	EFI_DEVICE_PATH_UTILITIES_PROTOCOL_GUID;
16 
17 struct 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  */
27 static 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  */
54 static 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  */
92 static 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 
275 EFI_UNIT_TEST(dputil) = {
276 	.name = "device path utilities protocol",
277 	.phase = EFI_EXECUTE_BEFORE_BOOTTIME_EXIT,
278 	.setup = setup,
279 	.execute = execute,
280 };
281