xref: /freebsd/stand/efi/libefi/devpath.c (revision 454630c7)
1ca987d46SWarner Losh /*-
2ca987d46SWarner Losh  * Copyright (c) 2016 John Baldwin <jhb@FreeBSD.org>
3ca987d46SWarner Losh  *
4ca987d46SWarner Losh  * Redistribution and use in source and binary forms, with or without
5ca987d46SWarner Losh  * modification, are permitted provided that the following conditions
6ca987d46SWarner Losh  * are met:
7ca987d46SWarner Losh  * 1. Redistributions of source code must retain the above copyright
8ca987d46SWarner Losh  *    notice, this list of conditions and the following disclaimer.
9ca987d46SWarner Losh  * 2. Redistributions in binary form must reproduce the above copyright
10ca987d46SWarner Losh  *    notice, this list of conditions and the following disclaimer in the
11ca987d46SWarner Losh  *    documentation and/or other materials provided with the distribution.
12ca987d46SWarner Losh  *
13ca987d46SWarner Losh  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
14ca987d46SWarner Losh  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15ca987d46SWarner Losh  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16ca987d46SWarner Losh  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
17ca987d46SWarner Losh  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
18ca987d46SWarner Losh  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
19ca987d46SWarner Losh  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
20ca987d46SWarner Losh  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
21ca987d46SWarner Losh  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
22ca987d46SWarner Losh  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
23ca987d46SWarner Losh  * SUCH DAMAGE.
24ca987d46SWarner Losh  */
25ca987d46SWarner Losh 
26ca987d46SWarner Losh #include <sys/cdefs.h>
27ca987d46SWarner Losh __FBSDID("$FreeBSD$");
28ca987d46SWarner Losh 
29ca987d46SWarner Losh #include <efi.h>
30ca987d46SWarner Losh #include <efilib.h>
3116b07b25SWarner Losh #include <efichar.h>
325403ae84SToomas Soome #include <uuid.h>
335403ae84SToomas Soome #include <machine/_inttypes.h>
34ca987d46SWarner Losh 
35ca987d46SWarner Losh static EFI_GUID ImageDevicePathGUID =
36ca987d46SWarner Losh     EFI_LOADED_IMAGE_DEVICE_PATH_PROTOCOL_GUID;
37ca987d46SWarner Losh static EFI_GUID DevicePathGUID = DEVICE_PATH_PROTOCOL;
38ca987d46SWarner Losh static EFI_GUID DevicePathToTextGUID = EFI_DEVICE_PATH_TO_TEXT_PROTOCOL_GUID;
3916b07b25SWarner Losh static EFI_DEVICE_PATH_TO_TEXT_PROTOCOL *toTextProtocol;
405403ae84SToomas Soome static EFI_GUID DevicePathFromTextGUID =
415403ae84SToomas Soome     EFI_DEVICE_PATH_FROM_TEXT_PROTOCOL_GUID;
4216b07b25SWarner Losh static EFI_DEVICE_PATH_FROM_TEXT_PROTOCOL *fromTextProtocol;
43ca987d46SWarner Losh 
44ca987d46SWarner Losh EFI_DEVICE_PATH *
45ca987d46SWarner Losh efi_lookup_image_devpath(EFI_HANDLE handle)
46ca987d46SWarner Losh {
47ca987d46SWarner Losh 	EFI_DEVICE_PATH *devpath;
48ca987d46SWarner Losh 	EFI_STATUS status;
49ca987d46SWarner Losh 
50110d56cbSToomas Soome 	status = OpenProtocolByHandle(handle, &ImageDevicePathGUID,
51110d56cbSToomas Soome 	    (void **)&devpath);
52ca987d46SWarner Losh 	if (EFI_ERROR(status))
53ca987d46SWarner Losh 		devpath = NULL;
54ca987d46SWarner Losh 	return (devpath);
55ca987d46SWarner Losh }
56ca987d46SWarner Losh 
57ca987d46SWarner Losh EFI_DEVICE_PATH *
58ca987d46SWarner Losh efi_lookup_devpath(EFI_HANDLE handle)
59ca987d46SWarner Losh {
60ca987d46SWarner Losh 	EFI_DEVICE_PATH *devpath;
61ca987d46SWarner Losh 	EFI_STATUS status;
62ca987d46SWarner Losh 
63110d56cbSToomas Soome 	status = OpenProtocolByHandle(handle, &DevicePathGUID,
64110d56cbSToomas Soome 	    (void **)&devpath);
65ca987d46SWarner Losh 	if (EFI_ERROR(status))
66ca987d46SWarner Losh 		devpath = NULL;
67ca987d46SWarner Losh 	return (devpath);
68ca987d46SWarner Losh }
69ca987d46SWarner Losh 
70523a713fSToomas Soome void
71523a713fSToomas Soome efi_close_devpath(EFI_HANDLE handle)
72523a713fSToomas Soome {
73523a713fSToomas Soome 	EFI_STATUS status;
74523a713fSToomas Soome 
75523a713fSToomas Soome 	status = BS->CloseProtocol(handle, &DevicePathGUID, IH, NULL);
76523a713fSToomas Soome 	if (EFI_ERROR(status))
77523a713fSToomas Soome 		printf("CloseProtocol error: %lu\n", EFI_ERROR_CODE(status));
78523a713fSToomas Soome }
79523a713fSToomas Soome 
805403ae84SToomas Soome static char *
815403ae84SToomas Soome efi_make_tail(char *suffix)
825403ae84SToomas Soome {
835403ae84SToomas Soome 	char *tail;
845403ae84SToomas Soome 
855403ae84SToomas Soome 	tail = NULL;
865403ae84SToomas Soome 	if (suffix != NULL)
875403ae84SToomas Soome 		(void)asprintf(&tail, "/%s", suffix);
885403ae84SToomas Soome 	else
895403ae84SToomas Soome 		tail = strdup("");
905403ae84SToomas Soome 	return (tail);
915403ae84SToomas Soome }
925403ae84SToomas Soome 
935403ae84SToomas Soome typedef struct {
945403ae84SToomas Soome 	EFI_DEVICE_PATH	Header;
955403ae84SToomas Soome 	EFI_GUID	Guid;
965403ae84SToomas Soome 	UINT8		VendorDefinedData[1];
975403ae84SToomas Soome } __packed VENDOR_DEVICE_PATH_WITH_DATA;
985403ae84SToomas Soome 
995403ae84SToomas Soome static char *
1005403ae84SToomas Soome efi_vendor_path(const char *type, VENDOR_DEVICE_PATH *node, char *suffix)
1015403ae84SToomas Soome {
1025403ae84SToomas Soome 	uint32_t size = DevicePathNodeLength(&node->Header) - sizeof(*node);
1035403ae84SToomas Soome 	VENDOR_DEVICE_PATH_WITH_DATA *dp = (VENDOR_DEVICE_PATH_WITH_DATA *)node;
1045403ae84SToomas Soome 	char *name, *tail, *head;
1055403ae84SToomas Soome 	char *uuid;
1065403ae84SToomas Soome 	int rv;
1075403ae84SToomas Soome 
1085403ae84SToomas Soome 	uuid_to_string((const uuid_t *)(void *)&node->Guid, &uuid, &rv);
1095403ae84SToomas Soome 	if (rv != uuid_s_ok)
1105403ae84SToomas Soome 		return (NULL);
1115403ae84SToomas Soome 
1125403ae84SToomas Soome 	tail = efi_make_tail(suffix);
1135403ae84SToomas Soome 	rv = asprintf(&head, "%sVendor(%s)[%x:", type, uuid, size);
1145403ae84SToomas Soome 	free(uuid);
1155403ae84SToomas Soome 	if (rv < 0)
1165403ae84SToomas Soome 		return (NULL);
1175403ae84SToomas Soome 
1185403ae84SToomas Soome 	if (DevicePathNodeLength(&node->Header) > sizeof(*node)) {
1195403ae84SToomas Soome 		for (uint32_t i = 0; i < size; i++) {
1205403ae84SToomas Soome 			rv = asprintf(&name, "%s%02x", head,
1215403ae84SToomas Soome 			    dp->VendorDefinedData[i]);
1225403ae84SToomas Soome 			if (rv < 0) {
1235403ae84SToomas Soome 				free(tail);
1245403ae84SToomas Soome 				free(head);
1255403ae84SToomas Soome 				return (NULL);
1265403ae84SToomas Soome 			}
1275403ae84SToomas Soome 			free(head);
1285403ae84SToomas Soome 			head = name;
1295403ae84SToomas Soome 		}
1305403ae84SToomas Soome 	}
1315403ae84SToomas Soome 
1325403ae84SToomas Soome 	if (asprintf(&name, "%s]%s", head, tail) < 0)
1335403ae84SToomas Soome 		name = NULL;
1345403ae84SToomas Soome 	free(head);
1355403ae84SToomas Soome 	free(tail);
1365403ae84SToomas Soome 	return (name);
1375403ae84SToomas Soome }
1385403ae84SToomas Soome 
1395403ae84SToomas Soome static char *
1405403ae84SToomas Soome efi_hw_dev_path(EFI_DEVICE_PATH *node, char *suffix)
1415403ae84SToomas Soome {
1425403ae84SToomas Soome 	uint8_t subtype = DevicePathSubType(node);
1435403ae84SToomas Soome 	char *name, *tail;
1445403ae84SToomas Soome 
1455403ae84SToomas Soome 	tail = efi_make_tail(suffix);
1465403ae84SToomas Soome 	switch (subtype) {
1475403ae84SToomas Soome 	case HW_PCI_DP:
1485403ae84SToomas Soome 		if (asprintf(&name, "Pci(%x,%x)%s",
149454630c7SKyle Evans 		    ((PCI_DEVICE_PATH *)node)->Device,
150454630c7SKyle Evans 		    ((PCI_DEVICE_PATH *)node)->Function, tail) < 0)
1515403ae84SToomas Soome 			name = NULL;
1525403ae84SToomas Soome 		break;
1535403ae84SToomas Soome 	case HW_PCCARD_DP:
1545403ae84SToomas Soome 		if (asprintf(&name, "PCCARD(%x)%s",
1555403ae84SToomas Soome 		    ((PCCARD_DEVICE_PATH *)node)->FunctionNumber, tail) < 0)
1565403ae84SToomas Soome 			name = NULL;
1575403ae84SToomas Soome 		break;
1585403ae84SToomas Soome 	case HW_MEMMAP_DP:
1595403ae84SToomas Soome 		if (asprintf(&name, "MMap(%x,%" PRIx64 ",%" PRIx64 ")%s",
1605403ae84SToomas Soome 		    ((MEMMAP_DEVICE_PATH *)node)->MemoryType,
1615403ae84SToomas Soome 		    ((MEMMAP_DEVICE_PATH *)node)->StartingAddress,
1625403ae84SToomas Soome 		    ((MEMMAP_DEVICE_PATH *)node)->EndingAddress, tail) < 0)
1635403ae84SToomas Soome 			name = NULL;
1645403ae84SToomas Soome 		break;
1655403ae84SToomas Soome 	case HW_VENDOR_DP:
1665403ae84SToomas Soome 		name = efi_vendor_path("Hardware",
1675403ae84SToomas Soome 		    (VENDOR_DEVICE_PATH *)node, tail);
1685403ae84SToomas Soome 		break;
1695403ae84SToomas Soome 	case HW_CONTROLLER_DP:
1705403ae84SToomas Soome 		if (asprintf(&name, "Ctrl(%x)%s",
1715403ae84SToomas Soome 		    ((CONTROLLER_DEVICE_PATH *)node)->Controller, tail) < 0)
1725403ae84SToomas Soome 			name = NULL;
1735403ae84SToomas Soome 		break;
1745403ae84SToomas Soome 	default:
1755403ae84SToomas Soome 		if (asprintf(&name, "UnknownHW(%x)%s", subtype, tail) < 0)
1765403ae84SToomas Soome 			name = NULL;
1775403ae84SToomas Soome 		break;
1785403ae84SToomas Soome 	}
1795403ae84SToomas Soome 	free(tail);
1805403ae84SToomas Soome 	return (name);
1815403ae84SToomas Soome }
1825403ae84SToomas Soome 
1835403ae84SToomas Soome static char *
1845403ae84SToomas Soome efi_acpi_dev_path(EFI_DEVICE_PATH *node, char *suffix)
1855403ae84SToomas Soome {
1865403ae84SToomas Soome 	uint8_t subtype = DevicePathSubType(node);
1875403ae84SToomas Soome 	ACPI_HID_DEVICE_PATH *acpi = (ACPI_HID_DEVICE_PATH *)node;
1885403ae84SToomas Soome 	char *name, *tail;
1895403ae84SToomas Soome 
1905403ae84SToomas Soome 	tail = efi_make_tail(suffix);
1915403ae84SToomas Soome 	switch (subtype) {
1925403ae84SToomas Soome 	case ACPI_DP:
1935403ae84SToomas Soome 		if ((acpi->HID & PNP_EISA_ID_MASK) == PNP_EISA_ID_CONST) {
1945403ae84SToomas Soome 			switch (EISA_ID_TO_NUM (acpi->HID)) {
1955403ae84SToomas Soome 			case 0x0a03:
1965403ae84SToomas Soome 				if (asprintf(&name, "PciRoot(%x)%s",
1975403ae84SToomas Soome 				    acpi->UID, tail) < 0)
1985403ae84SToomas Soome 					name = NULL;
1995403ae84SToomas Soome 				break;
2005403ae84SToomas Soome 			case 0x0a08:
2015403ae84SToomas Soome 				if (asprintf(&name, "PcieRoot(%x)%s",
2025403ae84SToomas Soome 				    acpi->UID, tail) < 0)
2035403ae84SToomas Soome 					name = NULL;
2045403ae84SToomas Soome 				break;
2055403ae84SToomas Soome 			case 0x0604:
2065403ae84SToomas Soome 				if (asprintf(&name, "Floppy(%x)%s",
2075403ae84SToomas Soome 				    acpi->UID, tail) < 0)
2085403ae84SToomas Soome 					name = NULL;
2095403ae84SToomas Soome 				break;
2105403ae84SToomas Soome 			case 0x0301:
2115403ae84SToomas Soome 				if (asprintf(&name, "Keyboard(%x)%s",
2125403ae84SToomas Soome 				    acpi->UID, tail) < 0)
2135403ae84SToomas Soome 					name = NULL;
2145403ae84SToomas Soome 				break;
2155403ae84SToomas Soome 			case 0x0501:
2165403ae84SToomas Soome 				if (asprintf(&name, "Serial(%x)%s",
2175403ae84SToomas Soome 				    acpi->UID, tail) < 0)
2185403ae84SToomas Soome 					name = NULL;
2195403ae84SToomas Soome 				break;
2205403ae84SToomas Soome 			case 0x0401:
2215403ae84SToomas Soome 				if (asprintf(&name, "ParallelPort(%x)%s",
2225403ae84SToomas Soome 				    acpi->UID, tail) < 0)
2235403ae84SToomas Soome 					name = NULL;
2245403ae84SToomas Soome 				break;
2255403ae84SToomas Soome 			default:
2265403ae84SToomas Soome 				if (asprintf(&name, "Acpi(PNP%04x,%x)%s",
2275403ae84SToomas Soome 				    EISA_ID_TO_NUM(acpi->HID),
2285403ae84SToomas Soome 				    acpi->UID, tail) < 0)
2295403ae84SToomas Soome 					name = NULL;
2305403ae84SToomas Soome 				break;
2315403ae84SToomas Soome 			}
2325403ae84SToomas Soome 		} else {
2335403ae84SToomas Soome 			if (asprintf(&name, "Acpi(%08x,%x)%s",
2345403ae84SToomas Soome 			    acpi->HID, acpi->UID, tail) < 0)
2355403ae84SToomas Soome 				name = NULL;
2365403ae84SToomas Soome 		}
2375403ae84SToomas Soome 		break;
2385403ae84SToomas Soome 	case ACPI_EXTENDED_DP:
2395403ae84SToomas Soome 	default:
2405403ae84SToomas Soome 		if (asprintf(&name, "UnknownACPI(%x)%s", subtype, tail) < 0)
2415403ae84SToomas Soome 			name = NULL;
2425403ae84SToomas Soome 		break;
2435403ae84SToomas Soome 	}
2445403ae84SToomas Soome 	free(tail);
2455403ae84SToomas Soome 	return (name);
2465403ae84SToomas Soome }
2475403ae84SToomas Soome 
2485403ae84SToomas Soome static char *
2495403ae84SToomas Soome efi_messaging_dev_path(EFI_DEVICE_PATH *node, char *suffix)
2505403ae84SToomas Soome {
2515403ae84SToomas Soome 	uint8_t subtype = DevicePathSubType(node);
2525403ae84SToomas Soome 	char *name;
2535403ae84SToomas Soome 	char *tail;
2545403ae84SToomas Soome 
2555403ae84SToomas Soome 	tail = efi_make_tail(suffix);
2565403ae84SToomas Soome 	switch (subtype) {
2575403ae84SToomas Soome 	case MSG_ATAPI_DP:
2585403ae84SToomas Soome 		if (asprintf(&name, "ATA(%s,%s,%x)%s",
2595403ae84SToomas Soome 		    ((ATAPI_DEVICE_PATH *)node)->PrimarySecondary == 1 ?
2605403ae84SToomas Soome 		    "Secondary" : "Primary",
2615403ae84SToomas Soome 		    ((ATAPI_DEVICE_PATH *)node)->SlaveMaster == 1 ?
2625403ae84SToomas Soome 		    "Slave" : "Master",
2635403ae84SToomas Soome 		    ((ATAPI_DEVICE_PATH *)node)->Lun, tail) < 0)
2645403ae84SToomas Soome 			name = NULL;
2655403ae84SToomas Soome 		break;
2665403ae84SToomas Soome 	case MSG_SCSI_DP:
2675403ae84SToomas Soome 		if (asprintf(&name, "SCSI(%x,%x)%s",
2685403ae84SToomas Soome 		    ((SCSI_DEVICE_PATH *)node)->Pun,
2695403ae84SToomas Soome 		    ((SCSI_DEVICE_PATH *)node)->Lun, tail) < 0)
2705403ae84SToomas Soome 			name = NULL;
2715403ae84SToomas Soome 		break;
2725403ae84SToomas Soome 	case MSG_FIBRECHANNEL_DP:
2735403ae84SToomas Soome 		if (asprintf(&name, "Fibre(%" PRIx64 ",%" PRIx64 ")%s",
2745403ae84SToomas Soome 		    ((FIBRECHANNEL_DEVICE_PATH *)node)->WWN,
2755403ae84SToomas Soome 		    ((FIBRECHANNEL_DEVICE_PATH *)node)->Lun, tail) < 0)
2765403ae84SToomas Soome 			name = NULL;
2775403ae84SToomas Soome 		break;
2785403ae84SToomas Soome 	case MSG_1394_DP:
2795403ae84SToomas Soome 		if (asprintf(&name, "I1394(%016" PRIx64 ")%s",
2805403ae84SToomas Soome 		    ((F1394_DEVICE_PATH *)node)->Guid, tail) < 0)
2815403ae84SToomas Soome 			name = NULL;
2825403ae84SToomas Soome 		break;
2835403ae84SToomas Soome 	case MSG_USB_DP:
2845403ae84SToomas Soome 		if (asprintf(&name, "USB(%x,%x)%s",
2855403ae84SToomas Soome 		    ((USB_DEVICE_PATH *)node)->ParentPortNumber,
2865403ae84SToomas Soome 		    ((USB_DEVICE_PATH *)node)->InterfaceNumber, tail) < 0)
2875403ae84SToomas Soome 			name = NULL;
2885403ae84SToomas Soome 		break;
2895403ae84SToomas Soome 	case MSG_USB_CLASS_DP:
2905403ae84SToomas Soome 		if (asprintf(&name, "UsbClass(%x,%x,%x,%x,%x)%s",
2915403ae84SToomas Soome 		    ((USB_CLASS_DEVICE_PATH *)node)->VendorId,
2925403ae84SToomas Soome 		    ((USB_CLASS_DEVICE_PATH *)node)->ProductId,
2935403ae84SToomas Soome 		    ((USB_CLASS_DEVICE_PATH *)node)->DeviceClass,
2945403ae84SToomas Soome 		    ((USB_CLASS_DEVICE_PATH *)node)->DeviceSubClass,
2955403ae84SToomas Soome 		    ((USB_CLASS_DEVICE_PATH *)node)->DeviceProtocol, tail) < 0)
2965403ae84SToomas Soome 			name = NULL;
2975403ae84SToomas Soome 		break;
2985403ae84SToomas Soome 	case MSG_MAC_ADDR_DP:
2995403ae84SToomas Soome 		if (asprintf(&name, "MAC(%02x:%02x:%02x:%02x:%02x:%02x,%x)%s",
3005403ae84SToomas Soome 		    ((MAC_ADDR_DEVICE_PATH *)node)->MacAddress.Addr[0],
3015403ae84SToomas Soome 		    ((MAC_ADDR_DEVICE_PATH *)node)->MacAddress.Addr[1],
3025403ae84SToomas Soome 		    ((MAC_ADDR_DEVICE_PATH *)node)->MacAddress.Addr[2],
3035403ae84SToomas Soome 		    ((MAC_ADDR_DEVICE_PATH *)node)->MacAddress.Addr[3],
3045403ae84SToomas Soome 		    ((MAC_ADDR_DEVICE_PATH *)node)->MacAddress.Addr[4],
3055403ae84SToomas Soome 		    ((MAC_ADDR_DEVICE_PATH *)node)->MacAddress.Addr[5],
3065403ae84SToomas Soome 		    ((MAC_ADDR_DEVICE_PATH *)node)->IfType, tail) < 0)
3075403ae84SToomas Soome 			name = NULL;
3085403ae84SToomas Soome 		break;
3095403ae84SToomas Soome 	case MSG_VENDOR_DP:
3105403ae84SToomas Soome 		name = efi_vendor_path("Messaging",
3115403ae84SToomas Soome 		    (VENDOR_DEVICE_PATH *)node, tail);
3125403ae84SToomas Soome 		break;
3135403ae84SToomas Soome 	case MSG_UART_DP:
3145403ae84SToomas Soome 		if (asprintf(&name, "UART(%" PRIu64 ",%u,%x,%x)%s",
3155403ae84SToomas Soome 		    ((UART_DEVICE_PATH *)node)->BaudRate,
3165403ae84SToomas Soome 		    ((UART_DEVICE_PATH *)node)->DataBits,
3175403ae84SToomas Soome 		    ((UART_DEVICE_PATH *)node)->Parity,
3185403ae84SToomas Soome 		    ((UART_DEVICE_PATH *)node)->StopBits, tail) < 0)
3195403ae84SToomas Soome 			name = NULL;
3205403ae84SToomas Soome 		break;
3215403ae84SToomas Soome 	case MSG_SATA_DP:
3225403ae84SToomas Soome 		if (asprintf(&name, "Sata(%x,%x,%x)%s",
3235403ae84SToomas Soome 		    ((SATA_DEVICE_PATH *)node)->HBAPortNumber,
3245403ae84SToomas Soome 		    ((SATA_DEVICE_PATH *)node)->PortMultiplierPortNumber,
3255403ae84SToomas Soome 		    ((SATA_DEVICE_PATH *)node)->Lun, tail) < 0)
3265403ae84SToomas Soome 			name = NULL;
3275403ae84SToomas Soome 		break;
3285403ae84SToomas Soome 	default:
3295403ae84SToomas Soome 		if (asprintf(&name, "UnknownMessaging(%x)%s",
3305403ae84SToomas Soome 		    subtype, tail) < 0)
3315403ae84SToomas Soome 			name = NULL;
3325403ae84SToomas Soome 		break;
3335403ae84SToomas Soome 	}
3345403ae84SToomas Soome 	free(tail);
3355403ae84SToomas Soome 	return (name);
3365403ae84SToomas Soome }
3375403ae84SToomas Soome 
3385403ae84SToomas Soome static char *
3395403ae84SToomas Soome efi_media_dev_path(EFI_DEVICE_PATH *node, char *suffix)
3405403ae84SToomas Soome {
3415403ae84SToomas Soome 	uint8_t subtype = DevicePathSubType(node);
3425403ae84SToomas Soome 	HARDDRIVE_DEVICE_PATH *hd;
3435403ae84SToomas Soome 	char *name;
3445403ae84SToomas Soome 	char *str;
3455403ae84SToomas Soome 	char *tail;
3465403ae84SToomas Soome 	int rv;
3475403ae84SToomas Soome 
3485403ae84SToomas Soome 	tail = efi_make_tail(suffix);
3495403ae84SToomas Soome 	name = NULL;
3505403ae84SToomas Soome 	switch (subtype) {
3515403ae84SToomas Soome 	case MEDIA_HARDDRIVE_DP:
3525403ae84SToomas Soome 		hd = (HARDDRIVE_DEVICE_PATH *)node;
3535403ae84SToomas Soome 		switch (hd->SignatureType) {
3545403ae84SToomas Soome 		case SIGNATURE_TYPE_MBR:
3555403ae84SToomas Soome 			if (asprintf(&name, "HD(%d,MBR,%08x,%" PRIx64
3565403ae84SToomas Soome 			    ",%" PRIx64 ")%s",
3575403ae84SToomas Soome 			    hd->PartitionNumber,
3585403ae84SToomas Soome 			    *((uint32_t *)(uintptr_t)&hd->Signature[0]),
3595403ae84SToomas Soome 			    hd->PartitionStart,
3605403ae84SToomas Soome 			    hd->PartitionSize, tail) < 0)
3615403ae84SToomas Soome 				name = NULL;
3625403ae84SToomas Soome 			break;
3635403ae84SToomas Soome 		case SIGNATURE_TYPE_GUID:
3645403ae84SToomas Soome 			name = NULL;
3655403ae84SToomas Soome 			uuid_to_string((const uuid_t *)(void *)
3665403ae84SToomas Soome 			    &hd->Signature[0], &str, &rv);
3675403ae84SToomas Soome 			if (rv != uuid_s_ok)
3685403ae84SToomas Soome 				break;
3695403ae84SToomas Soome 			rv = asprintf(&name, "HD(%d,GPT,%s,%" PRIx64 ",%"
3705403ae84SToomas Soome 			    PRIx64 ")%s",
3715403ae84SToomas Soome 			    hd->PartitionNumber, str,
3725403ae84SToomas Soome 			    hd->PartitionStart, hd->PartitionSize, tail);
3735403ae84SToomas Soome 			free(str);
3745403ae84SToomas Soome 			break;
3755403ae84SToomas Soome 		default:
3765403ae84SToomas Soome 			if (asprintf(&name, "HD(%d,%d,0)%s",
3775403ae84SToomas Soome 			    hd->PartitionNumber,
3785403ae84SToomas Soome 			    hd->SignatureType, tail) < 0) {
3795403ae84SToomas Soome 				name = NULL;
3805403ae84SToomas Soome 			}
3815403ae84SToomas Soome 			break;
3825403ae84SToomas Soome 		}
3835403ae84SToomas Soome 		break;
3845403ae84SToomas Soome 	case MEDIA_CDROM_DP:
3855403ae84SToomas Soome 		if (asprintf(&name, "CD(%x,%" PRIx64 ",%" PRIx64 ")%s",
3865403ae84SToomas Soome 		    ((CDROM_DEVICE_PATH *)node)->BootEntry,
3875403ae84SToomas Soome 		    ((CDROM_DEVICE_PATH *)node)->PartitionStart,
3885403ae84SToomas Soome 		    ((CDROM_DEVICE_PATH *)node)->PartitionSize, tail) < 0) {
3895403ae84SToomas Soome 			name = NULL;
3905403ae84SToomas Soome 		}
3915403ae84SToomas Soome 		break;
3925403ae84SToomas Soome 	case MEDIA_VENDOR_DP:
3935403ae84SToomas Soome 		name = efi_vendor_path("Media",
3945403ae84SToomas Soome 		    (VENDOR_DEVICE_PATH *)node, tail);
3955403ae84SToomas Soome 		break;
3965403ae84SToomas Soome 	case MEDIA_FILEPATH_DP:
3975403ae84SToomas Soome 		name = NULL;
3985403ae84SToomas Soome 		str = NULL;
3995403ae84SToomas Soome 		if (ucs2_to_utf8(((FILEPATH_DEVICE_PATH *)node)->PathName,
4005403ae84SToomas Soome 		    &str) == 0) {
4015403ae84SToomas Soome 			(void)asprintf(&name, "%s%s", str, tail);
4025403ae84SToomas Soome 			free(str);
4035403ae84SToomas Soome 		}
4045403ae84SToomas Soome 		break;
4055403ae84SToomas Soome 	case MEDIA_PROTOCOL_DP:
4065403ae84SToomas Soome 		name = NULL;
4075403ae84SToomas Soome 		uuid_to_string((const uuid_t *)(void *)
4085403ae84SToomas Soome 		    &((MEDIA_PROTOCOL_DEVICE_PATH *)node)->Protocol,
4095403ae84SToomas Soome 		    &str, &rv);
4105403ae84SToomas Soome 		if (rv != uuid_s_ok)
4115403ae84SToomas Soome 			break;
4125403ae84SToomas Soome 		rv = asprintf(&name, "Protocol(%s)%s", str, tail);
4135403ae84SToomas Soome 		free(str);
4145403ae84SToomas Soome 		break;
4155403ae84SToomas Soome 	default:
4165403ae84SToomas Soome 		if (asprintf(&name, "UnknownMedia(%x)%s",
4175403ae84SToomas Soome 		    subtype, tail) < 0)
4185403ae84SToomas Soome 			name = NULL;
4195403ae84SToomas Soome 	}
4205403ae84SToomas Soome 	free(tail);
4215403ae84SToomas Soome 	return (name);
4225403ae84SToomas Soome }
4235403ae84SToomas Soome 
4245403ae84SToomas Soome static char *
4255403ae84SToomas Soome efi_translate_devpath(EFI_DEVICE_PATH *devpath)
4265403ae84SToomas Soome {
4275403ae84SToomas Soome 	EFI_DEVICE_PATH *dp = NextDevicePathNode(devpath);
4285403ae84SToomas Soome 	char *name, *ptr;
4295403ae84SToomas Soome 	uint8_t type;
4305403ae84SToomas Soome 
4315403ae84SToomas Soome 	if (!IsDevicePathEnd(devpath))
4325403ae84SToomas Soome 		name = efi_translate_devpath(dp);
4335403ae84SToomas Soome 	else
4345403ae84SToomas Soome 		return (NULL);
4355403ae84SToomas Soome 
4365403ae84SToomas Soome 	ptr = NULL;
4375403ae84SToomas Soome 	type = DevicePathType(devpath);
4385403ae84SToomas Soome 	switch (type) {
4395403ae84SToomas Soome 	case HARDWARE_DEVICE_PATH:
4405403ae84SToomas Soome 		ptr = efi_hw_dev_path(devpath, name);
4415403ae84SToomas Soome 		break;
4425403ae84SToomas Soome 	case ACPI_DEVICE_PATH:
4435403ae84SToomas Soome 		ptr = efi_acpi_dev_path(devpath, name);
4445403ae84SToomas Soome 		break;
4455403ae84SToomas Soome 	case MESSAGING_DEVICE_PATH:
4465403ae84SToomas Soome 		ptr = efi_messaging_dev_path(devpath, name);
4475403ae84SToomas Soome 		break;
4485403ae84SToomas Soome 	case MEDIA_DEVICE_PATH:
4495403ae84SToomas Soome 		ptr = efi_media_dev_path(devpath, name);
4505403ae84SToomas Soome 		break;
4515403ae84SToomas Soome 	case BBS_DEVICE_PATH:
4525403ae84SToomas Soome 	default:
4535403ae84SToomas Soome 		if (asprintf(&ptr, "UnknownPath(%x)%s", type,
4545403ae84SToomas Soome 		    name? name : "") < 0)
4555403ae84SToomas Soome 			ptr = NULL;
4565403ae84SToomas Soome 		break;
4575403ae84SToomas Soome 	}
4585403ae84SToomas Soome 
4595403ae84SToomas Soome 	if (ptr != NULL) {
4605403ae84SToomas Soome 		free(name);
4615403ae84SToomas Soome 		name = ptr;
4625403ae84SToomas Soome 	}
4635403ae84SToomas Soome 	return (name);
4645403ae84SToomas Soome }
4655403ae84SToomas Soome 
4665403ae84SToomas Soome static CHAR16 *
4675403ae84SToomas Soome efi_devpath_to_name(EFI_DEVICE_PATH *devpath)
4685403ae84SToomas Soome {
4695403ae84SToomas Soome 	char *name = NULL;
4705403ae84SToomas Soome 	CHAR16 *ptr = NULL;
4715403ae84SToomas Soome 	size_t len;
4725403ae84SToomas Soome 	int rv;
4735403ae84SToomas Soome 
4745403ae84SToomas Soome 	name = efi_translate_devpath(devpath);
4755403ae84SToomas Soome 	if (name == NULL)
4765403ae84SToomas Soome 		return (NULL);
4775403ae84SToomas Soome 
4785403ae84SToomas Soome 	/*
4795403ae84SToomas Soome 	 * We need to return memory from AllocatePool, so it can be freed
4805403ae84SToomas Soome 	 * with FreePool() in efi_free_devpath_name().
4815403ae84SToomas Soome 	 */
4825403ae84SToomas Soome 	rv = utf8_to_ucs2(name, &ptr, &len);
4835403ae84SToomas Soome 	free(name);
4845403ae84SToomas Soome 	if (rv == 0) {
4855403ae84SToomas Soome 		CHAR16 *out = NULL;
4865403ae84SToomas Soome 		EFI_STATUS status;
4875403ae84SToomas Soome 
4885403ae84SToomas Soome 		status = BS->AllocatePool(EfiLoaderData, len, (void **)&out);
4895403ae84SToomas Soome 		if (EFI_ERROR(status)) {
4905403ae84SToomas Soome 			free(ptr);
4915403ae84SToomas Soome                 	return (out);
4925403ae84SToomas Soome 		}
4935403ae84SToomas Soome 		memcpy(out, ptr, len);
4945403ae84SToomas Soome 		free(ptr);
4955403ae84SToomas Soome 		ptr = out;
4965403ae84SToomas Soome 	}
4975403ae84SToomas Soome 
4985403ae84SToomas Soome 	return (ptr);
4995403ae84SToomas Soome }
5005403ae84SToomas Soome 
501ca987d46SWarner Losh CHAR16 *
502ca987d46SWarner Losh efi_devpath_name(EFI_DEVICE_PATH *devpath)
503ca987d46SWarner Losh {
504ca987d46SWarner Losh 	EFI_STATUS status;
505ca987d46SWarner Losh 
506ca987d46SWarner Losh 	if (devpath == NULL)
507ca987d46SWarner Losh 		return (NULL);
50816b07b25SWarner Losh 	if (toTextProtocol == NULL) {
509ca987d46SWarner Losh 		status = BS->LocateProtocol(&DevicePathToTextGUID, NULL,
51016b07b25SWarner Losh 		    (VOID **)&toTextProtocol);
511ca987d46SWarner Losh 		if (EFI_ERROR(status))
51216b07b25SWarner Losh 			toTextProtocol = NULL;
513ca987d46SWarner Losh 	}
51416b07b25SWarner Losh 	if (toTextProtocol == NULL)
5155403ae84SToomas Soome 		return (efi_devpath_to_name(devpath));
516ca987d46SWarner Losh 
51716b07b25SWarner Losh 	return (toTextProtocol->ConvertDevicePathToText(devpath, TRUE, TRUE));
518ca987d46SWarner Losh }
519ca987d46SWarner Losh 
520ca987d46SWarner Losh void
521ca987d46SWarner Losh efi_free_devpath_name(CHAR16 *text)
522ca987d46SWarner Losh {
5235403ae84SToomas Soome 	if (text != NULL)
524ca987d46SWarner Losh 		BS->FreePool(text);
525ca987d46SWarner Losh }
526ca987d46SWarner Losh 
527ca987d46SWarner Losh EFI_DEVICE_PATH *
52816b07b25SWarner Losh efi_name_to_devpath(const char *path)
52916b07b25SWarner Losh {
53016b07b25SWarner Losh 	EFI_DEVICE_PATH *devpath;
53116b07b25SWarner Losh 	CHAR16 *uv;
53216b07b25SWarner Losh 	size_t ul;
53316b07b25SWarner Losh 
53416b07b25SWarner Losh 	uv = NULL;
53516b07b25SWarner Losh 	if (utf8_to_ucs2(path, &uv, &ul) != 0)
53616b07b25SWarner Losh 		return (NULL);
53716b07b25SWarner Losh 	devpath = efi_name_to_devpath16(uv);
53816b07b25SWarner Losh 	free(uv);
53916b07b25SWarner Losh 	return (devpath);
54016b07b25SWarner Losh }
54116b07b25SWarner Losh 
54216b07b25SWarner Losh EFI_DEVICE_PATH *
54316b07b25SWarner Losh efi_name_to_devpath16(CHAR16 *path)
54416b07b25SWarner Losh {
54516b07b25SWarner Losh 	EFI_STATUS status;
54616b07b25SWarner Losh 
54716b07b25SWarner Losh 	if (path == NULL)
54816b07b25SWarner Losh 		return (NULL);
54916b07b25SWarner Losh 	if (fromTextProtocol == NULL) {
55016b07b25SWarner Losh 		status = BS->LocateProtocol(&DevicePathFromTextGUID, NULL,
55116b07b25SWarner Losh 		    (VOID **)&fromTextProtocol);
55216b07b25SWarner Losh 		if (EFI_ERROR(status))
55316b07b25SWarner Losh 			fromTextProtocol = NULL;
55416b07b25SWarner Losh 	}
55516b07b25SWarner Losh 	if (fromTextProtocol == NULL)
55616b07b25SWarner Losh 		return (NULL);
55716b07b25SWarner Losh 
55816b07b25SWarner Losh 	return (fromTextProtocol->ConvertTextToDevicePath(path));
55916b07b25SWarner Losh }
56016b07b25SWarner Losh 
56116b07b25SWarner Losh void efi_devpath_free(EFI_DEVICE_PATH *devpath)
56216b07b25SWarner Losh {
56316b07b25SWarner Losh 
56416b07b25SWarner Losh 	BS->FreePool(devpath);
56516b07b25SWarner Losh }
56616b07b25SWarner Losh 
56716b07b25SWarner Losh EFI_DEVICE_PATH *
568ca987d46SWarner Losh efi_devpath_last_node(EFI_DEVICE_PATH *devpath)
569ca987d46SWarner Losh {
570ca987d46SWarner Losh 
571ca987d46SWarner Losh 	if (IsDevicePathEnd(devpath))
572ca987d46SWarner Losh 		return (NULL);
573ca987d46SWarner Losh 	while (!IsDevicePathEnd(NextDevicePathNode(devpath)))
574ca987d46SWarner Losh 		devpath = NextDevicePathNode(devpath);
575ca987d46SWarner Losh 	return (devpath);
576ca987d46SWarner Losh }
577ca987d46SWarner Losh 
578ca987d46SWarner Losh EFI_DEVICE_PATH *
579ca987d46SWarner Losh efi_devpath_trim(EFI_DEVICE_PATH *devpath)
580ca987d46SWarner Losh {
581ca987d46SWarner Losh 	EFI_DEVICE_PATH *node, *copy;
582ca987d46SWarner Losh 	size_t prefix, len;
583ca987d46SWarner Losh 
584ca987d46SWarner Losh 	if ((node = efi_devpath_last_node(devpath)) == NULL)
585ca987d46SWarner Losh 		return (NULL);
586ca987d46SWarner Losh 	prefix = (UINT8 *)node - (UINT8 *)devpath;
587ca987d46SWarner Losh 	if (prefix == 0)
588ca987d46SWarner Losh 		return (NULL);
589ca987d46SWarner Losh 	len = prefix + DevicePathNodeLength(NextDevicePathNode(node));
590ca987d46SWarner Losh 	copy = malloc(len);
591ca987d46SWarner Losh 	if (copy != NULL) {
592ca987d46SWarner Losh 		memcpy(copy, devpath, prefix);
593ca987d46SWarner Losh 		node = (EFI_DEVICE_PATH *)((UINT8 *)copy + prefix);
594ca987d46SWarner Losh 		SetDevicePathEndNode(node);
595ca987d46SWarner Losh 	}
596ca987d46SWarner Losh 	return (copy);
597ca987d46SWarner Losh }
598ca987d46SWarner Losh 
599ca987d46SWarner Losh EFI_HANDLE
600ca987d46SWarner Losh efi_devpath_handle(EFI_DEVICE_PATH *devpath)
601ca987d46SWarner Losh {
602ca987d46SWarner Losh 	EFI_STATUS status;
603ca987d46SWarner Losh 	EFI_HANDLE h;
604ca987d46SWarner Losh 
605ca987d46SWarner Losh 	/*
606ca987d46SWarner Losh 	 * There isn't a standard way to locate a handle for a given
607ca987d46SWarner Losh 	 * device path.  However, querying the EFI_DEVICE_PATH protocol
608ca987d46SWarner Losh 	 * for a given device path should give us a handle for the
609ca987d46SWarner Losh 	 * closest node in the path to the end that is valid.
610ca987d46SWarner Losh 	 */
611ca987d46SWarner Losh 	status = BS->LocateDevicePath(&DevicePathGUID, &devpath, &h);
612ca987d46SWarner Losh 	if (EFI_ERROR(status))
613ca987d46SWarner Losh 		return (NULL);
614ca987d46SWarner Losh 	return (h);
615ca987d46SWarner Losh }
616ca987d46SWarner Losh 
617ca987d46SWarner Losh bool
61813850b36SWarner Losh efi_devpath_match_node(EFI_DEVICE_PATH *devpath1, EFI_DEVICE_PATH *devpath2)
619ca987d46SWarner Losh {
620ca987d46SWarner Losh 	size_t len;
621ca987d46SWarner Losh 
622ca987d46SWarner Losh 	if (devpath1 == NULL || devpath2 == NULL)
623ca987d46SWarner Losh 		return (false);
624ca987d46SWarner Losh 	if (DevicePathType(devpath1) != DevicePathType(devpath2) ||
625ca987d46SWarner Losh 	    DevicePathSubType(devpath1) != DevicePathSubType(devpath2))
626ca987d46SWarner Losh 		return (false);
627ca987d46SWarner Losh 	len = DevicePathNodeLength(devpath1);
628ca987d46SWarner Losh 	if (len != DevicePathNodeLength(devpath2))
629ca987d46SWarner Losh 		return (false);
630ca987d46SWarner Losh 	if (memcmp(devpath1, devpath2, len) != 0)
631ca987d46SWarner Losh 		return (false);
63213850b36SWarner Losh 	return (true);
63313850b36SWarner Losh }
634ca987d46SWarner Losh 
63583ffeb8bSWarner Losh static bool
63683ffeb8bSWarner Losh _efi_devpath_match(EFI_DEVICE_PATH *devpath1, EFI_DEVICE_PATH *devpath2,
63783ffeb8bSWarner Losh     bool ignore_media)
63813850b36SWarner Losh {
63913850b36SWarner Losh 
64013850b36SWarner Losh 	if (devpath1 == NULL || devpath2 == NULL)
64113850b36SWarner Losh 		return (false);
64213850b36SWarner Losh 
64313850b36SWarner Losh 	while (true) {
64483ffeb8bSWarner Losh 		if (ignore_media &&
64583ffeb8bSWarner Losh 		    IsDevicePathType(devpath1, MEDIA_DEVICE_PATH) &&
64683ffeb8bSWarner Losh 		    IsDevicePathType(devpath2, MEDIA_DEVICE_PATH))
64783ffeb8bSWarner Losh 			return (true);
64813850b36SWarner Losh 		if (!efi_devpath_match_node(devpath1, devpath2))
64913850b36SWarner Losh 			return false;
650ca987d46SWarner Losh 		if (IsDevicePathEnd(devpath1))
651ca987d46SWarner Losh 			break;
652ca987d46SWarner Losh 		devpath1 = NextDevicePathNode(devpath1);
653ca987d46SWarner Losh 		devpath2 = NextDevicePathNode(devpath2);
654ca987d46SWarner Losh 	}
655ca987d46SWarner Losh 	return (true);
656ca987d46SWarner Losh }
65783ffeb8bSWarner Losh /*
65883ffeb8bSWarner Losh  * Are two devpaths identical?
65983ffeb8bSWarner Losh  */
66083ffeb8bSWarner Losh bool
66183ffeb8bSWarner Losh efi_devpath_match(EFI_DEVICE_PATH *devpath1, EFI_DEVICE_PATH *devpath2)
66283ffeb8bSWarner Losh {
66383ffeb8bSWarner Losh 	return _efi_devpath_match(devpath1, devpath2, false);
66483ffeb8bSWarner Losh }
66583ffeb8bSWarner Losh 
66683ffeb8bSWarner Losh /*
66783ffeb8bSWarner Losh  * Like efi_devpath_match, but stops at when we hit the media device
66883ffeb8bSWarner Losh  * path node that specifies the partition information. If we match
66983ffeb8bSWarner Losh  * up to that point, then we're on the same disk.
67083ffeb8bSWarner Losh  */
67183ffeb8bSWarner Losh bool
67283ffeb8bSWarner Losh efi_devpath_same_disk(EFI_DEVICE_PATH *devpath1, EFI_DEVICE_PATH *devpath2)
67383ffeb8bSWarner Losh {
67483ffeb8bSWarner Losh 	return _efi_devpath_match(devpath1, devpath2, true);
67583ffeb8bSWarner Losh }
676ca987d46SWarner Losh 
677ca987d46SWarner Losh bool
678ca987d46SWarner Losh efi_devpath_is_prefix(EFI_DEVICE_PATH *prefix, EFI_DEVICE_PATH *path)
679ca987d46SWarner Losh {
680ca987d46SWarner Losh 	size_t len;
681ca987d46SWarner Losh 
682ca987d46SWarner Losh 	if (prefix == NULL || path == NULL)
683ca987d46SWarner Losh 		return (false);
684ca987d46SWarner Losh 
685ca987d46SWarner Losh 	while (1) {
686ca987d46SWarner Losh 		if (IsDevicePathEnd(prefix))
687ca987d46SWarner Losh 			break;
688ca987d46SWarner Losh 
689ca987d46SWarner Losh 		if (DevicePathType(prefix) != DevicePathType(path) ||
690ca987d46SWarner Losh 		    DevicePathSubType(prefix) != DevicePathSubType(path))
691ca987d46SWarner Losh 			return (false);
692ca987d46SWarner Losh 
693ca987d46SWarner Losh 		len = DevicePathNodeLength(prefix);
694ca987d46SWarner Losh 		if (len != DevicePathNodeLength(path))
695ca987d46SWarner Losh 			return (false);
696ca987d46SWarner Losh 
697ca987d46SWarner Losh 		if (memcmp(prefix, path, len) != 0)
698ca987d46SWarner Losh 			return (false);
699ca987d46SWarner Losh 
700ca987d46SWarner Losh 		prefix = NextDevicePathNode(prefix);
701ca987d46SWarner Losh 		path = NextDevicePathNode(path);
702ca987d46SWarner Losh 	}
703ca987d46SWarner Losh 	return (true);
704ca987d46SWarner Losh }
705ee4e1d58SWarner Losh 
706ee4e1d58SWarner Losh /*
707ee4e1d58SWarner Losh  * Skip over the 'prefix' part of path and return the part of the path
708ee4e1d58SWarner Losh  * that starts with the first node that's a MEDIA_DEVICE_PATH.
709ee4e1d58SWarner Losh  */
710ee4e1d58SWarner Losh EFI_DEVICE_PATH *
711ee4e1d58SWarner Losh efi_devpath_to_media_path(EFI_DEVICE_PATH *path)
712ee4e1d58SWarner Losh {
713ee4e1d58SWarner Losh 
714ee4e1d58SWarner Losh 	while (!IsDevicePathEnd(path)) {
715ee4e1d58SWarner Losh 		if (DevicePathType(path) == MEDIA_DEVICE_PATH)
716ee4e1d58SWarner Losh 			return (path);
717ee4e1d58SWarner Losh 		path = NextDevicePathNode(path);
718ee4e1d58SWarner Losh 	}
719ee4e1d58SWarner Losh 	return (NULL);
720ee4e1d58SWarner Losh }
721c6c2a73cSWarner Losh 
722c6c2a73cSWarner Losh UINTN
723c6c2a73cSWarner Losh efi_devpath_length(EFI_DEVICE_PATH  *path)
724c6c2a73cSWarner Losh {
725c6c2a73cSWarner Losh 	EFI_DEVICE_PATH *start = path;
726c6c2a73cSWarner Losh 
727c6c2a73cSWarner Losh 	while (!IsDevicePathEnd(path))
728c6c2a73cSWarner Losh 		path = NextDevicePathNode(path);
729c6c2a73cSWarner Losh 	return ((UINTN)path - (UINTN)start) + DevicePathNodeLength(path);
730c6c2a73cSWarner Losh }
731b9e19b07SWarner Losh 
732b9e19b07SWarner Losh EFI_HANDLE
733b9e19b07SWarner Losh efi_devpath_to_handle(EFI_DEVICE_PATH *path, EFI_HANDLE *handles, unsigned nhandles)
734b9e19b07SWarner Losh {
735b9e19b07SWarner Losh 	unsigned i;
736b9e19b07SWarner Losh 	EFI_DEVICE_PATH *media, *devpath;
737b9e19b07SWarner Losh 	EFI_HANDLE h;
738b9e19b07SWarner Losh 
739b9e19b07SWarner Losh 	media = efi_devpath_to_media_path(path);
740b9e19b07SWarner Losh 	if (media == NULL)
741b9e19b07SWarner Losh 		return (NULL);
742b9e19b07SWarner Losh 	for (i = 0; i < nhandles; i++) {
743b9e19b07SWarner Losh 		h = handles[i];
744b9e19b07SWarner Losh 		devpath = efi_lookup_devpath(h);
745b9e19b07SWarner Losh 		if (devpath == NULL)
746b9e19b07SWarner Losh 			continue;
747b9e19b07SWarner Losh 		if (!efi_devpath_match_node(media, efi_devpath_to_media_path(devpath)))
748b9e19b07SWarner Losh 			continue;
749b9e19b07SWarner Losh 		return (h);
750b9e19b07SWarner Losh 	}
751b9e19b07SWarner Losh 	return (NULL);
752b9e19b07SWarner Losh }
753