Path: blob/master/drivers/firmware/efi/dev-path-parser.c
26428 views
// SPDX-License-Identifier: GPL-2.01/*2* dev-path-parser.c - EFI Device Path parser3* Copyright (C) 2016 Lukas Wunner <[email protected]>4*5* This program is free software; you can redistribute it and/or modify6* it under the terms of the GNU General Public License (version 2) as7* published by the Free Software Foundation.8*/910#include <linux/acpi.h>11#include <linux/efi.h>12#include <linux/pci.h>1314static long __init parse_acpi_path(const struct efi_dev_path *node,15struct device *parent, struct device **child)16{17struct acpi_device *adev;18struct device *phys_dev;19char hid[ACPI_ID_LEN];2021if (node->header.length != 12)22return -EINVAL;2324sprintf(hid, "%c%c%c%04X",25'A' + ((node->acpi.hid >> 10) & 0x1f) - 1,26'A' + ((node->acpi.hid >> 5) & 0x1f) - 1,27'A' + ((node->acpi.hid >> 0) & 0x1f) - 1,28node->acpi.hid >> 16);2930for_each_acpi_dev_match(adev, hid, NULL, -1) {31if (acpi_dev_uid_match(adev, node->acpi.uid))32break;33if (!acpi_device_uid(adev) && node->acpi.uid == 0)34break;35}36if (!adev)37return -ENODEV;3839phys_dev = acpi_get_first_physical_node(adev);40if (phys_dev) {41*child = get_device(phys_dev);42acpi_dev_put(adev);43} else44*child = &adev->dev;4546return 0;47}4849static int __init match_pci_dev(struct device *dev, const void *data)50{51unsigned int devfn = *(const unsigned int *)data;5253return dev_is_pci(dev) && to_pci_dev(dev)->devfn == devfn;54}5556static long __init parse_pci_path(const struct efi_dev_path *node,57struct device *parent, struct device **child)58{59unsigned int devfn;6061if (node->header.length != 6)62return -EINVAL;63if (!parent)64return -EINVAL;6566devfn = PCI_DEVFN(node->pci.dev, node->pci.fn);6768*child = device_find_child(parent, &devfn, match_pci_dev);69if (!*child)70return -ENODEV;7172return 0;73}7475/*76* Insert parsers for further node types here.77*78* Each parser takes a pointer to the @node and to the @parent (will be NULL79* for the first device path node). If a device corresponding to @node was80* found below @parent, its reference count should be incremented and the81* device returned in @child.82*83* The return value should be 0 on success or a negative int on failure.84* The special return values 0x01 (EFI_DEV_END_INSTANCE) and 0xFF85* (EFI_DEV_END_ENTIRE) signal the end of the device path, only86* parse_end_path() is supposed to return this.87*88* Be sure to validate the node length and contents before commencing the89* search for a device.90*/9192static long __init parse_end_path(const struct efi_dev_path *node,93struct device *parent, struct device **child)94{95if (node->header.length != 4)96return -EINVAL;97if (node->header.sub_type != EFI_DEV_END_INSTANCE &&98node->header.sub_type != EFI_DEV_END_ENTIRE)99return -EINVAL;100if (!parent)101return -ENODEV;102103*child = get_device(parent);104return node->header.sub_type;105}106107/**108* efi_get_device_by_path - find device by EFI Device Path109* @node: EFI Device Path110* @len: maximum length of EFI Device Path in bytes111*112* Parse a series of EFI Device Path nodes at @node and find the corresponding113* device. If the device was found, its reference count is incremented and a114* pointer to it is returned. The caller needs to drop the reference with115* put_device() after use. The @node pointer is updated to point to the116* location immediately after the "End of Hardware Device Path" node.117*118* If another Device Path instance follows, @len is decremented by the number119* of bytes consumed. Otherwise @len is set to %0.120*121* If a Device Path node is malformed or its corresponding device is not found,122* @node is updated to point to this offending node and an ERR_PTR is returned.123*124* If @len is initially %0, the function returns %NULL. Thus, to iterate over125* all instances in a path, the following idiom may be used:126*127* while (!IS_ERR_OR_NULL(dev = efi_get_device_by_path(&node, &len))) {128* // do something with dev129* put_device(dev);130* }131* if (IS_ERR(dev))132* // report error133*134* Devices can only be found if they're already instantiated. Most buses135* instantiate devices in the "subsys" initcall level, hence the earliest136* initcall level in which this function should be called is "fs".137*138* Returns the device on success or139* %ERR_PTR(-ENODEV) if no device was found,140* %ERR_PTR(-EINVAL) if a node is malformed or exceeds @len,141* %ERR_PTR(-ENOTSUPP) if support for a node type is not yet implemented.142*/143struct device * __init efi_get_device_by_path(const struct efi_dev_path **node,144size_t *len)145{146struct device *parent = NULL, *child;147long ret = 0;148149if (!*len)150return NULL;151152while (!ret) {153if (*len < 4 || *len < (*node)->header.length)154ret = -EINVAL;155else if ((*node)->header.type == EFI_DEV_ACPI &&156(*node)->header.sub_type == EFI_DEV_BASIC_ACPI)157ret = parse_acpi_path(*node, parent, &child);158else if ((*node)->header.type == EFI_DEV_HW &&159(*node)->header.sub_type == EFI_DEV_PCI)160ret = parse_pci_path(*node, parent, &child);161else if (((*node)->header.type == EFI_DEV_END_PATH ||162(*node)->header.type == EFI_DEV_END_PATH2))163ret = parse_end_path(*node, parent, &child);164else165ret = -ENOTSUPP;166167put_device(parent);168if (ret < 0)169return ERR_PTR(ret);170171parent = child;172*node = (void *)*node + (*node)->header.length;173*len -= (*node)->header.length;174}175176if (ret == EFI_DEV_END_ENTIRE)177*len = 0;178179return child;180}181182183