18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * VFIO PCI interrupt handling
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (C) 2012 Red Hat, Inc.  All rights reserved.
68c2ecf20Sopenharmony_ci *     Author: Alex Williamson <alex.williamson@redhat.com>
78c2ecf20Sopenharmony_ci *
88c2ecf20Sopenharmony_ci * Derived from original vfio:
98c2ecf20Sopenharmony_ci * Copyright 2010 Cisco Systems, Inc.  All rights reserved.
108c2ecf20Sopenharmony_ci * Author: Tom Lyon, pugs@cisco.com
118c2ecf20Sopenharmony_ci */
128c2ecf20Sopenharmony_ci
138c2ecf20Sopenharmony_ci#include <linux/device.h>
148c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
158c2ecf20Sopenharmony_ci#include <linux/eventfd.h>
168c2ecf20Sopenharmony_ci#include <linux/msi.h>
178c2ecf20Sopenharmony_ci#include <linux/pci.h>
188c2ecf20Sopenharmony_ci#include <linux/file.h>
198c2ecf20Sopenharmony_ci#include <linux/vfio.h>
208c2ecf20Sopenharmony_ci#include <linux/wait.h>
218c2ecf20Sopenharmony_ci#include <linux/slab.h>
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_ci#include "vfio_pci_private.h"
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_ci/*
268c2ecf20Sopenharmony_ci * INTx
278c2ecf20Sopenharmony_ci */
288c2ecf20Sopenharmony_cistatic void vfio_send_intx_eventfd(void *opaque, void *unused)
298c2ecf20Sopenharmony_ci{
308c2ecf20Sopenharmony_ci	struct vfio_pci_device *vdev = opaque;
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_ci	if (likely(is_intx(vdev) && !vdev->virq_disabled))
338c2ecf20Sopenharmony_ci		eventfd_signal(vdev->ctx[0].trigger, 1);
348c2ecf20Sopenharmony_ci}
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_civoid vfio_pci_intx_mask(struct vfio_pci_device *vdev)
378c2ecf20Sopenharmony_ci{
388c2ecf20Sopenharmony_ci	struct pci_dev *pdev = vdev->pdev;
398c2ecf20Sopenharmony_ci	unsigned long flags;
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_ci	spin_lock_irqsave(&vdev->irqlock, flags);
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_ci	/*
448c2ecf20Sopenharmony_ci	 * Masking can come from interrupt, ioctl, or config space
458c2ecf20Sopenharmony_ci	 * via INTx disable.  The latter means this can get called
468c2ecf20Sopenharmony_ci	 * even when not using intx delivery.  In this case, just
478c2ecf20Sopenharmony_ci	 * try to have the physical bit follow the virtual bit.
488c2ecf20Sopenharmony_ci	 */
498c2ecf20Sopenharmony_ci	if (unlikely(!is_intx(vdev))) {
508c2ecf20Sopenharmony_ci		if (vdev->pci_2_3)
518c2ecf20Sopenharmony_ci			pci_intx(pdev, 0);
528c2ecf20Sopenharmony_ci	} else if (!vdev->ctx[0].masked) {
538c2ecf20Sopenharmony_ci		/*
548c2ecf20Sopenharmony_ci		 * Can't use check_and_mask here because we always want to
558c2ecf20Sopenharmony_ci		 * mask, not just when something is pending.
568c2ecf20Sopenharmony_ci		 */
578c2ecf20Sopenharmony_ci		if (vdev->pci_2_3)
588c2ecf20Sopenharmony_ci			pci_intx(pdev, 0);
598c2ecf20Sopenharmony_ci		else
608c2ecf20Sopenharmony_ci			disable_irq_nosync(pdev->irq);
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_ci		vdev->ctx[0].masked = true;
638c2ecf20Sopenharmony_ci	}
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&vdev->irqlock, flags);
668c2ecf20Sopenharmony_ci}
678c2ecf20Sopenharmony_ci
688c2ecf20Sopenharmony_ci/*
698c2ecf20Sopenharmony_ci * If this is triggered by an eventfd, we can't call eventfd_signal
708c2ecf20Sopenharmony_ci * or else we'll deadlock on the eventfd wait queue.  Return >0 when
718c2ecf20Sopenharmony_ci * a signal is necessary, which can then be handled via a work queue
728c2ecf20Sopenharmony_ci * or directly depending on the caller.
738c2ecf20Sopenharmony_ci */
748c2ecf20Sopenharmony_cistatic int vfio_pci_intx_unmask_handler(void *opaque, void *unused)
758c2ecf20Sopenharmony_ci{
768c2ecf20Sopenharmony_ci	struct vfio_pci_device *vdev = opaque;
778c2ecf20Sopenharmony_ci	struct pci_dev *pdev = vdev->pdev;
788c2ecf20Sopenharmony_ci	unsigned long flags;
798c2ecf20Sopenharmony_ci	int ret = 0;
808c2ecf20Sopenharmony_ci
818c2ecf20Sopenharmony_ci	spin_lock_irqsave(&vdev->irqlock, flags);
828c2ecf20Sopenharmony_ci
838c2ecf20Sopenharmony_ci	/*
848c2ecf20Sopenharmony_ci	 * Unmasking comes from ioctl or config, so again, have the
858c2ecf20Sopenharmony_ci	 * physical bit follow the virtual even when not using INTx.
868c2ecf20Sopenharmony_ci	 */
878c2ecf20Sopenharmony_ci	if (unlikely(!is_intx(vdev))) {
888c2ecf20Sopenharmony_ci		if (vdev->pci_2_3)
898c2ecf20Sopenharmony_ci			pci_intx(pdev, 1);
908c2ecf20Sopenharmony_ci	} else if (vdev->ctx[0].masked && !vdev->virq_disabled) {
918c2ecf20Sopenharmony_ci		/*
928c2ecf20Sopenharmony_ci		 * A pending interrupt here would immediately trigger,
938c2ecf20Sopenharmony_ci		 * but we can avoid that overhead by just re-sending
948c2ecf20Sopenharmony_ci		 * the interrupt to the user.
958c2ecf20Sopenharmony_ci		 */
968c2ecf20Sopenharmony_ci		if (vdev->pci_2_3) {
978c2ecf20Sopenharmony_ci			if (!pci_check_and_unmask_intx(pdev))
988c2ecf20Sopenharmony_ci				ret = 1;
998c2ecf20Sopenharmony_ci		} else
1008c2ecf20Sopenharmony_ci			enable_irq(pdev->irq);
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_ci		vdev->ctx[0].masked = (ret > 0);
1038c2ecf20Sopenharmony_ci	}
1048c2ecf20Sopenharmony_ci
1058c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&vdev->irqlock, flags);
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_ci	return ret;
1088c2ecf20Sopenharmony_ci}
1098c2ecf20Sopenharmony_ci
1108c2ecf20Sopenharmony_civoid vfio_pci_intx_unmask(struct vfio_pci_device *vdev)
1118c2ecf20Sopenharmony_ci{
1128c2ecf20Sopenharmony_ci	if (vfio_pci_intx_unmask_handler(vdev, NULL) > 0)
1138c2ecf20Sopenharmony_ci		vfio_send_intx_eventfd(vdev, NULL);
1148c2ecf20Sopenharmony_ci}
1158c2ecf20Sopenharmony_ci
1168c2ecf20Sopenharmony_cistatic irqreturn_t vfio_intx_handler(int irq, void *dev_id)
1178c2ecf20Sopenharmony_ci{
1188c2ecf20Sopenharmony_ci	struct vfio_pci_device *vdev = dev_id;
1198c2ecf20Sopenharmony_ci	unsigned long flags;
1208c2ecf20Sopenharmony_ci	int ret = IRQ_NONE;
1218c2ecf20Sopenharmony_ci
1228c2ecf20Sopenharmony_ci	spin_lock_irqsave(&vdev->irqlock, flags);
1238c2ecf20Sopenharmony_ci
1248c2ecf20Sopenharmony_ci	if (!vdev->pci_2_3) {
1258c2ecf20Sopenharmony_ci		disable_irq_nosync(vdev->pdev->irq);
1268c2ecf20Sopenharmony_ci		vdev->ctx[0].masked = true;
1278c2ecf20Sopenharmony_ci		ret = IRQ_HANDLED;
1288c2ecf20Sopenharmony_ci	} else if (!vdev->ctx[0].masked &&  /* may be shared */
1298c2ecf20Sopenharmony_ci		   pci_check_and_mask_intx(vdev->pdev)) {
1308c2ecf20Sopenharmony_ci		vdev->ctx[0].masked = true;
1318c2ecf20Sopenharmony_ci		ret = IRQ_HANDLED;
1328c2ecf20Sopenharmony_ci	}
1338c2ecf20Sopenharmony_ci
1348c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&vdev->irqlock, flags);
1358c2ecf20Sopenharmony_ci
1368c2ecf20Sopenharmony_ci	if (ret == IRQ_HANDLED)
1378c2ecf20Sopenharmony_ci		vfio_send_intx_eventfd(vdev, NULL);
1388c2ecf20Sopenharmony_ci
1398c2ecf20Sopenharmony_ci	return ret;
1408c2ecf20Sopenharmony_ci}
1418c2ecf20Sopenharmony_ci
1428c2ecf20Sopenharmony_cistatic int vfio_intx_enable(struct vfio_pci_device *vdev)
1438c2ecf20Sopenharmony_ci{
1448c2ecf20Sopenharmony_ci	if (!is_irq_none(vdev))
1458c2ecf20Sopenharmony_ci		return -EINVAL;
1468c2ecf20Sopenharmony_ci
1478c2ecf20Sopenharmony_ci	if (!vdev->pdev->irq)
1488c2ecf20Sopenharmony_ci		return -ENODEV;
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_ci	vdev->ctx = kzalloc(sizeof(struct vfio_pci_irq_ctx), GFP_KERNEL);
1518c2ecf20Sopenharmony_ci	if (!vdev->ctx)
1528c2ecf20Sopenharmony_ci		return -ENOMEM;
1538c2ecf20Sopenharmony_ci
1548c2ecf20Sopenharmony_ci	vdev->num_ctx = 1;
1558c2ecf20Sopenharmony_ci
1568c2ecf20Sopenharmony_ci	/*
1578c2ecf20Sopenharmony_ci	 * If the virtual interrupt is masked, restore it.  Devices
1588c2ecf20Sopenharmony_ci	 * supporting DisINTx can be masked at the hardware level
1598c2ecf20Sopenharmony_ci	 * here, non-PCI-2.3 devices will have to wait until the
1608c2ecf20Sopenharmony_ci	 * interrupt is enabled.
1618c2ecf20Sopenharmony_ci	 */
1628c2ecf20Sopenharmony_ci	vdev->ctx[0].masked = vdev->virq_disabled;
1638c2ecf20Sopenharmony_ci	if (vdev->pci_2_3)
1648c2ecf20Sopenharmony_ci		pci_intx(vdev->pdev, !vdev->ctx[0].masked);
1658c2ecf20Sopenharmony_ci
1668c2ecf20Sopenharmony_ci	vdev->irq_type = VFIO_PCI_INTX_IRQ_INDEX;
1678c2ecf20Sopenharmony_ci
1688c2ecf20Sopenharmony_ci	return 0;
1698c2ecf20Sopenharmony_ci}
1708c2ecf20Sopenharmony_ci
1718c2ecf20Sopenharmony_cistatic int vfio_intx_set_signal(struct vfio_pci_device *vdev, int fd)
1728c2ecf20Sopenharmony_ci{
1738c2ecf20Sopenharmony_ci	struct pci_dev *pdev = vdev->pdev;
1748c2ecf20Sopenharmony_ci	unsigned long irqflags = IRQF_SHARED;
1758c2ecf20Sopenharmony_ci	struct eventfd_ctx *trigger;
1768c2ecf20Sopenharmony_ci	unsigned long flags;
1778c2ecf20Sopenharmony_ci	int ret;
1788c2ecf20Sopenharmony_ci
1798c2ecf20Sopenharmony_ci	if (vdev->ctx[0].trigger) {
1808c2ecf20Sopenharmony_ci		free_irq(pdev->irq, vdev);
1818c2ecf20Sopenharmony_ci		kfree(vdev->ctx[0].name);
1828c2ecf20Sopenharmony_ci		eventfd_ctx_put(vdev->ctx[0].trigger);
1838c2ecf20Sopenharmony_ci		vdev->ctx[0].trigger = NULL;
1848c2ecf20Sopenharmony_ci	}
1858c2ecf20Sopenharmony_ci
1868c2ecf20Sopenharmony_ci	if (fd < 0) /* Disable only */
1878c2ecf20Sopenharmony_ci		return 0;
1888c2ecf20Sopenharmony_ci
1898c2ecf20Sopenharmony_ci	vdev->ctx[0].name = kasprintf(GFP_KERNEL, "vfio-intx(%s)",
1908c2ecf20Sopenharmony_ci				      pci_name(pdev));
1918c2ecf20Sopenharmony_ci	if (!vdev->ctx[0].name)
1928c2ecf20Sopenharmony_ci		return -ENOMEM;
1938c2ecf20Sopenharmony_ci
1948c2ecf20Sopenharmony_ci	trigger = eventfd_ctx_fdget(fd);
1958c2ecf20Sopenharmony_ci	if (IS_ERR(trigger)) {
1968c2ecf20Sopenharmony_ci		kfree(vdev->ctx[0].name);
1978c2ecf20Sopenharmony_ci		return PTR_ERR(trigger);
1988c2ecf20Sopenharmony_ci	}
1998c2ecf20Sopenharmony_ci
2008c2ecf20Sopenharmony_ci	vdev->ctx[0].trigger = trigger;
2018c2ecf20Sopenharmony_ci
2028c2ecf20Sopenharmony_ci	if (!vdev->pci_2_3)
2038c2ecf20Sopenharmony_ci		irqflags = 0;
2048c2ecf20Sopenharmony_ci
2058c2ecf20Sopenharmony_ci	ret = request_irq(pdev->irq, vfio_intx_handler,
2068c2ecf20Sopenharmony_ci			  irqflags, vdev->ctx[0].name, vdev);
2078c2ecf20Sopenharmony_ci	if (ret) {
2088c2ecf20Sopenharmony_ci		vdev->ctx[0].trigger = NULL;
2098c2ecf20Sopenharmony_ci		kfree(vdev->ctx[0].name);
2108c2ecf20Sopenharmony_ci		eventfd_ctx_put(trigger);
2118c2ecf20Sopenharmony_ci		return ret;
2128c2ecf20Sopenharmony_ci	}
2138c2ecf20Sopenharmony_ci
2148c2ecf20Sopenharmony_ci	/*
2158c2ecf20Sopenharmony_ci	 * INTx disable will stick across the new irq setup,
2168c2ecf20Sopenharmony_ci	 * disable_irq won't.
2178c2ecf20Sopenharmony_ci	 */
2188c2ecf20Sopenharmony_ci	spin_lock_irqsave(&vdev->irqlock, flags);
2198c2ecf20Sopenharmony_ci	if (!vdev->pci_2_3 && vdev->ctx[0].masked)
2208c2ecf20Sopenharmony_ci		disable_irq_nosync(pdev->irq);
2218c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&vdev->irqlock, flags);
2228c2ecf20Sopenharmony_ci
2238c2ecf20Sopenharmony_ci	return 0;
2248c2ecf20Sopenharmony_ci}
2258c2ecf20Sopenharmony_ci
2268c2ecf20Sopenharmony_cistatic void vfio_intx_disable(struct vfio_pci_device *vdev)
2278c2ecf20Sopenharmony_ci{
2288c2ecf20Sopenharmony_ci	vfio_virqfd_disable(&vdev->ctx[0].unmask);
2298c2ecf20Sopenharmony_ci	vfio_virqfd_disable(&vdev->ctx[0].mask);
2308c2ecf20Sopenharmony_ci	vfio_intx_set_signal(vdev, -1);
2318c2ecf20Sopenharmony_ci	vdev->irq_type = VFIO_PCI_NUM_IRQS;
2328c2ecf20Sopenharmony_ci	vdev->num_ctx = 0;
2338c2ecf20Sopenharmony_ci	kfree(vdev->ctx);
2348c2ecf20Sopenharmony_ci}
2358c2ecf20Sopenharmony_ci
2368c2ecf20Sopenharmony_ci/*
2378c2ecf20Sopenharmony_ci * MSI/MSI-X
2388c2ecf20Sopenharmony_ci */
2398c2ecf20Sopenharmony_cistatic irqreturn_t vfio_msihandler(int irq, void *arg)
2408c2ecf20Sopenharmony_ci{
2418c2ecf20Sopenharmony_ci	struct eventfd_ctx *trigger = arg;
2428c2ecf20Sopenharmony_ci
2438c2ecf20Sopenharmony_ci	eventfd_signal(trigger, 1);
2448c2ecf20Sopenharmony_ci	return IRQ_HANDLED;
2458c2ecf20Sopenharmony_ci}
2468c2ecf20Sopenharmony_ci
2478c2ecf20Sopenharmony_cistatic int vfio_msi_enable(struct vfio_pci_device *vdev, int nvec, bool msix)
2488c2ecf20Sopenharmony_ci{
2498c2ecf20Sopenharmony_ci	struct pci_dev *pdev = vdev->pdev;
2508c2ecf20Sopenharmony_ci	unsigned int flag = msix ? PCI_IRQ_MSIX : PCI_IRQ_MSI;
2518c2ecf20Sopenharmony_ci	int ret;
2528c2ecf20Sopenharmony_ci	u16 cmd;
2538c2ecf20Sopenharmony_ci
2548c2ecf20Sopenharmony_ci	if (!is_irq_none(vdev))
2558c2ecf20Sopenharmony_ci		return -EINVAL;
2568c2ecf20Sopenharmony_ci
2578c2ecf20Sopenharmony_ci	vdev->ctx = kcalloc(nvec, sizeof(struct vfio_pci_irq_ctx), GFP_KERNEL);
2588c2ecf20Sopenharmony_ci	if (!vdev->ctx)
2598c2ecf20Sopenharmony_ci		return -ENOMEM;
2608c2ecf20Sopenharmony_ci
2618c2ecf20Sopenharmony_ci	/* return the number of supported vectors if we can't get all: */
2628c2ecf20Sopenharmony_ci	cmd = vfio_pci_memory_lock_and_enable(vdev);
2638c2ecf20Sopenharmony_ci	ret = pci_alloc_irq_vectors(pdev, 1, nvec, flag);
2648c2ecf20Sopenharmony_ci	if (ret < nvec) {
2658c2ecf20Sopenharmony_ci		if (ret > 0)
2668c2ecf20Sopenharmony_ci			pci_free_irq_vectors(pdev);
2678c2ecf20Sopenharmony_ci		vfio_pci_memory_unlock_and_restore(vdev, cmd);
2688c2ecf20Sopenharmony_ci		kfree(vdev->ctx);
2698c2ecf20Sopenharmony_ci		return ret;
2708c2ecf20Sopenharmony_ci	}
2718c2ecf20Sopenharmony_ci	vfio_pci_memory_unlock_and_restore(vdev, cmd);
2728c2ecf20Sopenharmony_ci
2738c2ecf20Sopenharmony_ci	vdev->num_ctx = nvec;
2748c2ecf20Sopenharmony_ci	vdev->irq_type = msix ? VFIO_PCI_MSIX_IRQ_INDEX :
2758c2ecf20Sopenharmony_ci				VFIO_PCI_MSI_IRQ_INDEX;
2768c2ecf20Sopenharmony_ci
2778c2ecf20Sopenharmony_ci	if (!msix) {
2788c2ecf20Sopenharmony_ci		/*
2798c2ecf20Sopenharmony_ci		 * Compute the virtual hardware field for max msi vectors -
2808c2ecf20Sopenharmony_ci		 * it is the log base 2 of the number of vectors.
2818c2ecf20Sopenharmony_ci		 */
2828c2ecf20Sopenharmony_ci		vdev->msi_qmax = fls(nvec * 2 - 1) - 1;
2838c2ecf20Sopenharmony_ci	}
2848c2ecf20Sopenharmony_ci
2858c2ecf20Sopenharmony_ci	return 0;
2868c2ecf20Sopenharmony_ci}
2878c2ecf20Sopenharmony_ci
2888c2ecf20Sopenharmony_cistatic int vfio_msi_set_vector_signal(struct vfio_pci_device *vdev,
2898c2ecf20Sopenharmony_ci				      int vector, int fd, bool msix)
2908c2ecf20Sopenharmony_ci{
2918c2ecf20Sopenharmony_ci	struct pci_dev *pdev = vdev->pdev;
2928c2ecf20Sopenharmony_ci	struct eventfd_ctx *trigger;
2938c2ecf20Sopenharmony_ci	int irq, ret;
2948c2ecf20Sopenharmony_ci	u16 cmd;
2958c2ecf20Sopenharmony_ci
2968c2ecf20Sopenharmony_ci	if (vector < 0 || vector >= vdev->num_ctx)
2978c2ecf20Sopenharmony_ci		return -EINVAL;
2988c2ecf20Sopenharmony_ci
2998c2ecf20Sopenharmony_ci	irq = pci_irq_vector(pdev, vector);
3008c2ecf20Sopenharmony_ci
3018c2ecf20Sopenharmony_ci	if (vdev->ctx[vector].trigger) {
3028c2ecf20Sopenharmony_ci		irq_bypass_unregister_producer(&vdev->ctx[vector].producer);
3038c2ecf20Sopenharmony_ci
3048c2ecf20Sopenharmony_ci		cmd = vfio_pci_memory_lock_and_enable(vdev);
3058c2ecf20Sopenharmony_ci		free_irq(irq, vdev->ctx[vector].trigger);
3068c2ecf20Sopenharmony_ci		vfio_pci_memory_unlock_and_restore(vdev, cmd);
3078c2ecf20Sopenharmony_ci
3088c2ecf20Sopenharmony_ci		kfree(vdev->ctx[vector].name);
3098c2ecf20Sopenharmony_ci		eventfd_ctx_put(vdev->ctx[vector].trigger);
3108c2ecf20Sopenharmony_ci		vdev->ctx[vector].trigger = NULL;
3118c2ecf20Sopenharmony_ci	}
3128c2ecf20Sopenharmony_ci
3138c2ecf20Sopenharmony_ci	if (fd < 0)
3148c2ecf20Sopenharmony_ci		return 0;
3158c2ecf20Sopenharmony_ci
3168c2ecf20Sopenharmony_ci	vdev->ctx[vector].name = kasprintf(GFP_KERNEL, "vfio-msi%s[%d](%s)",
3178c2ecf20Sopenharmony_ci					   msix ? "x" : "", vector,
3188c2ecf20Sopenharmony_ci					   pci_name(pdev));
3198c2ecf20Sopenharmony_ci	if (!vdev->ctx[vector].name)
3208c2ecf20Sopenharmony_ci		return -ENOMEM;
3218c2ecf20Sopenharmony_ci
3228c2ecf20Sopenharmony_ci	trigger = eventfd_ctx_fdget(fd);
3238c2ecf20Sopenharmony_ci	if (IS_ERR(trigger)) {
3248c2ecf20Sopenharmony_ci		kfree(vdev->ctx[vector].name);
3258c2ecf20Sopenharmony_ci		return PTR_ERR(trigger);
3268c2ecf20Sopenharmony_ci	}
3278c2ecf20Sopenharmony_ci
3288c2ecf20Sopenharmony_ci	/*
3298c2ecf20Sopenharmony_ci	 * The MSIx vector table resides in device memory which may be cleared
3308c2ecf20Sopenharmony_ci	 * via backdoor resets. We don't allow direct access to the vector
3318c2ecf20Sopenharmony_ci	 * table so even if a userspace driver attempts to save/restore around
3328c2ecf20Sopenharmony_ci	 * such a reset it would be unsuccessful. To avoid this, restore the
3338c2ecf20Sopenharmony_ci	 * cached value of the message prior to enabling.
3348c2ecf20Sopenharmony_ci	 */
3358c2ecf20Sopenharmony_ci	cmd = vfio_pci_memory_lock_and_enable(vdev);
3368c2ecf20Sopenharmony_ci	if (msix) {
3378c2ecf20Sopenharmony_ci		struct msi_msg msg;
3388c2ecf20Sopenharmony_ci
3398c2ecf20Sopenharmony_ci		get_cached_msi_msg(irq, &msg);
3408c2ecf20Sopenharmony_ci		pci_write_msi_msg(irq, &msg);
3418c2ecf20Sopenharmony_ci	}
3428c2ecf20Sopenharmony_ci
3438c2ecf20Sopenharmony_ci	ret = request_irq(irq, vfio_msihandler, 0,
3448c2ecf20Sopenharmony_ci			  vdev->ctx[vector].name, trigger);
3458c2ecf20Sopenharmony_ci	vfio_pci_memory_unlock_and_restore(vdev, cmd);
3468c2ecf20Sopenharmony_ci	if (ret) {
3478c2ecf20Sopenharmony_ci		kfree(vdev->ctx[vector].name);
3488c2ecf20Sopenharmony_ci		eventfd_ctx_put(trigger);
3498c2ecf20Sopenharmony_ci		return ret;
3508c2ecf20Sopenharmony_ci	}
3518c2ecf20Sopenharmony_ci
3528c2ecf20Sopenharmony_ci	vdev->ctx[vector].producer.token = trigger;
3538c2ecf20Sopenharmony_ci	vdev->ctx[vector].producer.irq = irq;
3548c2ecf20Sopenharmony_ci	ret = irq_bypass_register_producer(&vdev->ctx[vector].producer);
3558c2ecf20Sopenharmony_ci	if (unlikely(ret)) {
3568c2ecf20Sopenharmony_ci		dev_info(&pdev->dev,
3578c2ecf20Sopenharmony_ci		"irq bypass producer (token %p) registration fails: %d\n",
3588c2ecf20Sopenharmony_ci		vdev->ctx[vector].producer.token, ret);
3598c2ecf20Sopenharmony_ci
3608c2ecf20Sopenharmony_ci		vdev->ctx[vector].producer.token = NULL;
3618c2ecf20Sopenharmony_ci	}
3628c2ecf20Sopenharmony_ci	vdev->ctx[vector].trigger = trigger;
3638c2ecf20Sopenharmony_ci
3648c2ecf20Sopenharmony_ci	return 0;
3658c2ecf20Sopenharmony_ci}
3668c2ecf20Sopenharmony_ci
3678c2ecf20Sopenharmony_cistatic int vfio_msi_set_block(struct vfio_pci_device *vdev, unsigned start,
3688c2ecf20Sopenharmony_ci			      unsigned count, int32_t *fds, bool msix)
3698c2ecf20Sopenharmony_ci{
3708c2ecf20Sopenharmony_ci	int i, j, ret = 0;
3718c2ecf20Sopenharmony_ci
3728c2ecf20Sopenharmony_ci	if (start >= vdev->num_ctx || start + count > vdev->num_ctx)
3738c2ecf20Sopenharmony_ci		return -EINVAL;
3748c2ecf20Sopenharmony_ci
3758c2ecf20Sopenharmony_ci	for (i = 0, j = start; i < count && !ret; i++, j++) {
3768c2ecf20Sopenharmony_ci		int fd = fds ? fds[i] : -1;
3778c2ecf20Sopenharmony_ci		ret = vfio_msi_set_vector_signal(vdev, j, fd, msix);
3788c2ecf20Sopenharmony_ci	}
3798c2ecf20Sopenharmony_ci
3808c2ecf20Sopenharmony_ci	if (ret) {
3818c2ecf20Sopenharmony_ci		for (--j; j >= (int)start; j--)
3828c2ecf20Sopenharmony_ci			vfio_msi_set_vector_signal(vdev, j, -1, msix);
3838c2ecf20Sopenharmony_ci	}
3848c2ecf20Sopenharmony_ci
3858c2ecf20Sopenharmony_ci	return ret;
3868c2ecf20Sopenharmony_ci}
3878c2ecf20Sopenharmony_ci
3888c2ecf20Sopenharmony_cistatic void vfio_msi_disable(struct vfio_pci_device *vdev, bool msix)
3898c2ecf20Sopenharmony_ci{
3908c2ecf20Sopenharmony_ci	struct pci_dev *pdev = vdev->pdev;
3918c2ecf20Sopenharmony_ci	int i;
3928c2ecf20Sopenharmony_ci	u16 cmd;
3938c2ecf20Sopenharmony_ci
3948c2ecf20Sopenharmony_ci	for (i = 0; i < vdev->num_ctx; i++) {
3958c2ecf20Sopenharmony_ci		vfio_virqfd_disable(&vdev->ctx[i].unmask);
3968c2ecf20Sopenharmony_ci		vfio_virqfd_disable(&vdev->ctx[i].mask);
3978c2ecf20Sopenharmony_ci	}
3988c2ecf20Sopenharmony_ci
3998c2ecf20Sopenharmony_ci	vfio_msi_set_block(vdev, 0, vdev->num_ctx, NULL, msix);
4008c2ecf20Sopenharmony_ci
4018c2ecf20Sopenharmony_ci	cmd = vfio_pci_memory_lock_and_enable(vdev);
4028c2ecf20Sopenharmony_ci	pci_free_irq_vectors(pdev);
4038c2ecf20Sopenharmony_ci	vfio_pci_memory_unlock_and_restore(vdev, cmd);
4048c2ecf20Sopenharmony_ci
4058c2ecf20Sopenharmony_ci	/*
4068c2ecf20Sopenharmony_ci	 * Both disable paths above use pci_intx_for_msi() to clear DisINTx
4078c2ecf20Sopenharmony_ci	 * via their shutdown paths.  Restore for NoINTx devices.
4088c2ecf20Sopenharmony_ci	 */
4098c2ecf20Sopenharmony_ci	if (vdev->nointx)
4108c2ecf20Sopenharmony_ci		pci_intx(pdev, 0);
4118c2ecf20Sopenharmony_ci
4128c2ecf20Sopenharmony_ci	vdev->irq_type = VFIO_PCI_NUM_IRQS;
4138c2ecf20Sopenharmony_ci	vdev->num_ctx = 0;
4148c2ecf20Sopenharmony_ci	kfree(vdev->ctx);
4158c2ecf20Sopenharmony_ci}
4168c2ecf20Sopenharmony_ci
4178c2ecf20Sopenharmony_ci/*
4188c2ecf20Sopenharmony_ci * IOCTL support
4198c2ecf20Sopenharmony_ci */
4208c2ecf20Sopenharmony_cistatic int vfio_pci_set_intx_unmask(struct vfio_pci_device *vdev,
4218c2ecf20Sopenharmony_ci				    unsigned index, unsigned start,
4228c2ecf20Sopenharmony_ci				    unsigned count, uint32_t flags, void *data)
4238c2ecf20Sopenharmony_ci{
4248c2ecf20Sopenharmony_ci	if (!is_intx(vdev) || start != 0 || count != 1)
4258c2ecf20Sopenharmony_ci		return -EINVAL;
4268c2ecf20Sopenharmony_ci
4278c2ecf20Sopenharmony_ci	if (flags & VFIO_IRQ_SET_DATA_NONE) {
4288c2ecf20Sopenharmony_ci		vfio_pci_intx_unmask(vdev);
4298c2ecf20Sopenharmony_ci	} else if (flags & VFIO_IRQ_SET_DATA_BOOL) {
4308c2ecf20Sopenharmony_ci		uint8_t unmask = *(uint8_t *)data;
4318c2ecf20Sopenharmony_ci		if (unmask)
4328c2ecf20Sopenharmony_ci			vfio_pci_intx_unmask(vdev);
4338c2ecf20Sopenharmony_ci	} else if (flags & VFIO_IRQ_SET_DATA_EVENTFD) {
4348c2ecf20Sopenharmony_ci		int32_t fd = *(int32_t *)data;
4358c2ecf20Sopenharmony_ci		if (fd >= 0)
4368c2ecf20Sopenharmony_ci			return vfio_virqfd_enable((void *) vdev,
4378c2ecf20Sopenharmony_ci						  vfio_pci_intx_unmask_handler,
4388c2ecf20Sopenharmony_ci						  vfio_send_intx_eventfd, NULL,
4398c2ecf20Sopenharmony_ci						  &vdev->ctx[0].unmask, fd);
4408c2ecf20Sopenharmony_ci
4418c2ecf20Sopenharmony_ci		vfio_virqfd_disable(&vdev->ctx[0].unmask);
4428c2ecf20Sopenharmony_ci	}
4438c2ecf20Sopenharmony_ci
4448c2ecf20Sopenharmony_ci	return 0;
4458c2ecf20Sopenharmony_ci}
4468c2ecf20Sopenharmony_ci
4478c2ecf20Sopenharmony_cistatic int vfio_pci_set_intx_mask(struct vfio_pci_device *vdev,
4488c2ecf20Sopenharmony_ci				  unsigned index, unsigned start,
4498c2ecf20Sopenharmony_ci				  unsigned count, uint32_t flags, void *data)
4508c2ecf20Sopenharmony_ci{
4518c2ecf20Sopenharmony_ci	if (!is_intx(vdev) || start != 0 || count != 1)
4528c2ecf20Sopenharmony_ci		return -EINVAL;
4538c2ecf20Sopenharmony_ci
4548c2ecf20Sopenharmony_ci	if (flags & VFIO_IRQ_SET_DATA_NONE) {
4558c2ecf20Sopenharmony_ci		vfio_pci_intx_mask(vdev);
4568c2ecf20Sopenharmony_ci	} else if (flags & VFIO_IRQ_SET_DATA_BOOL) {
4578c2ecf20Sopenharmony_ci		uint8_t mask = *(uint8_t *)data;
4588c2ecf20Sopenharmony_ci		if (mask)
4598c2ecf20Sopenharmony_ci			vfio_pci_intx_mask(vdev);
4608c2ecf20Sopenharmony_ci	} else if (flags & VFIO_IRQ_SET_DATA_EVENTFD) {
4618c2ecf20Sopenharmony_ci		return -ENOTTY; /* XXX implement me */
4628c2ecf20Sopenharmony_ci	}
4638c2ecf20Sopenharmony_ci
4648c2ecf20Sopenharmony_ci	return 0;
4658c2ecf20Sopenharmony_ci}
4668c2ecf20Sopenharmony_ci
4678c2ecf20Sopenharmony_cistatic int vfio_pci_set_intx_trigger(struct vfio_pci_device *vdev,
4688c2ecf20Sopenharmony_ci				     unsigned index, unsigned start,
4698c2ecf20Sopenharmony_ci				     unsigned count, uint32_t flags, void *data)
4708c2ecf20Sopenharmony_ci{
4718c2ecf20Sopenharmony_ci	if (is_intx(vdev) && !count && (flags & VFIO_IRQ_SET_DATA_NONE)) {
4728c2ecf20Sopenharmony_ci		vfio_intx_disable(vdev);
4738c2ecf20Sopenharmony_ci		return 0;
4748c2ecf20Sopenharmony_ci	}
4758c2ecf20Sopenharmony_ci
4768c2ecf20Sopenharmony_ci	if (!(is_intx(vdev) || is_irq_none(vdev)) || start != 0 || count != 1)
4778c2ecf20Sopenharmony_ci		return -EINVAL;
4788c2ecf20Sopenharmony_ci
4798c2ecf20Sopenharmony_ci	if (flags & VFIO_IRQ_SET_DATA_EVENTFD) {
4808c2ecf20Sopenharmony_ci		int32_t fd = *(int32_t *)data;
4818c2ecf20Sopenharmony_ci		int ret;
4828c2ecf20Sopenharmony_ci
4838c2ecf20Sopenharmony_ci		if (is_intx(vdev))
4848c2ecf20Sopenharmony_ci			return vfio_intx_set_signal(vdev, fd);
4858c2ecf20Sopenharmony_ci
4868c2ecf20Sopenharmony_ci		ret = vfio_intx_enable(vdev);
4878c2ecf20Sopenharmony_ci		if (ret)
4888c2ecf20Sopenharmony_ci			return ret;
4898c2ecf20Sopenharmony_ci
4908c2ecf20Sopenharmony_ci		ret = vfio_intx_set_signal(vdev, fd);
4918c2ecf20Sopenharmony_ci		if (ret)
4928c2ecf20Sopenharmony_ci			vfio_intx_disable(vdev);
4938c2ecf20Sopenharmony_ci
4948c2ecf20Sopenharmony_ci		return ret;
4958c2ecf20Sopenharmony_ci	}
4968c2ecf20Sopenharmony_ci
4978c2ecf20Sopenharmony_ci	if (!is_intx(vdev))
4988c2ecf20Sopenharmony_ci		return -EINVAL;
4998c2ecf20Sopenharmony_ci
5008c2ecf20Sopenharmony_ci	if (flags & VFIO_IRQ_SET_DATA_NONE) {
5018c2ecf20Sopenharmony_ci		vfio_send_intx_eventfd(vdev, NULL);
5028c2ecf20Sopenharmony_ci	} else if (flags & VFIO_IRQ_SET_DATA_BOOL) {
5038c2ecf20Sopenharmony_ci		uint8_t trigger = *(uint8_t *)data;
5048c2ecf20Sopenharmony_ci		if (trigger)
5058c2ecf20Sopenharmony_ci			vfio_send_intx_eventfd(vdev, NULL);
5068c2ecf20Sopenharmony_ci	}
5078c2ecf20Sopenharmony_ci	return 0;
5088c2ecf20Sopenharmony_ci}
5098c2ecf20Sopenharmony_ci
5108c2ecf20Sopenharmony_cistatic int vfio_pci_set_msi_trigger(struct vfio_pci_device *vdev,
5118c2ecf20Sopenharmony_ci				    unsigned index, unsigned start,
5128c2ecf20Sopenharmony_ci				    unsigned count, uint32_t flags, void *data)
5138c2ecf20Sopenharmony_ci{
5148c2ecf20Sopenharmony_ci	int i;
5158c2ecf20Sopenharmony_ci	bool msix = (index == VFIO_PCI_MSIX_IRQ_INDEX) ? true : false;
5168c2ecf20Sopenharmony_ci
5178c2ecf20Sopenharmony_ci	if (irq_is(vdev, index) && !count && (flags & VFIO_IRQ_SET_DATA_NONE)) {
5188c2ecf20Sopenharmony_ci		vfio_msi_disable(vdev, msix);
5198c2ecf20Sopenharmony_ci		return 0;
5208c2ecf20Sopenharmony_ci	}
5218c2ecf20Sopenharmony_ci
5228c2ecf20Sopenharmony_ci	if (!(irq_is(vdev, index) || is_irq_none(vdev)))
5238c2ecf20Sopenharmony_ci		return -EINVAL;
5248c2ecf20Sopenharmony_ci
5258c2ecf20Sopenharmony_ci	if (flags & VFIO_IRQ_SET_DATA_EVENTFD) {
5268c2ecf20Sopenharmony_ci		int32_t *fds = data;
5278c2ecf20Sopenharmony_ci		int ret;
5288c2ecf20Sopenharmony_ci
5298c2ecf20Sopenharmony_ci		if (vdev->irq_type == index)
5308c2ecf20Sopenharmony_ci			return vfio_msi_set_block(vdev, start, count,
5318c2ecf20Sopenharmony_ci						  fds, msix);
5328c2ecf20Sopenharmony_ci
5338c2ecf20Sopenharmony_ci		ret = vfio_msi_enable(vdev, start + count, msix);
5348c2ecf20Sopenharmony_ci		if (ret)
5358c2ecf20Sopenharmony_ci			return ret;
5368c2ecf20Sopenharmony_ci
5378c2ecf20Sopenharmony_ci		ret = vfio_msi_set_block(vdev, start, count, fds, msix);
5388c2ecf20Sopenharmony_ci		if (ret)
5398c2ecf20Sopenharmony_ci			vfio_msi_disable(vdev, msix);
5408c2ecf20Sopenharmony_ci
5418c2ecf20Sopenharmony_ci		return ret;
5428c2ecf20Sopenharmony_ci	}
5438c2ecf20Sopenharmony_ci
5448c2ecf20Sopenharmony_ci	if (!irq_is(vdev, index) || start + count > vdev->num_ctx)
5458c2ecf20Sopenharmony_ci		return -EINVAL;
5468c2ecf20Sopenharmony_ci
5478c2ecf20Sopenharmony_ci	for (i = start; i < start + count; i++) {
5488c2ecf20Sopenharmony_ci		if (!vdev->ctx[i].trigger)
5498c2ecf20Sopenharmony_ci			continue;
5508c2ecf20Sopenharmony_ci		if (flags & VFIO_IRQ_SET_DATA_NONE) {
5518c2ecf20Sopenharmony_ci			eventfd_signal(vdev->ctx[i].trigger, 1);
5528c2ecf20Sopenharmony_ci		} else if (flags & VFIO_IRQ_SET_DATA_BOOL) {
5538c2ecf20Sopenharmony_ci			uint8_t *bools = data;
5548c2ecf20Sopenharmony_ci			if (bools[i - start])
5558c2ecf20Sopenharmony_ci				eventfd_signal(vdev->ctx[i].trigger, 1);
5568c2ecf20Sopenharmony_ci		}
5578c2ecf20Sopenharmony_ci	}
5588c2ecf20Sopenharmony_ci	return 0;
5598c2ecf20Sopenharmony_ci}
5608c2ecf20Sopenharmony_ci
5618c2ecf20Sopenharmony_cistatic int vfio_pci_set_ctx_trigger_single(struct eventfd_ctx **ctx,
5628c2ecf20Sopenharmony_ci					   unsigned int count, uint32_t flags,
5638c2ecf20Sopenharmony_ci					   void *data)
5648c2ecf20Sopenharmony_ci{
5658c2ecf20Sopenharmony_ci	/* DATA_NONE/DATA_BOOL enables loopback testing */
5668c2ecf20Sopenharmony_ci	if (flags & VFIO_IRQ_SET_DATA_NONE) {
5678c2ecf20Sopenharmony_ci		if (*ctx) {
5688c2ecf20Sopenharmony_ci			if (count) {
5698c2ecf20Sopenharmony_ci				eventfd_signal(*ctx, 1);
5708c2ecf20Sopenharmony_ci			} else {
5718c2ecf20Sopenharmony_ci				eventfd_ctx_put(*ctx);
5728c2ecf20Sopenharmony_ci				*ctx = NULL;
5738c2ecf20Sopenharmony_ci			}
5748c2ecf20Sopenharmony_ci			return 0;
5758c2ecf20Sopenharmony_ci		}
5768c2ecf20Sopenharmony_ci	} else if (flags & VFIO_IRQ_SET_DATA_BOOL) {
5778c2ecf20Sopenharmony_ci		uint8_t trigger;
5788c2ecf20Sopenharmony_ci
5798c2ecf20Sopenharmony_ci		if (!count)
5808c2ecf20Sopenharmony_ci			return -EINVAL;
5818c2ecf20Sopenharmony_ci
5828c2ecf20Sopenharmony_ci		trigger = *(uint8_t *)data;
5838c2ecf20Sopenharmony_ci		if (trigger && *ctx)
5848c2ecf20Sopenharmony_ci			eventfd_signal(*ctx, 1);
5858c2ecf20Sopenharmony_ci
5868c2ecf20Sopenharmony_ci		return 0;
5878c2ecf20Sopenharmony_ci	} else if (flags & VFIO_IRQ_SET_DATA_EVENTFD) {
5888c2ecf20Sopenharmony_ci		int32_t fd;
5898c2ecf20Sopenharmony_ci
5908c2ecf20Sopenharmony_ci		if (!count)
5918c2ecf20Sopenharmony_ci			return -EINVAL;
5928c2ecf20Sopenharmony_ci
5938c2ecf20Sopenharmony_ci		fd = *(int32_t *)data;
5948c2ecf20Sopenharmony_ci		if (fd == -1) {
5958c2ecf20Sopenharmony_ci			if (*ctx)
5968c2ecf20Sopenharmony_ci				eventfd_ctx_put(*ctx);
5978c2ecf20Sopenharmony_ci			*ctx = NULL;
5988c2ecf20Sopenharmony_ci		} else if (fd >= 0) {
5998c2ecf20Sopenharmony_ci			struct eventfd_ctx *efdctx;
6008c2ecf20Sopenharmony_ci
6018c2ecf20Sopenharmony_ci			efdctx = eventfd_ctx_fdget(fd);
6028c2ecf20Sopenharmony_ci			if (IS_ERR(efdctx))
6038c2ecf20Sopenharmony_ci				return PTR_ERR(efdctx);
6048c2ecf20Sopenharmony_ci
6058c2ecf20Sopenharmony_ci			if (*ctx)
6068c2ecf20Sopenharmony_ci				eventfd_ctx_put(*ctx);
6078c2ecf20Sopenharmony_ci
6088c2ecf20Sopenharmony_ci			*ctx = efdctx;
6098c2ecf20Sopenharmony_ci		}
6108c2ecf20Sopenharmony_ci		return 0;
6118c2ecf20Sopenharmony_ci	}
6128c2ecf20Sopenharmony_ci
6138c2ecf20Sopenharmony_ci	return -EINVAL;
6148c2ecf20Sopenharmony_ci}
6158c2ecf20Sopenharmony_ci
6168c2ecf20Sopenharmony_cistatic int vfio_pci_set_err_trigger(struct vfio_pci_device *vdev,
6178c2ecf20Sopenharmony_ci				    unsigned index, unsigned start,
6188c2ecf20Sopenharmony_ci				    unsigned count, uint32_t flags, void *data)
6198c2ecf20Sopenharmony_ci{
6208c2ecf20Sopenharmony_ci	if (index != VFIO_PCI_ERR_IRQ_INDEX || start != 0 || count > 1)
6218c2ecf20Sopenharmony_ci		return -EINVAL;
6228c2ecf20Sopenharmony_ci
6238c2ecf20Sopenharmony_ci	return vfio_pci_set_ctx_trigger_single(&vdev->err_trigger,
6248c2ecf20Sopenharmony_ci					       count, flags, data);
6258c2ecf20Sopenharmony_ci}
6268c2ecf20Sopenharmony_ci
6278c2ecf20Sopenharmony_cistatic int vfio_pci_set_req_trigger(struct vfio_pci_device *vdev,
6288c2ecf20Sopenharmony_ci				    unsigned index, unsigned start,
6298c2ecf20Sopenharmony_ci				    unsigned count, uint32_t flags, void *data)
6308c2ecf20Sopenharmony_ci{
6318c2ecf20Sopenharmony_ci	if (index != VFIO_PCI_REQ_IRQ_INDEX || start != 0 || count > 1)
6328c2ecf20Sopenharmony_ci		return -EINVAL;
6338c2ecf20Sopenharmony_ci
6348c2ecf20Sopenharmony_ci	return vfio_pci_set_ctx_trigger_single(&vdev->req_trigger,
6358c2ecf20Sopenharmony_ci					       count, flags, data);
6368c2ecf20Sopenharmony_ci}
6378c2ecf20Sopenharmony_ci
6388c2ecf20Sopenharmony_ciint vfio_pci_set_irqs_ioctl(struct vfio_pci_device *vdev, uint32_t flags,
6398c2ecf20Sopenharmony_ci			    unsigned index, unsigned start, unsigned count,
6408c2ecf20Sopenharmony_ci			    void *data)
6418c2ecf20Sopenharmony_ci{
6428c2ecf20Sopenharmony_ci	int (*func)(struct vfio_pci_device *vdev, unsigned index,
6438c2ecf20Sopenharmony_ci		    unsigned start, unsigned count, uint32_t flags,
6448c2ecf20Sopenharmony_ci		    void *data) = NULL;
6458c2ecf20Sopenharmony_ci
6468c2ecf20Sopenharmony_ci	switch (index) {
6478c2ecf20Sopenharmony_ci	case VFIO_PCI_INTX_IRQ_INDEX:
6488c2ecf20Sopenharmony_ci		switch (flags & VFIO_IRQ_SET_ACTION_TYPE_MASK) {
6498c2ecf20Sopenharmony_ci		case VFIO_IRQ_SET_ACTION_MASK:
6508c2ecf20Sopenharmony_ci			func = vfio_pci_set_intx_mask;
6518c2ecf20Sopenharmony_ci			break;
6528c2ecf20Sopenharmony_ci		case VFIO_IRQ_SET_ACTION_UNMASK:
6538c2ecf20Sopenharmony_ci			func = vfio_pci_set_intx_unmask;
6548c2ecf20Sopenharmony_ci			break;
6558c2ecf20Sopenharmony_ci		case VFIO_IRQ_SET_ACTION_TRIGGER:
6568c2ecf20Sopenharmony_ci			func = vfio_pci_set_intx_trigger;
6578c2ecf20Sopenharmony_ci			break;
6588c2ecf20Sopenharmony_ci		}
6598c2ecf20Sopenharmony_ci		break;
6608c2ecf20Sopenharmony_ci	case VFIO_PCI_MSI_IRQ_INDEX:
6618c2ecf20Sopenharmony_ci	case VFIO_PCI_MSIX_IRQ_INDEX:
6628c2ecf20Sopenharmony_ci		switch (flags & VFIO_IRQ_SET_ACTION_TYPE_MASK) {
6638c2ecf20Sopenharmony_ci		case VFIO_IRQ_SET_ACTION_MASK:
6648c2ecf20Sopenharmony_ci		case VFIO_IRQ_SET_ACTION_UNMASK:
6658c2ecf20Sopenharmony_ci			/* XXX Need masking support exported */
6668c2ecf20Sopenharmony_ci			break;
6678c2ecf20Sopenharmony_ci		case VFIO_IRQ_SET_ACTION_TRIGGER:
6688c2ecf20Sopenharmony_ci			func = vfio_pci_set_msi_trigger;
6698c2ecf20Sopenharmony_ci			break;
6708c2ecf20Sopenharmony_ci		}
6718c2ecf20Sopenharmony_ci		break;
6728c2ecf20Sopenharmony_ci	case VFIO_PCI_ERR_IRQ_INDEX:
6738c2ecf20Sopenharmony_ci		switch (flags & VFIO_IRQ_SET_ACTION_TYPE_MASK) {
6748c2ecf20Sopenharmony_ci		case VFIO_IRQ_SET_ACTION_TRIGGER:
6758c2ecf20Sopenharmony_ci			if (pci_is_pcie(vdev->pdev))
6768c2ecf20Sopenharmony_ci				func = vfio_pci_set_err_trigger;
6778c2ecf20Sopenharmony_ci			break;
6788c2ecf20Sopenharmony_ci		}
6798c2ecf20Sopenharmony_ci		break;
6808c2ecf20Sopenharmony_ci	case VFIO_PCI_REQ_IRQ_INDEX:
6818c2ecf20Sopenharmony_ci		switch (flags & VFIO_IRQ_SET_ACTION_TYPE_MASK) {
6828c2ecf20Sopenharmony_ci		case VFIO_IRQ_SET_ACTION_TRIGGER:
6838c2ecf20Sopenharmony_ci			func = vfio_pci_set_req_trigger;
6848c2ecf20Sopenharmony_ci			break;
6858c2ecf20Sopenharmony_ci		}
6868c2ecf20Sopenharmony_ci		break;
6878c2ecf20Sopenharmony_ci	}
6888c2ecf20Sopenharmony_ci
6898c2ecf20Sopenharmony_ci	if (!func)
6908c2ecf20Sopenharmony_ci		return -ENOTTY;
6918c2ecf20Sopenharmony_ci
6928c2ecf20Sopenharmony_ci	return func(vdev, index, start, count, flags, data);
6938c2ecf20Sopenharmony_ci}
694