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