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