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