1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * dev-path-parser.c - EFI Device Path parser 4 * Copyright (C) 2016 Lukas Wunner <lukas@wunner.de> 5 * 6 * This program is free software; you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License (version 2) as 8 * published by the Free Software Foundation. 9 */ 10 11 #include <linux/acpi.h> 12 #include <linux/efi.h> 13 #include <linux/pci.h> 14 15 struct acpi_hid_uid { 16 struct acpi_device_id hid[2]; 17 char uid[11]; /* UINT_MAX + null byte */ 18 }; 19 20 static int __init match_acpi_dev(struct device *dev, const void *data) 21 { 22 struct acpi_hid_uid hid_uid = *(const struct acpi_hid_uid *)data; 23 struct acpi_device *adev = to_acpi_device(dev); 24 25 if (acpi_match_device_ids(adev, hid_uid.hid)) 26 return 0; 27 28 if (adev->pnp.unique_id) 29 return !strcmp(adev->pnp.unique_id, hid_uid.uid); 30 else 31 return !strcmp("0", hid_uid.uid); 32 } 33 34 static long __init parse_acpi_path(const struct efi_dev_path *node, 35 struct device *parent, struct device **child) 36 { 37 struct acpi_hid_uid hid_uid = {}; 38 struct device *phys_dev; 39 40 if (node->header.length != 12) 41 return -EINVAL; 42 43 sprintf(hid_uid.hid[0].id, "%c%c%c%04X", 44 'A' + ((node->acpi.hid >> 10) & 0x1f) - 1, 45 'A' + ((node->acpi.hid >> 5) & 0x1f) - 1, 46 'A' + ((node->acpi.hid >> 0) & 0x1f) - 1, 47 node->acpi.hid >> 16); 48 sprintf(hid_uid.uid, "%u", node->acpi.uid); 49 50 *child = bus_find_device(&acpi_bus_type, NULL, &hid_uid, 51 match_acpi_dev); 52 if (!*child) 53 return -ENODEV; 54 55 phys_dev = acpi_get_first_physical_node(to_acpi_device(*child)); 56 if (phys_dev) { 57 get_device(phys_dev); 58 put_device(*child); 59 *child = phys_dev; 60 } 61 62 return 0; 63 } 64 65 static int __init match_pci_dev(struct device *dev, void *data) 66 { 67 unsigned int devfn = *(unsigned int *)data; 68 69 return dev_is_pci(dev) && to_pci_dev(dev)->devfn == devfn; 70 } 71 72 static long __init parse_pci_path(const struct efi_dev_path *node, 73 struct device *parent, struct device **child) 74 { 75 unsigned int devfn; 76 77 if (node->header.length != 6) 78 return -EINVAL; 79 if (!parent) 80 return -EINVAL; 81 82 devfn = PCI_DEVFN(node->pci.dev, node->pci.fn); 83 84 *child = device_find_child(parent, &devfn, match_pci_dev); 85 if (!*child) 86 return -ENODEV; 87 88 return 0; 89 } 90 91 /* 92 * Insert parsers for further node types here. 93 * 94 * Each parser takes a pointer to the @node and to the @parent (will be NULL 95 * for the first device path node). If a device corresponding to @node was 96 * found below @parent, its reference count should be incremented and the 97 * device returned in @child. 98 * 99 * The return value should be 0 on success or a negative int on failure. 100 * The special return values 0x01 (EFI_DEV_END_INSTANCE) and 0xFF 101 * (EFI_DEV_END_ENTIRE) signal the end of the device path, only 102 * parse_end_path() is supposed to return this. 103 * 104 * Be sure to validate the node length and contents before commencing the 105 * search for a device. 106 */ 107 108 static long __init parse_end_path(const struct efi_dev_path *node, 109 struct device *parent, struct device **child) 110 { 111 if (node->header.length != 4) 112 return -EINVAL; 113 if (node->header.sub_type != EFI_DEV_END_INSTANCE && 114 node->header.sub_type != EFI_DEV_END_ENTIRE) 115 return -EINVAL; 116 if (!parent) 117 return -ENODEV; 118 119 *child = get_device(parent); 120 return node->header.sub_type; 121 } 122 123 /** 124 * efi_get_device_by_path - find device by EFI Device Path 125 * @node: EFI Device Path 126 * @len: maximum length of EFI Device Path in bytes 127 * 128 * Parse a series of EFI Device Path nodes at @node and find the corresponding 129 * device. If the device was found, its reference count is incremented and a 130 * pointer to it is returned. The caller needs to drop the reference with 131 * put_device() after use. The @node pointer is updated to point to the 132 * location immediately after the "End of Hardware Device Path" node. 133 * 134 * If another Device Path instance follows, @len is decremented by the number 135 * of bytes consumed. Otherwise @len is set to %0. 136 * 137 * If a Device Path node is malformed or its corresponding device is not found, 138 * @node is updated to point to this offending node and an ERR_PTR is returned. 139 * 140 * If @len is initially %0, the function returns %NULL. Thus, to iterate over 141 * all instances in a path, the following idiom may be used: 142 * 143 * while (!IS_ERR_OR_NULL(dev = efi_get_device_by_path(&node, &len))) { 144 * // do something with dev 145 * put_device(dev); 146 * } 147 * if (IS_ERR(dev)) 148 * // report error 149 * 150 * Devices can only be found if they're already instantiated. Most buses 151 * instantiate devices in the "subsys" initcall level, hence the earliest 152 * initcall level in which this function should be called is "fs". 153 * 154 * Returns the device on success or 155 * %ERR_PTR(-ENODEV) if no device was found, 156 * %ERR_PTR(-EINVAL) if a node is malformed or exceeds @len, 157 * %ERR_PTR(-ENOTSUPP) if support for a node type is not yet implemented. 158 */ 159 struct device * __init efi_get_device_by_path(const struct efi_dev_path **node, 160 size_t *len) 161 { 162 struct device *parent = NULL, *child; 163 long ret = 0; 164 165 if (!*len) 166 return NULL; 167 168 while (!ret) { 169 if (*len < 4 || *len < (*node)->header.length) 170 ret = -EINVAL; 171 else if ((*node)->header.type == EFI_DEV_ACPI && 172 (*node)->header.sub_type == EFI_DEV_BASIC_ACPI) 173 ret = parse_acpi_path(*node, parent, &child); 174 else if ((*node)->header.type == EFI_DEV_HW && 175 (*node)->header.sub_type == EFI_DEV_PCI) 176 ret = parse_pci_path(*node, parent, &child); 177 else if (((*node)->header.type == EFI_DEV_END_PATH || 178 (*node)->header.type == EFI_DEV_END_PATH2)) 179 ret = parse_end_path(*node, parent, &child); 180 else 181 ret = -ENOTSUPP; 182 183 put_device(parent); 184 if (ret < 0) 185 return ERR_PTR(ret); 186 187 parent = child; 188 *node = (void *)*node + (*node)->header.length; 189 *len -= (*node)->header.length; 190 } 191 192 if (ret == EFI_DEV_END_ENTIRE) 193 *len = 0; 194 195 return child; 196 } 197