cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

dev-path-parser.c (5470B)


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