xref: /illumos-gate/usr/src/boot/efi/libefi/devpath.c (revision 8119dad84d6416f13557b0ba8e2aaf9064cbcfd3)
1 /*-
2  * Copyright (c) 2016 John Baldwin <jhb@FreeBSD.org>
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  */
26 
27 #include <sys/cdefs.h>
28 
29 #include <efi.h>
30 #include <efilib.h>
31 #include <efidevp.h>
32 #include <Protocol/DevicePath.h>
33 #include <Protocol/DevicePathFromText.h>
34 #include <Protocol/DevicePathToText.h>
35 #include <Protocol/LoadedImage.h>
36 
37 EFI_GUID gEfiLoadedImageDevicePathProtocolGuid =
38     EFI_LOADED_IMAGE_DEVICE_PATH_PROTOCOL_GUID;
39 EFI_GUID gEfiDevicePathProtocolGuid = DEVICE_PATH_PROTOCOL;
40 EFI_GUID gEfiDevicePathToTextProtocolGuid =
41     EFI_DEVICE_PATH_TO_TEXT_PROTOCOL_GUID;
42 static EFI_DEVICE_PATH_TO_TEXT_PROTOCOL *textProtocol;
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,
51 	    &gEfiLoadedImageDevicePathProtocolGuid,
52 	    (void **)&devpath);
53 	if (EFI_ERROR(status))
54 		devpath = NULL;
55 	return (devpath);
56 }
57 
58 EFI_DEVICE_PATH *
59 efi_lookup_devpath(EFI_HANDLE handle)
60 {
61 	EFI_DEVICE_PATH *devpath;
62 	EFI_STATUS status;
63 
64 	status = OpenProtocolByHandle(handle, &gEfiDevicePathProtocolGuid,
65 	    (void **)&devpath);
66 	if (EFI_ERROR(status))
67 		devpath = NULL;
68 	return (devpath);
69 }
70 
71 void
72 efi_close_devpath(EFI_HANDLE handle)
73 {
74 	EFI_STATUS status;
75 
76 	status = BS->CloseProtocol(handle, &gEfiDevicePathProtocolGuid, IH,
77 	    NULL);
78 	if (EFI_ERROR(status))
79 		printf("CloseProtocol error: %lu\n", DECODE_ERROR(status));
80 }
81 
82 CHAR16 *
83 efi_devpath_name(EFI_DEVICE_PATH *devpath)
84 {
85 	static bool once = true;
86 	EFI_STATUS status;
87 
88 	if (devpath == NULL)
89 		return (NULL);
90 	if (once) {
91 		status = BS->LocateProtocol(&gEfiDevicePathToTextProtocolGuid,
92 		    NULL, (void **)&textProtocol);
93 		if (EFI_ERROR(status))
94 			textProtocol = NULL;
95 		once = false;
96 	}
97 	if (textProtocol == NULL)
98 		return (NULL);
99 
100 	return (textProtocol->ConvertDevicePathToText(devpath, TRUE, TRUE));
101 }
102 
103 void
104 efi_free_devpath_name(CHAR16 *text)
105 {
106 
107 	BS->FreePool(text);
108 }
109 
110 EFI_DEVICE_PATH *
111 efi_devpath_last_node(EFI_DEVICE_PATH *devpath)
112 {
113 
114 	if (IsDevicePathEnd(devpath))
115 		return (NULL);
116 	while (!IsDevicePathEnd(NextDevicePathNode(devpath)))
117 		devpath = NextDevicePathNode(devpath);
118 	return (devpath);
119 }
120 
121 /*
122  * Walk device path nodes, return next instance or end node.
123  */
124 EFI_DEVICE_PATH *
125 efi_devpath_next_instance(EFI_DEVICE_PATH *devpath)
126 {
127 	while (!IsDevicePathEnd(devpath)) {
128 		devpath = NextDevicePathNode(devpath);
129 		if (IsDevicePathEndType(devpath) &&
130 		    devpath->SubType == END_INSTANCE_DEVICE_PATH_SUBTYPE) {
131 			devpath = NextDevicePathNode(devpath);
132 			break;
133 		}
134 	}
135 	return (devpath);
136 }
137 
138 EFI_DEVICE_PATH *
139 efi_devpath_trim(EFI_DEVICE_PATH *devpath)
140 {
141 	EFI_DEVICE_PATH *node, *copy;
142 	size_t prefix, len;
143 
144 	if ((node = efi_devpath_last_node(devpath)) == NULL)
145 		return (NULL);
146 	prefix = (UINT8 *)node - (UINT8 *)devpath;
147 	if (prefix == 0)
148 		return (NULL);
149 	len = prefix + DevicePathNodeLength(NextDevicePathNode(node));
150 	copy = malloc(len);
151 	if (copy != NULL) {
152 		memcpy(copy, devpath, prefix);
153 		node = (EFI_DEVICE_PATH *)((UINT8 *)copy + prefix);
154 		SetDevicePathEndNode(node);
155 	}
156 	return (copy);
157 }
158 
159 EFI_HANDLE
160 efi_devpath_handle(EFI_DEVICE_PATH *devpath)
161 {
162 	EFI_STATUS status;
163 	EFI_HANDLE h;
164 
165 	/*
166 	 * There isn't a standard way to locate a handle for a given
167 	 * device path.  However, querying the EFI_DEVICE_PATH protocol
168 	 * for a given device path should give us a handle for the
169 	 * closest node in the path to the end that is valid.
170 	 */
171 	status = BS->LocateDevicePath(&gEfiDevicePathProtocolGuid,
172 	    &devpath, &h);
173 	if (EFI_ERROR(status))
174 		return (NULL);
175 	return (h);
176 }
177 
178 bool
179 efi_devpath_match(EFI_DEVICE_PATH *devpath1, EFI_DEVICE_PATH *devpath2)
180 {
181 	size_t len;
182 
183 	if (devpath1 == NULL || devpath2 == NULL)
184 		return (false);
185 
186 	while (true) {
187 		if (DevicePathType(devpath1) != DevicePathType(devpath2) ||
188 		    DevicePathSubType(devpath1) != DevicePathSubType(devpath2))
189 			return (false);
190 
191 		len = DevicePathNodeLength(devpath1);
192 		if (len != DevicePathNodeLength(devpath2))
193 			return (false);
194 
195 		if (memcmp(devpath1, devpath2, len) != 0)
196 			return (false);
197 
198 		if (IsDevicePathEnd(devpath1))
199 			break;
200 		devpath1 = NextDevicePathNode(devpath1);
201 		devpath2 = NextDevicePathNode(devpath2);
202 	}
203 	return (true);
204 }
205 
206 bool
207 efi_devpath_is_prefix(EFI_DEVICE_PATH *prefix, EFI_DEVICE_PATH *path)
208 {
209 	size_t len;
210 
211 	if (prefix == NULL || path == NULL)
212 		return (false);
213 
214 	while (1) {
215 		if (IsDevicePathEnd(prefix))
216 			break;
217 
218 		if (DevicePathType(prefix) != DevicePathType(path) ||
219 		    DevicePathSubType(prefix) != DevicePathSubType(path))
220 			return (false);
221 
222 		len = DevicePathNodeLength(prefix);
223 		if (len != DevicePathNodeLength(path))
224 			return (false);
225 
226 		if (memcmp(prefix, path, len) != 0)
227 			return (false);
228 
229 		prefix = NextDevicePathNode(prefix);
230 		path = NextDevicePathNode(path);
231 	}
232 	return (true);
233 }
234