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

vfio_platform_irq.c (7116B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/*
      3 * VFIO platform devices interrupt handling
      4 *
      5 * Copyright (C) 2013 - Virtual Open Systems
      6 * Author: Antonios Motakis <a.motakis@virtualopensystems.com>
      7 */
      8
      9#include <linux/eventfd.h>
     10#include <linux/interrupt.h>
     11#include <linux/slab.h>
     12#include <linux/types.h>
     13#include <linux/vfio.h>
     14#include <linux/irq.h>
     15
     16#include "vfio_platform_private.h"
     17
     18static void vfio_platform_mask(struct vfio_platform_irq *irq_ctx)
     19{
     20	unsigned long flags;
     21
     22	spin_lock_irqsave(&irq_ctx->lock, flags);
     23
     24	if (!irq_ctx->masked) {
     25		disable_irq_nosync(irq_ctx->hwirq);
     26		irq_ctx->masked = true;
     27	}
     28
     29	spin_unlock_irqrestore(&irq_ctx->lock, flags);
     30}
     31
     32static int vfio_platform_mask_handler(void *opaque, void *unused)
     33{
     34	struct vfio_platform_irq *irq_ctx = opaque;
     35
     36	vfio_platform_mask(irq_ctx);
     37
     38	return 0;
     39}
     40
     41static int vfio_platform_set_irq_mask(struct vfio_platform_device *vdev,
     42				      unsigned index, unsigned start,
     43				      unsigned count, uint32_t flags,
     44				      void *data)
     45{
     46	if (start != 0 || count != 1)
     47		return -EINVAL;
     48
     49	if (!(vdev->irqs[index].flags & VFIO_IRQ_INFO_MASKABLE))
     50		return -EINVAL;
     51
     52	if (flags & VFIO_IRQ_SET_DATA_EVENTFD) {
     53		int32_t fd = *(int32_t *)data;
     54
     55		if (fd >= 0)
     56			return vfio_virqfd_enable((void *) &vdev->irqs[index],
     57						  vfio_platform_mask_handler,
     58						  NULL, NULL,
     59						  &vdev->irqs[index].mask, fd);
     60
     61		vfio_virqfd_disable(&vdev->irqs[index].mask);
     62		return 0;
     63	}
     64
     65	if (flags & VFIO_IRQ_SET_DATA_NONE) {
     66		vfio_platform_mask(&vdev->irqs[index]);
     67
     68	} else if (flags & VFIO_IRQ_SET_DATA_BOOL) {
     69		uint8_t mask = *(uint8_t *)data;
     70
     71		if (mask)
     72			vfio_platform_mask(&vdev->irqs[index]);
     73	}
     74
     75	return 0;
     76}
     77
     78static void vfio_platform_unmask(struct vfio_platform_irq *irq_ctx)
     79{
     80	unsigned long flags;
     81
     82	spin_lock_irqsave(&irq_ctx->lock, flags);
     83
     84	if (irq_ctx->masked) {
     85		enable_irq(irq_ctx->hwirq);
     86		irq_ctx->masked = false;
     87	}
     88
     89	spin_unlock_irqrestore(&irq_ctx->lock, flags);
     90}
     91
     92static int vfio_platform_unmask_handler(void *opaque, void *unused)
     93{
     94	struct vfio_platform_irq *irq_ctx = opaque;
     95
     96	vfio_platform_unmask(irq_ctx);
     97
     98	return 0;
     99}
    100
    101static int vfio_platform_set_irq_unmask(struct vfio_platform_device *vdev,
    102					unsigned index, unsigned start,
    103					unsigned count, uint32_t flags,
    104					void *data)
    105{
    106	if (start != 0 || count != 1)
    107		return -EINVAL;
    108
    109	if (!(vdev->irqs[index].flags & VFIO_IRQ_INFO_MASKABLE))
    110		return -EINVAL;
    111
    112	if (flags & VFIO_IRQ_SET_DATA_EVENTFD) {
    113		int32_t fd = *(int32_t *)data;
    114
    115		if (fd >= 0)
    116			return vfio_virqfd_enable((void *) &vdev->irqs[index],
    117						  vfio_platform_unmask_handler,
    118						  NULL, NULL,
    119						  &vdev->irqs[index].unmask,
    120						  fd);
    121
    122		vfio_virqfd_disable(&vdev->irqs[index].unmask);
    123		return 0;
    124	}
    125
    126	if (flags & VFIO_IRQ_SET_DATA_NONE) {
    127		vfio_platform_unmask(&vdev->irqs[index]);
    128
    129	} else if (flags & VFIO_IRQ_SET_DATA_BOOL) {
    130		uint8_t unmask = *(uint8_t *)data;
    131
    132		if (unmask)
    133			vfio_platform_unmask(&vdev->irqs[index]);
    134	}
    135
    136	return 0;
    137}
    138
    139static irqreturn_t vfio_automasked_irq_handler(int irq, void *dev_id)
    140{
    141	struct vfio_platform_irq *irq_ctx = dev_id;
    142	unsigned long flags;
    143	int ret = IRQ_NONE;
    144
    145	spin_lock_irqsave(&irq_ctx->lock, flags);
    146
    147	if (!irq_ctx->masked) {
    148		ret = IRQ_HANDLED;
    149
    150		/* automask maskable interrupts */
    151		disable_irq_nosync(irq_ctx->hwirq);
    152		irq_ctx->masked = true;
    153	}
    154
    155	spin_unlock_irqrestore(&irq_ctx->lock, flags);
    156
    157	if (ret == IRQ_HANDLED)
    158		eventfd_signal(irq_ctx->trigger, 1);
    159
    160	return ret;
    161}
    162
    163static irqreturn_t vfio_irq_handler(int irq, void *dev_id)
    164{
    165	struct vfio_platform_irq *irq_ctx = dev_id;
    166
    167	eventfd_signal(irq_ctx->trigger, 1);
    168
    169	return IRQ_HANDLED;
    170}
    171
    172static int vfio_set_trigger(struct vfio_platform_device *vdev, int index,
    173			    int fd, irq_handler_t handler)
    174{
    175	struct vfio_platform_irq *irq = &vdev->irqs[index];
    176	struct eventfd_ctx *trigger;
    177	int ret;
    178
    179	if (irq->trigger) {
    180		irq_clear_status_flags(irq->hwirq, IRQ_NOAUTOEN);
    181		free_irq(irq->hwirq, irq);
    182		kfree(irq->name);
    183		eventfd_ctx_put(irq->trigger);
    184		irq->trigger = NULL;
    185	}
    186
    187	if (fd < 0) /* Disable only */
    188		return 0;
    189
    190	irq->name = kasprintf(GFP_KERNEL, "vfio-irq[%d](%s)",
    191						irq->hwirq, vdev->name);
    192	if (!irq->name)
    193		return -ENOMEM;
    194
    195	trigger = eventfd_ctx_fdget(fd);
    196	if (IS_ERR(trigger)) {
    197		kfree(irq->name);
    198		return PTR_ERR(trigger);
    199	}
    200
    201	irq->trigger = trigger;
    202
    203	irq_set_status_flags(irq->hwirq, IRQ_NOAUTOEN);
    204	ret = request_irq(irq->hwirq, handler, 0, irq->name, irq);
    205	if (ret) {
    206		kfree(irq->name);
    207		eventfd_ctx_put(trigger);
    208		irq->trigger = NULL;
    209		return ret;
    210	}
    211
    212	if (!irq->masked)
    213		enable_irq(irq->hwirq);
    214
    215	return 0;
    216}
    217
    218static int vfio_platform_set_irq_trigger(struct vfio_platform_device *vdev,
    219					 unsigned index, unsigned start,
    220					 unsigned count, uint32_t flags,
    221					 void *data)
    222{
    223	struct vfio_platform_irq *irq = &vdev->irqs[index];
    224	irq_handler_t handler;
    225
    226	if (vdev->irqs[index].flags & VFIO_IRQ_INFO_AUTOMASKED)
    227		handler = vfio_automasked_irq_handler;
    228	else
    229		handler = vfio_irq_handler;
    230
    231	if (!count && (flags & VFIO_IRQ_SET_DATA_NONE))
    232		return vfio_set_trigger(vdev, index, -1, handler);
    233
    234	if (start != 0 || count != 1)
    235		return -EINVAL;
    236
    237	if (flags & VFIO_IRQ_SET_DATA_EVENTFD) {
    238		int32_t fd = *(int32_t *)data;
    239
    240		return vfio_set_trigger(vdev, index, fd, handler);
    241	}
    242
    243	if (flags & VFIO_IRQ_SET_DATA_NONE) {
    244		handler(irq->hwirq, irq);
    245
    246	} else if (flags & VFIO_IRQ_SET_DATA_BOOL) {
    247		uint8_t trigger = *(uint8_t *)data;
    248
    249		if (trigger)
    250			handler(irq->hwirq, irq);
    251	}
    252
    253	return 0;
    254}
    255
    256int vfio_platform_set_irqs_ioctl(struct vfio_platform_device *vdev,
    257				 uint32_t flags, unsigned index, unsigned start,
    258				 unsigned count, void *data)
    259{
    260	int (*func)(struct vfio_platform_device *vdev, unsigned index,
    261		    unsigned start, unsigned count, uint32_t flags,
    262		    void *data) = NULL;
    263
    264	switch (flags & VFIO_IRQ_SET_ACTION_TYPE_MASK) {
    265	case VFIO_IRQ_SET_ACTION_MASK:
    266		func = vfio_platform_set_irq_mask;
    267		break;
    268	case VFIO_IRQ_SET_ACTION_UNMASK:
    269		func = vfio_platform_set_irq_unmask;
    270		break;
    271	case VFIO_IRQ_SET_ACTION_TRIGGER:
    272		func = vfio_platform_set_irq_trigger;
    273		break;
    274	}
    275
    276	if (!func)
    277		return -ENOTTY;
    278
    279	return func(vdev, index, start, count, flags, data);
    280}
    281
    282int vfio_platform_irq_init(struct vfio_platform_device *vdev)
    283{
    284	int cnt = 0, i;
    285
    286	while (vdev->get_irq(vdev, cnt) >= 0)
    287		cnt++;
    288
    289	vdev->irqs = kcalloc(cnt, sizeof(struct vfio_platform_irq), GFP_KERNEL);
    290	if (!vdev->irqs)
    291		return -ENOMEM;
    292
    293	for (i = 0; i < cnt; i++) {
    294		int hwirq = vdev->get_irq(vdev, i);
    295
    296		if (hwirq < 0)
    297			goto err;
    298
    299		spin_lock_init(&vdev->irqs[i].lock);
    300
    301		vdev->irqs[i].flags = VFIO_IRQ_INFO_EVENTFD;
    302
    303		if (irq_get_trigger_type(hwirq) & IRQ_TYPE_LEVEL_MASK)
    304			vdev->irqs[i].flags |= VFIO_IRQ_INFO_MASKABLE
    305						| VFIO_IRQ_INFO_AUTOMASKED;
    306
    307		vdev->irqs[i].count = 1;
    308		vdev->irqs[i].hwirq = hwirq;
    309		vdev->irqs[i].masked = false;
    310	}
    311
    312	vdev->num_irqs = cnt;
    313
    314	return 0;
    315err:
    316	kfree(vdev->irqs);
    317	return -EINVAL;
    318}
    319
    320void vfio_platform_irq_cleanup(struct vfio_platform_device *vdev)
    321{
    322	int i;
    323
    324	for (i = 0; i < vdev->num_irqs; i++)
    325		vfio_set_trigger(vdev, i, -1, NULL);
    326
    327	vdev->num_irqs = 0;
    328	kfree(vdev->irqs);
    329}