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

irqdomain.c (8011B)


      1// SPDX-License-Identifier: GPL-2.0
      2/*
      3 * PCI Message Signaled Interrupt (MSI) - irqdomain support
      4 */
      5#include <linux/acpi_iort.h>
      6#include <linux/irqdomain.h>
      7#include <linux/of_irq.h>
      8
      9#include "msi.h"
     10
     11int pci_msi_setup_msi_irqs(struct pci_dev *dev, int nvec, int type)
     12{
     13	struct irq_domain *domain;
     14
     15	domain = dev_get_msi_domain(&dev->dev);
     16	if (domain && irq_domain_is_hierarchy(domain))
     17		return msi_domain_alloc_irqs_descs_locked(domain, &dev->dev, nvec);
     18
     19	return pci_msi_legacy_setup_msi_irqs(dev, nvec, type);
     20}
     21
     22void pci_msi_teardown_msi_irqs(struct pci_dev *dev)
     23{
     24	struct irq_domain *domain;
     25
     26	domain = dev_get_msi_domain(&dev->dev);
     27	if (domain && irq_domain_is_hierarchy(domain))
     28		msi_domain_free_irqs_descs_locked(domain, &dev->dev);
     29	else
     30		pci_msi_legacy_teardown_msi_irqs(dev);
     31	msi_free_msi_descs(&dev->dev);
     32}
     33
     34/**
     35 * pci_msi_domain_write_msg - Helper to write MSI message to PCI config space
     36 * @irq_data:	Pointer to interrupt data of the MSI interrupt
     37 * @msg:	Pointer to the message
     38 */
     39static void pci_msi_domain_write_msg(struct irq_data *irq_data, struct msi_msg *msg)
     40{
     41	struct msi_desc *desc = irq_data_get_msi_desc(irq_data);
     42
     43	/*
     44	 * For MSI-X desc->irq is always equal to irq_data->irq. For
     45	 * MSI only the first interrupt of MULTI MSI passes the test.
     46	 */
     47	if (desc->irq == irq_data->irq)
     48		__pci_write_msi_msg(desc, msg);
     49}
     50
     51/**
     52 * pci_msi_domain_calc_hwirq - Generate a unique ID for an MSI source
     53 * @desc:	Pointer to the MSI descriptor
     54 *
     55 * The ID number is only used within the irqdomain.
     56 */
     57static irq_hw_number_t pci_msi_domain_calc_hwirq(struct msi_desc *desc)
     58{
     59	struct pci_dev *dev = msi_desc_to_pci_dev(desc);
     60
     61	return (irq_hw_number_t)desc->msi_index |
     62		pci_dev_id(dev) << 11 |
     63		(pci_domain_nr(dev->bus) & 0xFFFFFFFF) << 27;
     64}
     65
     66static inline bool pci_msi_desc_is_multi_msi(struct msi_desc *desc)
     67{
     68	return !desc->pci.msi_attrib.is_msix && desc->nvec_used > 1;
     69}
     70
     71/**
     72 * pci_msi_domain_check_cap - Verify that @domain supports the capabilities
     73 *			      for @dev
     74 * @domain:	The interrupt domain to check
     75 * @info:	The domain info for verification
     76 * @dev:	The device to check
     77 *
     78 * Returns:
     79 *  0 if the functionality is supported
     80 *  1 if Multi MSI is requested, but the domain does not support it
     81 *  -ENOTSUPP otherwise
     82 */
     83static int pci_msi_domain_check_cap(struct irq_domain *domain,
     84				    struct msi_domain_info *info,
     85				    struct device *dev)
     86{
     87	struct msi_desc *desc = msi_first_desc(dev, MSI_DESC_ALL);
     88
     89	/* Special handling to support __pci_enable_msi_range() */
     90	if (pci_msi_desc_is_multi_msi(desc) &&
     91	    !(info->flags & MSI_FLAG_MULTI_PCI_MSI))
     92		return 1;
     93
     94	if (desc->pci.msi_attrib.is_msix) {
     95		if (!(info->flags & MSI_FLAG_PCI_MSIX))
     96			return -ENOTSUPP;
     97
     98		if (info->flags & MSI_FLAG_MSIX_CONTIGUOUS) {
     99			unsigned int idx = 0;
    100
    101			/* Check for gaps in the entry indices */
    102			msi_for_each_desc(desc, dev, MSI_DESC_ALL) {
    103				if (desc->msi_index != idx++)
    104					return -ENOTSUPP;
    105			}
    106		}
    107	}
    108	return 0;
    109}
    110
    111static void pci_msi_domain_set_desc(msi_alloc_info_t *arg,
    112				    struct msi_desc *desc)
    113{
    114	arg->desc = desc;
    115	arg->hwirq = pci_msi_domain_calc_hwirq(desc);
    116}
    117
    118static struct msi_domain_ops pci_msi_domain_ops_default = {
    119	.set_desc	= pci_msi_domain_set_desc,
    120	.msi_check	= pci_msi_domain_check_cap,
    121};
    122
    123static void pci_msi_domain_update_dom_ops(struct msi_domain_info *info)
    124{
    125	struct msi_domain_ops *ops = info->ops;
    126
    127	if (ops == NULL) {
    128		info->ops = &pci_msi_domain_ops_default;
    129	} else {
    130		if (ops->set_desc == NULL)
    131			ops->set_desc = pci_msi_domain_set_desc;
    132		if (ops->msi_check == NULL)
    133			ops->msi_check = pci_msi_domain_check_cap;
    134	}
    135}
    136
    137static void pci_msi_domain_update_chip_ops(struct msi_domain_info *info)
    138{
    139	struct irq_chip *chip = info->chip;
    140
    141	BUG_ON(!chip);
    142	if (!chip->irq_write_msi_msg)
    143		chip->irq_write_msi_msg = pci_msi_domain_write_msg;
    144	if (!chip->irq_mask)
    145		chip->irq_mask = pci_msi_mask_irq;
    146	if (!chip->irq_unmask)
    147		chip->irq_unmask = pci_msi_unmask_irq;
    148}
    149
    150/**
    151 * pci_msi_create_irq_domain - Create a MSI interrupt domain
    152 * @fwnode:	Optional fwnode of the interrupt controller
    153 * @info:	MSI domain info
    154 * @parent:	Parent irq domain
    155 *
    156 * Updates the domain and chip ops and creates a MSI interrupt domain.
    157 *
    158 * Returns:
    159 * A domain pointer or NULL in case of failure.
    160 */
    161struct irq_domain *pci_msi_create_irq_domain(struct fwnode_handle *fwnode,
    162					     struct msi_domain_info *info,
    163					     struct irq_domain *parent)
    164{
    165	struct irq_domain *domain;
    166
    167	if (WARN_ON(info->flags & MSI_FLAG_LEVEL_CAPABLE))
    168		info->flags &= ~MSI_FLAG_LEVEL_CAPABLE;
    169
    170	if (info->flags & MSI_FLAG_USE_DEF_DOM_OPS)
    171		pci_msi_domain_update_dom_ops(info);
    172	if (info->flags & MSI_FLAG_USE_DEF_CHIP_OPS)
    173		pci_msi_domain_update_chip_ops(info);
    174
    175	info->flags |= MSI_FLAG_ACTIVATE_EARLY | MSI_FLAG_DEV_SYSFS;
    176	if (IS_ENABLED(CONFIG_GENERIC_IRQ_RESERVATION_MODE))
    177		info->flags |= MSI_FLAG_MUST_REACTIVATE;
    178
    179	/* PCI-MSI is oneshot-safe */
    180	info->chip->flags |= IRQCHIP_ONESHOT_SAFE;
    181
    182	domain = msi_create_irq_domain(fwnode, info, parent);
    183	if (!domain)
    184		return NULL;
    185
    186	irq_domain_update_bus_token(domain, DOMAIN_BUS_PCI_MSI);
    187	return domain;
    188}
    189EXPORT_SYMBOL_GPL(pci_msi_create_irq_domain);
    190
    191/*
    192 * Users of the generic MSI infrastructure expect a device to have a single ID,
    193 * so with DMA aliases we have to pick the least-worst compromise. Devices with
    194 * DMA phantom functions tend to still emit MSIs from the real function number,
    195 * so we ignore those and only consider topological aliases where either the
    196 * alias device or RID appears on a different bus number. We also make the
    197 * reasonable assumption that bridges are walked in an upstream direction (so
    198 * the last one seen wins), and the much braver assumption that the most likely
    199 * case is that of PCI->PCIe so we should always use the alias RID. This echoes
    200 * the logic from intel_irq_remapping's set_msi_sid(), which presumably works
    201 * well enough in practice; in the face of the horrible PCIe<->PCI-X conditions
    202 * for taking ownership all we can really do is close our eyes and hope...
    203 */
    204static int get_msi_id_cb(struct pci_dev *pdev, u16 alias, void *data)
    205{
    206	u32 *pa = data;
    207	u8 bus = PCI_BUS_NUM(*pa);
    208
    209	if (pdev->bus->number != bus || PCI_BUS_NUM(alias) != bus)
    210		*pa = alias;
    211
    212	return 0;
    213}
    214
    215/**
    216 * pci_msi_domain_get_msi_rid - Get the MSI requester id (RID)
    217 * @domain:	The interrupt domain
    218 * @pdev:	The PCI device.
    219 *
    220 * The RID for a device is formed from the alias, with a firmware
    221 * supplied mapping applied
    222 *
    223 * Returns: The RID.
    224 */
    225u32 pci_msi_domain_get_msi_rid(struct irq_domain *domain, struct pci_dev *pdev)
    226{
    227	struct device_node *of_node;
    228	u32 rid = pci_dev_id(pdev);
    229
    230	pci_for_each_dma_alias(pdev, get_msi_id_cb, &rid);
    231
    232	of_node = irq_domain_get_of_node(domain);
    233	rid = of_node ? of_msi_map_id(&pdev->dev, of_node, rid) :
    234			iort_msi_map_id(&pdev->dev, rid);
    235
    236	return rid;
    237}
    238
    239/**
    240 * pci_msi_get_device_domain - Get the MSI domain for a given PCI device
    241 * @pdev:	The PCI device
    242 *
    243 * Use the firmware data to find a device-specific MSI domain
    244 * (i.e. not one that is set as a default).
    245 *
    246 * Returns: The corresponding MSI domain or NULL if none has been found.
    247 */
    248struct irq_domain *pci_msi_get_device_domain(struct pci_dev *pdev)
    249{
    250	struct irq_domain *dom;
    251	u32 rid = pci_dev_id(pdev);
    252
    253	pci_for_each_dma_alias(pdev, get_msi_id_cb, &rid);
    254	dom = of_msi_map_get_device_domain(&pdev->dev, rid, DOMAIN_BUS_PCI_MSI);
    255	if (!dom)
    256		dom = iort_get_device_domain(&pdev->dev, rid,
    257					     DOMAIN_BUS_PCI_MSI);
    258	return dom;
    259}
    260
    261/**
    262 * pci_dev_has_special_msi_domain - Check whether the device is handled by
    263 *				    a non-standard PCI-MSI domain
    264 * @pdev:	The PCI device to check.
    265 *
    266 * Returns: True if the device irqdomain or the bus irqdomain is
    267 * non-standard PCI/MSI.
    268 */
    269bool pci_dev_has_special_msi_domain(struct pci_dev *pdev)
    270{
    271	struct irq_domain *dom = dev_get_msi_domain(&pdev->dev);
    272
    273	if (!dom)
    274		dom = dev_get_msi_domain(&pdev->bus->dev);
    275
    276	if (!dom)
    277		return true;
    278
    279	return dom->bus_token != DOMAIN_BUS_PCI_MSI;
    280}