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