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 47ca987d46SWarner Losh status = BS->HandleProtocol(handle, &ImageDevicePathGUID, 48ca987d46SWarner Losh (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 60ca987d46SWarner Losh status = BS->HandleProtocol(handle, &DevicePathGUID, (VOID **)&devpath); 61ca987d46SWarner Losh if (EFI_ERROR(status)) 62ca987d46SWarner Losh devpath = NULL; 63ca987d46SWarner Losh return (devpath); 64ca987d46SWarner Losh } 65ca987d46SWarner Losh 66ca987d46SWarner Losh CHAR16 * 67ca987d46SWarner Losh efi_devpath_name(EFI_DEVICE_PATH *devpath) 68ca987d46SWarner Losh { 69ca987d46SWarner Losh EFI_STATUS status; 70ca987d46SWarner Losh 71ca987d46SWarner Losh if (devpath == NULL) 72ca987d46SWarner Losh return (NULL); 7316b07b25SWarner Losh if (toTextProtocol == NULL) { 74ca987d46SWarner Losh status = BS->LocateProtocol(&DevicePathToTextGUID, NULL, 7516b07b25SWarner Losh (VOID **)&toTextProtocol); 76ca987d46SWarner Losh if (EFI_ERROR(status)) 7716b07b25SWarner Losh toTextProtocol = NULL; 78ca987d46SWarner Losh } 7916b07b25SWarner Losh if (toTextProtocol == NULL) 80ca987d46SWarner Losh return (NULL); 81ca987d46SWarner Losh 8216b07b25SWarner Losh return (toTextProtocol->ConvertDevicePathToText(devpath, TRUE, TRUE)); 83ca987d46SWarner Losh } 84ca987d46SWarner Losh 85ca987d46SWarner Losh void 86ca987d46SWarner Losh efi_free_devpath_name(CHAR16 *text) 87ca987d46SWarner Losh { 88ca987d46SWarner Losh 89ca987d46SWarner Losh BS->FreePool(text); 90ca987d46SWarner Losh } 91ca987d46SWarner Losh 92ca987d46SWarner Losh EFI_DEVICE_PATH * 9316b07b25SWarner Losh efi_name_to_devpath(const char *path) 9416b07b25SWarner Losh { 9516b07b25SWarner Losh EFI_DEVICE_PATH *devpath; 9616b07b25SWarner Losh CHAR16 *uv; 9716b07b25SWarner Losh size_t ul; 9816b07b25SWarner Losh 9916b07b25SWarner Losh uv = NULL; 10016b07b25SWarner Losh if (utf8_to_ucs2(path, &uv, &ul) != 0) 10116b07b25SWarner Losh return (NULL); 10216b07b25SWarner Losh devpath = efi_name_to_devpath16(uv); 10316b07b25SWarner Losh free(uv); 10416b07b25SWarner Losh return (devpath); 10516b07b25SWarner Losh } 10616b07b25SWarner Losh 10716b07b25SWarner Losh EFI_DEVICE_PATH * 10816b07b25SWarner Losh efi_name_to_devpath16(CHAR16 *path) 10916b07b25SWarner Losh { 11016b07b25SWarner Losh EFI_STATUS status; 11116b07b25SWarner Losh 11216b07b25SWarner Losh if (path == NULL) 11316b07b25SWarner Losh return (NULL); 11416b07b25SWarner Losh if (fromTextProtocol == NULL) { 11516b07b25SWarner Losh status = BS->LocateProtocol(&DevicePathFromTextGUID, NULL, 11616b07b25SWarner Losh (VOID **)&fromTextProtocol); 11716b07b25SWarner Losh if (EFI_ERROR(status)) 11816b07b25SWarner Losh fromTextProtocol = NULL; 11916b07b25SWarner Losh } 12016b07b25SWarner Losh if (fromTextProtocol == NULL) 12116b07b25SWarner Losh return (NULL); 12216b07b25SWarner Losh 12316b07b25SWarner Losh return (fromTextProtocol->ConvertTextToDevicePath(path)); 12416b07b25SWarner Losh } 12516b07b25SWarner Losh 12616b07b25SWarner Losh void efi_devpath_free(EFI_DEVICE_PATH *devpath) 12716b07b25SWarner Losh { 12816b07b25SWarner Losh 12916b07b25SWarner Losh BS->FreePool(devpath); 13016b07b25SWarner Losh } 13116b07b25SWarner Losh 13216b07b25SWarner Losh EFI_DEVICE_PATH * 133ca987d46SWarner Losh efi_devpath_last_node(EFI_DEVICE_PATH *devpath) 134ca987d46SWarner Losh { 135ca987d46SWarner Losh 136ca987d46SWarner Losh if (IsDevicePathEnd(devpath)) 137ca987d46SWarner Losh return (NULL); 138ca987d46SWarner Losh while (!IsDevicePathEnd(NextDevicePathNode(devpath))) 139ca987d46SWarner Losh devpath = NextDevicePathNode(devpath); 140ca987d46SWarner Losh return (devpath); 141ca987d46SWarner Losh } 142ca987d46SWarner Losh 143ca987d46SWarner Losh EFI_DEVICE_PATH * 144ca987d46SWarner Losh efi_devpath_trim(EFI_DEVICE_PATH *devpath) 145ca987d46SWarner Losh { 146ca987d46SWarner Losh EFI_DEVICE_PATH *node, *copy; 147ca987d46SWarner Losh size_t prefix, len; 148ca987d46SWarner Losh 149ca987d46SWarner Losh if ((node = efi_devpath_last_node(devpath)) == NULL) 150ca987d46SWarner Losh return (NULL); 151ca987d46SWarner Losh prefix = (UINT8 *)node - (UINT8 *)devpath; 152ca987d46SWarner Losh if (prefix == 0) 153ca987d46SWarner Losh return (NULL); 154ca987d46SWarner Losh len = prefix + DevicePathNodeLength(NextDevicePathNode(node)); 155ca987d46SWarner Losh copy = malloc(len); 156ca987d46SWarner Losh if (copy != NULL) { 157ca987d46SWarner Losh memcpy(copy, devpath, prefix); 158ca987d46SWarner Losh node = (EFI_DEVICE_PATH *)((UINT8 *)copy + prefix); 159ca987d46SWarner Losh SetDevicePathEndNode(node); 160ca987d46SWarner Losh } 161ca987d46SWarner Losh return (copy); 162ca987d46SWarner Losh } 163ca987d46SWarner Losh 164ca987d46SWarner Losh EFI_HANDLE 165ca987d46SWarner Losh efi_devpath_handle(EFI_DEVICE_PATH *devpath) 166ca987d46SWarner Losh { 167ca987d46SWarner Losh EFI_STATUS status; 168ca987d46SWarner Losh EFI_HANDLE h; 169ca987d46SWarner Losh 170ca987d46SWarner Losh /* 171ca987d46SWarner Losh * There isn't a standard way to locate a handle for a given 172ca987d46SWarner Losh * device path. However, querying the EFI_DEVICE_PATH protocol 173ca987d46SWarner Losh * for a given device path should give us a handle for the 174ca987d46SWarner Losh * closest node in the path to the end that is valid. 175ca987d46SWarner Losh */ 176ca987d46SWarner Losh status = BS->LocateDevicePath(&DevicePathGUID, &devpath, &h); 177ca987d46SWarner Losh if (EFI_ERROR(status)) 178ca987d46SWarner Losh return (NULL); 179ca987d46SWarner Losh return (h); 180ca987d46SWarner Losh } 181ca987d46SWarner Losh 182ca987d46SWarner Losh bool 18313850b36SWarner Losh efi_devpath_match_node(EFI_DEVICE_PATH *devpath1, EFI_DEVICE_PATH *devpath2) 184ca987d46SWarner Losh { 185ca987d46SWarner Losh size_t len; 186ca987d46SWarner Losh 187ca987d46SWarner Losh if (devpath1 == NULL || devpath2 == NULL) 188ca987d46SWarner Losh return (false); 189ca987d46SWarner Losh if (DevicePathType(devpath1) != DevicePathType(devpath2) || 190ca987d46SWarner Losh DevicePathSubType(devpath1) != DevicePathSubType(devpath2)) 191ca987d46SWarner Losh return (false); 192ca987d46SWarner Losh len = DevicePathNodeLength(devpath1); 193ca987d46SWarner Losh if (len != DevicePathNodeLength(devpath2)) 194ca987d46SWarner Losh return (false); 195ca987d46SWarner Losh if (memcmp(devpath1, devpath2, len) != 0) 196ca987d46SWarner Losh return (false); 19713850b36SWarner Losh return (true); 19813850b36SWarner Losh } 199ca987d46SWarner Losh 20013850b36SWarner Losh bool 20113850b36SWarner Losh efi_devpath_match(EFI_DEVICE_PATH *devpath1, EFI_DEVICE_PATH *devpath2) 20213850b36SWarner Losh { 20313850b36SWarner Losh 20413850b36SWarner Losh if (devpath1 == NULL || devpath2 == NULL) 20513850b36SWarner Losh return (false); 20613850b36SWarner Losh 20713850b36SWarner Losh while (true) { 20813850b36SWarner Losh if (!efi_devpath_match_node(devpath1, devpath2)) 20913850b36SWarner Losh return false; 210ca987d46SWarner Losh if (IsDevicePathEnd(devpath1)) 211ca987d46SWarner Losh break; 212ca987d46SWarner Losh devpath1 = NextDevicePathNode(devpath1); 213ca987d46SWarner Losh devpath2 = NextDevicePathNode(devpath2); 214ca987d46SWarner Losh } 215ca987d46SWarner Losh return (true); 216ca987d46SWarner Losh } 217ca987d46SWarner Losh 218ca987d46SWarner Losh bool 219ca987d46SWarner Losh efi_devpath_is_prefix(EFI_DEVICE_PATH *prefix, EFI_DEVICE_PATH *path) 220ca987d46SWarner Losh { 221ca987d46SWarner Losh size_t len; 222ca987d46SWarner Losh 223ca987d46SWarner Losh if (prefix == NULL || path == NULL) 224ca987d46SWarner Losh return (false); 225ca987d46SWarner Losh 226ca987d46SWarner Losh while (1) { 227ca987d46SWarner Losh if (IsDevicePathEnd(prefix)) 228ca987d46SWarner Losh break; 229ca987d46SWarner Losh 230ca987d46SWarner Losh if (DevicePathType(prefix) != DevicePathType(path) || 231ca987d46SWarner Losh DevicePathSubType(prefix) != DevicePathSubType(path)) 232ca987d46SWarner Losh return (false); 233ca987d46SWarner Losh 234ca987d46SWarner Losh len = DevicePathNodeLength(prefix); 235ca987d46SWarner Losh if (len != DevicePathNodeLength(path)) 236ca987d46SWarner Losh return (false); 237ca987d46SWarner Losh 238ca987d46SWarner Losh if (memcmp(prefix, path, len) != 0) 239ca987d46SWarner Losh return (false); 240ca987d46SWarner Losh 241ca987d46SWarner Losh prefix = NextDevicePathNode(prefix); 242ca987d46SWarner Losh path = NextDevicePathNode(path); 243ca987d46SWarner Losh } 244ca987d46SWarner Losh return (true); 245ca987d46SWarner Losh } 246ee4e1d58SWarner Losh 247ee4e1d58SWarner Losh /* 248ee4e1d58SWarner Losh * Skip over the 'prefix' part of path and return the part of the path 249ee4e1d58SWarner Losh * that starts with the first node that's a MEDIA_DEVICE_PATH. 250ee4e1d58SWarner Losh */ 251ee4e1d58SWarner Losh EFI_DEVICE_PATH * 252ee4e1d58SWarner Losh efi_devpath_to_media_path(EFI_DEVICE_PATH *path) 253ee4e1d58SWarner Losh { 254ee4e1d58SWarner Losh 255ee4e1d58SWarner Losh while (!IsDevicePathEnd(path)) { 256ee4e1d58SWarner Losh if (DevicePathType(path) == MEDIA_DEVICE_PATH) 257ee4e1d58SWarner Losh return (path); 258ee4e1d58SWarner Losh path = NextDevicePathNode(path); 259ee4e1d58SWarner Losh } 260ee4e1d58SWarner Losh return (NULL); 261ee4e1d58SWarner Losh } 262c6c2a73cSWarner Losh 263c6c2a73cSWarner Losh UINTN 264c6c2a73cSWarner Losh efi_devpath_length(EFI_DEVICE_PATH *path) 265c6c2a73cSWarner Losh { 266c6c2a73cSWarner Losh EFI_DEVICE_PATH *start = path; 267c6c2a73cSWarner Losh 268c6c2a73cSWarner Losh while (!IsDevicePathEnd(path)) 269c6c2a73cSWarner Losh path = NextDevicePathNode(path); 270c6c2a73cSWarner Losh return ((UINTN)path - (UINTN)start) + DevicePathNodeLength(path); 271c6c2a73cSWarner Losh } 272b9e19b07SWarner Losh 273b9e19b07SWarner Losh EFI_HANDLE 274b9e19b07SWarner Losh efi_devpath_to_handle(EFI_DEVICE_PATH *path, EFI_HANDLE *handles, unsigned nhandles) 275b9e19b07SWarner Losh { 276b9e19b07SWarner Losh unsigned i; 277b9e19b07SWarner Losh EFI_DEVICE_PATH *media, *devpath; 278b9e19b07SWarner Losh EFI_HANDLE h; 279b9e19b07SWarner Losh 280b9e19b07SWarner Losh media = efi_devpath_to_media_path(path); 281b9e19b07SWarner Losh if (media == NULL) 282b9e19b07SWarner Losh return (NULL); 283b9e19b07SWarner Losh for (i = 0; i < nhandles; i++) { 284b9e19b07SWarner Losh h = handles[i]; 285b9e19b07SWarner Losh devpath = efi_lookup_devpath(h); 286b9e19b07SWarner Losh if (devpath == NULL) 287b9e19b07SWarner Losh continue; 288b9e19b07SWarner Losh if (!efi_devpath_match_node(media, efi_devpath_to_media_path(devpath))) 289b9e19b07SWarner Losh continue; 290b9e19b07SWarner Losh return (h); 291b9e19b07SWarner Losh } 292b9e19b07SWarner Losh return (NULL); 293b9e19b07SWarner Losh } 294