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> 325403ae84SToomas Soome #include <uuid.h> 335403ae84SToomas Soome #include <machine/_inttypes.h> 34ca987d46SWarner Losh 35ca987d46SWarner Losh static EFI_GUID ImageDevicePathGUID = 36ca987d46SWarner Losh EFI_LOADED_IMAGE_DEVICE_PATH_PROTOCOL_GUID; 37ca987d46SWarner Losh static EFI_GUID DevicePathGUID = DEVICE_PATH_PROTOCOL; 38ca987d46SWarner Losh static EFI_GUID DevicePathToTextGUID = EFI_DEVICE_PATH_TO_TEXT_PROTOCOL_GUID; 3916b07b25SWarner Losh static EFI_DEVICE_PATH_TO_TEXT_PROTOCOL *toTextProtocol; 405403ae84SToomas Soome static EFI_GUID DevicePathFromTextGUID = 415403ae84SToomas Soome EFI_DEVICE_PATH_FROM_TEXT_PROTOCOL_GUID; 4216b07b25SWarner Losh static EFI_DEVICE_PATH_FROM_TEXT_PROTOCOL *fromTextProtocol; 43ca987d46SWarner Losh 44ca987d46SWarner Losh EFI_DEVICE_PATH * 45ca987d46SWarner Losh efi_lookup_image_devpath(EFI_HANDLE handle) 46ca987d46SWarner Losh { 47ca987d46SWarner Losh EFI_DEVICE_PATH *devpath; 48ca987d46SWarner Losh EFI_STATUS status; 49ca987d46SWarner Losh 50110d56cbSToomas Soome status = OpenProtocolByHandle(handle, &ImageDevicePathGUID, 51110d56cbSToomas Soome (void **)&devpath); 52ca987d46SWarner Losh if (EFI_ERROR(status)) 53ca987d46SWarner Losh devpath = NULL; 54ca987d46SWarner Losh return (devpath); 55ca987d46SWarner Losh } 56ca987d46SWarner Losh 57ca987d46SWarner Losh EFI_DEVICE_PATH * 58ca987d46SWarner Losh efi_lookup_devpath(EFI_HANDLE handle) 59ca987d46SWarner Losh { 60ca987d46SWarner Losh EFI_DEVICE_PATH *devpath; 61ca987d46SWarner Losh EFI_STATUS status; 62ca987d46SWarner Losh 63110d56cbSToomas Soome status = OpenProtocolByHandle(handle, &DevicePathGUID, 64110d56cbSToomas Soome (void **)&devpath); 65ca987d46SWarner Losh if (EFI_ERROR(status)) 66ca987d46SWarner Losh devpath = NULL; 67ca987d46SWarner Losh return (devpath); 68ca987d46SWarner Losh } 69ca987d46SWarner Losh 70523a713fSToomas Soome void 71523a713fSToomas Soome efi_close_devpath(EFI_HANDLE handle) 72523a713fSToomas Soome { 73523a713fSToomas Soome EFI_STATUS status; 74523a713fSToomas Soome 75523a713fSToomas Soome status = BS->CloseProtocol(handle, &DevicePathGUID, IH, NULL); 76523a713fSToomas Soome if (EFI_ERROR(status)) 77523a713fSToomas Soome printf("CloseProtocol error: %lu\n", EFI_ERROR_CODE(status)); 78523a713fSToomas Soome } 79523a713fSToomas Soome 805403ae84SToomas Soome static char * 815403ae84SToomas Soome efi_make_tail(char *suffix) 825403ae84SToomas Soome { 835403ae84SToomas Soome char *tail; 845403ae84SToomas Soome 855403ae84SToomas Soome tail = NULL; 865403ae84SToomas Soome if (suffix != NULL) 875403ae84SToomas Soome (void)asprintf(&tail, "/%s", suffix); 885403ae84SToomas Soome else 895403ae84SToomas Soome tail = strdup(""); 905403ae84SToomas Soome return (tail); 915403ae84SToomas Soome } 925403ae84SToomas Soome 935403ae84SToomas Soome typedef struct { 945403ae84SToomas Soome EFI_DEVICE_PATH Header; 955403ae84SToomas Soome EFI_GUID Guid; 965403ae84SToomas Soome UINT8 VendorDefinedData[1]; 975403ae84SToomas Soome } __packed VENDOR_DEVICE_PATH_WITH_DATA; 985403ae84SToomas Soome 995403ae84SToomas Soome static char * 1005403ae84SToomas Soome efi_vendor_path(const char *type, VENDOR_DEVICE_PATH *node, char *suffix) 1015403ae84SToomas Soome { 1025403ae84SToomas Soome uint32_t size = DevicePathNodeLength(&node->Header) - sizeof(*node); 1035403ae84SToomas Soome VENDOR_DEVICE_PATH_WITH_DATA *dp = (VENDOR_DEVICE_PATH_WITH_DATA *)node; 1045403ae84SToomas Soome char *name, *tail, *head; 1055403ae84SToomas Soome char *uuid; 1065403ae84SToomas Soome int rv; 1075403ae84SToomas Soome 1085403ae84SToomas Soome uuid_to_string((const uuid_t *)(void *)&node->Guid, &uuid, &rv); 1095403ae84SToomas Soome if (rv != uuid_s_ok) 1105403ae84SToomas Soome return (NULL); 1115403ae84SToomas Soome 1125403ae84SToomas Soome tail = efi_make_tail(suffix); 1135403ae84SToomas Soome rv = asprintf(&head, "%sVendor(%s)[%x:", type, uuid, size); 1145403ae84SToomas Soome free(uuid); 1155403ae84SToomas Soome if (rv < 0) 1165403ae84SToomas Soome return (NULL); 1175403ae84SToomas Soome 1185403ae84SToomas Soome if (DevicePathNodeLength(&node->Header) > sizeof(*node)) { 1195403ae84SToomas Soome for (uint32_t i = 0; i < size; i++) { 1205403ae84SToomas Soome rv = asprintf(&name, "%s%02x", head, 1215403ae84SToomas Soome dp->VendorDefinedData[i]); 1225403ae84SToomas Soome if (rv < 0) { 1235403ae84SToomas Soome free(tail); 1245403ae84SToomas Soome free(head); 1255403ae84SToomas Soome return (NULL); 1265403ae84SToomas Soome } 1275403ae84SToomas Soome free(head); 1285403ae84SToomas Soome head = name; 1295403ae84SToomas Soome } 1305403ae84SToomas Soome } 1315403ae84SToomas Soome 1325403ae84SToomas Soome if (asprintf(&name, "%s]%s", head, tail) < 0) 1335403ae84SToomas Soome name = NULL; 1345403ae84SToomas Soome free(head); 1355403ae84SToomas Soome free(tail); 1365403ae84SToomas Soome return (name); 1375403ae84SToomas Soome } 1385403ae84SToomas Soome 1395403ae84SToomas Soome static char * 1405403ae84SToomas Soome efi_hw_dev_path(EFI_DEVICE_PATH *node, char *suffix) 1415403ae84SToomas Soome { 1425403ae84SToomas Soome uint8_t subtype = DevicePathSubType(node); 1435403ae84SToomas Soome char *name, *tail; 1445403ae84SToomas Soome 1455403ae84SToomas Soome tail = efi_make_tail(suffix); 1465403ae84SToomas Soome switch (subtype) { 1475403ae84SToomas Soome case HW_PCI_DP: 1485403ae84SToomas Soome if (asprintf(&name, "Pci(%x,%x)%s", 149454630c7SKyle Evans ((PCI_DEVICE_PATH *)node)->Device, 150454630c7SKyle Evans ((PCI_DEVICE_PATH *)node)->Function, tail) < 0) 1515403ae84SToomas Soome name = NULL; 1525403ae84SToomas Soome break; 1535403ae84SToomas Soome case HW_PCCARD_DP: 1545403ae84SToomas Soome if (asprintf(&name, "PCCARD(%x)%s", 1555403ae84SToomas Soome ((PCCARD_DEVICE_PATH *)node)->FunctionNumber, tail) < 0) 1565403ae84SToomas Soome name = NULL; 1575403ae84SToomas Soome break; 1585403ae84SToomas Soome case HW_MEMMAP_DP: 1595403ae84SToomas Soome if (asprintf(&name, "MMap(%x,%" PRIx64 ",%" PRIx64 ")%s", 1605403ae84SToomas Soome ((MEMMAP_DEVICE_PATH *)node)->MemoryType, 1615403ae84SToomas Soome ((MEMMAP_DEVICE_PATH *)node)->StartingAddress, 1625403ae84SToomas Soome ((MEMMAP_DEVICE_PATH *)node)->EndingAddress, tail) < 0) 1635403ae84SToomas Soome name = NULL; 1645403ae84SToomas Soome break; 1655403ae84SToomas Soome case HW_VENDOR_DP: 1665403ae84SToomas Soome name = efi_vendor_path("Hardware", 1675403ae84SToomas Soome (VENDOR_DEVICE_PATH *)node, tail); 1685403ae84SToomas Soome break; 1695403ae84SToomas Soome case HW_CONTROLLER_DP: 1705403ae84SToomas Soome if (asprintf(&name, "Ctrl(%x)%s", 1715403ae84SToomas Soome ((CONTROLLER_DEVICE_PATH *)node)->Controller, tail) < 0) 1725403ae84SToomas Soome name = NULL; 1735403ae84SToomas Soome break; 1745403ae84SToomas Soome default: 1755403ae84SToomas Soome if (asprintf(&name, "UnknownHW(%x)%s", subtype, tail) < 0) 1765403ae84SToomas Soome name = NULL; 1775403ae84SToomas Soome break; 1785403ae84SToomas Soome } 1795403ae84SToomas Soome free(tail); 1805403ae84SToomas Soome return (name); 1815403ae84SToomas Soome } 1825403ae84SToomas Soome 1835403ae84SToomas Soome static char * 1845403ae84SToomas Soome efi_acpi_dev_path(EFI_DEVICE_PATH *node, char *suffix) 1855403ae84SToomas Soome { 1865403ae84SToomas Soome uint8_t subtype = DevicePathSubType(node); 1875403ae84SToomas Soome ACPI_HID_DEVICE_PATH *acpi = (ACPI_HID_DEVICE_PATH *)node; 1885403ae84SToomas Soome char *name, *tail; 1895403ae84SToomas Soome 1905403ae84SToomas Soome tail = efi_make_tail(suffix); 1915403ae84SToomas Soome switch (subtype) { 1925403ae84SToomas Soome case ACPI_DP: 1935403ae84SToomas Soome if ((acpi->HID & PNP_EISA_ID_MASK) == PNP_EISA_ID_CONST) { 1945403ae84SToomas Soome switch (EISA_ID_TO_NUM (acpi->HID)) { 1955403ae84SToomas Soome case 0x0a03: 1965403ae84SToomas Soome if (asprintf(&name, "PciRoot(%x)%s", 1975403ae84SToomas Soome acpi->UID, tail) < 0) 1985403ae84SToomas Soome name = NULL; 1995403ae84SToomas Soome break; 2005403ae84SToomas Soome case 0x0a08: 2015403ae84SToomas Soome if (asprintf(&name, "PcieRoot(%x)%s", 2025403ae84SToomas Soome acpi->UID, tail) < 0) 2035403ae84SToomas Soome name = NULL; 2045403ae84SToomas Soome break; 2055403ae84SToomas Soome case 0x0604: 2065403ae84SToomas Soome if (asprintf(&name, "Floppy(%x)%s", 2075403ae84SToomas Soome acpi->UID, tail) < 0) 2085403ae84SToomas Soome name = NULL; 2095403ae84SToomas Soome break; 2105403ae84SToomas Soome case 0x0301: 2115403ae84SToomas Soome if (asprintf(&name, "Keyboard(%x)%s", 2125403ae84SToomas Soome acpi->UID, tail) < 0) 2135403ae84SToomas Soome name = NULL; 2145403ae84SToomas Soome break; 2155403ae84SToomas Soome case 0x0501: 2165403ae84SToomas Soome if (asprintf(&name, "Serial(%x)%s", 2175403ae84SToomas Soome acpi->UID, tail) < 0) 2185403ae84SToomas Soome name = NULL; 2195403ae84SToomas Soome break; 2205403ae84SToomas Soome case 0x0401: 2215403ae84SToomas Soome if (asprintf(&name, "ParallelPort(%x)%s", 2225403ae84SToomas Soome acpi->UID, tail) < 0) 2235403ae84SToomas Soome name = NULL; 2245403ae84SToomas Soome break; 2255403ae84SToomas Soome default: 2265403ae84SToomas Soome if (asprintf(&name, "Acpi(PNP%04x,%x)%s", 2275403ae84SToomas Soome EISA_ID_TO_NUM(acpi->HID), 2285403ae84SToomas Soome acpi->UID, tail) < 0) 2295403ae84SToomas Soome name = NULL; 2305403ae84SToomas Soome break; 2315403ae84SToomas Soome } 2325403ae84SToomas Soome } else { 2335403ae84SToomas Soome if (asprintf(&name, "Acpi(%08x,%x)%s", 2345403ae84SToomas Soome acpi->HID, acpi->UID, tail) < 0) 2355403ae84SToomas Soome name = NULL; 2365403ae84SToomas Soome } 2375403ae84SToomas Soome break; 2385403ae84SToomas Soome case ACPI_EXTENDED_DP: 2395403ae84SToomas Soome default: 2405403ae84SToomas Soome if (asprintf(&name, "UnknownACPI(%x)%s", subtype, tail) < 0) 2415403ae84SToomas Soome name = NULL; 2425403ae84SToomas Soome break; 2435403ae84SToomas Soome } 2445403ae84SToomas Soome free(tail); 2455403ae84SToomas Soome return (name); 2465403ae84SToomas Soome } 2475403ae84SToomas Soome 2485403ae84SToomas Soome static char * 2495403ae84SToomas Soome efi_messaging_dev_path(EFI_DEVICE_PATH *node, char *suffix) 2505403ae84SToomas Soome { 2515403ae84SToomas Soome uint8_t subtype = DevicePathSubType(node); 2525403ae84SToomas Soome char *name; 2535403ae84SToomas Soome char *tail; 2545403ae84SToomas Soome 2555403ae84SToomas Soome tail = efi_make_tail(suffix); 2565403ae84SToomas Soome switch (subtype) { 2575403ae84SToomas Soome case MSG_ATAPI_DP: 2585403ae84SToomas Soome if (asprintf(&name, "ATA(%s,%s,%x)%s", 2595403ae84SToomas Soome ((ATAPI_DEVICE_PATH *)node)->PrimarySecondary == 1 ? 2605403ae84SToomas Soome "Secondary" : "Primary", 2615403ae84SToomas Soome ((ATAPI_DEVICE_PATH *)node)->SlaveMaster == 1 ? 2625403ae84SToomas Soome "Slave" : "Master", 2635403ae84SToomas Soome ((ATAPI_DEVICE_PATH *)node)->Lun, tail) < 0) 2645403ae84SToomas Soome name = NULL; 2655403ae84SToomas Soome break; 2665403ae84SToomas Soome case MSG_SCSI_DP: 2675403ae84SToomas Soome if (asprintf(&name, "SCSI(%x,%x)%s", 2685403ae84SToomas Soome ((SCSI_DEVICE_PATH *)node)->Pun, 2695403ae84SToomas Soome ((SCSI_DEVICE_PATH *)node)->Lun, tail) < 0) 2705403ae84SToomas Soome name = NULL; 2715403ae84SToomas Soome break; 2725403ae84SToomas Soome case MSG_FIBRECHANNEL_DP: 2735403ae84SToomas Soome if (asprintf(&name, "Fibre(%" PRIx64 ",%" PRIx64 ")%s", 2745403ae84SToomas Soome ((FIBRECHANNEL_DEVICE_PATH *)node)->WWN, 2755403ae84SToomas Soome ((FIBRECHANNEL_DEVICE_PATH *)node)->Lun, tail) < 0) 2765403ae84SToomas Soome name = NULL; 2775403ae84SToomas Soome break; 2785403ae84SToomas Soome case MSG_1394_DP: 2795403ae84SToomas Soome if (asprintf(&name, "I1394(%016" PRIx64 ")%s", 2805403ae84SToomas Soome ((F1394_DEVICE_PATH *)node)->Guid, tail) < 0) 2815403ae84SToomas Soome name = NULL; 2825403ae84SToomas Soome break; 2835403ae84SToomas Soome case MSG_USB_DP: 2845403ae84SToomas Soome if (asprintf(&name, "USB(%x,%x)%s", 2855403ae84SToomas Soome ((USB_DEVICE_PATH *)node)->ParentPortNumber, 2865403ae84SToomas Soome ((USB_DEVICE_PATH *)node)->InterfaceNumber, tail) < 0) 2875403ae84SToomas Soome name = NULL; 2885403ae84SToomas Soome break; 2895403ae84SToomas Soome case MSG_USB_CLASS_DP: 2905403ae84SToomas Soome if (asprintf(&name, "UsbClass(%x,%x,%x,%x,%x)%s", 2915403ae84SToomas Soome ((USB_CLASS_DEVICE_PATH *)node)->VendorId, 2925403ae84SToomas Soome ((USB_CLASS_DEVICE_PATH *)node)->ProductId, 2935403ae84SToomas Soome ((USB_CLASS_DEVICE_PATH *)node)->DeviceClass, 2945403ae84SToomas Soome ((USB_CLASS_DEVICE_PATH *)node)->DeviceSubClass, 2955403ae84SToomas Soome ((USB_CLASS_DEVICE_PATH *)node)->DeviceProtocol, tail) < 0) 2965403ae84SToomas Soome name = NULL; 2975403ae84SToomas Soome break; 2985403ae84SToomas Soome case MSG_MAC_ADDR_DP: 2995403ae84SToomas Soome if (asprintf(&name, "MAC(%02x:%02x:%02x:%02x:%02x:%02x,%x)%s", 3005403ae84SToomas Soome ((MAC_ADDR_DEVICE_PATH *)node)->MacAddress.Addr[0], 3015403ae84SToomas Soome ((MAC_ADDR_DEVICE_PATH *)node)->MacAddress.Addr[1], 3025403ae84SToomas Soome ((MAC_ADDR_DEVICE_PATH *)node)->MacAddress.Addr[2], 3035403ae84SToomas Soome ((MAC_ADDR_DEVICE_PATH *)node)->MacAddress.Addr[3], 3045403ae84SToomas Soome ((MAC_ADDR_DEVICE_PATH *)node)->MacAddress.Addr[4], 3055403ae84SToomas Soome ((MAC_ADDR_DEVICE_PATH *)node)->MacAddress.Addr[5], 3065403ae84SToomas Soome ((MAC_ADDR_DEVICE_PATH *)node)->IfType, tail) < 0) 3075403ae84SToomas Soome name = NULL; 3085403ae84SToomas Soome break; 3095403ae84SToomas Soome case MSG_VENDOR_DP: 3105403ae84SToomas Soome name = efi_vendor_path("Messaging", 3115403ae84SToomas Soome (VENDOR_DEVICE_PATH *)node, tail); 3125403ae84SToomas Soome break; 3135403ae84SToomas Soome case MSG_UART_DP: 3145403ae84SToomas Soome if (asprintf(&name, "UART(%" PRIu64 ",%u,%x,%x)%s", 3155403ae84SToomas Soome ((UART_DEVICE_PATH *)node)->BaudRate, 3165403ae84SToomas Soome ((UART_DEVICE_PATH *)node)->DataBits, 3175403ae84SToomas Soome ((UART_DEVICE_PATH *)node)->Parity, 3185403ae84SToomas Soome ((UART_DEVICE_PATH *)node)->StopBits, tail) < 0) 3195403ae84SToomas Soome name = NULL; 3205403ae84SToomas Soome break; 3215403ae84SToomas Soome case MSG_SATA_DP: 3225403ae84SToomas Soome if (asprintf(&name, "Sata(%x,%x,%x)%s", 3235403ae84SToomas Soome ((SATA_DEVICE_PATH *)node)->HBAPortNumber, 3245403ae84SToomas Soome ((SATA_DEVICE_PATH *)node)->PortMultiplierPortNumber, 3255403ae84SToomas Soome ((SATA_DEVICE_PATH *)node)->Lun, tail) < 0) 3265403ae84SToomas Soome name = NULL; 3275403ae84SToomas Soome break; 3285403ae84SToomas Soome default: 3295403ae84SToomas Soome if (asprintf(&name, "UnknownMessaging(%x)%s", 3305403ae84SToomas Soome subtype, tail) < 0) 3315403ae84SToomas Soome name = NULL; 3325403ae84SToomas Soome break; 3335403ae84SToomas Soome } 3345403ae84SToomas Soome free(tail); 3355403ae84SToomas Soome return (name); 3365403ae84SToomas Soome } 3375403ae84SToomas Soome 3385403ae84SToomas Soome static char * 3395403ae84SToomas Soome efi_media_dev_path(EFI_DEVICE_PATH *node, char *suffix) 3405403ae84SToomas Soome { 3415403ae84SToomas Soome uint8_t subtype = DevicePathSubType(node); 3425403ae84SToomas Soome HARDDRIVE_DEVICE_PATH *hd; 3435403ae84SToomas Soome char *name; 3445403ae84SToomas Soome char *str; 3455403ae84SToomas Soome char *tail; 3465403ae84SToomas Soome int rv; 3475403ae84SToomas Soome 3485403ae84SToomas Soome tail = efi_make_tail(suffix); 3495403ae84SToomas Soome name = NULL; 3505403ae84SToomas Soome switch (subtype) { 3515403ae84SToomas Soome case MEDIA_HARDDRIVE_DP: 3525403ae84SToomas Soome hd = (HARDDRIVE_DEVICE_PATH *)node; 3535403ae84SToomas Soome switch (hd->SignatureType) { 3545403ae84SToomas Soome case SIGNATURE_TYPE_MBR: 3555403ae84SToomas Soome if (asprintf(&name, "HD(%d,MBR,%08x,%" PRIx64 3565403ae84SToomas Soome ",%" PRIx64 ")%s", 3575403ae84SToomas Soome hd->PartitionNumber, 3585403ae84SToomas Soome *((uint32_t *)(uintptr_t)&hd->Signature[0]), 3595403ae84SToomas Soome hd->PartitionStart, 3605403ae84SToomas Soome hd->PartitionSize, tail) < 0) 3615403ae84SToomas Soome name = NULL; 3625403ae84SToomas Soome break; 3635403ae84SToomas Soome case SIGNATURE_TYPE_GUID: 3645403ae84SToomas Soome name = NULL; 3655403ae84SToomas Soome uuid_to_string((const uuid_t *)(void *) 3665403ae84SToomas Soome &hd->Signature[0], &str, &rv); 3675403ae84SToomas Soome if (rv != uuid_s_ok) 3685403ae84SToomas Soome break; 3695403ae84SToomas Soome rv = asprintf(&name, "HD(%d,GPT,%s,%" PRIx64 ",%" 3705403ae84SToomas Soome PRIx64 ")%s", 3715403ae84SToomas Soome hd->PartitionNumber, str, 3725403ae84SToomas Soome hd->PartitionStart, hd->PartitionSize, tail); 3735403ae84SToomas Soome free(str); 3745403ae84SToomas Soome break; 3755403ae84SToomas Soome default: 3765403ae84SToomas Soome if (asprintf(&name, "HD(%d,%d,0)%s", 3775403ae84SToomas Soome hd->PartitionNumber, 3785403ae84SToomas Soome hd->SignatureType, tail) < 0) { 3795403ae84SToomas Soome name = NULL; 3805403ae84SToomas Soome } 3815403ae84SToomas Soome break; 3825403ae84SToomas Soome } 3835403ae84SToomas Soome break; 3845403ae84SToomas Soome case MEDIA_CDROM_DP: 3855403ae84SToomas Soome if (asprintf(&name, "CD(%x,%" PRIx64 ",%" PRIx64 ")%s", 3865403ae84SToomas Soome ((CDROM_DEVICE_PATH *)node)->BootEntry, 3875403ae84SToomas Soome ((CDROM_DEVICE_PATH *)node)->PartitionStart, 3885403ae84SToomas Soome ((CDROM_DEVICE_PATH *)node)->PartitionSize, tail) < 0) { 3895403ae84SToomas Soome name = NULL; 3905403ae84SToomas Soome } 3915403ae84SToomas Soome break; 3925403ae84SToomas Soome case MEDIA_VENDOR_DP: 3935403ae84SToomas Soome name = efi_vendor_path("Media", 3945403ae84SToomas Soome (VENDOR_DEVICE_PATH *)node, tail); 3955403ae84SToomas Soome break; 3965403ae84SToomas Soome case MEDIA_FILEPATH_DP: 3975403ae84SToomas Soome name = NULL; 3985403ae84SToomas Soome str = NULL; 3995403ae84SToomas Soome if (ucs2_to_utf8(((FILEPATH_DEVICE_PATH *)node)->PathName, 4005403ae84SToomas Soome &str) == 0) { 4015403ae84SToomas Soome (void)asprintf(&name, "%s%s", str, tail); 4025403ae84SToomas Soome free(str); 4035403ae84SToomas Soome } 4045403ae84SToomas Soome break; 4055403ae84SToomas Soome case MEDIA_PROTOCOL_DP: 4065403ae84SToomas Soome name = NULL; 4075403ae84SToomas Soome uuid_to_string((const uuid_t *)(void *) 4085403ae84SToomas Soome &((MEDIA_PROTOCOL_DEVICE_PATH *)node)->Protocol, 4095403ae84SToomas Soome &str, &rv); 4105403ae84SToomas Soome if (rv != uuid_s_ok) 4115403ae84SToomas Soome break; 4125403ae84SToomas Soome rv = asprintf(&name, "Protocol(%s)%s", str, tail); 4135403ae84SToomas Soome free(str); 4145403ae84SToomas Soome break; 4155403ae84SToomas Soome default: 4165403ae84SToomas Soome if (asprintf(&name, "UnknownMedia(%x)%s", 4175403ae84SToomas Soome subtype, tail) < 0) 4185403ae84SToomas Soome name = NULL; 4195403ae84SToomas Soome } 4205403ae84SToomas Soome free(tail); 4215403ae84SToomas Soome return (name); 4225403ae84SToomas Soome } 4235403ae84SToomas Soome 4245403ae84SToomas Soome static char * 4255403ae84SToomas Soome efi_translate_devpath(EFI_DEVICE_PATH *devpath) 4265403ae84SToomas Soome { 4275403ae84SToomas Soome EFI_DEVICE_PATH *dp = NextDevicePathNode(devpath); 4285403ae84SToomas Soome char *name, *ptr; 4295403ae84SToomas Soome uint8_t type; 4305403ae84SToomas Soome 4315403ae84SToomas Soome if (!IsDevicePathEnd(devpath)) 4325403ae84SToomas Soome name = efi_translate_devpath(dp); 4335403ae84SToomas Soome else 4345403ae84SToomas Soome return (NULL); 4355403ae84SToomas Soome 4365403ae84SToomas Soome ptr = NULL; 4375403ae84SToomas Soome type = DevicePathType(devpath); 4385403ae84SToomas Soome switch (type) { 4395403ae84SToomas Soome case HARDWARE_DEVICE_PATH: 4405403ae84SToomas Soome ptr = efi_hw_dev_path(devpath, name); 4415403ae84SToomas Soome break; 4425403ae84SToomas Soome case ACPI_DEVICE_PATH: 4435403ae84SToomas Soome ptr = efi_acpi_dev_path(devpath, name); 4445403ae84SToomas Soome break; 4455403ae84SToomas Soome case MESSAGING_DEVICE_PATH: 4465403ae84SToomas Soome ptr = efi_messaging_dev_path(devpath, name); 4475403ae84SToomas Soome break; 4485403ae84SToomas Soome case MEDIA_DEVICE_PATH: 4495403ae84SToomas Soome ptr = efi_media_dev_path(devpath, name); 4505403ae84SToomas Soome break; 4515403ae84SToomas Soome case BBS_DEVICE_PATH: 4525403ae84SToomas Soome default: 4535403ae84SToomas Soome if (asprintf(&ptr, "UnknownPath(%x)%s", type, 4545403ae84SToomas Soome name? name : "") < 0) 4555403ae84SToomas Soome ptr = NULL; 4565403ae84SToomas Soome break; 4575403ae84SToomas Soome } 4585403ae84SToomas Soome 4595403ae84SToomas Soome if (ptr != NULL) { 4605403ae84SToomas Soome free(name); 4615403ae84SToomas Soome name = ptr; 4625403ae84SToomas Soome } 4635403ae84SToomas Soome return (name); 4645403ae84SToomas Soome } 4655403ae84SToomas Soome 4665403ae84SToomas Soome static CHAR16 * 4675403ae84SToomas Soome efi_devpath_to_name(EFI_DEVICE_PATH *devpath) 4685403ae84SToomas Soome { 4695403ae84SToomas Soome char *name = NULL; 4705403ae84SToomas Soome CHAR16 *ptr = NULL; 4715403ae84SToomas Soome size_t len; 4725403ae84SToomas Soome int rv; 4735403ae84SToomas Soome 4745403ae84SToomas Soome name = efi_translate_devpath(devpath); 4755403ae84SToomas Soome if (name == NULL) 4765403ae84SToomas Soome return (NULL); 4775403ae84SToomas Soome 4785403ae84SToomas Soome /* 4795403ae84SToomas Soome * We need to return memory from AllocatePool, so it can be freed 4805403ae84SToomas Soome * with FreePool() in efi_free_devpath_name(). 4815403ae84SToomas Soome */ 4825403ae84SToomas Soome rv = utf8_to_ucs2(name, &ptr, &len); 4835403ae84SToomas Soome free(name); 4845403ae84SToomas Soome if (rv == 0) { 4855403ae84SToomas Soome CHAR16 *out = NULL; 4865403ae84SToomas Soome EFI_STATUS status; 4875403ae84SToomas Soome 4885403ae84SToomas Soome status = BS->AllocatePool(EfiLoaderData, len, (void **)&out); 4895403ae84SToomas Soome if (EFI_ERROR(status)) { 4905403ae84SToomas Soome free(ptr); 4915403ae84SToomas Soome return (out); 4925403ae84SToomas Soome } 4935403ae84SToomas Soome memcpy(out, ptr, len); 4945403ae84SToomas Soome free(ptr); 4955403ae84SToomas Soome ptr = out; 4965403ae84SToomas Soome } 4975403ae84SToomas Soome 4985403ae84SToomas Soome return (ptr); 4995403ae84SToomas Soome } 5005403ae84SToomas Soome 501ca987d46SWarner Losh CHAR16 * 502ca987d46SWarner Losh efi_devpath_name(EFI_DEVICE_PATH *devpath) 503ca987d46SWarner Losh { 504ca987d46SWarner Losh EFI_STATUS status; 505ca987d46SWarner Losh 506ca987d46SWarner Losh if (devpath == NULL) 507ca987d46SWarner Losh return (NULL); 50816b07b25SWarner Losh if (toTextProtocol == NULL) { 509ca987d46SWarner Losh status = BS->LocateProtocol(&DevicePathToTextGUID, NULL, 51016b07b25SWarner Losh (VOID **)&toTextProtocol); 511ca987d46SWarner Losh if (EFI_ERROR(status)) 51216b07b25SWarner Losh toTextProtocol = NULL; 513ca987d46SWarner Losh } 51416b07b25SWarner Losh if (toTextProtocol == NULL) 5155403ae84SToomas Soome return (efi_devpath_to_name(devpath)); 516ca987d46SWarner Losh 51716b07b25SWarner Losh return (toTextProtocol->ConvertDevicePathToText(devpath, TRUE, TRUE)); 518ca987d46SWarner Losh } 519ca987d46SWarner Losh 520ca987d46SWarner Losh void 521ca987d46SWarner Losh efi_free_devpath_name(CHAR16 *text) 522ca987d46SWarner Losh { 5235403ae84SToomas Soome if (text != NULL) 524ca987d46SWarner Losh BS->FreePool(text); 525ca987d46SWarner Losh } 526ca987d46SWarner Losh 527ca987d46SWarner Losh EFI_DEVICE_PATH * 52816b07b25SWarner Losh efi_name_to_devpath(const char *path) 52916b07b25SWarner Losh { 53016b07b25SWarner Losh EFI_DEVICE_PATH *devpath; 53116b07b25SWarner Losh CHAR16 *uv; 53216b07b25SWarner Losh size_t ul; 53316b07b25SWarner Losh 53416b07b25SWarner Losh uv = NULL; 53516b07b25SWarner Losh if (utf8_to_ucs2(path, &uv, &ul) != 0) 53616b07b25SWarner Losh return (NULL); 53716b07b25SWarner Losh devpath = efi_name_to_devpath16(uv); 53816b07b25SWarner Losh free(uv); 53916b07b25SWarner Losh return (devpath); 54016b07b25SWarner Losh } 54116b07b25SWarner Losh 54216b07b25SWarner Losh EFI_DEVICE_PATH * 54316b07b25SWarner Losh efi_name_to_devpath16(CHAR16 *path) 54416b07b25SWarner Losh { 54516b07b25SWarner Losh EFI_STATUS status; 54616b07b25SWarner Losh 54716b07b25SWarner Losh if (path == NULL) 54816b07b25SWarner Losh return (NULL); 54916b07b25SWarner Losh if (fromTextProtocol == NULL) { 55016b07b25SWarner Losh status = BS->LocateProtocol(&DevicePathFromTextGUID, NULL, 55116b07b25SWarner Losh (VOID **)&fromTextProtocol); 55216b07b25SWarner Losh if (EFI_ERROR(status)) 55316b07b25SWarner Losh fromTextProtocol = NULL; 55416b07b25SWarner Losh } 55516b07b25SWarner Losh if (fromTextProtocol == NULL) 55616b07b25SWarner Losh return (NULL); 55716b07b25SWarner Losh 55816b07b25SWarner Losh return (fromTextProtocol->ConvertTextToDevicePath(path)); 55916b07b25SWarner Losh } 56016b07b25SWarner Losh 56116b07b25SWarner Losh void efi_devpath_free(EFI_DEVICE_PATH *devpath) 56216b07b25SWarner Losh { 56316b07b25SWarner Losh 56416b07b25SWarner Losh BS->FreePool(devpath); 56516b07b25SWarner Losh } 56616b07b25SWarner Losh 56716b07b25SWarner Losh EFI_DEVICE_PATH * 568ca987d46SWarner Losh efi_devpath_last_node(EFI_DEVICE_PATH *devpath) 569ca987d46SWarner Losh { 570ca987d46SWarner Losh 571ca987d46SWarner Losh if (IsDevicePathEnd(devpath)) 572ca987d46SWarner Losh return (NULL); 573ca987d46SWarner Losh while (!IsDevicePathEnd(NextDevicePathNode(devpath))) 574ca987d46SWarner Losh devpath = NextDevicePathNode(devpath); 575ca987d46SWarner Losh return (devpath); 576ca987d46SWarner Losh } 577ca987d46SWarner Losh 578ca987d46SWarner Losh EFI_DEVICE_PATH * 579ca987d46SWarner Losh efi_devpath_trim(EFI_DEVICE_PATH *devpath) 580ca987d46SWarner Losh { 581ca987d46SWarner Losh EFI_DEVICE_PATH *node, *copy; 582ca987d46SWarner Losh size_t prefix, len; 583ca987d46SWarner Losh 584ca987d46SWarner Losh if ((node = efi_devpath_last_node(devpath)) == NULL) 585ca987d46SWarner Losh return (NULL); 586ca987d46SWarner Losh prefix = (UINT8 *)node - (UINT8 *)devpath; 587ca987d46SWarner Losh if (prefix == 0) 588ca987d46SWarner Losh return (NULL); 589ca987d46SWarner Losh len = prefix + DevicePathNodeLength(NextDevicePathNode(node)); 590ca987d46SWarner Losh copy = malloc(len); 591ca987d46SWarner Losh if (copy != NULL) { 592ca987d46SWarner Losh memcpy(copy, devpath, prefix); 593ca987d46SWarner Losh node = (EFI_DEVICE_PATH *)((UINT8 *)copy + prefix); 594ca987d46SWarner Losh SetDevicePathEndNode(node); 595ca987d46SWarner Losh } 596ca987d46SWarner Losh return (copy); 597ca987d46SWarner Losh } 598ca987d46SWarner Losh 599ca987d46SWarner Losh EFI_HANDLE 600ca987d46SWarner Losh efi_devpath_handle(EFI_DEVICE_PATH *devpath) 601ca987d46SWarner Losh { 602ca987d46SWarner Losh EFI_STATUS status; 603ca987d46SWarner Losh EFI_HANDLE h; 604ca987d46SWarner Losh 605ca987d46SWarner Losh /* 606ca987d46SWarner Losh * There isn't a standard way to locate a handle for a given 607ca987d46SWarner Losh * device path. However, querying the EFI_DEVICE_PATH protocol 608ca987d46SWarner Losh * for a given device path should give us a handle for the 609ca987d46SWarner Losh * closest node in the path to the end that is valid. 610ca987d46SWarner Losh */ 611ca987d46SWarner Losh status = BS->LocateDevicePath(&DevicePathGUID, &devpath, &h); 612ca987d46SWarner Losh if (EFI_ERROR(status)) 613ca987d46SWarner Losh return (NULL); 614ca987d46SWarner Losh return (h); 615ca987d46SWarner Losh } 616ca987d46SWarner Losh 617ca987d46SWarner Losh bool 61813850b36SWarner Losh efi_devpath_match_node(EFI_DEVICE_PATH *devpath1, EFI_DEVICE_PATH *devpath2) 619ca987d46SWarner Losh { 620ca987d46SWarner Losh size_t len; 621ca987d46SWarner Losh 622ca987d46SWarner Losh if (devpath1 == NULL || devpath2 == NULL) 623ca987d46SWarner Losh return (false); 624ca987d46SWarner Losh if (DevicePathType(devpath1) != DevicePathType(devpath2) || 625ca987d46SWarner Losh DevicePathSubType(devpath1) != DevicePathSubType(devpath2)) 626ca987d46SWarner Losh return (false); 627ca987d46SWarner Losh len = DevicePathNodeLength(devpath1); 628ca987d46SWarner Losh if (len != DevicePathNodeLength(devpath2)) 629ca987d46SWarner Losh return (false); 630ca987d46SWarner Losh if (memcmp(devpath1, devpath2, len) != 0) 631ca987d46SWarner Losh return (false); 63213850b36SWarner Losh return (true); 63313850b36SWarner Losh } 634ca987d46SWarner Losh 63583ffeb8bSWarner Losh static bool 63683ffeb8bSWarner Losh _efi_devpath_match(EFI_DEVICE_PATH *devpath1, EFI_DEVICE_PATH *devpath2, 63783ffeb8bSWarner Losh bool ignore_media) 63813850b36SWarner Losh { 63913850b36SWarner Losh 64013850b36SWarner Losh if (devpath1 == NULL || devpath2 == NULL) 64113850b36SWarner Losh return (false); 64213850b36SWarner Losh 64313850b36SWarner Losh while (true) { 64483ffeb8bSWarner Losh if (ignore_media && 64583ffeb8bSWarner Losh IsDevicePathType(devpath1, MEDIA_DEVICE_PATH) && 64683ffeb8bSWarner Losh IsDevicePathType(devpath2, MEDIA_DEVICE_PATH)) 64783ffeb8bSWarner Losh return (true); 64813850b36SWarner Losh if (!efi_devpath_match_node(devpath1, devpath2)) 64913850b36SWarner Losh return false; 650ca987d46SWarner Losh if (IsDevicePathEnd(devpath1)) 651ca987d46SWarner Losh break; 652ca987d46SWarner Losh devpath1 = NextDevicePathNode(devpath1); 653ca987d46SWarner Losh devpath2 = NextDevicePathNode(devpath2); 654ca987d46SWarner Losh } 655ca987d46SWarner Losh return (true); 656ca987d46SWarner Losh } 65783ffeb8bSWarner Losh /* 65883ffeb8bSWarner Losh * Are two devpaths identical? 65983ffeb8bSWarner Losh */ 66083ffeb8bSWarner Losh bool 66183ffeb8bSWarner Losh efi_devpath_match(EFI_DEVICE_PATH *devpath1, EFI_DEVICE_PATH *devpath2) 66283ffeb8bSWarner Losh { 66383ffeb8bSWarner Losh return _efi_devpath_match(devpath1, devpath2, false); 66483ffeb8bSWarner Losh } 66583ffeb8bSWarner Losh 66683ffeb8bSWarner Losh /* 66783ffeb8bSWarner Losh * Like efi_devpath_match, but stops at when we hit the media device 66883ffeb8bSWarner Losh * path node that specifies the partition information. If we match 66983ffeb8bSWarner Losh * up to that point, then we're on the same disk. 67083ffeb8bSWarner Losh */ 67183ffeb8bSWarner Losh bool 67283ffeb8bSWarner Losh efi_devpath_same_disk(EFI_DEVICE_PATH *devpath1, EFI_DEVICE_PATH *devpath2) 67383ffeb8bSWarner Losh { 67483ffeb8bSWarner Losh return _efi_devpath_match(devpath1, devpath2, true); 67583ffeb8bSWarner Losh } 676ca987d46SWarner Losh 677ca987d46SWarner Losh bool 678ca987d46SWarner Losh efi_devpath_is_prefix(EFI_DEVICE_PATH *prefix, EFI_DEVICE_PATH *path) 679ca987d46SWarner Losh { 680ca987d46SWarner Losh size_t len; 681ca987d46SWarner Losh 682ca987d46SWarner Losh if (prefix == NULL || path == NULL) 683ca987d46SWarner Losh return (false); 684ca987d46SWarner Losh 685ca987d46SWarner Losh while (1) { 686ca987d46SWarner Losh if (IsDevicePathEnd(prefix)) 687ca987d46SWarner Losh break; 688ca987d46SWarner Losh 689ca987d46SWarner Losh if (DevicePathType(prefix) != DevicePathType(path) || 690ca987d46SWarner Losh DevicePathSubType(prefix) != DevicePathSubType(path)) 691ca987d46SWarner Losh return (false); 692ca987d46SWarner Losh 693ca987d46SWarner Losh len = DevicePathNodeLength(prefix); 694ca987d46SWarner Losh if (len != DevicePathNodeLength(path)) 695ca987d46SWarner Losh return (false); 696ca987d46SWarner Losh 697ca987d46SWarner Losh if (memcmp(prefix, path, len) != 0) 698ca987d46SWarner Losh return (false); 699ca987d46SWarner Losh 700ca987d46SWarner Losh prefix = NextDevicePathNode(prefix); 701ca987d46SWarner Losh path = NextDevicePathNode(path); 702ca987d46SWarner Losh } 703ca987d46SWarner Losh return (true); 704ca987d46SWarner Losh } 705ee4e1d58SWarner Losh 706ee4e1d58SWarner Losh /* 707ee4e1d58SWarner Losh * Skip over the 'prefix' part of path and return the part of the path 708ee4e1d58SWarner Losh * that starts with the first node that's a MEDIA_DEVICE_PATH. 709ee4e1d58SWarner Losh */ 710ee4e1d58SWarner Losh EFI_DEVICE_PATH * 711ee4e1d58SWarner Losh efi_devpath_to_media_path(EFI_DEVICE_PATH *path) 712ee4e1d58SWarner Losh { 713ee4e1d58SWarner Losh 714ee4e1d58SWarner Losh while (!IsDevicePathEnd(path)) { 715ee4e1d58SWarner Losh if (DevicePathType(path) == MEDIA_DEVICE_PATH) 716ee4e1d58SWarner Losh return (path); 717ee4e1d58SWarner Losh path = NextDevicePathNode(path); 718ee4e1d58SWarner Losh } 719ee4e1d58SWarner Losh return (NULL); 720ee4e1d58SWarner Losh } 721c6c2a73cSWarner Losh 722c6c2a73cSWarner Losh UINTN 723c6c2a73cSWarner Losh efi_devpath_length(EFI_DEVICE_PATH *path) 724c6c2a73cSWarner Losh { 725c6c2a73cSWarner Losh EFI_DEVICE_PATH *start = path; 726c6c2a73cSWarner Losh 727c6c2a73cSWarner Losh while (!IsDevicePathEnd(path)) 728c6c2a73cSWarner Losh path = NextDevicePathNode(path); 729c6c2a73cSWarner Losh return ((UINTN)path - (UINTN)start) + DevicePathNodeLength(path); 730c6c2a73cSWarner Losh } 731b9e19b07SWarner Losh 732b9e19b07SWarner Losh EFI_HANDLE 733b9e19b07SWarner Losh efi_devpath_to_handle(EFI_DEVICE_PATH *path, EFI_HANDLE *handles, unsigned nhandles) 734b9e19b07SWarner Losh { 735b9e19b07SWarner Losh unsigned i; 736b9e19b07SWarner Losh EFI_DEVICE_PATH *media, *devpath; 737b9e19b07SWarner Losh EFI_HANDLE h; 738b9e19b07SWarner Losh 739b9e19b07SWarner Losh media = efi_devpath_to_media_path(path); 740b9e19b07SWarner Losh if (media == NULL) 741b9e19b07SWarner Losh return (NULL); 742b9e19b07SWarner Losh for (i = 0; i < nhandles; i++) { 743b9e19b07SWarner Losh h = handles[i]; 744b9e19b07SWarner Losh devpath = efi_lookup_devpath(h); 745b9e19b07SWarner Losh if (devpath == NULL) 746b9e19b07SWarner Losh continue; 747b9e19b07SWarner Losh if (!efi_devpath_match_node(media, efi_devpath_to_media_path(devpath))) 748b9e19b07SWarner Losh continue; 749b9e19b07SWarner Losh return (h); 750b9e19b07SWarner Losh } 751b9e19b07SWarner Losh return (NULL); 752b9e19b07SWarner Losh } 753