xref: /freebsd/stand/efi/libefi/devpath.c (revision 43b8edb320519c9887a5d953c4cf8a91f0ca8d14)
1 /*-
2  * Copyright (c) 2016 John Baldwin <jhb@FreeBSD.org>
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
14  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
17  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
18  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
19  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
20  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
21  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
22  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
23  * SUCH DAMAGE.
24  */
25 
26 #include <efi.h>
27 #include <efilib.h>
28 #include <efichar.h>
29 #include <uuid.h>
30 #include <machine/_inttypes.h>
31 #include <Protocol/DevicePathToText.h>
32 #include <Protocol/DevicePathFromText.h>
33 
34 static EFI_GUID ImageDevicePathGUID =
35     EFI_LOADED_IMAGE_DEVICE_PATH_PROTOCOL_GUID;
36 static EFI_GUID DevicePathGUID = DEVICE_PATH_PROTOCOL;
37 static EFI_GUID DevicePathToTextGUID = EFI_DEVICE_PATH_TO_TEXT_PROTOCOL_GUID;
38 static EFI_DEVICE_PATH_TO_TEXT_PROTOCOL *toTextProtocol;
39 static EFI_GUID DevicePathFromTextGUID =
40     EFI_DEVICE_PATH_FROM_TEXT_PROTOCOL_GUID;
41 static EFI_DEVICE_PATH_FROM_TEXT_PROTOCOL *fromTextProtocol;
42 
43 EFI_DEVICE_PATH *
efi_lookup_image_devpath(EFI_HANDLE handle)44 efi_lookup_image_devpath(EFI_HANDLE handle)
45 {
46 	EFI_DEVICE_PATH *devpath;
47 	EFI_STATUS status;
48 
49 	status = OpenProtocolByHandle(handle, &ImageDevicePathGUID,
50 	    (void **)&devpath);
51 	if (EFI_ERROR(status))
52 		devpath = NULL;
53 	return (devpath);
54 }
55 
56 EFI_DEVICE_PATH *
efi_lookup_devpath(EFI_HANDLE handle)57 efi_lookup_devpath(EFI_HANDLE handle)
58 {
59 	EFI_DEVICE_PATH *devpath;
60 	EFI_STATUS status;
61 
62 	status = OpenProtocolByHandle(handle, &DevicePathGUID,
63 	    (void **)&devpath);
64 	if (EFI_ERROR(status))
65 		devpath = NULL;
66 	return (devpath);
67 }
68 
69 void
efi_close_devpath(EFI_HANDLE handle)70 efi_close_devpath(EFI_HANDLE handle)
71 {
72 	EFI_STATUS status;
73 
74 	status = BS->CloseProtocol(handle, &DevicePathGUID, IH, NULL);
75 	if (EFI_ERROR(status))
76 		printf("CloseProtocol error: %lu\n", DECODE_ERROR(status));
77 }
78 
79 static char *
efi_make_tail(char * suffix)80 efi_make_tail(char *suffix)
81 {
82 	char *tail;
83 
84 	tail = NULL;
85 	if (suffix != NULL)
86 		(void)asprintf(&tail, "/%s", suffix);
87 	else
88 		tail = strdup("");
89 	return (tail);
90 }
91 
92 typedef struct {
93 	EFI_DEVICE_PATH	Header;
94 	EFI_GUID	Guid;
95 	UINT8		VendorDefinedData[1];
96 } __packed VENDOR_DEVICE_PATH_WITH_DATA;
97 
98 static char *
efi_vendor_path(const char * type,VENDOR_DEVICE_PATH * node,char * suffix)99 efi_vendor_path(const char *type, VENDOR_DEVICE_PATH *node, char *suffix)
100 {
101 	uint32_t size = DevicePathNodeLength(&node->Header) - sizeof(*node);
102 	VENDOR_DEVICE_PATH_WITH_DATA *dp = (VENDOR_DEVICE_PATH_WITH_DATA *)node;
103 	char *name, *tail, *head;
104 	char *uuid;
105 	int rv;
106 
107 	uuid_to_string((const uuid_t *)(void *)&node->Guid, &uuid, &rv);
108 	if (rv != uuid_s_ok)
109 		return (NULL);
110 
111 	tail = efi_make_tail(suffix);
112 	rv = asprintf(&head, "%sVendor(%s)[%x:", type, uuid, size);
113 	free(uuid);
114 	if (rv < 0)
115 		return (NULL);
116 
117 	if (DevicePathNodeLength(&node->Header) > sizeof(*node)) {
118 		for (uint32_t i = 0; i < size; i++) {
119 			rv = asprintf(&name, "%s%02x", head,
120 			    dp->VendorDefinedData[i]);
121 			if (rv < 0) {
122 				free(tail);
123 				free(head);
124 				return (NULL);
125 			}
126 			free(head);
127 			head = name;
128 		}
129 	}
130 
131 	if (asprintf(&name, "%s]%s", head, tail) < 0)
132 		name = NULL;
133 	free(head);
134 	free(tail);
135 	return (name);
136 }
137 
138 static char *
efi_hw_dev_path(EFI_DEVICE_PATH * node,char * suffix)139 efi_hw_dev_path(EFI_DEVICE_PATH *node, char *suffix)
140 {
141 	uint8_t subtype = DevicePathSubType(node);
142 	char *name, *tail;
143 
144 	tail = efi_make_tail(suffix);
145 	switch (subtype) {
146 	case HW_PCI_DP:
147 		if (asprintf(&name, "Pci(%x,%x)%s",
148 		    ((PCI_DEVICE_PATH *)node)->Device,
149 		    ((PCI_DEVICE_PATH *)node)->Function, tail) < 0)
150 			name = NULL;
151 		break;
152 	case HW_PCCARD_DP:
153 		if (asprintf(&name, "PCCARD(%x)%s",
154 		    ((PCCARD_DEVICE_PATH *)node)->FunctionNumber, tail) < 0)
155 			name = NULL;
156 		break;
157 	case HW_MEMMAP_DP:
158 		if (asprintf(&name, "MMap(%x,%" PRIx64 ",%" PRIx64 ")%s",
159 		    ((MEMMAP_DEVICE_PATH *)node)->MemoryType,
160 		    ((MEMMAP_DEVICE_PATH *)node)->StartingAddress,
161 		    ((MEMMAP_DEVICE_PATH *)node)->EndingAddress, tail) < 0)
162 			name = NULL;
163 		break;
164 	case HW_VENDOR_DP:
165 		name = efi_vendor_path("Hardware",
166 		    (VENDOR_DEVICE_PATH *)node, tail);
167 		break;
168 	case HW_CONTROLLER_DP:
169 		if (asprintf(&name, "Ctrl(%x)%s",
170 		    ((CONTROLLER_DEVICE_PATH *)node)->ControllerNumber, tail) < 0)
171 			name = NULL;
172 		break;
173 	default:
174 		if (asprintf(&name, "UnknownHW(%x)%s", subtype, tail) < 0)
175 			name = NULL;
176 		break;
177 	}
178 	free(tail);
179 	return (name);
180 }
181 
182 static char *
efi_acpi_dev_path(EFI_DEVICE_PATH * node,char * suffix)183 efi_acpi_dev_path(EFI_DEVICE_PATH *node, char *suffix)
184 {
185 	uint8_t subtype = DevicePathSubType(node);
186 	ACPI_HID_DEVICE_PATH *acpi = (ACPI_HID_DEVICE_PATH *)node;
187 	char *name, *tail;
188 
189 	tail = efi_make_tail(suffix);
190 	switch (subtype) {
191 	case ACPI_DP:
192 		if ((acpi->HID & PNP_EISA_ID_MASK) == PNP_EISA_ID_CONST) {
193 			switch (EISA_ID_TO_NUM (acpi->HID)) {
194 			case 0x0a03:
195 				if (asprintf(&name, "PciRoot(%x)%s",
196 				    acpi->UID, tail) < 0)
197 					name = NULL;
198 				break;
199 			case 0x0a08:
200 				if (asprintf(&name, "PcieRoot(%x)%s",
201 				    acpi->UID, tail) < 0)
202 					name = NULL;
203 				break;
204 			case 0x0604:
205 				if (asprintf(&name, "Floppy(%x)%s",
206 				    acpi->UID, tail) < 0)
207 					name = NULL;
208 				break;
209 			case 0x0301:
210 				if (asprintf(&name, "Keyboard(%x)%s",
211 				    acpi->UID, tail) < 0)
212 					name = NULL;
213 				break;
214 			case 0x0501:
215 				if (asprintf(&name, "Serial(%x)%s",
216 				    acpi->UID, tail) < 0)
217 					name = NULL;
218 				break;
219 			case 0x0401:
220 				if (asprintf(&name, "ParallelPort(%x)%s",
221 				    acpi->UID, tail) < 0)
222 					name = NULL;
223 				break;
224 			default:
225 				if (asprintf(&name, "Acpi(PNP%04x,%x)%s",
226 				    EISA_ID_TO_NUM(acpi->HID),
227 				    acpi->UID, tail) < 0)
228 					name = NULL;
229 				break;
230 			}
231 		} else {
232 			if (asprintf(&name, "Acpi(%08x,%x)%s",
233 			    acpi->HID, acpi->UID, tail) < 0)
234 				name = NULL;
235 		}
236 		break;
237 	case ACPI_EXTENDED_DP:
238 	default:
239 		if (asprintf(&name, "UnknownACPI(%x)%s", subtype, tail) < 0)
240 			name = NULL;
241 		break;
242 	}
243 	free(tail);
244 	return (name);
245 }
246 
247 static char *
efi_messaging_dev_path(EFI_DEVICE_PATH * node,char * suffix)248 efi_messaging_dev_path(EFI_DEVICE_PATH *node, char *suffix)
249 {
250 	uint8_t subtype = DevicePathSubType(node);
251 	char *name;
252 	char *tail;
253 
254 	tail = efi_make_tail(suffix);
255 	switch (subtype) {
256 	case MSG_ATAPI_DP:
257 		if (asprintf(&name, "ATA(%s,%s,%x)%s",
258 		    ((ATAPI_DEVICE_PATH *)node)->PrimarySecondary == 1 ?
259 		    "Secondary" : "Primary",
260 		    ((ATAPI_DEVICE_PATH *)node)->SlaveMaster == 1 ?
261 		    "Slave" : "Master",
262 		    ((ATAPI_DEVICE_PATH *)node)->Lun, tail) < 0)
263 			name = NULL;
264 		break;
265 	case MSG_SCSI_DP:
266 		if (asprintf(&name, "SCSI(%x,%x)%s",
267 		    ((SCSI_DEVICE_PATH *)node)->Pun,
268 		    ((SCSI_DEVICE_PATH *)node)->Lun, tail) < 0)
269 			name = NULL;
270 		break;
271 	case MSG_FIBRECHANNEL_DP:
272 		if (asprintf(&name, "Fibre(%" PRIx64 ",%" PRIx64 ")%s",
273 		    ((FIBRECHANNEL_DEVICE_PATH *)node)->WWN,
274 		    ((FIBRECHANNEL_DEVICE_PATH *)node)->Lun, tail) < 0)
275 			name = NULL;
276 		break;
277 	case MSG_1394_DP:
278 		if (asprintf(&name, "I1394(%016" PRIx64 ")%s",
279 		    ((F1394_DEVICE_PATH *)node)->Guid, tail) < 0)
280 			name = NULL;
281 		break;
282 	case MSG_USB_DP:
283 		if (asprintf(&name, "USB(%x,%x)%s",
284 		    ((USB_DEVICE_PATH *)node)->ParentPortNumber,
285 		    ((USB_DEVICE_PATH *)node)->InterfaceNumber, tail) < 0)
286 			name = NULL;
287 		break;
288 	case MSG_USB_CLASS_DP:
289 		if (asprintf(&name, "UsbClass(%x,%x,%x,%x,%x)%s",
290 		    ((USB_CLASS_DEVICE_PATH *)node)->VendorId,
291 		    ((USB_CLASS_DEVICE_PATH *)node)->ProductId,
292 		    ((USB_CLASS_DEVICE_PATH *)node)->DeviceClass,
293 		    ((USB_CLASS_DEVICE_PATH *)node)->DeviceSubClass,
294 		    ((USB_CLASS_DEVICE_PATH *)node)->DeviceProtocol, tail) < 0)
295 			name = NULL;
296 		break;
297 	case MSG_MAC_ADDR_DP:
298 		if (asprintf(&name, "MAC(%02x:%02x:%02x:%02x:%02x:%02x,%x)%s",
299 		    ((MAC_ADDR_DEVICE_PATH *)node)->MacAddress.Addr[0],
300 		    ((MAC_ADDR_DEVICE_PATH *)node)->MacAddress.Addr[1],
301 		    ((MAC_ADDR_DEVICE_PATH *)node)->MacAddress.Addr[2],
302 		    ((MAC_ADDR_DEVICE_PATH *)node)->MacAddress.Addr[3],
303 		    ((MAC_ADDR_DEVICE_PATH *)node)->MacAddress.Addr[4],
304 		    ((MAC_ADDR_DEVICE_PATH *)node)->MacAddress.Addr[5],
305 		    ((MAC_ADDR_DEVICE_PATH *)node)->IfType, tail) < 0)
306 			name = NULL;
307 		break;
308 	case MSG_VENDOR_DP:
309 		name = efi_vendor_path("Messaging",
310 		    (VENDOR_DEVICE_PATH *)node, tail);
311 		break;
312 	case MSG_UART_DP:
313 		if (asprintf(&name, "UART(%" PRIu64 ",%u,%x,%x)%s",
314 		    ((UART_DEVICE_PATH *)node)->BaudRate,
315 		    ((UART_DEVICE_PATH *)node)->DataBits,
316 		    ((UART_DEVICE_PATH *)node)->Parity,
317 		    ((UART_DEVICE_PATH *)node)->StopBits, tail) < 0)
318 			name = NULL;
319 		break;
320 	case MSG_SATA_DP:
321 		if (asprintf(&name, "Sata(%x,%x,%x)%s",
322 		    ((SATA_DEVICE_PATH *)node)->HBAPortNumber,
323 		    ((SATA_DEVICE_PATH *)node)->PortMultiplierPortNumber,
324 		    ((SATA_DEVICE_PATH *)node)->Lun, tail) < 0)
325 			name = NULL;
326 		break;
327 	default:
328 		if (asprintf(&name, "UnknownMessaging(%x)%s",
329 		    subtype, tail) < 0)
330 			name = NULL;
331 		break;
332 	}
333 	free(tail);
334 	return (name);
335 }
336 
337 static char *
efi_media_dev_path(EFI_DEVICE_PATH * node,char * suffix)338 efi_media_dev_path(EFI_DEVICE_PATH *node, char *suffix)
339 {
340 	uint8_t subtype = DevicePathSubType(node);
341 	HARDDRIVE_DEVICE_PATH *hd;
342 	char *name;
343 	char *str;
344 	char *tail;
345 	int rv;
346 
347 	tail = efi_make_tail(suffix);
348 	name = NULL;
349 	switch (subtype) {
350 	case MEDIA_HARDDRIVE_DP:
351 		hd = (HARDDRIVE_DEVICE_PATH *)node;
352 		switch (hd->SignatureType) {
353 		case SIGNATURE_TYPE_MBR:
354 			if (asprintf(&name, "HD(%d,MBR,%08x,%" PRIx64
355 			    ",%" PRIx64 ")%s",
356 			    hd->PartitionNumber,
357 			    *((uint32_t *)(uintptr_t)&hd->Signature[0]),
358 			    hd->PartitionStart,
359 			    hd->PartitionSize, tail) < 0)
360 				name = NULL;
361 			break;
362 		case SIGNATURE_TYPE_GUID:
363 			name = NULL;
364 			uuid_to_string((const uuid_t *)(void *)
365 			    &hd->Signature[0], &str, &rv);
366 			if (rv != uuid_s_ok)
367 				break;
368 			rv = asprintf(&name, "HD(%d,GPT,%s,%" PRIx64 ",%"
369 			    PRIx64 ")%s",
370 			    hd->PartitionNumber, str,
371 			    hd->PartitionStart, hd->PartitionSize, tail);
372 			free(str);
373 			break;
374 		default:
375 			if (asprintf(&name, "HD(%d,%d,0)%s",
376 			    hd->PartitionNumber,
377 			    hd->SignatureType, tail) < 0) {
378 				name = NULL;
379 			}
380 			break;
381 		}
382 		break;
383 	case MEDIA_CDROM_DP:
384 		if (asprintf(&name, "CD(%x,%" PRIx64 ",%" PRIx64 ")%s",
385 		    ((CDROM_DEVICE_PATH *)node)->BootEntry,
386 		    ((CDROM_DEVICE_PATH *)node)->PartitionStart,
387 		    ((CDROM_DEVICE_PATH *)node)->PartitionSize, tail) < 0) {
388 			name = NULL;
389 		}
390 		break;
391 	case MEDIA_VENDOR_DP:
392 		name = efi_vendor_path("Media",
393 		    (VENDOR_DEVICE_PATH *)node, tail);
394 		break;
395 	case MEDIA_FILEPATH_DP:
396 		name = NULL;
397 		str = NULL;
398 		if (ucs2_to_utf8(((FILEPATH_DEVICE_PATH *)node)->PathName,
399 		    &str) == 0) {
400 			(void)asprintf(&name, "%s%s", str, tail);
401 			free(str);
402 		}
403 		break;
404 	case MEDIA_PROTOCOL_DP:
405 		name = NULL;
406 		uuid_to_string((const uuid_t *)(void *)
407 		    &((MEDIA_PROTOCOL_DEVICE_PATH *)node)->Protocol,
408 		    &str, &rv);
409 		if (rv != uuid_s_ok)
410 			break;
411 		rv = asprintf(&name, "Protocol(%s)%s", str, tail);
412 		free(str);
413 		break;
414 	default:
415 		if (asprintf(&name, "UnknownMedia(%x)%s",
416 		    subtype, tail) < 0)
417 			name = NULL;
418 	}
419 	free(tail);
420 	return (name);
421 }
422 
423 static char *
efi_translate_devpath(EFI_DEVICE_PATH * devpath)424 efi_translate_devpath(EFI_DEVICE_PATH *devpath)
425 {
426 	EFI_DEVICE_PATH *dp = NextDevicePathNode(devpath);
427 	char *name, *ptr;
428 	uint8_t type;
429 
430 	if (!IsDevicePathEnd(devpath))
431 		name = efi_translate_devpath(dp);
432 	else
433 		return (NULL);
434 
435 	ptr = NULL;
436 	type = DevicePathType(devpath);
437 	switch (type) {
438 	case HARDWARE_DEVICE_PATH:
439 		ptr = efi_hw_dev_path(devpath, name);
440 		break;
441 	case ACPI_DEVICE_PATH:
442 		ptr = efi_acpi_dev_path(devpath, name);
443 		break;
444 	case MESSAGING_DEVICE_PATH:
445 		ptr = efi_messaging_dev_path(devpath, name);
446 		break;
447 	case MEDIA_DEVICE_PATH:
448 		ptr = efi_media_dev_path(devpath, name);
449 		break;
450 	case BBS_DEVICE_PATH:
451 	default:
452 		if (asprintf(&ptr, "UnknownPath(%x)%s", type,
453 		    name? name : "") < 0)
454 			ptr = NULL;
455 		break;
456 	}
457 
458 	if (ptr != NULL) {
459 		free(name);
460 		name = ptr;
461 	}
462 	return (name);
463 }
464 
465 static CHAR16 *
efi_devpath_to_name(EFI_DEVICE_PATH * devpath)466 efi_devpath_to_name(EFI_DEVICE_PATH *devpath)
467 {
468 	char *name = NULL;
469 	CHAR16 *ptr = NULL;
470 	size_t len;
471 	int rv;
472 
473 	name = efi_translate_devpath(devpath);
474 	if (name == NULL)
475 		return (NULL);
476 
477 	/*
478 	 * We need to return memory from AllocatePool, so it can be freed
479 	 * with FreePool() in efi_free_devpath_name().
480 	 */
481 	rv = utf8_to_ucs2(name, &ptr, &len);
482 	free(name);
483 	if (rv == 0) {
484 		CHAR16 *out = NULL;
485 		EFI_STATUS status;
486 
487 		status = BS->AllocatePool(EfiLoaderData, len, (void **)&out);
488 		if (EFI_ERROR(status)) {
489 			free(ptr);
490                 	return (out);
491 		}
492 		memcpy(out, ptr, len);
493 		free(ptr);
494 		ptr = out;
495 	}
496 
497 	return (ptr);
498 }
499 
500 CHAR16 *
efi_devpath_name(EFI_DEVICE_PATH * devpath)501 efi_devpath_name(EFI_DEVICE_PATH *devpath)
502 {
503 	EFI_STATUS status;
504 
505 	if (devpath == NULL)
506 		return (NULL);
507 	if (toTextProtocol == NULL) {
508 		status = BS->LocateProtocol(&DevicePathToTextGUID, NULL,
509 		    (VOID **)&toTextProtocol);
510 		if (EFI_ERROR(status))
511 			toTextProtocol = NULL;
512 	}
513 	if (toTextProtocol == NULL)
514 		return (efi_devpath_to_name(devpath));
515 
516 	return (toTextProtocol->ConvertDevicePathToText(devpath, TRUE, TRUE));
517 }
518 
519 void
efi_free_devpath_name(CHAR16 * text)520 efi_free_devpath_name(CHAR16 *text)
521 {
522 	if (text != NULL)
523 		BS->FreePool(text);
524 }
525 
526 EFI_DEVICE_PATH *
efi_name_to_devpath(const char * path)527 efi_name_to_devpath(const char *path)
528 {
529 	EFI_DEVICE_PATH *devpath;
530 	CHAR16 *uv;
531 	size_t ul;
532 
533 	uv = NULL;
534 	if (utf8_to_ucs2(path, &uv, &ul) != 0)
535 		return (NULL);
536 	devpath = efi_name_to_devpath16(uv);
537 	free(uv);
538 	return (devpath);
539 }
540 
541 EFI_DEVICE_PATH *
efi_name_to_devpath16(CHAR16 * path)542 efi_name_to_devpath16(CHAR16 *path)
543 {
544 	EFI_STATUS status;
545 
546 	if (path == NULL)
547 		return (NULL);
548 	if (fromTextProtocol == NULL) {
549 		status = BS->LocateProtocol(&DevicePathFromTextGUID, NULL,
550 		    (VOID **)&fromTextProtocol);
551 		if (EFI_ERROR(status))
552 			fromTextProtocol = NULL;
553 	}
554 	if (fromTextProtocol == NULL)
555 		return (NULL);
556 
557 	return (fromTextProtocol->ConvertTextToDevicePath(path));
558 }
559 
efi_devpath_free(EFI_DEVICE_PATH * devpath)560 void efi_devpath_free(EFI_DEVICE_PATH *devpath)
561 {
562 
563 	BS->FreePool(devpath);
564 }
565 
566 EFI_DEVICE_PATH *
efi_devpath_last_node(EFI_DEVICE_PATH * devpath)567 efi_devpath_last_node(EFI_DEVICE_PATH *devpath)
568 {
569 
570 	if (IsDevicePathEnd(devpath))
571 		return (NULL);
572 	while (!IsDevicePathEnd(NextDevicePathNode(devpath)))
573 		devpath = NextDevicePathNode(devpath);
574 	return (devpath);
575 }
576 
577 /*
578  * Walk device path nodes, return next instance or end node.
579  */
580 EFI_DEVICE_PATH *
efi_devpath_next_instance(EFI_DEVICE_PATH * devpath)581 efi_devpath_next_instance(EFI_DEVICE_PATH *devpath)
582 {
583 	while (!IsDevicePathEnd(devpath)) {
584 		devpath = NextDevicePathNode(devpath);
585 		if (IsDevicePathEndType(devpath) &&
586 		    devpath->SubType == END_INSTANCE_DEVICE_PATH_SUBTYPE) {
587 			devpath = NextDevicePathNode(devpath);
588 			break;
589 		}
590 	}
591 	return (devpath);
592 }
593 
594 EFI_DEVICE_PATH *
efi_devpath_trim(EFI_DEVICE_PATH * devpath)595 efi_devpath_trim(EFI_DEVICE_PATH *devpath)
596 {
597 	EFI_DEVICE_PATH *node, *copy;
598 	size_t prefix, len;
599 
600 	if ((node = efi_devpath_last_node(devpath)) == NULL)
601 		return (NULL);
602 	prefix = (UINT8 *)node - (UINT8 *)devpath;
603 	if (prefix == 0)
604 		return (NULL);
605 	len = prefix + DevicePathNodeLength(NextDevicePathNode(node));
606 	copy = malloc(len);
607 	if (copy != NULL) {
608 		memcpy(copy, devpath, prefix);
609 		node = (EFI_DEVICE_PATH *)((UINT8 *)copy + prefix);
610 		SetDevicePathEndNode(node);
611 	}
612 	return (copy);
613 }
614 
615 EFI_HANDLE
efi_devpath_handle(EFI_DEVICE_PATH * devpath)616 efi_devpath_handle(EFI_DEVICE_PATH *devpath)
617 {
618 	EFI_STATUS status;
619 	EFI_HANDLE h;
620 
621 	/*
622 	 * There isn't a standard way to locate a handle for a given
623 	 * device path.  However, querying the EFI_DEVICE_PATH protocol
624 	 * for a given device path should give us a handle for the
625 	 * closest node in the path to the end that is valid.
626 	 */
627 	status = BS->LocateDevicePath(&DevicePathGUID, &devpath, &h);
628 	if (EFI_ERROR(status))
629 		return (NULL);
630 	return (h);
631 }
632 
633 bool
efi_devpath_match_node(EFI_DEVICE_PATH * devpath1,EFI_DEVICE_PATH * devpath2)634 efi_devpath_match_node(EFI_DEVICE_PATH *devpath1, EFI_DEVICE_PATH *devpath2)
635 {
636 	size_t len;
637 
638 	if (devpath1 == NULL || devpath2 == NULL)
639 		return (false);
640 	if (DevicePathType(devpath1) != DevicePathType(devpath2) ||
641 	    DevicePathSubType(devpath1) != DevicePathSubType(devpath2))
642 		return (false);
643 	len = DevicePathNodeLength(devpath1);
644 	if (len != DevicePathNodeLength(devpath2))
645 		return (false);
646 	if (memcmp(devpath1, devpath2, len) != 0)
647 		return (false);
648 	return (true);
649 }
650 
651 static bool
_efi_devpath_match(EFI_DEVICE_PATH * devpath1,EFI_DEVICE_PATH * devpath2,bool ignore_media)652 _efi_devpath_match(EFI_DEVICE_PATH *devpath1, EFI_DEVICE_PATH *devpath2,
653     bool ignore_media)
654 {
655 
656 	if (devpath1 == NULL || devpath2 == NULL)
657 		return (false);
658 
659 	while (true) {
660 		if (ignore_media &&
661 		    IsDevicePathType(devpath1, MEDIA_DEVICE_PATH) &&
662 		    IsDevicePathType(devpath2, MEDIA_DEVICE_PATH))
663 			return (true);
664 		if (!efi_devpath_match_node(devpath1, devpath2))
665 			return false;
666 		if (IsDevicePathEnd(devpath1))
667 			break;
668 		devpath1 = NextDevicePathNode(devpath1);
669 		devpath2 = NextDevicePathNode(devpath2);
670 	}
671 	return (true);
672 }
673 /*
674  * Are two devpaths identical?
675  */
676 bool
efi_devpath_match(EFI_DEVICE_PATH * devpath1,EFI_DEVICE_PATH * devpath2)677 efi_devpath_match(EFI_DEVICE_PATH *devpath1, EFI_DEVICE_PATH *devpath2)
678 {
679 	return _efi_devpath_match(devpath1, devpath2, false);
680 }
681 
682 /*
683  * Like efi_devpath_match, but stops at when we hit the media device
684  * path node that specifies the partition information. If we match
685  * up to that point, then we're on the same disk.
686  */
687 bool
efi_devpath_same_disk(EFI_DEVICE_PATH * devpath1,EFI_DEVICE_PATH * devpath2)688 efi_devpath_same_disk(EFI_DEVICE_PATH *devpath1, EFI_DEVICE_PATH *devpath2)
689 {
690 	return _efi_devpath_match(devpath1, devpath2, true);
691 }
692 
693 bool
efi_devpath_is_prefix(EFI_DEVICE_PATH * prefix,EFI_DEVICE_PATH * path)694 efi_devpath_is_prefix(EFI_DEVICE_PATH *prefix, EFI_DEVICE_PATH *path)
695 {
696 	size_t len;
697 
698 	if (prefix == NULL || path == NULL)
699 		return (false);
700 
701 	while (1) {
702 		if (IsDevicePathEnd(prefix))
703 			break;
704 
705 		if (DevicePathType(prefix) != DevicePathType(path) ||
706 		    DevicePathSubType(prefix) != DevicePathSubType(path))
707 			return (false);
708 
709 		len = DevicePathNodeLength(prefix);
710 		if (len != DevicePathNodeLength(path))
711 			return (false);
712 
713 		if (memcmp(prefix, path, len) != 0)
714 			return (false);
715 
716 		prefix = NextDevicePathNode(prefix);
717 		path = NextDevicePathNode(path);
718 	}
719 	return (true);
720 }
721 
722 /*
723  * Skip over the 'prefix' part of path and return the part of the path
724  * that starts with the first node that's a MEDIA_DEVICE_PATH.
725  */
726 EFI_DEVICE_PATH *
efi_devpath_to_media_path(EFI_DEVICE_PATH * path)727 efi_devpath_to_media_path(EFI_DEVICE_PATH *path)
728 {
729 
730 	while (!IsDevicePathEnd(path)) {
731 		if (DevicePathType(path) == MEDIA_DEVICE_PATH)
732 			return (path);
733 		path = NextDevicePathNode(path);
734 	}
735 	return (NULL);
736 }
737 
738 UINTN
efi_devpath_length(EFI_DEVICE_PATH * path)739 efi_devpath_length(EFI_DEVICE_PATH  *path)
740 {
741 	EFI_DEVICE_PATH *start = path;
742 
743 	while (!IsDevicePathEnd(path))
744 		path = NextDevicePathNode(path);
745 	return ((UINTN)path - (UINTN)start) + DevicePathNodeLength(path);
746 }
747 
748 EFI_HANDLE
efi_devpath_to_handle(EFI_DEVICE_PATH * path,EFI_HANDLE * handles,unsigned nhandles)749 efi_devpath_to_handle(EFI_DEVICE_PATH *path, EFI_HANDLE *handles, unsigned nhandles)
750 {
751 	unsigned i;
752 	EFI_DEVICE_PATH *media, *devpath;
753 	EFI_HANDLE h;
754 
755 	media = efi_devpath_to_media_path(path);
756 	if (media == NULL)
757 		return (NULL);
758 	for (i = 0; i < nhandles; i++) {
759 		h = handles[i];
760 		devpath = efi_lookup_devpath(h);
761 		if (devpath == NULL)
762 			continue;
763 		if (!efi_devpath_match_node(media, efi_devpath_to_media_path(devpath)))
764 			continue;
765 		return (h);
766 	}
767 	return (NULL);
768 }
769