1
0
Fork 0
alistair23-linux/drivers/firmware/efi/dev-path-parser.c

197 lines
5.6 KiB
C
Raw Normal View History

// SPDX-License-Identifier: GPL-2.0
efi: Add device path parser We're about to extended the efistub to retrieve device properties from EFI on Apple Macs. The properties use EFI Device Paths to indicate the device they belong to. This commit adds a parser which, given an EFI Device Path, locates the corresponding struct device and returns a reference to it. Initially only ACPI and PCI Device Path nodes are supported, these are the only types needed for Apple device properties (the corresponding macOS function AppleACPIPlatformExpert::matchEFIDevicePath() does not support any others). Further node types can be added with little to moderate effort. Apple device properties is currently the only use case of this parser, but Peter Jones intends to use it to match up devices with the ConInDev/ConOutDev/ErrOutDev variables and add sysfs attributes to these devices to say the hardware supports using them as console. Thus, make this parser a separate component which can be selected with config option EFI_DEV_PATH_PARSER. It can in principle be compiled as a module if acpi_get_first_physical_node() and acpi_bus_type are exported (and efi_get_device_by_path() itself is exported). The dependency on CONFIG_ACPI is needed for acpi_match_device_ids(). It can be removed if an empty inline stub is added for that function. Signed-off-by: Lukas Wunner <lukas@wunner.de> Signed-off-by: Matt Fleming <matt@codeblueprint.co.uk> Cc: Andreas Noever <andreas.noever@gmail.com> Cc: Ard Biesheuvel <ard.biesheuvel@linaro.org> Cc: Linus Torvalds <torvalds@linux-foundation.org> Cc: Peter Jones <pjones@redhat.com> Cc: Peter Zijlstra <peterz@infradead.org> Cc: Thomas Gleixner <tglx@linutronix.de> Cc: linux-efi@vger.kernel.org Link: http://lkml.kernel.org/r/20161112213237.8804-7-matt@codeblueprint.co.uk Signed-off-by: Ingo Molnar <mingo@kernel.org>
2016-11-12 14:32:34 -07:00
/*
* dev-path-parser.c - EFI Device Path parser
* Copyright (C) 2016 Lukas Wunner <lukas@wunner.de>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License (version 2) as
* published by the Free Software Foundation.
*/
#include <linux/acpi.h>
#include <linux/efi.h>
#include <linux/pci.h>
struct acpi_hid_uid {
struct acpi_device_id hid[2];
char uid[11]; /* UINT_MAX + null byte */
};
bus_find_device: Unify the match callback with class_find_device There is an arbitrary difference between the prototypes of bus_find_device() and class_find_device() preventing their callers from passing the same pair of data and match() arguments to both of them, which is the const qualifier used in the prototype of class_find_device(). If that qualifier is also used in the bus_find_device() prototype, it will be possible to pass the same match() callback function to both bus_find_device() and class_find_device(), which will allow some optimizations to be made in order to avoid code duplication going forward. Also with that, constify the "data" parameter as it is passed as a const to the match function. For this reason, change the prototype of bus_find_device() to match the prototype of class_find_device() and adjust its callers to use the const qualifier in accordance with the new prototype of it. Cc: Alexander Shishkin <alexander.shishkin@linux.intel.com> Cc: Andrew Lunn <andrew@lunn.ch> Cc: Andreas Noever <andreas.noever@gmail.com> Cc: Arnd Bergmann <arnd@arndb.de> Cc: Bjorn Helgaas <bhelgaas@google.com> Cc: Corey Minyard <minyard@acm.org> Cc: Christian Borntraeger <borntraeger@de.ibm.com> Cc: David Kershner <david.kershner@unisys.com> Cc: "David S. Miller" <davem@davemloft.net> Cc: David Airlie <airlied@linux.ie> Cc: Felipe Balbi <balbi@kernel.org> Cc: Frank Rowand <frowand.list@gmail.com> Cc: Grygorii Strashko <grygorii.strashko@ti.com> Cc: Harald Freudenberger <freude@linux.ibm.com> Cc: Hartmut Knaack <knaack.h@gmx.de> Cc: Heiko Stuebner <heiko@sntech.de> Cc: Jason Gunthorpe <jgg@ziepe.ca> Cc: Jonathan Cameron <jic23@kernel.org> Cc: "James E.J. Bottomley" <jejb@linux.ibm.com> Cc: Len Brown <lenb@kernel.org> Cc: Mark Brown <broonie@kernel.org> Cc: Michael Ellerman <mpe@ellerman.id.au> Cc: Michael Jamet <michael.jamet@intel.com> Cc: "Martin K. Petersen" <martin.petersen@oracle.com> Cc: Peter Oberparleiter <oberpar@linux.ibm.com> Cc: Sebastian Ott <sebott@linux.ibm.com> Cc: Srinivas Kandagatla <srinivas.kandagatla@linaro.org> Cc: Yehezkel Bernat <YehezkelShB@gmail.com> Cc: rafael@kernel.org Acked-by: Corey Minyard <minyard@acm.org> Acked-by: David Kershner <david.kershner@unisys.com> Acked-by: Mark Brown <broonie@kernel.org> Acked-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com> Acked-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org> Acked-by: Wolfram Sang <wsa@the-dreams.de> # for the I2C parts Acked-by: Rob Herring <robh@kernel.org> Signed-off-by: Suzuki K Poulose <suzuki.poulose@arm.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2019-06-14 11:53:59 -06:00
static int __init match_acpi_dev(struct device *dev, const void *data)
efi: Add device path parser We're about to extended the efistub to retrieve device properties from EFI on Apple Macs. The properties use EFI Device Paths to indicate the device they belong to. This commit adds a parser which, given an EFI Device Path, locates the corresponding struct device and returns a reference to it. Initially only ACPI and PCI Device Path nodes are supported, these are the only types needed for Apple device properties (the corresponding macOS function AppleACPIPlatformExpert::matchEFIDevicePath() does not support any others). Further node types can be added with little to moderate effort. Apple device properties is currently the only use case of this parser, but Peter Jones intends to use it to match up devices with the ConInDev/ConOutDev/ErrOutDev variables and add sysfs attributes to these devices to say the hardware supports using them as console. Thus, make this parser a separate component which can be selected with config option EFI_DEV_PATH_PARSER. It can in principle be compiled as a module if acpi_get_first_physical_node() and acpi_bus_type are exported (and efi_get_device_by_path() itself is exported). The dependency on CONFIG_ACPI is needed for acpi_match_device_ids(). It can be removed if an empty inline stub is added for that function. Signed-off-by: Lukas Wunner <lukas@wunner.de> Signed-off-by: Matt Fleming <matt@codeblueprint.co.uk> Cc: Andreas Noever <andreas.noever@gmail.com> Cc: Ard Biesheuvel <ard.biesheuvel@linaro.org> Cc: Linus Torvalds <torvalds@linux-foundation.org> Cc: Peter Jones <pjones@redhat.com> Cc: Peter Zijlstra <peterz@infradead.org> Cc: Thomas Gleixner <tglx@linutronix.de> Cc: linux-efi@vger.kernel.org Link: http://lkml.kernel.org/r/20161112213237.8804-7-matt@codeblueprint.co.uk Signed-off-by: Ingo Molnar <mingo@kernel.org>
2016-11-12 14:32:34 -07:00
{
bus_find_device: Unify the match callback with class_find_device There is an arbitrary difference between the prototypes of bus_find_device() and class_find_device() preventing their callers from passing the same pair of data and match() arguments to both of them, which is the const qualifier used in the prototype of class_find_device(). If that qualifier is also used in the bus_find_device() prototype, it will be possible to pass the same match() callback function to both bus_find_device() and class_find_device(), which will allow some optimizations to be made in order to avoid code duplication going forward. Also with that, constify the "data" parameter as it is passed as a const to the match function. For this reason, change the prototype of bus_find_device() to match the prototype of class_find_device() and adjust its callers to use the const qualifier in accordance with the new prototype of it. Cc: Alexander Shishkin <alexander.shishkin@linux.intel.com> Cc: Andrew Lunn <andrew@lunn.ch> Cc: Andreas Noever <andreas.noever@gmail.com> Cc: Arnd Bergmann <arnd@arndb.de> Cc: Bjorn Helgaas <bhelgaas@google.com> Cc: Corey Minyard <minyard@acm.org> Cc: Christian Borntraeger <borntraeger@de.ibm.com> Cc: David Kershner <david.kershner@unisys.com> Cc: "David S. Miller" <davem@davemloft.net> Cc: David Airlie <airlied@linux.ie> Cc: Felipe Balbi <balbi@kernel.org> Cc: Frank Rowand <frowand.list@gmail.com> Cc: Grygorii Strashko <grygorii.strashko@ti.com> Cc: Harald Freudenberger <freude@linux.ibm.com> Cc: Hartmut Knaack <knaack.h@gmx.de> Cc: Heiko Stuebner <heiko@sntech.de> Cc: Jason Gunthorpe <jgg@ziepe.ca> Cc: Jonathan Cameron <jic23@kernel.org> Cc: "James E.J. Bottomley" <jejb@linux.ibm.com> Cc: Len Brown <lenb@kernel.org> Cc: Mark Brown <broonie@kernel.org> Cc: Michael Ellerman <mpe@ellerman.id.au> Cc: Michael Jamet <michael.jamet@intel.com> Cc: "Martin K. Petersen" <martin.petersen@oracle.com> Cc: Peter Oberparleiter <oberpar@linux.ibm.com> Cc: Sebastian Ott <sebott@linux.ibm.com> Cc: Srinivas Kandagatla <srinivas.kandagatla@linaro.org> Cc: Yehezkel Bernat <YehezkelShB@gmail.com> Cc: rafael@kernel.org Acked-by: Corey Minyard <minyard@acm.org> Acked-by: David Kershner <david.kershner@unisys.com> Acked-by: Mark Brown <broonie@kernel.org> Acked-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com> Acked-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org> Acked-by: Wolfram Sang <wsa@the-dreams.de> # for the I2C parts Acked-by: Rob Herring <robh@kernel.org> Signed-off-by: Suzuki K Poulose <suzuki.poulose@arm.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2019-06-14 11:53:59 -06:00
struct acpi_hid_uid hid_uid = *(const struct acpi_hid_uid *)data;
efi: Add device path parser We're about to extended the efistub to retrieve device properties from EFI on Apple Macs. The properties use EFI Device Paths to indicate the device they belong to. This commit adds a parser which, given an EFI Device Path, locates the corresponding struct device and returns a reference to it. Initially only ACPI and PCI Device Path nodes are supported, these are the only types needed for Apple device properties (the corresponding macOS function AppleACPIPlatformExpert::matchEFIDevicePath() does not support any others). Further node types can be added with little to moderate effort. Apple device properties is currently the only use case of this parser, but Peter Jones intends to use it to match up devices with the ConInDev/ConOutDev/ErrOutDev variables and add sysfs attributes to these devices to say the hardware supports using them as console. Thus, make this parser a separate component which can be selected with config option EFI_DEV_PATH_PARSER. It can in principle be compiled as a module if acpi_get_first_physical_node() and acpi_bus_type are exported (and efi_get_device_by_path() itself is exported). The dependency on CONFIG_ACPI is needed for acpi_match_device_ids(). It can be removed if an empty inline stub is added for that function. Signed-off-by: Lukas Wunner <lukas@wunner.de> Signed-off-by: Matt Fleming <matt@codeblueprint.co.uk> Cc: Andreas Noever <andreas.noever@gmail.com> Cc: Ard Biesheuvel <ard.biesheuvel@linaro.org> Cc: Linus Torvalds <torvalds@linux-foundation.org> Cc: Peter Jones <pjones@redhat.com> Cc: Peter Zijlstra <peterz@infradead.org> Cc: Thomas Gleixner <tglx@linutronix.de> Cc: linux-efi@vger.kernel.org Link: http://lkml.kernel.org/r/20161112213237.8804-7-matt@codeblueprint.co.uk Signed-off-by: Ingo Molnar <mingo@kernel.org>
2016-11-12 14:32:34 -07:00
struct acpi_device *adev = to_acpi_device(dev);
if (acpi_match_device_ids(adev, hid_uid.hid))
return 0;
if (adev->pnp.unique_id)
return !strcmp(adev->pnp.unique_id, hid_uid.uid);
else
return !strcmp("0", hid_uid.uid);
}
static long __init parse_acpi_path(struct efi_dev_path *node,
struct device *parent, struct device **child)
{
struct acpi_hid_uid hid_uid = {};
struct device *phys_dev;
if (node->length != 12)
return -EINVAL;
sprintf(hid_uid.hid[0].id, "%c%c%c%04X",
'A' + ((node->acpi.hid >> 10) & 0x1f) - 1,
'A' + ((node->acpi.hid >> 5) & 0x1f) - 1,
'A' + ((node->acpi.hid >> 0) & 0x1f) - 1,
node->acpi.hid >> 16);
sprintf(hid_uid.uid, "%u", node->acpi.uid);
*child = bus_find_device(&acpi_bus_type, NULL, &hid_uid,
match_acpi_dev);
if (!*child)
return -ENODEV;
phys_dev = acpi_get_first_physical_node(to_acpi_device(*child));
if (phys_dev) {
get_device(phys_dev);
put_device(*child);
*child = phys_dev;
}
return 0;
}
static int __init match_pci_dev(struct device *dev, void *data)
{
unsigned int devfn = *(unsigned int *)data;
return dev_is_pci(dev) && to_pci_dev(dev)->devfn == devfn;
}
static long __init parse_pci_path(struct efi_dev_path *node,
struct device *parent, struct device **child)
{
unsigned int devfn;
if (node->length != 6)
return -EINVAL;
if (!parent)
return -EINVAL;
devfn = PCI_DEVFN(node->pci.dev, node->pci.fn);
*child = device_find_child(parent, &devfn, match_pci_dev);
if (!*child)
return -ENODEV;
return 0;
}
/*
* Insert parsers for further node types here.
*
* Each parser takes a pointer to the @node and to the @parent (will be NULL
* for the first device path node). If a device corresponding to @node was
* found below @parent, its reference count should be incremented and the
* device returned in @child.
*
* The return value should be 0 on success or a negative int on failure.
* The special return values 0x01 (EFI_DEV_END_INSTANCE) and 0xFF
* (EFI_DEV_END_ENTIRE) signal the end of the device path, only
* parse_end_path() is supposed to return this.
*
* Be sure to validate the node length and contents before commencing the
* search for a device.
*/
static long __init parse_end_path(struct efi_dev_path *node,
struct device *parent, struct device **child)
{
if (node->length != 4)
return -EINVAL;
if (node->sub_type != EFI_DEV_END_INSTANCE &&
node->sub_type != EFI_DEV_END_ENTIRE)
return -EINVAL;
if (!parent)
return -ENODEV;
*child = get_device(parent);
return node->sub_type;
}
/**
* efi_get_device_by_path - find device by EFI Device Path
* @node: EFI Device Path
* @len: maximum length of EFI Device Path in bytes
*
* Parse a series of EFI Device Path nodes at @node and find the corresponding
* device. If the device was found, its reference count is incremented and a
* pointer to it is returned. The caller needs to drop the reference with
* put_device() after use. The @node pointer is updated to point to the
* location immediately after the "End of Hardware Device Path" node.
*
* If another Device Path instance follows, @len is decremented by the number
* of bytes consumed. Otherwise @len is set to %0.
*
* If a Device Path node is malformed or its corresponding device is not found,
* @node is updated to point to this offending node and an ERR_PTR is returned.
*
* If @len is initially %0, the function returns %NULL. Thus, to iterate over
* all instances in a path, the following idiom may be used:
*
* while (!IS_ERR_OR_NULL(dev = efi_get_device_by_path(&node, &len))) {
* // do something with dev
* put_device(dev);
* }
* if (IS_ERR(dev))
* // report error
*
* Devices can only be found if they're already instantiated. Most buses
* instantiate devices in the "subsys" initcall level, hence the earliest
* initcall level in which this function should be called is "fs".
*
* Returns the device on success or
* %ERR_PTR(-ENODEV) if no device was found,
* %ERR_PTR(-EINVAL) if a node is malformed or exceeds @len,
* %ERR_PTR(-ENOTSUPP) if support for a node type is not yet implemented.
*/
struct device * __init efi_get_device_by_path(struct efi_dev_path **node,
size_t *len)
{
struct device *parent = NULL, *child;
long ret = 0;
if (!*len)
return NULL;
while (!ret) {
if (*len < 4 || *len < (*node)->length)
ret = -EINVAL;
else if ((*node)->type == EFI_DEV_ACPI &&
(*node)->sub_type == EFI_DEV_BASIC_ACPI)
ret = parse_acpi_path(*node, parent, &child);
else if ((*node)->type == EFI_DEV_HW &&
(*node)->sub_type == EFI_DEV_PCI)
ret = parse_pci_path(*node, parent, &child);
else if (((*node)->type == EFI_DEV_END_PATH ||
(*node)->type == EFI_DEV_END_PATH2))
ret = parse_end_path(*node, parent, &child);
else
ret = -ENOTSUPP;
put_device(parent);
if (ret < 0)
return ERR_PTR(ret);
parent = child;
*node = (void *)*node + (*node)->length;
*len -= (*node)->length;
}
if (ret == EFI_DEV_END_ENTIRE)
*len = 0;
return child;
}