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 33 static EFI_GUID ImageDevicePathGUID = 34 EFI_LOADED_IMAGE_DEVICE_PATH_PROTOCOL_GUID; 35 static EFI_GUID DevicePathGUID = DEVICE_PATH_PROTOCOL; 36 static EFI_GUID DevicePathToTextGUID = EFI_DEVICE_PATH_TO_TEXT_PROTOCOL_GUID; 37 static EFI_DEVICE_PATH_TO_TEXT_PROTOCOL *toTextProtocol; 38 static EFI_GUID DevicePathFromTextGUID = EFI_DEVICE_PATH_FROM_TEXT_PROTOCOL_GUID; 39 static EFI_DEVICE_PATH_FROM_TEXT_PROTOCOL *fromTextProtocol; 40 41 EFI_DEVICE_PATH * 42 efi_lookup_image_devpath(EFI_HANDLE handle) 43 { 44 EFI_DEVICE_PATH *devpath; 45 EFI_STATUS status; 46 47 status = BS->HandleProtocol(handle, &ImageDevicePathGUID, 48 (VOID **)&devpath); 49 if (EFI_ERROR(status)) 50 devpath = NULL; 51 return (devpath); 52 } 53 54 EFI_DEVICE_PATH * 55 efi_lookup_devpath(EFI_HANDLE handle) 56 { 57 EFI_DEVICE_PATH *devpath; 58 EFI_STATUS status; 59 60 status = BS->HandleProtocol(handle, &DevicePathGUID, (VOID **)&devpath); 61 if (EFI_ERROR(status)) 62 devpath = NULL; 63 return (devpath); 64 } 65 66 CHAR16 * 67 efi_devpath_name(EFI_DEVICE_PATH *devpath) 68 { 69 EFI_STATUS status; 70 71 if (devpath == NULL) 72 return (NULL); 73 if (toTextProtocol == NULL) { 74 status = BS->LocateProtocol(&DevicePathToTextGUID, NULL, 75 (VOID **)&toTextProtocol); 76 if (EFI_ERROR(status)) 77 toTextProtocol = NULL; 78 } 79 if (toTextProtocol == NULL) 80 return (NULL); 81 82 return (toTextProtocol->ConvertDevicePathToText(devpath, TRUE, TRUE)); 83 } 84 85 void 86 efi_free_devpath_name(CHAR16 *text) 87 { 88 89 BS->FreePool(text); 90 } 91 92 EFI_DEVICE_PATH * 93 efi_name_to_devpath(const char *path) 94 { 95 EFI_DEVICE_PATH *devpath; 96 CHAR16 *uv; 97 size_t ul; 98 99 uv = NULL; 100 if (utf8_to_ucs2(path, &uv, &ul) != 0) 101 return (NULL); 102 devpath = efi_name_to_devpath16(uv); 103 free(uv); 104 return (devpath); 105 } 106 107 EFI_DEVICE_PATH * 108 efi_name_to_devpath16(CHAR16 *path) 109 { 110 EFI_STATUS status; 111 112 if (path == NULL) 113 return (NULL); 114 if (fromTextProtocol == NULL) { 115 status = BS->LocateProtocol(&DevicePathFromTextGUID, NULL, 116 (VOID **)&fromTextProtocol); 117 if (EFI_ERROR(status)) 118 fromTextProtocol = NULL; 119 } 120 if (fromTextProtocol == NULL) 121 return (NULL); 122 123 return (fromTextProtocol->ConvertTextToDevicePath(path)); 124 } 125 126 void efi_devpath_free(EFI_DEVICE_PATH *devpath) 127 { 128 129 BS->FreePool(devpath); 130 } 131 132 EFI_DEVICE_PATH * 133 efi_devpath_last_node(EFI_DEVICE_PATH *devpath) 134 { 135 136 if (IsDevicePathEnd(devpath)) 137 return (NULL); 138 while (!IsDevicePathEnd(NextDevicePathNode(devpath))) 139 devpath = NextDevicePathNode(devpath); 140 return (devpath); 141 } 142 143 EFI_DEVICE_PATH * 144 efi_devpath_trim(EFI_DEVICE_PATH *devpath) 145 { 146 EFI_DEVICE_PATH *node, *copy; 147 size_t prefix, len; 148 149 if ((node = efi_devpath_last_node(devpath)) == NULL) 150 return (NULL); 151 prefix = (UINT8 *)node - (UINT8 *)devpath; 152 if (prefix == 0) 153 return (NULL); 154 len = prefix + DevicePathNodeLength(NextDevicePathNode(node)); 155 copy = malloc(len); 156 if (copy != NULL) { 157 memcpy(copy, devpath, prefix); 158 node = (EFI_DEVICE_PATH *)((UINT8 *)copy + prefix); 159 SetDevicePathEndNode(node); 160 } 161 return (copy); 162 } 163 164 EFI_HANDLE 165 efi_devpath_handle(EFI_DEVICE_PATH *devpath) 166 { 167 EFI_STATUS status; 168 EFI_HANDLE h; 169 170 /* 171 * There isn't a standard way to locate a handle for a given 172 * device path. However, querying the EFI_DEVICE_PATH protocol 173 * for a given device path should give us a handle for the 174 * closest node in the path to the end that is valid. 175 */ 176 status = BS->LocateDevicePath(&DevicePathGUID, &devpath, &h); 177 if (EFI_ERROR(status)) 178 return (NULL); 179 return (h); 180 } 181 182 bool 183 efi_devpath_match_node(EFI_DEVICE_PATH *devpath1, EFI_DEVICE_PATH *devpath2) 184 { 185 size_t len; 186 187 if (devpath1 == NULL || devpath2 == NULL) 188 return (false); 189 if (DevicePathType(devpath1) != DevicePathType(devpath2) || 190 DevicePathSubType(devpath1) != DevicePathSubType(devpath2)) 191 return (false); 192 len = DevicePathNodeLength(devpath1); 193 if (len != DevicePathNodeLength(devpath2)) 194 return (false); 195 if (memcmp(devpath1, devpath2, len) != 0) 196 return (false); 197 return (true); 198 } 199 200 static bool 201 _efi_devpath_match(EFI_DEVICE_PATH *devpath1, EFI_DEVICE_PATH *devpath2, 202 bool ignore_media) 203 { 204 205 if (devpath1 == NULL || devpath2 == NULL) 206 return (false); 207 208 while (true) { 209 if (ignore_media && 210 IsDevicePathType(devpath1, MEDIA_DEVICE_PATH) && 211 IsDevicePathType(devpath2, MEDIA_DEVICE_PATH)) 212 return (true); 213 if (!efi_devpath_match_node(devpath1, devpath2)) 214 return false; 215 if (IsDevicePathEnd(devpath1)) 216 break; 217 devpath1 = NextDevicePathNode(devpath1); 218 devpath2 = NextDevicePathNode(devpath2); 219 } 220 return (true); 221 } 222 /* 223 * Are two devpaths identical? 224 */ 225 bool 226 efi_devpath_match(EFI_DEVICE_PATH *devpath1, EFI_DEVICE_PATH *devpath2) 227 { 228 return _efi_devpath_match(devpath1, devpath2, false); 229 } 230 231 /* 232 * Like efi_devpath_match, but stops at when we hit the media device 233 * path node that specifies the partition information. If we match 234 * up to that point, then we're on the same disk. 235 */ 236 bool 237 efi_devpath_same_disk(EFI_DEVICE_PATH *devpath1, EFI_DEVICE_PATH *devpath2) 238 { 239 return _efi_devpath_match(devpath1, devpath2, true); 240 } 241 242 bool 243 efi_devpath_is_prefix(EFI_DEVICE_PATH *prefix, EFI_DEVICE_PATH *path) 244 { 245 size_t len; 246 247 if (prefix == NULL || path == NULL) 248 return (false); 249 250 while (1) { 251 if (IsDevicePathEnd(prefix)) 252 break; 253 254 if (DevicePathType(prefix) != DevicePathType(path) || 255 DevicePathSubType(prefix) != DevicePathSubType(path)) 256 return (false); 257 258 len = DevicePathNodeLength(prefix); 259 if (len != DevicePathNodeLength(path)) 260 return (false); 261 262 if (memcmp(prefix, path, len) != 0) 263 return (false); 264 265 prefix = NextDevicePathNode(prefix); 266 path = NextDevicePathNode(path); 267 } 268 return (true); 269 } 270 271 /* 272 * Skip over the 'prefix' part of path and return the part of the path 273 * that starts with the first node that's a MEDIA_DEVICE_PATH. 274 */ 275 EFI_DEVICE_PATH * 276 efi_devpath_to_media_path(EFI_DEVICE_PATH *path) 277 { 278 279 while (!IsDevicePathEnd(path)) { 280 if (DevicePathType(path) == MEDIA_DEVICE_PATH) 281 return (path); 282 path = NextDevicePathNode(path); 283 } 284 return (NULL); 285 } 286 287 UINTN 288 efi_devpath_length(EFI_DEVICE_PATH *path) 289 { 290 EFI_DEVICE_PATH *start = path; 291 292 while (!IsDevicePathEnd(path)) 293 path = NextDevicePathNode(path); 294 return ((UINTN)path - (UINTN)start) + DevicePathNodeLength(path); 295 } 296 297 EFI_HANDLE 298 efi_devpath_to_handle(EFI_DEVICE_PATH *path, EFI_HANDLE *handles, unsigned nhandles) 299 { 300 unsigned i; 301 EFI_DEVICE_PATH *media, *devpath; 302 EFI_HANDLE h; 303 304 media = efi_devpath_to_media_path(path); 305 if (media == NULL) 306 return (NULL); 307 for (i = 0; i < nhandles; i++) { 308 h = handles[i]; 309 devpath = efi_lookup_devpath(h); 310 if (devpath == NULL) 311 continue; 312 if (!efi_devpath_match_node(media, efi_devpath_to_media_path(devpath))) 313 continue; 314 return (h); 315 } 316 return (NULL); 317 } 318