From 0580e5eb97f90250b3b3a146dd6568f754dec55a Mon Sep 17 00:00:00 2001 From: tsoome Date: Thu, 7 Nov 2019 11:17:03 +0000 Subject: [PATCH] loader: implement fallback efi_devpath_to_name() UEFI 1.10 on macs does not seem to provide devpath to name translation, provide our own (limited) version, so we can get information about commmon devices. MFC after: 1 week --- stand/efi/libefi/devpath.c | 432 ++++++++++++++++++++++++++++++++++++- 1 file changed, 428 insertions(+), 4 deletions(-) diff --git a/stand/efi/libefi/devpath.c b/stand/efi/libefi/devpath.c index c4c97a6f623d..26da7e15e7d8 100644 --- a/stand/efi/libefi/devpath.c +++ b/stand/efi/libefi/devpath.c @@ -29,13 +29,16 @@ __FBSDID("$FreeBSD$"); #include #include #include +#include +#include static EFI_GUID ImageDevicePathGUID = EFI_LOADED_IMAGE_DEVICE_PATH_PROTOCOL_GUID; static EFI_GUID DevicePathGUID = DEVICE_PATH_PROTOCOL; static EFI_GUID DevicePathToTextGUID = EFI_DEVICE_PATH_TO_TEXT_PROTOCOL_GUID; static EFI_DEVICE_PATH_TO_TEXT_PROTOCOL *toTextProtocol; -static EFI_GUID DevicePathFromTextGUID = EFI_DEVICE_PATH_FROM_TEXT_PROTOCOL_GUID; +static EFI_GUID DevicePathFromTextGUID = + EFI_DEVICE_PATH_FROM_TEXT_PROTOCOL_GUID; static EFI_DEVICE_PATH_FROM_TEXT_PROTOCOL *fromTextProtocol; EFI_DEVICE_PATH * @@ -64,6 +67,427 @@ efi_lookup_devpath(EFI_HANDLE handle) return (devpath); } +static char * +efi_make_tail(char *suffix) +{ + char *tail; + + tail = NULL; + if (suffix != NULL) + (void)asprintf(&tail, "/%s", suffix); + else + tail = strdup(""); + return (tail); +} + +typedef struct { + EFI_DEVICE_PATH Header; + EFI_GUID Guid; + UINT8 VendorDefinedData[1]; +} __packed VENDOR_DEVICE_PATH_WITH_DATA; + +static char * +efi_vendor_path(const char *type, VENDOR_DEVICE_PATH *node, char *suffix) +{ + uint32_t size = DevicePathNodeLength(&node->Header) - sizeof(*node); + VENDOR_DEVICE_PATH_WITH_DATA *dp = (VENDOR_DEVICE_PATH_WITH_DATA *)node; + char *name, *tail, *head; + char *uuid; + int rv; + + uuid_to_string((const uuid_t *)(void *)&node->Guid, &uuid, &rv); + if (rv != uuid_s_ok) + return (NULL); + + tail = efi_make_tail(suffix); + rv = asprintf(&head, "%sVendor(%s)[%x:", type, uuid, size); + free(uuid); + if (rv < 0) + return (NULL); + + if (DevicePathNodeLength(&node->Header) > sizeof(*node)) { + for (uint32_t i = 0; i < size; i++) { + rv = asprintf(&name, "%s%02x", head, + dp->VendorDefinedData[i]); + if (rv < 0) { + free(tail); + free(head); + return (NULL); + } + free(head); + head = name; + } + } + + if (asprintf(&name, "%s]%s", head, tail) < 0) + name = NULL; + free(head); + free(tail); + return (name); +} + +static char * +efi_hw_dev_path(EFI_DEVICE_PATH *node, char *suffix) +{ + uint8_t subtype = DevicePathSubType(node); + char *name, *tail; + + tail = efi_make_tail(suffix); + switch (subtype) { + case HW_PCI_DP: + if (asprintf(&name, "Pci(%x,%x)%s", + ((PCI_DEVICE_PATH *)node)->Function, + ((PCI_DEVICE_PATH *)node)->Device, tail) < 0) + name = NULL; + break; + case HW_PCCARD_DP: + if (asprintf(&name, "PCCARD(%x)%s", + ((PCCARD_DEVICE_PATH *)node)->FunctionNumber, tail) < 0) + name = NULL; + break; + case HW_MEMMAP_DP: + if (asprintf(&name, "MMap(%x,%" PRIx64 ",%" PRIx64 ")%s", + ((MEMMAP_DEVICE_PATH *)node)->MemoryType, + ((MEMMAP_DEVICE_PATH *)node)->StartingAddress, + ((MEMMAP_DEVICE_PATH *)node)->EndingAddress, tail) < 0) + name = NULL; + break; + case HW_VENDOR_DP: + name = efi_vendor_path("Hardware", + (VENDOR_DEVICE_PATH *)node, tail); + break; + case HW_CONTROLLER_DP: + if (asprintf(&name, "Ctrl(%x)%s", + ((CONTROLLER_DEVICE_PATH *)node)->Controller, tail) < 0) + name = NULL; + break; + default: + if (asprintf(&name, "UnknownHW(%x)%s", subtype, tail) < 0) + name = NULL; + break; + } + free(tail); + return (name); +} + +static char * +efi_acpi_dev_path(EFI_DEVICE_PATH *node, char *suffix) +{ + uint8_t subtype = DevicePathSubType(node); + ACPI_HID_DEVICE_PATH *acpi = (ACPI_HID_DEVICE_PATH *)node; + char *name, *tail; + + tail = efi_make_tail(suffix); + switch (subtype) { + case ACPI_DP: + if ((acpi->HID & PNP_EISA_ID_MASK) == PNP_EISA_ID_CONST) { + switch (EISA_ID_TO_NUM (acpi->HID)) { + case 0x0a03: + if (asprintf(&name, "PciRoot(%x)%s", + acpi->UID, tail) < 0) + name = NULL; + break; + case 0x0a08: + if (asprintf(&name, "PcieRoot(%x)%s", + acpi->UID, tail) < 0) + name = NULL; + break; + case 0x0604: + if (asprintf(&name, "Floppy(%x)%s", + acpi->UID, tail) < 0) + name = NULL; + break; + case 0x0301: + if (asprintf(&name, "Keyboard(%x)%s", + acpi->UID, tail) < 0) + name = NULL; + break; + case 0x0501: + if (asprintf(&name, "Serial(%x)%s", + acpi->UID, tail) < 0) + name = NULL; + break; + case 0x0401: + if (asprintf(&name, "ParallelPort(%x)%s", + acpi->UID, tail) < 0) + name = NULL; + break; + default: + if (asprintf(&name, "Acpi(PNP%04x,%x)%s", + EISA_ID_TO_NUM(acpi->HID), + acpi->UID, tail) < 0) + name = NULL; + break; + } + } else { + if (asprintf(&name, "Acpi(%08x,%x)%s", + acpi->HID, acpi->UID, tail) < 0) + name = NULL; + } + break; + case ACPI_EXTENDED_DP: + default: + if (asprintf(&name, "UnknownACPI(%x)%s", subtype, tail) < 0) + name = NULL; + break; + } + free(tail); + return (name); +} + +static char * +efi_messaging_dev_path(EFI_DEVICE_PATH *node, char *suffix) +{ + uint8_t subtype = DevicePathSubType(node); + char *name; + char *tail; + + tail = efi_make_tail(suffix); + switch (subtype) { + case MSG_ATAPI_DP: + if (asprintf(&name, "ATA(%s,%s,%x)%s", + ((ATAPI_DEVICE_PATH *)node)->PrimarySecondary == 1 ? + "Secondary" : "Primary", + ((ATAPI_DEVICE_PATH *)node)->SlaveMaster == 1 ? + "Slave" : "Master", + ((ATAPI_DEVICE_PATH *)node)->Lun, tail) < 0) + name = NULL; + break; + case MSG_SCSI_DP: + if (asprintf(&name, "SCSI(%x,%x)%s", + ((SCSI_DEVICE_PATH *)node)->Pun, + ((SCSI_DEVICE_PATH *)node)->Lun, tail) < 0) + name = NULL; + break; + case MSG_FIBRECHANNEL_DP: + if (asprintf(&name, "Fibre(%" PRIx64 ",%" PRIx64 ")%s", + ((FIBRECHANNEL_DEVICE_PATH *)node)->WWN, + ((FIBRECHANNEL_DEVICE_PATH *)node)->Lun, tail) < 0) + name = NULL; + break; + case MSG_1394_DP: + if (asprintf(&name, "I1394(%016" PRIx64 ")%s", + ((F1394_DEVICE_PATH *)node)->Guid, tail) < 0) + name = NULL; + break; + case MSG_USB_DP: + if (asprintf(&name, "USB(%x,%x)%s", + ((USB_DEVICE_PATH *)node)->ParentPortNumber, + ((USB_DEVICE_PATH *)node)->InterfaceNumber, tail) < 0) + name = NULL; + break; + case MSG_USB_CLASS_DP: + if (asprintf(&name, "UsbClass(%x,%x,%x,%x,%x)%s", + ((USB_CLASS_DEVICE_PATH *)node)->VendorId, + ((USB_CLASS_DEVICE_PATH *)node)->ProductId, + ((USB_CLASS_DEVICE_PATH *)node)->DeviceClass, + ((USB_CLASS_DEVICE_PATH *)node)->DeviceSubClass, + ((USB_CLASS_DEVICE_PATH *)node)->DeviceProtocol, tail) < 0) + name = NULL; + break; + case MSG_MAC_ADDR_DP: + if (asprintf(&name, "MAC(%02x:%02x:%02x:%02x:%02x:%02x,%x)%s", + ((MAC_ADDR_DEVICE_PATH *)node)->MacAddress.Addr[0], + ((MAC_ADDR_DEVICE_PATH *)node)->MacAddress.Addr[1], + ((MAC_ADDR_DEVICE_PATH *)node)->MacAddress.Addr[2], + ((MAC_ADDR_DEVICE_PATH *)node)->MacAddress.Addr[3], + ((MAC_ADDR_DEVICE_PATH *)node)->MacAddress.Addr[4], + ((MAC_ADDR_DEVICE_PATH *)node)->MacAddress.Addr[5], + ((MAC_ADDR_DEVICE_PATH *)node)->IfType, tail) < 0) + name = NULL; + break; + case MSG_VENDOR_DP: + name = efi_vendor_path("Messaging", + (VENDOR_DEVICE_PATH *)node, tail); + break; + case MSG_UART_DP: + if (asprintf(&name, "UART(%" PRIu64 ",%u,%x,%x)%s", + ((UART_DEVICE_PATH *)node)->BaudRate, + ((UART_DEVICE_PATH *)node)->DataBits, + ((UART_DEVICE_PATH *)node)->Parity, + ((UART_DEVICE_PATH *)node)->StopBits, tail) < 0) + name = NULL; + break; + case MSG_SATA_DP: + if (asprintf(&name, "Sata(%x,%x,%x)%s", + ((SATA_DEVICE_PATH *)node)->HBAPortNumber, + ((SATA_DEVICE_PATH *)node)->PortMultiplierPortNumber, + ((SATA_DEVICE_PATH *)node)->Lun, tail) < 0) + name = NULL; + break; + default: + if (asprintf(&name, "UnknownMessaging(%x)%s", + subtype, tail) < 0) + name = NULL; + break; + } + free(tail); + return (name); +} + +static char * +efi_media_dev_path(EFI_DEVICE_PATH *node, char *suffix) +{ + uint8_t subtype = DevicePathSubType(node); + HARDDRIVE_DEVICE_PATH *hd; + char *name; + char *str; + char *tail; + int rv; + + tail = efi_make_tail(suffix); + name = NULL; + switch (subtype) { + case MEDIA_HARDDRIVE_DP: + hd = (HARDDRIVE_DEVICE_PATH *)node; + switch (hd->SignatureType) { + case SIGNATURE_TYPE_MBR: + if (asprintf(&name, "HD(%d,MBR,%08x,%" PRIx64 + ",%" PRIx64 ")%s", + hd->PartitionNumber, + *((uint32_t *)(uintptr_t)&hd->Signature[0]), + hd->PartitionStart, + hd->PartitionSize, tail) < 0) + name = NULL; + break; + case SIGNATURE_TYPE_GUID: + name = NULL; + uuid_to_string((const uuid_t *)(void *) + &hd->Signature[0], &str, &rv); + if (rv != uuid_s_ok) + break; + rv = asprintf(&name, "HD(%d,GPT,%s,%" PRIx64 ",%" + PRIx64 ")%s", + hd->PartitionNumber, str, + hd->PartitionStart, hd->PartitionSize, tail); + free(str); + break; + default: + if (asprintf(&name, "HD(%d,%d,0)%s", + hd->PartitionNumber, + hd->SignatureType, tail) < 0) { + name = NULL; + } + break; + } + break; + case MEDIA_CDROM_DP: + if (asprintf(&name, "CD(%x,%" PRIx64 ",%" PRIx64 ")%s", + ((CDROM_DEVICE_PATH *)node)->BootEntry, + ((CDROM_DEVICE_PATH *)node)->PartitionStart, + ((CDROM_DEVICE_PATH *)node)->PartitionSize, tail) < 0) { + name = NULL; + } + break; + case MEDIA_VENDOR_DP: + name = efi_vendor_path("Media", + (VENDOR_DEVICE_PATH *)node, tail); + break; + case MEDIA_FILEPATH_DP: + name = NULL; + str = NULL; + if (ucs2_to_utf8(((FILEPATH_DEVICE_PATH *)node)->PathName, + &str) == 0) { + (void)asprintf(&name, "%s%s", str, tail); + free(str); + } + break; + case MEDIA_PROTOCOL_DP: + name = NULL; + uuid_to_string((const uuid_t *)(void *) + &((MEDIA_PROTOCOL_DEVICE_PATH *)node)->Protocol, + &str, &rv); + if (rv != uuid_s_ok) + break; + rv = asprintf(&name, "Protocol(%s)%s", str, tail); + free(str); + break; + default: + if (asprintf(&name, "UnknownMedia(%x)%s", + subtype, tail) < 0) + name = NULL; + } + free(tail); + return (name); +} + +static char * +efi_translate_devpath(EFI_DEVICE_PATH *devpath) +{ + EFI_DEVICE_PATH *dp = NextDevicePathNode(devpath); + char *name, *ptr; + uint8_t type; + + if (!IsDevicePathEnd(devpath)) + name = efi_translate_devpath(dp); + else + return (NULL); + + ptr = NULL; + type = DevicePathType(devpath); + switch (type) { + case HARDWARE_DEVICE_PATH: + ptr = efi_hw_dev_path(devpath, name); + break; + case ACPI_DEVICE_PATH: + ptr = efi_acpi_dev_path(devpath, name); + break; + case MESSAGING_DEVICE_PATH: + ptr = efi_messaging_dev_path(devpath, name); + break; + case MEDIA_DEVICE_PATH: + ptr = efi_media_dev_path(devpath, name); + break; + case BBS_DEVICE_PATH: + default: + if (asprintf(&ptr, "UnknownPath(%x)%s", type, + name? name : "") < 0) + ptr = NULL; + break; + } + + if (ptr != NULL) { + free(name); + name = ptr; + } + return (name); +} + +static CHAR16 * +efi_devpath_to_name(EFI_DEVICE_PATH *devpath) +{ + char *name = NULL; + CHAR16 *ptr = NULL; + size_t len; + int rv; + + name = efi_translate_devpath(devpath); + if (name == NULL) + return (NULL); + + /* + * We need to return memory from AllocatePool, so it can be freed + * with FreePool() in efi_free_devpath_name(). + */ + rv = utf8_to_ucs2(name, &ptr, &len); + free(name); + if (rv == 0) { + CHAR16 *out = NULL; + EFI_STATUS status; + + status = BS->AllocatePool(EfiLoaderData, len, (void **)&out); + if (EFI_ERROR(status)) { + free(ptr); + return (out); + } + memcpy(out, ptr, len); + free(ptr); + ptr = out; + } + + return (ptr); +} + CHAR16 * efi_devpath_name(EFI_DEVICE_PATH *devpath) { @@ -78,7 +502,7 @@ efi_devpath_name(EFI_DEVICE_PATH *devpath) toTextProtocol = NULL; } if (toTextProtocol == NULL) - return (NULL); + return (efi_devpath_to_name(devpath)); return (toTextProtocol->ConvertDevicePathToText(devpath, TRUE, TRUE)); } @@ -86,8 +510,8 @@ efi_devpath_name(EFI_DEVICE_PATH *devpath) void efi_free_devpath_name(CHAR16 *text) { - - BS->FreePool(text); + if (text != NULL) + BS->FreePool(text); } EFI_DEVICE_PATH *