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