1ca987d46SWarner Losh /*- 2ca987d46SWarner Losh * Copyright (c) 2016 John Baldwin <jhb@FreeBSD.org> 3ca987d46SWarner Losh * 4ca987d46SWarner Losh * Redistribution and use in source and binary forms, with or without 5ca987d46SWarner Losh * modification, are permitted provided that the following conditions 6ca987d46SWarner Losh * are met: 7ca987d46SWarner Losh * 1. Redistributions of source code must retain the above copyright 8ca987d46SWarner Losh * notice, this list of conditions and the following disclaimer. 9ca987d46SWarner Losh * 2. Redistributions in binary form must reproduce the above copyright 10ca987d46SWarner Losh * notice, this list of conditions and the following disclaimer in the 11ca987d46SWarner Losh * documentation and/or other materials provided with the distribution. 12ca987d46SWarner Losh * 13ca987d46SWarner Losh * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 14ca987d46SWarner Losh * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15ca987d46SWarner Losh * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 16ca987d46SWarner Losh * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 17ca987d46SWarner Losh * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 18ca987d46SWarner Losh * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 19ca987d46SWarner Losh * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 20ca987d46SWarner Losh * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 21ca987d46SWarner Losh * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 22ca987d46SWarner Losh * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 23ca987d46SWarner Losh * SUCH DAMAGE. 24ca987d46SWarner Losh */ 25ca987d46SWarner Losh 26ca987d46SWarner Losh #include <sys/cdefs.h> 27ca987d46SWarner Losh __FBSDID("$FreeBSD$"); 28ca987d46SWarner Losh 29ca987d46SWarner Losh #include <efi.h> 30ca987d46SWarner Losh #include <efilib.h> 3116b07b25SWarner Losh #include <efichar.h> 32ca987d46SWarner Losh 33ca987d46SWarner Losh static EFI_GUID ImageDevicePathGUID = 34ca987d46SWarner Losh EFI_LOADED_IMAGE_DEVICE_PATH_PROTOCOL_GUID; 35ca987d46SWarner Losh static EFI_GUID DevicePathGUID = DEVICE_PATH_PROTOCOL; 36ca987d46SWarner Losh static EFI_GUID DevicePathToTextGUID = EFI_DEVICE_PATH_TO_TEXT_PROTOCOL_GUID; 3716b07b25SWarner Losh static EFI_DEVICE_PATH_TO_TEXT_PROTOCOL *toTextProtocol; 3816b07b25SWarner Losh static EFI_GUID DevicePathFromTextGUID = EFI_DEVICE_PATH_FROM_TEXT_PROTOCOL_GUID; 3916b07b25SWarner Losh static EFI_DEVICE_PATH_FROM_TEXT_PROTOCOL *fromTextProtocol; 40ca987d46SWarner Losh 41ca987d46SWarner Losh EFI_DEVICE_PATH * 42ca987d46SWarner Losh efi_lookup_image_devpath(EFI_HANDLE handle) 43ca987d46SWarner Losh { 44ca987d46SWarner Losh EFI_DEVICE_PATH *devpath; 45ca987d46SWarner Losh EFI_STATUS status; 46ca987d46SWarner Losh 47110d56cbSToomas Soome status = OpenProtocolByHandle(handle, &ImageDevicePathGUID, 48110d56cbSToomas Soome (void **)&devpath); 49ca987d46SWarner Losh if (EFI_ERROR(status)) 50ca987d46SWarner Losh devpath = NULL; 51ca987d46SWarner Losh return (devpath); 52ca987d46SWarner Losh } 53ca987d46SWarner Losh 54ca987d46SWarner Losh EFI_DEVICE_PATH * 55ca987d46SWarner Losh efi_lookup_devpath(EFI_HANDLE handle) 56ca987d46SWarner Losh { 57ca987d46SWarner Losh EFI_DEVICE_PATH *devpath; 58ca987d46SWarner Losh EFI_STATUS status; 59ca987d46SWarner Losh 60110d56cbSToomas Soome status = OpenProtocolByHandle(handle, &DevicePathGUID, 61110d56cbSToomas Soome (void **)&devpath); 62ca987d46SWarner Losh if (EFI_ERROR(status)) 63ca987d46SWarner Losh devpath = NULL; 64ca987d46SWarner Losh return (devpath); 65ca987d46SWarner Losh } 66ca987d46SWarner Losh 67ca987d46SWarner Losh CHAR16 * 68ca987d46SWarner Losh efi_devpath_name(EFI_DEVICE_PATH *devpath) 69ca987d46SWarner Losh { 70ca987d46SWarner Losh EFI_STATUS status; 71ca987d46SWarner Losh 72ca987d46SWarner Losh if (devpath == NULL) 73ca987d46SWarner Losh return (NULL); 7416b07b25SWarner Losh if (toTextProtocol == NULL) { 75ca987d46SWarner Losh status = BS->LocateProtocol(&DevicePathToTextGUID, NULL, 7616b07b25SWarner Losh (VOID **)&toTextProtocol); 77ca987d46SWarner Losh if (EFI_ERROR(status)) 7816b07b25SWarner Losh toTextProtocol = NULL; 79ca987d46SWarner Losh } 8016b07b25SWarner Losh if (toTextProtocol == NULL) 81ca987d46SWarner Losh return (NULL); 82ca987d46SWarner Losh 8316b07b25SWarner Losh return (toTextProtocol->ConvertDevicePathToText(devpath, TRUE, TRUE)); 84ca987d46SWarner Losh } 85ca987d46SWarner Losh 86ca987d46SWarner Losh void 87ca987d46SWarner Losh efi_free_devpath_name(CHAR16 *text) 88ca987d46SWarner Losh { 89ca987d46SWarner Losh 90ca987d46SWarner Losh BS->FreePool(text); 91ca987d46SWarner Losh } 92ca987d46SWarner Losh 93ca987d46SWarner Losh EFI_DEVICE_PATH * 9416b07b25SWarner Losh efi_name_to_devpath(const char *path) 9516b07b25SWarner Losh { 9616b07b25SWarner Losh EFI_DEVICE_PATH *devpath; 9716b07b25SWarner Losh CHAR16 *uv; 9816b07b25SWarner Losh size_t ul; 9916b07b25SWarner Losh 10016b07b25SWarner Losh uv = NULL; 10116b07b25SWarner Losh if (utf8_to_ucs2(path, &uv, &ul) != 0) 10216b07b25SWarner Losh return (NULL); 10316b07b25SWarner Losh devpath = efi_name_to_devpath16(uv); 10416b07b25SWarner Losh free(uv); 10516b07b25SWarner Losh return (devpath); 10616b07b25SWarner Losh } 10716b07b25SWarner Losh 10816b07b25SWarner Losh EFI_DEVICE_PATH * 10916b07b25SWarner Losh efi_name_to_devpath16(CHAR16 *path) 11016b07b25SWarner Losh { 11116b07b25SWarner Losh EFI_STATUS status; 11216b07b25SWarner Losh 11316b07b25SWarner Losh if (path == NULL) 11416b07b25SWarner Losh return (NULL); 11516b07b25SWarner Losh if (fromTextProtocol == NULL) { 11616b07b25SWarner Losh status = BS->LocateProtocol(&DevicePathFromTextGUID, NULL, 11716b07b25SWarner Losh (VOID **)&fromTextProtocol); 11816b07b25SWarner Losh if (EFI_ERROR(status)) 11916b07b25SWarner Losh fromTextProtocol = NULL; 12016b07b25SWarner Losh } 12116b07b25SWarner Losh if (fromTextProtocol == NULL) 12216b07b25SWarner Losh return (NULL); 12316b07b25SWarner Losh 12416b07b25SWarner Losh return (fromTextProtocol->ConvertTextToDevicePath(path)); 12516b07b25SWarner Losh } 12616b07b25SWarner Losh 12716b07b25SWarner Losh void efi_devpath_free(EFI_DEVICE_PATH *devpath) 12816b07b25SWarner Losh { 12916b07b25SWarner Losh 13016b07b25SWarner Losh BS->FreePool(devpath); 13116b07b25SWarner Losh } 13216b07b25SWarner Losh 13316b07b25SWarner Losh EFI_DEVICE_PATH * 134ca987d46SWarner Losh efi_devpath_last_node(EFI_DEVICE_PATH *devpath) 135ca987d46SWarner Losh { 136ca987d46SWarner Losh 137ca987d46SWarner Losh if (IsDevicePathEnd(devpath)) 138ca987d46SWarner Losh return (NULL); 139ca987d46SWarner Losh while (!IsDevicePathEnd(NextDevicePathNode(devpath))) 140ca987d46SWarner Losh devpath = NextDevicePathNode(devpath); 141ca987d46SWarner Losh return (devpath); 142ca987d46SWarner Losh } 143ca987d46SWarner Losh 144ca987d46SWarner Losh EFI_DEVICE_PATH * 145ca987d46SWarner Losh efi_devpath_trim(EFI_DEVICE_PATH *devpath) 146ca987d46SWarner Losh { 147ca987d46SWarner Losh EFI_DEVICE_PATH *node, *copy; 148ca987d46SWarner Losh size_t prefix, len; 149ca987d46SWarner Losh 150ca987d46SWarner Losh if ((node = efi_devpath_last_node(devpath)) == NULL) 151ca987d46SWarner Losh return (NULL); 152ca987d46SWarner Losh prefix = (UINT8 *)node - (UINT8 *)devpath; 153ca987d46SWarner Losh if (prefix == 0) 154ca987d46SWarner Losh return (NULL); 155ca987d46SWarner Losh len = prefix + DevicePathNodeLength(NextDevicePathNode(node)); 156ca987d46SWarner Losh copy = malloc(len); 157ca987d46SWarner Losh if (copy != NULL) { 158ca987d46SWarner Losh memcpy(copy, devpath, prefix); 159ca987d46SWarner Losh node = (EFI_DEVICE_PATH *)((UINT8 *)copy + prefix); 160ca987d46SWarner Losh SetDevicePathEndNode(node); 161ca987d46SWarner Losh } 162ca987d46SWarner Losh return (copy); 163ca987d46SWarner Losh } 164ca987d46SWarner Losh 165ca987d46SWarner Losh EFI_HANDLE 166ca987d46SWarner Losh efi_devpath_handle(EFI_DEVICE_PATH *devpath) 167ca987d46SWarner Losh { 168ca987d46SWarner Losh EFI_STATUS status; 169ca987d46SWarner Losh EFI_HANDLE h; 170ca987d46SWarner Losh 171ca987d46SWarner Losh /* 172ca987d46SWarner Losh * There isn't a standard way to locate a handle for a given 173ca987d46SWarner Losh * device path. However, querying the EFI_DEVICE_PATH protocol 174ca987d46SWarner Losh * for a given device path should give us a handle for the 175ca987d46SWarner Losh * closest node in the path to the end that is valid. 176ca987d46SWarner Losh */ 177ca987d46SWarner Losh status = BS->LocateDevicePath(&DevicePathGUID, &devpath, &h); 178ca987d46SWarner Losh if (EFI_ERROR(status)) 179ca987d46SWarner Losh return (NULL); 180ca987d46SWarner Losh return (h); 181ca987d46SWarner Losh } 182ca987d46SWarner Losh 183ca987d46SWarner Losh bool 18413850b36SWarner Losh efi_devpath_match_node(EFI_DEVICE_PATH *devpath1, EFI_DEVICE_PATH *devpath2) 185ca987d46SWarner Losh { 186ca987d46SWarner Losh size_t len; 187ca987d46SWarner Losh 188ca987d46SWarner Losh if (devpath1 == NULL || devpath2 == NULL) 189ca987d46SWarner Losh return (false); 190ca987d46SWarner Losh if (DevicePathType(devpath1) != DevicePathType(devpath2) || 191ca987d46SWarner Losh DevicePathSubType(devpath1) != DevicePathSubType(devpath2)) 192ca987d46SWarner Losh return (false); 193ca987d46SWarner Losh len = DevicePathNodeLength(devpath1); 194ca987d46SWarner Losh if (len != DevicePathNodeLength(devpath2)) 195ca987d46SWarner Losh return (false); 196ca987d46SWarner Losh if (memcmp(devpath1, devpath2, len) != 0) 197ca987d46SWarner Losh return (false); 19813850b36SWarner Losh return (true); 19913850b36SWarner Losh } 200ca987d46SWarner Losh 20183ffeb8bSWarner Losh static bool 20283ffeb8bSWarner Losh _efi_devpath_match(EFI_DEVICE_PATH *devpath1, EFI_DEVICE_PATH *devpath2, 20383ffeb8bSWarner Losh bool ignore_media) 20413850b36SWarner Losh { 20513850b36SWarner Losh 20613850b36SWarner Losh if (devpath1 == NULL || devpath2 == NULL) 20713850b36SWarner Losh return (false); 20813850b36SWarner Losh 20913850b36SWarner Losh while (true) { 21083ffeb8bSWarner Losh if (ignore_media && 21183ffeb8bSWarner Losh IsDevicePathType(devpath1, MEDIA_DEVICE_PATH) && 21283ffeb8bSWarner Losh IsDevicePathType(devpath2, MEDIA_DEVICE_PATH)) 21383ffeb8bSWarner Losh return (true); 21413850b36SWarner Losh if (!efi_devpath_match_node(devpath1, devpath2)) 21513850b36SWarner Losh return false; 216ca987d46SWarner Losh if (IsDevicePathEnd(devpath1)) 217ca987d46SWarner Losh break; 218ca987d46SWarner Losh devpath1 = NextDevicePathNode(devpath1); 219ca987d46SWarner Losh devpath2 = NextDevicePathNode(devpath2); 220ca987d46SWarner Losh } 221ca987d46SWarner Losh return (true); 222ca987d46SWarner Losh } 22383ffeb8bSWarner Losh /* 22483ffeb8bSWarner Losh * Are two devpaths identical? 22583ffeb8bSWarner Losh */ 22683ffeb8bSWarner Losh bool 22783ffeb8bSWarner Losh efi_devpath_match(EFI_DEVICE_PATH *devpath1, EFI_DEVICE_PATH *devpath2) 22883ffeb8bSWarner Losh { 22983ffeb8bSWarner Losh return _efi_devpath_match(devpath1, devpath2, false); 23083ffeb8bSWarner Losh } 23183ffeb8bSWarner Losh 23283ffeb8bSWarner Losh /* 23383ffeb8bSWarner Losh * Like efi_devpath_match, but stops at when we hit the media device 23483ffeb8bSWarner Losh * path node that specifies the partition information. If we match 23583ffeb8bSWarner Losh * up to that point, then we're on the same disk. 23683ffeb8bSWarner Losh */ 23783ffeb8bSWarner Losh bool 23883ffeb8bSWarner Losh efi_devpath_same_disk(EFI_DEVICE_PATH *devpath1, EFI_DEVICE_PATH *devpath2) 23983ffeb8bSWarner Losh { 24083ffeb8bSWarner Losh return _efi_devpath_match(devpath1, devpath2, true); 24183ffeb8bSWarner Losh } 242ca987d46SWarner Losh 243ca987d46SWarner Losh bool 244ca987d46SWarner Losh efi_devpath_is_prefix(EFI_DEVICE_PATH *prefix, EFI_DEVICE_PATH *path) 245ca987d46SWarner Losh { 246ca987d46SWarner Losh size_t len; 247ca987d46SWarner Losh 248ca987d46SWarner Losh if (prefix == NULL || path == NULL) 249ca987d46SWarner Losh return (false); 250ca987d46SWarner Losh 251ca987d46SWarner Losh while (1) { 252ca987d46SWarner Losh if (IsDevicePathEnd(prefix)) 253ca987d46SWarner Losh break; 254ca987d46SWarner Losh 255ca987d46SWarner Losh if (DevicePathType(prefix) != DevicePathType(path) || 256ca987d46SWarner Losh DevicePathSubType(prefix) != DevicePathSubType(path)) 257ca987d46SWarner Losh return (false); 258ca987d46SWarner Losh 259ca987d46SWarner Losh len = DevicePathNodeLength(prefix); 260ca987d46SWarner Losh if (len != DevicePathNodeLength(path)) 261ca987d46SWarner Losh return (false); 262ca987d46SWarner Losh 263ca987d46SWarner Losh if (memcmp(prefix, path, len) != 0) 264ca987d46SWarner Losh return (false); 265ca987d46SWarner Losh 266ca987d46SWarner Losh prefix = NextDevicePathNode(prefix); 267ca987d46SWarner Losh path = NextDevicePathNode(path); 268ca987d46SWarner Losh } 269ca987d46SWarner Losh return (true); 270ca987d46SWarner Losh } 271ee4e1d58SWarner Losh 272ee4e1d58SWarner Losh /* 273ee4e1d58SWarner Losh * Skip over the 'prefix' part of path and return the part of the path 274ee4e1d58SWarner Losh * that starts with the first node that's a MEDIA_DEVICE_PATH. 275ee4e1d58SWarner Losh */ 276ee4e1d58SWarner Losh EFI_DEVICE_PATH * 277ee4e1d58SWarner Losh efi_devpath_to_media_path(EFI_DEVICE_PATH *path) 278ee4e1d58SWarner Losh { 279ee4e1d58SWarner Losh 280ee4e1d58SWarner Losh while (!IsDevicePathEnd(path)) { 281ee4e1d58SWarner Losh if (DevicePathType(path) == MEDIA_DEVICE_PATH) 282ee4e1d58SWarner Losh return (path); 283ee4e1d58SWarner Losh path = NextDevicePathNode(path); 284ee4e1d58SWarner Losh } 285ee4e1d58SWarner Losh return (NULL); 286ee4e1d58SWarner Losh } 287c6c2a73cSWarner Losh 288c6c2a73cSWarner Losh UINTN 289c6c2a73cSWarner Losh efi_devpath_length(EFI_DEVICE_PATH *path) 290c6c2a73cSWarner Losh { 291c6c2a73cSWarner Losh EFI_DEVICE_PATH *start = path; 292c6c2a73cSWarner Losh 293c6c2a73cSWarner Losh while (!IsDevicePathEnd(path)) 294c6c2a73cSWarner Losh path = NextDevicePathNode(path); 295c6c2a73cSWarner Losh return ((UINTN)path - (UINTN)start) + DevicePathNodeLength(path); 296c6c2a73cSWarner Losh } 297b9e19b07SWarner Losh 298b9e19b07SWarner Losh EFI_HANDLE 299b9e19b07SWarner Losh efi_devpath_to_handle(EFI_DEVICE_PATH *path, EFI_HANDLE *handles, unsigned nhandles) 300b9e19b07SWarner Losh { 301b9e19b07SWarner Losh unsigned i; 302b9e19b07SWarner Losh EFI_DEVICE_PATH *media, *devpath; 303b9e19b07SWarner Losh EFI_HANDLE h; 304b9e19b07SWarner Losh 305b9e19b07SWarner Losh media = efi_devpath_to_media_path(path); 306b9e19b07SWarner Losh if (media == NULL) 307b9e19b07SWarner Losh return (NULL); 308b9e19b07SWarner Losh for (i = 0; i < nhandles; i++) { 309b9e19b07SWarner Losh h = handles[i]; 310b9e19b07SWarner Losh devpath = efi_lookup_devpath(h); 311b9e19b07SWarner Losh if (devpath == NULL) 312b9e19b07SWarner Losh continue; 313b9e19b07SWarner Losh if (!efi_devpath_match_node(media, efi_devpath_to_media_path(devpath))) 314b9e19b07SWarner Losh continue; 315b9e19b07SWarner Losh return (h); 316b9e19b07SWarner Losh } 317b9e19b07SWarner Losh return (NULL); 318b9e19b07SWarner Losh } 319