devpath.c revision 367457
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: stable/11/stand/efi/libefi/devpath.c 367457 2020-11-07 18:10:59Z dim $");
28
29#include <efi.h>
30#include <efilib.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 *textProtocol;
37
38EFI_DEVICE_PATH *
39efi_lookup_image_devpath(EFI_HANDLE handle)
40{
41	EFI_DEVICE_PATH *devpath;
42	EFI_STATUS status;
43
44	status = BS->HandleProtocol(handle, &ImageDevicePathGUID,
45	    (VOID **)&devpath);
46	if (EFI_ERROR(status))
47		devpath = NULL;
48	return (devpath);
49}
50
51EFI_DEVICE_PATH *
52efi_lookup_devpath(EFI_HANDLE handle)
53{
54	EFI_DEVICE_PATH *devpath;
55	EFI_STATUS status;
56
57	status = BS->HandleProtocol(handle, &DevicePathGUID, (VOID **)&devpath);
58	if (EFI_ERROR(status))
59		devpath = NULL;
60	return (devpath);
61}
62
63CHAR16 *
64efi_devpath_name(EFI_DEVICE_PATH *devpath)
65{
66	static int once = 1;
67	EFI_STATUS status;
68
69	if (devpath == NULL)
70		return (NULL);
71	if (once) {
72		status = BS->LocateProtocol(&DevicePathToTextGUID, NULL,
73		    (VOID **)&textProtocol);
74		if (EFI_ERROR(status))
75			textProtocol = NULL;
76		once = 0;
77	}
78	if (textProtocol == NULL)
79		return (NULL);
80
81	return (textProtocol->ConvertDevicePathToText(devpath, TRUE, TRUE));
82}
83
84void
85efi_free_devpath_name(CHAR16 *text)
86{
87
88	BS->FreePool(text);
89}
90
91EFI_DEVICE_PATH *
92efi_devpath_last_node(EFI_DEVICE_PATH *devpath)
93{
94
95	if (IsDevicePathEnd(devpath))
96		return (NULL);
97	while (!IsDevicePathEnd(NextDevicePathNode(devpath)))
98		devpath = NextDevicePathNode(devpath);
99	return (devpath);
100}
101
102EFI_DEVICE_PATH *
103efi_devpath_trim(EFI_DEVICE_PATH *devpath)
104{
105	EFI_DEVICE_PATH *node, *copy;
106	size_t prefix, len;
107
108	if ((node = efi_devpath_last_node(devpath)) == NULL)
109		return (NULL);
110	prefix = (UINT8 *)node - (UINT8 *)devpath;
111	if (prefix == 0)
112		return (NULL);
113	len = prefix + DevicePathNodeLength(NextDevicePathNode(node));
114	copy = malloc(len);
115	if (copy != NULL) {
116		memcpy(copy, devpath, prefix);
117		node = (EFI_DEVICE_PATH *)((UINT8 *)copy + prefix);
118		SetDevicePathEndNode(node);
119	}
120	return (copy);
121}
122
123EFI_HANDLE
124efi_devpath_handle(EFI_DEVICE_PATH *devpath)
125{
126	EFI_STATUS status;
127	EFI_HANDLE h;
128
129	/*
130	 * There isn't a standard way to locate a handle for a given
131	 * device path.  However, querying the EFI_DEVICE_PATH protocol
132	 * for a given device path should give us a handle for the
133	 * closest node in the path to the end that is valid.
134	 */
135	status = BS->LocateDevicePath(&DevicePathGUID, &devpath, &h);
136	if (EFI_ERROR(status))
137		return (NULL);
138	return (h);
139}
140
141bool
142efi_devpath_match_node(EFI_DEVICE_PATH *devpath1, EFI_DEVICE_PATH *devpath2)
143{
144	size_t len;
145
146	if (devpath1 == NULL || devpath2 == NULL)
147		return (false);
148	if (DevicePathType(devpath1) != DevicePathType(devpath2) ||
149	    DevicePathSubType(devpath1) != DevicePathSubType(devpath2))
150		return (false);
151	len = DevicePathNodeLength(devpath1);
152	if (len != DevicePathNodeLength(devpath2))
153		return (false);
154	if (memcmp(devpath1, devpath2, len) != 0)
155		return (false);
156	return (true);
157}
158
159bool
160efi_devpath_match(EFI_DEVICE_PATH *devpath1, EFI_DEVICE_PATH *devpath2)
161{
162
163	if (devpath1 == NULL || devpath2 == NULL)
164		return (false);
165
166	while (true) {
167		if (!efi_devpath_match_node(devpath1, devpath2))
168			return false;
169		if (IsDevicePathEnd(devpath1))
170			break;
171		devpath1 = NextDevicePathNode(devpath1);
172		devpath2 = NextDevicePathNode(devpath2);
173	}
174	return (true);
175}
176
177bool
178efi_devpath_is_prefix(EFI_DEVICE_PATH *prefix, EFI_DEVICE_PATH *path)
179{
180	size_t len;
181
182	if (prefix == NULL || path == NULL)
183		return (false);
184
185	while (1) {
186		if (IsDevicePathEnd(prefix))
187			break;
188
189		if (DevicePathType(prefix) != DevicePathType(path) ||
190		    DevicePathSubType(prefix) != DevicePathSubType(path))
191			return (false);
192
193		len = DevicePathNodeLength(prefix);
194		if (len != DevicePathNodeLength(path))
195			return (false);
196
197		if (memcmp(prefix, path, len) != 0)
198			return (false);
199
200		prefix = NextDevicePathNode(prefix);
201		path = NextDevicePathNode(path);
202	}
203	return (true);
204}
205
206/*
207 * Skip over the 'prefix' part of path and return the part of the path
208 * that starts with the first node that's a MEDIA_DEVICE_PATH.
209 */
210EFI_DEVICE_PATH *
211efi_devpath_to_media_path(EFI_DEVICE_PATH *path)
212{
213
214	while (!IsDevicePathEnd(path)) {
215		if (DevicePathType(path) == MEDIA_DEVICE_PATH)
216			return (path);
217		path = NextDevicePathNode(path);
218	}
219	return (NULL);
220}
221
222UINTN
223efi_devpath_length(EFI_DEVICE_PATH  *path)
224{
225	EFI_DEVICE_PATH *start = path;
226
227	while (!IsDevicePathEnd(path))
228		path = NextDevicePathNode(path);
229	return ((UINTN)path - (UINTN)start) + DevicePathNodeLength(path);
230}
231