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 bool 201 efi_devpath_match(EFI_DEVICE_PATH *devpath1, EFI_DEVICE_PATH *devpath2) 202 { 203 204 if (devpath1 == NULL || devpath2 == NULL) 205 return (false); 206 207 while (true) { 208 if (!efi_devpath_match_node(devpath1, devpath2)) 209 return false; 210 if (IsDevicePathEnd(devpath1)) 211 break; 212 devpath1 = NextDevicePathNode(devpath1); 213 devpath2 = NextDevicePathNode(devpath2); 214 } 215 return (true); 216 } 217 218 bool 219 efi_devpath_is_prefix(EFI_DEVICE_PATH *prefix, EFI_DEVICE_PATH *path) 220 { 221 size_t len; 222 223 if (prefix == NULL || path == NULL) 224 return (false); 225 226 while (1) { 227 if (IsDevicePathEnd(prefix)) 228 break; 229 230 if (DevicePathType(prefix) != DevicePathType(path) || 231 DevicePathSubType(prefix) != DevicePathSubType(path)) 232 return (false); 233 234 len = DevicePathNodeLength(prefix); 235 if (len != DevicePathNodeLength(path)) 236 return (false); 237 238 if (memcmp(prefix, path, len) != 0) 239 return (false); 240 241 prefix = NextDevicePathNode(prefix); 242 path = NextDevicePathNode(path); 243 } 244 return (true); 245 } 246 247 /* 248 * Skip over the 'prefix' part of path and return the part of the path 249 * that starts with the first node that's a MEDIA_DEVICE_PATH. 250 */ 251 EFI_DEVICE_PATH * 252 efi_devpath_to_media_path(EFI_DEVICE_PATH *path) 253 { 254 255 while (!IsDevicePathEnd(path)) { 256 if (DevicePathType(path) == MEDIA_DEVICE_PATH) 257 return (path); 258 path = NextDevicePathNode(path); 259 } 260 return (NULL); 261 } 262 263 UINTN 264 efi_devpath_length(EFI_DEVICE_PATH *path) 265 { 266 EFI_DEVICE_PATH *start = path; 267 268 while (!IsDevicePathEnd(path)) 269 path = NextDevicePathNode(path); 270 return ((UINTN)path - (UINTN)start) + DevicePathNodeLength(path); 271 } 272