OpenWrt – Blame information for rev 1
?pathlinks?
Rev | Author | Line No. | Line |
---|---|---|---|
1 | office | 1 | From 92f0ef51270b2961f63b2e985831f5e9a6251a2f Mon Sep 17 00:00:00 2001 |
2 | From: Biwen Li <biwen.li@nxp.com> |
||
3 | Date: Tue, 30 Oct 2018 18:29:03 +0800 |
||
4 | Subject: [PATCH 25/40] vfio: support layerscape |
||
5 | This is an integrated patch of vfio for layerscape |
||
6 | |||
7 | Signed-off-by: Bharat Bhushan <Bharat.Bhushan@nxp.com> |
||
8 | Signed-off-by: Biwen Li <biwen.li@nxp.com> |
||
9 | --- |
||
10 | drivers/vfio/Kconfig | 1 + |
||
11 | drivers/vfio/Makefile | 1 + |
||
12 | drivers/vfio/fsl-mc/Kconfig | 9 + |
||
13 | drivers/vfio/fsl-mc/Makefile | 2 + |
||
14 | drivers/vfio/fsl-mc/vfio_fsl_mc.c | 751 ++++++++++++++++++++++ |
||
15 | drivers/vfio/fsl-mc/vfio_fsl_mc_intr.c | 199 ++++++ |
||
16 | drivers/vfio/fsl-mc/vfio_fsl_mc_private.h | 55 ++ |
||
17 | include/uapi/linux/vfio.h | 1 + |
||
18 | 8 files changed, 1019 insertions(+) |
||
19 | create mode 100644 drivers/vfio/fsl-mc/Kconfig |
||
20 | create mode 100644 drivers/vfio/fsl-mc/Makefile |
||
21 | create mode 100644 drivers/vfio/fsl-mc/vfio_fsl_mc.c |
||
22 | create mode 100644 drivers/vfio/fsl-mc/vfio_fsl_mc_intr.c |
||
23 | create mode 100644 drivers/vfio/fsl-mc/vfio_fsl_mc_private.h |
||
24 | |||
25 | --- a/drivers/vfio/Kconfig |
||
26 | +++ b/drivers/vfio/Kconfig |
||
27 | @@ -47,4 +47,5 @@ menuconfig VFIO_NOIOMMU |
||
28 | source "drivers/vfio/pci/Kconfig" |
||
29 | source "drivers/vfio/platform/Kconfig" |
||
30 | source "drivers/vfio/mdev/Kconfig" |
||
31 | +source "drivers/vfio/fsl-mc/Kconfig" |
||
32 | source "virt/lib/Kconfig" |
||
33 | --- a/drivers/vfio/Makefile |
||
34 | +++ b/drivers/vfio/Makefile |
||
35 | @@ -9,3 +9,4 @@ obj-$(CONFIG_VFIO_SPAPR_EEH) += vfio_spa |
||
36 | obj-$(CONFIG_VFIO_PCI) += pci/ |
||
37 | obj-$(CONFIG_VFIO_PLATFORM) += platform/ |
||
38 | obj-$(CONFIG_VFIO_MDEV) += mdev/ |
||
39 | +obj-$(CONFIG_VFIO_FSL_MC) += fsl-mc/ |
||
40 | --- /dev/null |
||
41 | +++ b/drivers/vfio/fsl-mc/Kconfig |
||
42 | @@ -0,0 +1,9 @@ |
||
43 | +config VFIO_FSL_MC |
||
44 | + tristate "VFIO support for QorIQ DPAA2 fsl-mc bus devices" |
||
45 | + depends on VFIO && FSL_MC_BUS && EVENTFD |
||
46 | + help |
||
47 | + Driver to enable support for the VFIO QorIQ DPAA2 fsl-mc |
||
48 | + (Management Complex) devices. This is required to passthrough |
||
49 | + fsl-mc bus devices using the VFIO framework. |
||
50 | + |
||
51 | + If you don't know what to do here, say N. |
||
52 | --- /dev/null |
||
53 | +++ b/drivers/vfio/fsl-mc/Makefile |
||
54 | @@ -0,0 +1,2 @@ |
||
55 | +vfio-fsl_mc-y := vfio_fsl_mc.o |
||
56 | +obj-$(CONFIG_VFIO_FSL_MC) += vfio_fsl_mc.o vfio_fsl_mc_intr.o |
||
57 | --- /dev/null |
||
58 | +++ b/drivers/vfio/fsl-mc/vfio_fsl_mc.c |
||
59 | @@ -0,0 +1,751 @@ |
||
60 | +/* |
||
61 | + * Freescale Management Complex (MC) device passthrough using VFIO |
||
62 | + * |
||
63 | + * Copyright (C) 2013-2016 Freescale Semiconductor, Inc. |
||
64 | + * Copyright 2016-2017 NXP |
||
65 | + * Author: Bharat Bhushan <bharat.bhushan@nxp.com> |
||
66 | + * |
||
67 | + * This file is licensed under the terms of the GNU General Public |
||
68 | + * License version 2. This program is licensed "as is" without any |
||
69 | + * warranty of any kind, whether express or implied. |
||
70 | + */ |
||
71 | + |
||
72 | +#include <linux/device.h> |
||
73 | +#include <linux/iommu.h> |
||
74 | +#include <linux/module.h> |
||
75 | +#include <linux/mutex.h> |
||
76 | +#include <linux/slab.h> |
||
77 | +#include <linux/types.h> |
||
78 | +#include <linux/vfio.h> |
||
79 | +#include <linux/delay.h> |
||
80 | +#include <linux/fsl/mc.h> |
||
81 | + |
||
82 | +#include "vfio_fsl_mc_private.h" |
||
83 | + |
||
84 | +#define DRIVER_VERSION "0.10" |
||
85 | +#define DRIVER_AUTHOR "Bharat Bhushan <bharat.bhushan@nxp.com>" |
||
86 | +#define DRIVER_DESC "VFIO for FSL-MC devices - User Level meta-driver" |
||
87 | + |
||
88 | +static DEFINE_MUTEX(driver_lock); |
||
89 | + |
||
90 | +/* FSl-MC device regions (address and size) are aligned to 64K. |
||
91 | + * While MC firmware reports size less than 64K for some objects (it actually |
||
92 | + * reports size which does not include reserved space beyond valid bytes). |
||
93 | + * Align the size to PAGE_SIZE for userspace to mmap. |
||
94 | + */ |
||
95 | +static size_t aligned_region_size(struct fsl_mc_device *mc_dev, int index) |
||
96 | +{ |
||
97 | + size_t size; |
||
98 | + |
||
99 | + size = resource_size(&mc_dev->regions[index]); |
||
100 | + return PAGE_ALIGN(size); |
||
101 | +} |
||
102 | + |
||
103 | +static int vfio_fsl_mc_regions_init(struct vfio_fsl_mc_device *vdev) |
||
104 | +{ |
||
105 | + struct fsl_mc_device *mc_dev = vdev->mc_dev; |
||
106 | + int count = mc_dev->obj_desc.region_count; |
||
107 | + int i; |
||
108 | + |
||
109 | + vdev->regions = kcalloc(count, sizeof(struct vfio_fsl_mc_region), |
||
110 | + GFP_KERNEL); |
||
111 | + if (!vdev->regions) |
||
112 | + return -ENOMEM; |
||
113 | + |
||
114 | + for (i = 0; i < mc_dev->obj_desc.region_count; i++) { |
||
115 | + vdev->regions[i].addr = mc_dev->regions[i].start; |
||
116 | + vdev->regions[i].size = aligned_region_size(mc_dev, i); |
||
117 | + vdev->regions[i].type = VFIO_FSL_MC_REGION_TYPE_MMIO; |
||
118 | + if (mc_dev->regions[i].flags & IORESOURCE_CACHEABLE) |
||
119 | + vdev->regions[i].type |= |
||
120 | + VFIO_FSL_MC_REGION_TYPE_CACHEABLE; |
||
121 | + vdev->regions[i].flags = VFIO_REGION_INFO_FLAG_MMAP; |
||
122 | + vdev->regions[i].flags |= VFIO_REGION_INFO_FLAG_READ; |
||
123 | + if (!(mc_dev->regions[i].flags & IORESOURCE_READONLY)) |
||
124 | + vdev->regions[i].flags |= VFIO_REGION_INFO_FLAG_WRITE; |
||
125 | + } |
||
126 | + |
||
127 | + vdev->num_regions = mc_dev->obj_desc.region_count; |
||
128 | + return 0; |
||
129 | +} |
||
130 | + |
||
131 | +static void vfio_fsl_mc_regions_cleanup(struct vfio_fsl_mc_device *vdev) |
||
132 | +{ |
||
133 | + int i; |
||
134 | + |
||
135 | + for (i = 0; i < vdev->num_regions; i++) |
||
136 | + iounmap(vdev->regions[i].ioaddr); |
||
137 | + |
||
138 | + vdev->num_regions = 0; |
||
139 | + kfree(vdev->regions); |
||
140 | +} |
||
141 | + |
||
142 | +static int vfio_fsl_mc_open(void *device_data) |
||
143 | +{ |
||
144 | + struct vfio_fsl_mc_device *vdev = device_data; |
||
145 | + int ret; |
||
146 | + |
||
147 | + if (!try_module_get(THIS_MODULE)) |
||
148 | + return -ENODEV; |
||
149 | + |
||
150 | + mutex_lock(&driver_lock); |
||
151 | + if (!vdev->refcnt) { |
||
152 | + ret = vfio_fsl_mc_regions_init(vdev); |
||
153 | + if (ret) |
||
154 | + goto error_region_init; |
||
155 | + |
||
156 | + ret = vfio_fsl_mc_irqs_init(vdev); |
||
157 | + if (ret) |
||
158 | + goto error_irq_init; |
||
159 | + } |
||
160 | + |
||
161 | + vdev->refcnt++; |
||
162 | + mutex_unlock(&driver_lock); |
||
163 | + return 0; |
||
164 | + |
||
165 | +error_irq_init: |
||
166 | + vfio_fsl_mc_regions_cleanup(vdev); |
||
167 | +error_region_init: |
||
168 | + mutex_unlock(&driver_lock); |
||
169 | + if (ret) |
||
170 | + module_put(THIS_MODULE); |
||
171 | + |
||
172 | + return ret; |
||
173 | +} |
||
174 | + |
||
175 | +static void vfio_fsl_mc_release(void *device_data) |
||
176 | +{ |
||
177 | + struct vfio_fsl_mc_device *vdev = device_data; |
||
178 | + struct fsl_mc_device *mc_dev = vdev->mc_dev; |
||
179 | + |
||
180 | + mutex_lock(&driver_lock); |
||
181 | + |
||
182 | + if (!(--vdev->refcnt)) { |
||
183 | + vfio_fsl_mc_regions_cleanup(vdev); |
||
184 | + vfio_fsl_mc_irqs_cleanup(vdev); |
||
185 | + } |
||
186 | + |
||
187 | + if (strcmp(mc_dev->obj_desc.type, "dprc") == 0) |
||
188 | + dprc_reset_container(mc_dev->mc_io, 0, mc_dev->mc_handle, |
||
189 | + mc_dev->obj_desc.id); |
||
190 | + |
||
191 | + mutex_unlock(&driver_lock); |
||
192 | + |
||
193 | + module_put(THIS_MODULE); |
||
194 | +} |
||
195 | + |
||
196 | +static long vfio_fsl_mc_ioctl(void *device_data, unsigned int cmd, |
||
197 | + unsigned long arg) |
||
198 | +{ |
||
199 | + struct vfio_fsl_mc_device *vdev = device_data; |
||
200 | + struct fsl_mc_device *mc_dev = vdev->mc_dev; |
||
201 | + unsigned long minsz; |
||
202 | + |
||
203 | + if (WARN_ON(!mc_dev)) |
||
204 | + return -ENODEV; |
||
205 | + |
||
206 | + switch (cmd) { |
||
207 | + case VFIO_DEVICE_GET_INFO: |
||
208 | + { |
||
209 | + struct vfio_device_info info; |
||
210 | + |
||
211 | + minsz = offsetofend(struct vfio_device_info, num_irqs); |
||
212 | + |
||
213 | + if (copy_from_user(&info, (void __user *)arg, minsz)) |
||
214 | + return -EFAULT; |
||
215 | + |
||
216 | + if (info.argsz < minsz) |
||
217 | + return -EINVAL; |
||
218 | + |
||
219 | + info.flags = VFIO_DEVICE_FLAGS_FSL_MC; |
||
220 | + info.num_regions = mc_dev->obj_desc.region_count; |
||
221 | + info.num_irqs = mc_dev->obj_desc.irq_count; |
||
222 | + |
||
223 | + return copy_to_user((void __user *)arg, &info, minsz); |
||
224 | + } |
||
225 | + case VFIO_DEVICE_GET_REGION_INFO: |
||
226 | + { |
||
227 | + struct vfio_region_info info; |
||
228 | + |
||
229 | + minsz = offsetofend(struct vfio_region_info, offset); |
||
230 | + |
||
231 | + if (copy_from_user(&info, (void __user *)arg, minsz)) |
||
232 | + return -EFAULT; |
||
233 | + |
||
234 | + if (info.argsz < minsz) |
||
235 | + return -EINVAL; |
||
236 | + |
||
237 | + if (info.index >= vdev->num_regions) |
||
238 | + return -EINVAL; |
||
239 | + |
||
240 | + /* map offset to the physical address */ |
||
241 | + info.offset = VFIO_FSL_MC_INDEX_TO_OFFSET(info.index); |
||
242 | + info.size = vdev->regions[info.index].size; |
||
243 | + info.flags = vdev->regions[info.index].flags; |
||
244 | + |
||
245 | + return copy_to_user((void __user *)arg, &info, minsz); |
||
246 | + } |
||
247 | + case VFIO_DEVICE_GET_IRQ_INFO: |
||
248 | + { |
||
249 | + struct vfio_irq_info info; |
||
250 | + |
||
251 | + minsz = offsetofend(struct vfio_irq_info, count); |
||
252 | + if (copy_from_user(&info, (void __user *)arg, minsz)) |
||
253 | + return -EFAULT; |
||
254 | + |
||
255 | + if (info.argsz < minsz) |
||
256 | + return -EINVAL; |
||
257 | + |
||
258 | + if (info.index >= mc_dev->obj_desc.irq_count) |
||
259 | + return -EINVAL; |
||
260 | + |
||
261 | + if (vdev->mc_irqs != NULL) { |
||
262 | + info.flags = vdev->mc_irqs[info.index].flags; |
||
263 | + info.count = vdev->mc_irqs[info.index].count; |
||
264 | + } else { |
||
265 | + /* |
||
266 | + * If IRQs are not initialized then these can not |
||
267 | + * be configuted and used by user-space/ |
||
268 | + */ |
||
269 | + info.flags = 0; |
||
270 | + info.count = 0; |
||
271 | + } |
||
272 | + |
||
273 | + return copy_to_user((void __user *)arg, &info, minsz); |
||
274 | + } |
||
275 | + case VFIO_DEVICE_SET_IRQS: |
||
276 | + { |
||
277 | + struct vfio_irq_set hdr; |
||
278 | + u8 *data = NULL; |
||
279 | + int ret = 0; |
||
280 | + |
||
281 | + minsz = offsetofend(struct vfio_irq_set, count); |
||
282 | + |
||
283 | + if (copy_from_user(&hdr, (void __user *)arg, minsz)) |
||
284 | + return -EFAULT; |
||
285 | + |
||
286 | + if (hdr.argsz < minsz) |
||
287 | + return -EINVAL; |
||
288 | + |
||
289 | + if (hdr.index >= mc_dev->obj_desc.irq_count) |
||
290 | + return -EINVAL; |
||
291 | + |
||
292 | + if (hdr.start != 0 || hdr.count > 1) |
||
293 | + return -EINVAL; |
||
294 | + |
||
295 | + if (hdr.count == 0 && |
||
296 | + (!(hdr.flags & VFIO_IRQ_SET_DATA_NONE) || |
||
297 | + !(hdr.flags & VFIO_IRQ_SET_ACTION_TRIGGER))) |
||
298 | + return -EINVAL; |
||
299 | + |
||
300 | + if (hdr.flags & ~(VFIO_IRQ_SET_DATA_TYPE_MASK | |
||
301 | + VFIO_IRQ_SET_ACTION_TYPE_MASK)) |
||
302 | + return -EINVAL; |
||
303 | + |
||
304 | + if (!(hdr.flags & VFIO_IRQ_SET_DATA_NONE)) { |
||
305 | + size_t size; |
||
306 | + |
||
307 | + if (hdr.flags & VFIO_IRQ_SET_DATA_BOOL) |
||
308 | + size = sizeof(uint8_t); |
||
309 | + else if (hdr.flags & VFIO_IRQ_SET_DATA_EVENTFD) |
||
310 | + size = sizeof(int32_t); |
||
311 | + else |
||
312 | + return -EINVAL; |
||
313 | + |
||
314 | + if (hdr.argsz - minsz < hdr.count * size) |
||
315 | + return -EINVAL; |
||
316 | + |
||
317 | + data = memdup_user((void __user *)(arg + minsz), |
||
318 | + hdr.count * size); |
||
319 | + if (IS_ERR(data)) |
||
320 | + return PTR_ERR(data); |
||
321 | + } |
||
322 | + |
||
323 | + ret = vfio_fsl_mc_set_irqs_ioctl(vdev, hdr.flags, |
||
324 | + hdr.index, hdr.start, |
||
325 | + hdr.count, data); |
||
326 | + return ret; |
||
327 | + } |
||
328 | + case VFIO_DEVICE_RESET: |
||
329 | + { |
||
330 | + return -EINVAL; |
||
331 | + } |
||
332 | + default: |
||
333 | + return -EINVAL; |
||
334 | + } |
||
335 | +} |
||
336 | + |
||
337 | +static ssize_t vfio_fsl_mc_read(void *device_data, char __user *buf, |
||
338 | + size_t count, loff_t *ppos) |
||
339 | +{ |
||
340 | + struct vfio_fsl_mc_device *vdev = device_data; |
||
341 | + unsigned int index = VFIO_FSL_MC_OFFSET_TO_INDEX(*ppos); |
||
342 | + loff_t off = *ppos & VFIO_FSL_MC_OFFSET_MASK; |
||
343 | + struct vfio_fsl_mc_region *region; |
||
344 | + uint64_t data[8]; |
||
345 | + int i; |
||
346 | + |
||
347 | + /* Read ioctl supported only for DPRC and DPMCP device */ |
||
348 | + if (strcmp(vdev->mc_dev->obj_desc.type, "dprc") && |
||
349 | + strcmp(vdev->mc_dev->obj_desc.type, "dpmcp")) |
||
350 | + return -EINVAL; |
||
351 | + |
||
352 | + if (index >= vdev->num_regions) |
||
353 | + return -EINVAL; |
||
354 | + |
||
355 | + region = &vdev->regions[index]; |
||
356 | + |
||
357 | + if (!(region->flags & VFIO_REGION_INFO_FLAG_READ)) |
||
358 | + return -EINVAL; |
||
359 | + |
||
360 | + if (!region->type & VFIO_FSL_MC_REGION_TYPE_MMIO) |
||
361 | + return -EINVAL; |
||
362 | + |
||
363 | + if (!region->ioaddr) { |
||
364 | + region->ioaddr = ioremap_nocache(region->addr, region->size); |
||
365 | + if (!region->ioaddr) |
||
366 | + return -ENOMEM; |
||
367 | + } |
||
368 | + |
||
369 | + if (count != 64 || off != 0) |
||
370 | + return -EINVAL; |
||
371 | + |
||
372 | + for (i = 7; i >= 0; i--) |
||
373 | + data[i] = readq(region->ioaddr + i * sizeof(uint64_t)); |
||
374 | + |
||
375 | + if (copy_to_user(buf, data, 64)) |
||
376 | + return -EFAULT; |
||
377 | + |
||
378 | + return count; |
||
379 | +} |
||
380 | + |
||
381 | +#define MC_CMD_COMPLETION_TIMEOUT_MS 5000 |
||
382 | +#define MC_CMD_COMPLETION_POLLING_MAX_SLEEP_USECS 500 |
||
383 | + |
||
384 | +static int vfio_fsl_mc_dprc_wait_for_response(void __iomem *ioaddr) |
||
385 | +{ |
||
386 | + enum mc_cmd_status status; |
||
387 | + unsigned long timeout_usecs = MC_CMD_COMPLETION_TIMEOUT_MS * 1000; |
||
388 | + |
||
389 | + for (;;) { |
||
390 | + u64 header; |
||
391 | + struct mc_cmd_header *resp_hdr; |
||
392 | + |
||
393 | + __iormb(); |
||
394 | + header = readq(ioaddr); |
||
395 | + __iormb(); |
||
396 | + |
||
397 | + resp_hdr = (struct mc_cmd_header *)&header; |
||
398 | + status = (enum mc_cmd_status)resp_hdr->status; |
||
399 | + if (status != MC_CMD_STATUS_READY) |
||
400 | + break; |
||
401 | + |
||
402 | + udelay(MC_CMD_COMPLETION_POLLING_MAX_SLEEP_USECS); |
||
403 | + timeout_usecs -= MC_CMD_COMPLETION_POLLING_MAX_SLEEP_USECS; |
||
404 | + if (timeout_usecs == 0) |
||
405 | + return -ETIMEDOUT; |
||
406 | + } |
||
407 | + |
||
408 | + return 0; |
||
409 | +} |
||
410 | + |
||
411 | +static int vfio_fsl_mc_send_command(void __iomem *ioaddr, uint64_t *cmd_data) |
||
412 | +{ |
||
413 | + int i; |
||
414 | + |
||
415 | + /* Write at command header in the end */ |
||
416 | + for (i = 7; i >= 0; i--) |
||
417 | + writeq(cmd_data[i], ioaddr + i * sizeof(uint64_t)); |
||
418 | + |
||
419 | + /* Wait for response before returning to user-space |
||
420 | + * This can be optimized in future to even prepare response |
||
421 | + * before returning to user-space and avoid read ioctl. |
||
422 | + */ |
||
423 | + return vfio_fsl_mc_dprc_wait_for_response(ioaddr); |
||
424 | +} |
||
425 | + |
||
426 | +static int vfio_handle_dprc_commands(void __iomem *ioaddr, uint64_t *cmd_data) |
||
427 | +{ |
||
428 | + uint64_t cmd_hdr = cmd_data[0]; |
||
429 | + int cmd = (cmd_hdr >> 52) & 0xfff; |
||
430 | + |
||
431 | + switch (cmd) { |
||
432 | + case DPRC_CMDID_OPEN: |
||
433 | + default: |
||
434 | + return vfio_fsl_mc_send_command(ioaddr, cmd_data); |
||
435 | + } |
||
436 | + |
||
437 | + return 0; |
||
438 | +} |
||
439 | + |
||
440 | +static ssize_t vfio_fsl_mc_write(void *device_data, const char __user *buf, |
||
441 | + size_t count, loff_t *ppos) |
||
442 | +{ |
||
443 | + struct vfio_fsl_mc_device *vdev = device_data; |
||
444 | + unsigned int index = VFIO_FSL_MC_OFFSET_TO_INDEX(*ppos); |
||
445 | + loff_t off = *ppos & VFIO_FSL_MC_OFFSET_MASK; |
||
446 | + struct vfio_fsl_mc_region *region; |
||
447 | + uint64_t data[8]; |
||
448 | + int ret; |
||
449 | + |
||
450 | + /* Write ioctl supported only for DPRC and DPMCP device */ |
||
451 | + if (strcmp(vdev->mc_dev->obj_desc.type, "dprc") && |
||
452 | + strcmp(vdev->mc_dev->obj_desc.type, "dpmcp")) |
||
453 | + return -EINVAL; |
||
454 | + |
||
455 | + if (index >= vdev->num_regions) |
||
456 | + return -EINVAL; |
||
457 | + |
||
458 | + region = &vdev->regions[index]; |
||
459 | + |
||
460 | + if (!(region->flags & VFIO_REGION_INFO_FLAG_WRITE)) |
||
461 | + return -EINVAL; |
||
462 | + |
||
463 | + if (!region->type & VFIO_FSL_MC_REGION_TYPE_MMIO) |
||
464 | + return -EINVAL; |
||
465 | + |
||
466 | + if (!region->ioaddr) { |
||
467 | + region->ioaddr = ioremap_nocache(region->addr, region->size); |
||
468 | + if (!region->ioaddr) |
||
469 | + return -ENOMEM; |
||
470 | + } |
||
471 | + |
||
472 | + if (count != 64 || off != 0) |
||
473 | + return -EINVAL; |
||
474 | + |
||
475 | + if (copy_from_user(&data, buf, 64)) |
||
476 | + return -EFAULT; |
||
477 | + |
||
478 | + ret = vfio_handle_dprc_commands(region->ioaddr, data); |
||
479 | + if (ret) |
||
480 | + return ret; |
||
481 | + |
||
482 | + return count; |
||
483 | +} |
||
484 | + |
||
485 | +static int vfio_fsl_mc_mmap_mmio(struct vfio_fsl_mc_region region, |
||
486 | + struct vm_area_struct *vma) |
||
487 | +{ |
||
488 | + u64 size = vma->vm_end - vma->vm_start; |
||
489 | + u64 pgoff, base; |
||
490 | + |
||
491 | + pgoff = vma->vm_pgoff & |
||
492 | + ((1U << (VFIO_FSL_MC_OFFSET_SHIFT - PAGE_SHIFT)) - 1); |
||
493 | + base = pgoff << PAGE_SHIFT; |
||
494 | + |
||
495 | + if (region.size < PAGE_SIZE || base + size > region.size) |
||
496 | + return -EINVAL; |
||
497 | + /* |
||
498 | + * Set the REGION_TYPE_CACHEABLE (QBman CENA regs) to be the |
||
499 | + * cache inhibited area of the portal to avoid coherency issues |
||
500 | + * if a user migrates to another core. |
||
501 | + */ |
||
502 | + if (region.type & VFIO_FSL_MC_REGION_TYPE_CACHEABLE) |
||
503 | + vma->vm_page_prot = pgprot_cached_ns(vma->vm_page_prot); |
||
504 | + else |
||
505 | + vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); |
||
506 | + |
||
507 | + vma->vm_pgoff = (region.addr >> PAGE_SHIFT) + pgoff; |
||
508 | + |
||
509 | + return remap_pfn_range(vma, vma->vm_start, vma->vm_pgoff, |
||
510 | + size, vma->vm_page_prot); |
||
511 | +} |
||
512 | + |
||
513 | +/* Allows mmaping fsl_mc device regions in assigned DPRC */ |
||
514 | +static int vfio_fsl_mc_mmap(void *device_data, struct vm_area_struct *vma) |
||
515 | +{ |
||
516 | + struct vfio_fsl_mc_device *vdev = device_data; |
||
517 | + struct fsl_mc_device *mc_dev = vdev->mc_dev; |
||
518 | + unsigned long size, addr; |
||
519 | + int index; |
||
520 | + |
||
521 | + index = vma->vm_pgoff >> (VFIO_FSL_MC_OFFSET_SHIFT - PAGE_SHIFT); |
||
522 | + |
||
523 | + if (vma->vm_end < vma->vm_start) |
||
524 | + return -EINVAL; |
||
525 | + if (vma->vm_start & ~PAGE_MASK) |
||
526 | + return -EINVAL; |
||
527 | + if (vma->vm_end & ~PAGE_MASK) |
||
528 | + return -EINVAL; |
||
529 | + if (!(vma->vm_flags & VM_SHARED)) |
||
530 | + return -EINVAL; |
||
531 | + if (index >= vdev->num_regions) |
||
532 | + return -EINVAL; |
||
533 | + |
||
534 | + if (!(vdev->regions[index].flags & VFIO_REGION_INFO_FLAG_MMAP)) |
||
535 | + return -EINVAL; |
||
536 | + |
||
537 | + if (!(vdev->regions[index].flags & VFIO_REGION_INFO_FLAG_READ) |
||
538 | + && (vma->vm_flags & VM_READ)) |
||
539 | + return -EINVAL; |
||
540 | + |
||
541 | + if (!(vdev->regions[index].flags & VFIO_REGION_INFO_FLAG_WRITE) |
||
542 | + && (vma->vm_flags & VM_WRITE)) |
||
543 | + return -EINVAL; |
||
544 | + |
||
545 | + addr = vdev->regions[index].addr; |
||
546 | + size = vdev->regions[index].size; |
||
547 | + |
||
548 | + vma->vm_private_data = mc_dev; |
||
549 | + |
||
550 | + if (vdev->regions[index].type & VFIO_FSL_MC_REGION_TYPE_MMIO) |
||
551 | + return vfio_fsl_mc_mmap_mmio(vdev->regions[index], vma); |
||
552 | + |
||
553 | + return -EFAULT; |
||
554 | +} |
||
555 | + |
||
556 | +static const struct vfio_device_ops vfio_fsl_mc_ops = { |
||
557 | + .name = "vfio-fsl-mc", |
||
558 | + .open = vfio_fsl_mc_open, |
||
559 | + .release = vfio_fsl_mc_release, |
||
560 | + .ioctl = vfio_fsl_mc_ioctl, |
||
561 | + .read = vfio_fsl_mc_read, |
||
562 | + .write = vfio_fsl_mc_write, |
||
563 | + .mmap = vfio_fsl_mc_mmap, |
||
564 | +}; |
||
565 | + |
||
566 | +static int vfio_fsl_mc_initialize_dprc(struct vfio_fsl_mc_device *vdev) |
||
567 | +{ |
||
568 | + struct device *root_dprc_dev; |
||
569 | + struct fsl_mc_device *mc_dev = vdev->mc_dev; |
||
570 | + struct device *dev = &mc_dev->dev; |
||
571 | + struct fsl_mc_bus *mc_bus; |
||
572 | + struct irq_domain *mc_msi_domain; |
||
573 | + unsigned int irq_count; |
||
574 | + int ret; |
||
575 | + |
||
576 | + /* device must be DPRC */ |
||
577 | + if (strcmp(mc_dev->obj_desc.type, "dprc")) |
||
578 | + return -EINVAL; |
||
579 | + |
||
580 | + /* mc_io must be un-initialized */ |
||
581 | + WARN_ON(mc_dev->mc_io); |
||
582 | + |
||
583 | + /* allocate a portal from the root DPRC for vfio use */ |
||
584 | + fsl_mc_get_root_dprc(dev, &root_dprc_dev); |
||
585 | + if (WARN_ON(!root_dprc_dev)) |
||
586 | + return -EINVAL; |
||
587 | + |
||
588 | + ret = fsl_mc_portal_allocate(to_fsl_mc_device(root_dprc_dev), |
||
589 | + FSL_MC_IO_ATOMIC_CONTEXT_PORTAL, |
||
590 | + &mc_dev->mc_io); |
||
591 | + if (ret < 0) |
||
592 | + goto clean_msi_domain; |
||
593 | + |
||
594 | + /* Reset MCP before move on */ |
||
595 | + ret = fsl_mc_portal_reset(mc_dev->mc_io); |
||
596 | + if (ret < 0) { |
||
597 | + dev_err(dev, "dprc portal reset failed: error = %d\n", ret); |
||
598 | + goto free_mc_portal; |
||
599 | + } |
||
600 | + |
||
601 | + /* MSI domain set up */ |
||
602 | + ret = fsl_mc_find_msi_domain(root_dprc_dev->parent, &mc_msi_domain); |
||
603 | + if (ret < 0) |
||
604 | + goto free_mc_portal; |
||
605 | + |
||
606 | + dev_set_msi_domain(&mc_dev->dev, mc_msi_domain); |
||
607 | + |
||
608 | + ret = dprc_open(mc_dev->mc_io, 0, mc_dev->obj_desc.id, |
||
609 | + &mc_dev->mc_handle); |
||
610 | + if (ret) { |
||
611 | + dev_err(dev, "dprc_open() failed: error = %d\n", ret); |
||
612 | + goto free_mc_portal; |
||
613 | + } |
||
614 | + |
||
615 | + /* Initialize resource pool */ |
||
616 | + fsl_mc_init_all_resource_pools(mc_dev); |
||
617 | + |
||
618 | + mc_bus = to_fsl_mc_bus(mc_dev); |
||
619 | + |
||
620 | + if (!mc_bus->irq_resources) { |
||
621 | + irq_count = FSL_MC_IRQ_POOL_MAX_TOTAL_IRQS; |
||
622 | + ret = fsl_mc_populate_irq_pool(mc_bus, irq_count); |
||
623 | + if (ret < 0) { |
||
624 | + dev_err(dev, "%s: Failed to init irq-pool\n", __func__); |
||
625 | + goto clean_resource_pool; |
||
626 | + } |
||
627 | + } |
||
628 | + |
||
629 | + mutex_init(&mc_bus->scan_mutex); |
||
630 | + |
||
631 | + mutex_lock(&mc_bus->scan_mutex); |
||
632 | + ret = dprc_scan_objects(mc_dev, mc_dev->driver_override, |
||
633 | + &irq_count); |
||
634 | + mutex_unlock(&mc_bus->scan_mutex); |
||
635 | + if (ret) { |
||
636 | + dev_err(dev, "dprc_scan_objects() fails (%d)\n", ret); |
||
637 | + goto clean_irq_pool; |
||
638 | + } |
||
639 | + |
||
640 | + if (irq_count > FSL_MC_IRQ_POOL_MAX_TOTAL_IRQS) { |
||
641 | + dev_warn(&mc_dev->dev, |
||
642 | + "IRQs needed (%u) exceed IRQs preallocated (%u)\n", |
||
643 | + irq_count, FSL_MC_IRQ_POOL_MAX_TOTAL_IRQS); |
||
644 | + } |
||
645 | + |
||
646 | + return 0; |
||
647 | + |
||
648 | +clean_irq_pool: |
||
649 | + fsl_mc_cleanup_irq_pool(mc_bus); |
||
650 | + |
||
651 | +clean_resource_pool: |
||
652 | + fsl_mc_cleanup_all_resource_pools(mc_dev); |
||
653 | + dprc_close(mc_dev->mc_io, 0, mc_dev->mc_handle); |
||
654 | + |
||
655 | +free_mc_portal: |
||
656 | + fsl_mc_portal_free(mc_dev->mc_io); |
||
657 | + |
||
658 | +clean_msi_domain: |
||
659 | + dev_set_msi_domain(&mc_dev->dev, NULL); |
||
660 | + |
||
661 | + return ret; |
||
662 | +} |
||
663 | + |
||
664 | +static int vfio_fsl_mc_device_remove(struct device *dev, void *data) |
||
665 | +{ |
||
666 | + struct fsl_mc_device *mc_dev; |
||
667 | + |
||
668 | + WARN_ON(dev == NULL); |
||
669 | + |
||
670 | + mc_dev = to_fsl_mc_device(dev); |
||
671 | + if (WARN_ON(mc_dev == NULL)) |
||
672 | + return -ENODEV; |
||
673 | + |
||
674 | + fsl_mc_device_remove(mc_dev); |
||
675 | + return 0; |
||
676 | +} |
||
677 | + |
||
678 | +static void vfio_fsl_mc_cleanup_dprc(struct vfio_fsl_mc_device *vdev) |
||
679 | +{ |
||
680 | + struct fsl_mc_device *mc_dev = vdev->mc_dev; |
||
681 | + struct fsl_mc_bus *mc_bus; |
||
682 | + |
||
683 | + /* device must be DPRC */ |
||
684 | + if (strcmp(mc_dev->obj_desc.type, "dprc")) |
||
685 | + return; |
||
686 | + |
||
687 | + device_for_each_child(&mc_dev->dev, NULL, vfio_fsl_mc_device_remove); |
||
688 | + |
||
689 | + mc_bus = to_fsl_mc_bus(mc_dev); |
||
690 | + if (dev_get_msi_domain(&mc_dev->dev)) |
||
691 | + fsl_mc_cleanup_irq_pool(mc_bus); |
||
692 | + |
||
693 | + dev_set_msi_domain(&mc_dev->dev, NULL); |
||
694 | + |
||
695 | + fsl_mc_cleanup_all_resource_pools(mc_dev); |
||
696 | + dprc_close(mc_dev->mc_io, 0, mc_dev->mc_handle); |
||
697 | + fsl_mc_portal_free(mc_dev->mc_io); |
||
698 | +} |
||
699 | + |
||
700 | +static int vfio_fsl_mc_probe(struct fsl_mc_device *mc_dev) |
||
701 | +{ |
||
702 | + struct iommu_group *group; |
||
703 | + struct vfio_fsl_mc_device *vdev; |
||
704 | + struct device *dev = &mc_dev->dev; |
||
705 | + int ret; |
||
706 | + |
||
707 | + group = vfio_iommu_group_get(dev); |
||
708 | + if (!group) { |
||
709 | + dev_err(dev, "%s: VFIO: No IOMMU group\n", __func__); |
||
710 | + return -EINVAL; |
||
711 | + } |
||
712 | + |
||
713 | + vdev = kzalloc(sizeof(*vdev), GFP_KERNEL); |
||
714 | + if (!vdev) { |
||
715 | + vfio_iommu_group_put(group, dev); |
||
716 | + return -ENOMEM; |
||
717 | + } |
||
718 | + |
||
719 | + vdev->mc_dev = mc_dev; |
||
720 | + |
||
721 | + ret = vfio_add_group_dev(dev, &vfio_fsl_mc_ops, vdev); |
||
722 | + if (ret) { |
||
723 | + dev_err(dev, "%s: Failed to add to vfio group\n", __func__); |
||
724 | + goto free_vfio_device; |
||
725 | + } |
||
726 | + |
||
727 | + /* DPRC container scanned and it's chilren bound with vfio driver */ |
||
728 | + if (strcmp(mc_dev->obj_desc.type, "dprc") == 0) { |
||
729 | + ret = vfio_fsl_mc_initialize_dprc(vdev); |
||
730 | + if (ret) { |
||
731 | + vfio_del_group_dev(dev); |
||
732 | + goto free_vfio_device; |
||
733 | + } |
||
734 | + } else { |
||
735 | + struct fsl_mc_device *mc_bus_dev; |
||
736 | + |
||
737 | + /* Non-dprc devices share mc_io from the parent dprc */ |
||
738 | + mc_bus_dev = to_fsl_mc_device(mc_dev->dev.parent); |
||
739 | + if (mc_bus_dev == NULL) { |
||
740 | + vfio_del_group_dev(dev); |
||
741 | + goto free_vfio_device; |
||
742 | + } |
||
743 | + |
||
744 | + mc_dev->mc_io = mc_bus_dev->mc_io; |
||
745 | + |
||
746 | + /* Inherit parent MSI domain */ |
||
747 | + dev_set_msi_domain(&mc_dev->dev, |
||
748 | + dev_get_msi_domain(mc_dev->dev.parent)); |
||
749 | + } |
||
750 | + return 0; |
||
751 | + |
||
752 | +free_vfio_device: |
||
753 | + kfree(vdev); |
||
754 | + vfio_iommu_group_put(group, dev); |
||
755 | + return ret; |
||
756 | +} |
||
757 | + |
||
758 | +static int vfio_fsl_mc_remove(struct fsl_mc_device *mc_dev) |
||
759 | +{ |
||
760 | + struct vfio_fsl_mc_device *vdev; |
||
761 | + struct device *dev = &mc_dev->dev; |
||
762 | + |
||
763 | + vdev = vfio_del_group_dev(dev); |
||
764 | + if (!vdev) |
||
765 | + return -EINVAL; |
||
766 | + |
||
767 | + if (strcmp(mc_dev->obj_desc.type, "dprc") == 0) |
||
768 | + vfio_fsl_mc_cleanup_dprc(vdev); |
||
769 | + else |
||
770 | + dev_set_msi_domain(&mc_dev->dev, NULL); |
||
771 | + |
||
772 | + mc_dev->mc_io = NULL; |
||
773 | + |
||
774 | + vfio_iommu_group_put(mc_dev->dev.iommu_group, dev); |
||
775 | + kfree(vdev); |
||
776 | + |
||
777 | + return 0; |
||
778 | +} |
||
779 | + |
||
780 | +/* |
||
781 | + * vfio-fsl_mc is a meta-driver, so use driver_override interface to |
||
782 | + * bind a fsl_mc container with this driver and match_id_table is NULL. |
||
783 | + */ |
||
784 | +static struct fsl_mc_driver vfio_fsl_mc_driver = { |
||
785 | + .probe = vfio_fsl_mc_probe, |
||
786 | + .remove = vfio_fsl_mc_remove, |
||
787 | + .match_id_table = NULL, |
||
788 | + .driver = { |
||
789 | + .name = "vfio-fsl-mc", |
||
790 | + .owner = THIS_MODULE, |
||
791 | + }, |
||
792 | +}; |
||
793 | + |
||
794 | +static int __init vfio_fsl_mc_driver_init(void) |
||
795 | +{ |
||
796 | + return fsl_mc_driver_register(&vfio_fsl_mc_driver); |
||
797 | +} |
||
798 | + |
||
799 | +static void __exit vfio_fsl_mc_driver_exit(void) |
||
800 | +{ |
||
801 | + fsl_mc_driver_unregister(&vfio_fsl_mc_driver); |
||
802 | +} |
||
803 | + |
||
804 | +module_init(vfio_fsl_mc_driver_init); |
||
805 | +module_exit(vfio_fsl_mc_driver_exit); |
||
806 | + |
||
807 | +MODULE_VERSION(DRIVER_VERSION); |
||
808 | +MODULE_LICENSE("GPL v2"); |
||
809 | +MODULE_AUTHOR(DRIVER_AUTHOR); |
||
810 | +MODULE_DESCRIPTION(DRIVER_DESC); |
||
811 | --- /dev/null |
||
812 | +++ b/drivers/vfio/fsl-mc/vfio_fsl_mc_intr.c |
||
813 | @@ -0,0 +1,199 @@ |
||
814 | +/* |
||
815 | + * Freescale Management Complex (MC) device passthrough using VFIO |
||
816 | + * |
||
817 | + * Copyright (C) 2013-2016 Freescale Semiconductor, Inc. |
||
818 | + * Author: Bharat Bhushan <bharat.bhushan@nxp.com> |
||
819 | + * |
||
820 | + * This file is licensed under the terms of the GNU General Public |
||
821 | + * License version 2. This program is licensed "as is" without any |
||
822 | + * warranty of any kind, whether express or implied. |
||
823 | + */ |
||
824 | + |
||
825 | +#include <linux/vfio.h> |
||
826 | +#include <linux/slab.h> |
||
827 | +#include <linux/types.h> |
||
828 | +#include <linux/eventfd.h> |
||
829 | +#include <linux/msi.h> |
||
830 | + |
||
831 | +#include "linux/fsl/mc.h" |
||
832 | +#include "vfio_fsl_mc_private.h" |
||
833 | + |
||
834 | +static irqreturn_t vfio_fsl_mc_irq_handler(int irq_num, void *arg) |
||
835 | +{ |
||
836 | + struct vfio_fsl_mc_irq *mc_irq = (struct vfio_fsl_mc_irq *)arg; |
||
837 | + |
||
838 | + eventfd_signal(mc_irq->trigger, 1); |
||
839 | + return IRQ_HANDLED; |
||
840 | +} |
||
841 | + |
||
842 | +static int vfio_fsl_mc_irq_mask(struct vfio_fsl_mc_device *vdev, |
||
843 | + unsigned int index, unsigned int start, |
||
844 | + unsigned int count, uint32_t flags, |
||
845 | + void *data) |
||
846 | +{ |
||
847 | + return -EINVAL; |
||
848 | +} |
||
849 | + |
||
850 | +static int vfio_fsl_mc_irq_unmask(struct vfio_fsl_mc_device *vdev, |
||
851 | + unsigned int index, unsigned int start, |
||
852 | + unsigned int count, uint32_t flags, |
||
853 | + void *data) |
||
854 | +{ |
||
855 | + return -EINVAL; |
||
856 | +} |
||
857 | + |
||
858 | +static int vfio_set_trigger(struct vfio_fsl_mc_device *vdev, |
||
859 | + int index, int fd) |
||
860 | +{ |
||
861 | + struct vfio_fsl_mc_irq *irq = &vdev->mc_irqs[index]; |
||
862 | + struct eventfd_ctx *trigger; |
||
863 | + int hwirq; |
||
864 | + int ret; |
||
865 | + |
||
866 | + hwirq = vdev->mc_dev->irqs[index]->msi_desc->irq; |
||
867 | + if (irq->trigger) { |
||
868 | + free_irq(hwirq, irq); |
||
869 | + kfree(irq->name); |
||
870 | + eventfd_ctx_put(irq->trigger); |
||
871 | + irq->trigger = NULL; |
||
872 | + } |
||
873 | + |
||
874 | + if (fd < 0) /* Disable only */ |
||
875 | + return 0; |
||
876 | + |
||
877 | + irq->name = kasprintf(GFP_KERNEL, "vfio-irq[%d](%s)", |
||
878 | + hwirq, dev_name(&vdev->mc_dev->dev)); |
||
879 | + if (!irq->name) |
||
880 | + return -ENOMEM; |
||
881 | + |
||
882 | + trigger = eventfd_ctx_fdget(fd); |
||
883 | + if (IS_ERR(trigger)) { |
||
884 | + kfree(irq->name); |
||
885 | + return PTR_ERR(trigger); |
||
886 | + } |
||
887 | + |
||
888 | + irq->trigger = trigger; |
||
889 | + |
||
890 | + ret = request_irq(hwirq, vfio_fsl_mc_irq_handler, 0, |
||
891 | + irq->name, irq); |
||
892 | + if (ret) { |
||
893 | + kfree(irq->name); |
||
894 | + eventfd_ctx_put(trigger); |
||
895 | + irq->trigger = NULL; |
||
896 | + return ret; |
||
897 | + } |
||
898 | + |
||
899 | + return 0; |
||
900 | +} |
||
901 | + |
||
902 | +int vfio_fsl_mc_irqs_init(struct vfio_fsl_mc_device *vdev) |
||
903 | +{ |
||
904 | + struct fsl_mc_device *mc_dev = vdev->mc_dev; |
||
905 | + struct vfio_fsl_mc_irq *mc_irq; |
||
906 | + int irq_count; |
||
907 | + int ret, i; |
||
908 | + |
||
909 | + /* Device does not support any interrupt */ |
||
910 | + if (mc_dev->obj_desc.irq_count == 0) |
||
911 | + return 0; |
||
912 | + |
||
913 | + irq_count = mc_dev->obj_desc.irq_count; |
||
914 | + |
||
915 | + mc_irq = kcalloc(irq_count, sizeof(*mc_irq), GFP_KERNEL); |
||
916 | + if (mc_irq == NULL) |
||
917 | + return -ENOMEM; |
||
918 | + |
||
919 | + /* Allocate IRQs */ |
||
920 | + ret = fsl_mc_allocate_irqs(mc_dev); |
||
921 | + if (ret) { |
||
922 | + kfree(mc_irq); |
||
923 | + return ret; |
||
924 | + } |
||
925 | + |
||
926 | + for (i = 0; i < irq_count; i++) { |
||
927 | + mc_irq[i].count = 1; |
||
928 | + mc_irq[i].flags = VFIO_IRQ_INFO_EVENTFD; |
||
929 | + } |
||
930 | + |
||
931 | + vdev->mc_irqs = mc_irq; |
||
932 | + |
||
933 | + return 0; |
||
934 | +} |
||
935 | + |
||
936 | +/* Free All IRQs for the given MC object */ |
||
937 | +void vfio_fsl_mc_irqs_cleanup(struct vfio_fsl_mc_device *vdev) |
||
938 | +{ |
||
939 | + struct fsl_mc_device *mc_dev = vdev->mc_dev; |
||
940 | + int irq_count = mc_dev->obj_desc.irq_count; |
||
941 | + int i; |
||
942 | + |
||
943 | + /* Device does not support any interrupt */ |
||
944 | + if (mc_dev->obj_desc.irq_count == 0) |
||
945 | + return; |
||
946 | + |
||
947 | + for (i = 0; i < irq_count; i++) |
||
948 | + vfio_set_trigger(vdev, i, -1); |
||
949 | + |
||
950 | + fsl_mc_free_irqs(mc_dev); |
||
951 | + kfree(vdev->mc_irqs); |
||
952 | +} |
||
953 | + |
||
954 | +static int vfio_fsl_mc_set_irq_trigger(struct vfio_fsl_mc_device *vdev, |
||
955 | + unsigned int index, unsigned int start, |
||
956 | + unsigned int count, uint32_t flags, |
||
957 | + void *data) |
||
958 | +{ |
||
959 | + struct vfio_fsl_mc_irq *irq = &vdev->mc_irqs[index]; |
||
960 | + int hwirq; |
||
961 | + |
||
962 | + if (!count && (flags & VFIO_IRQ_SET_DATA_NONE)) |
||
963 | + return vfio_set_trigger(vdev, index, -1); |
||
964 | + |
||
965 | + if (start != 0 || count != 1) |
||
966 | + return -EINVAL; |
||
967 | + |
||
968 | + if (flags & VFIO_IRQ_SET_DATA_EVENTFD) { |
||
969 | + int32_t fd = *(int32_t *)data; |
||
970 | + |
||
971 | + return vfio_set_trigger(vdev, index, fd); |
||
972 | + } |
||
973 | + |
||
974 | + hwirq = vdev->mc_dev->irqs[index]->msi_desc->irq; |
||
975 | + |
||
976 | + if (flags & VFIO_IRQ_SET_DATA_NONE) { |
||
977 | + vfio_fsl_mc_irq_handler(hwirq, irq); |
||
978 | + |
||
979 | + } else if (flags & VFIO_IRQ_SET_DATA_BOOL) { |
||
980 | + uint8_t trigger = *(uint8_t *)data; |
||
981 | + |
||
982 | + if (trigger) |
||
983 | + vfio_fsl_mc_irq_handler(hwirq, irq); |
||
984 | + } |
||
985 | + |
||
986 | + return 0; |
||
987 | +} |
||
988 | + |
||
989 | +int vfio_fsl_mc_set_irqs_ioctl(struct vfio_fsl_mc_device *vdev, |
||
990 | + uint32_t flags, unsigned int index, |
||
991 | + unsigned int start, unsigned int count, |
||
992 | + void *data) |
||
993 | +{ |
||
994 | + int ret = -ENOTTY; |
||
995 | + |
||
996 | + switch (flags & VFIO_IRQ_SET_ACTION_TYPE_MASK) { |
||
997 | + case VFIO_IRQ_SET_ACTION_MASK: |
||
998 | + ret = vfio_fsl_mc_irq_mask(vdev, index, start, count, |
||
999 | + flags, data); |
||
1000 | + break; |
||
1001 | + case VFIO_IRQ_SET_ACTION_UNMASK: |
||
1002 | + ret = vfio_fsl_mc_irq_unmask(vdev, index, start, count, |
||
1003 | + flags, data); |
||
1004 | + break; |
||
1005 | + case VFIO_IRQ_SET_ACTION_TRIGGER: |
||
1006 | + ret = vfio_fsl_mc_set_irq_trigger(vdev, index, start, |
||
1007 | + count, flags, data); |
||
1008 | + break; |
||
1009 | + } |
||
1010 | + |
||
1011 | + return ret; |
||
1012 | +} |
||
1013 | --- /dev/null |
||
1014 | +++ b/drivers/vfio/fsl-mc/vfio_fsl_mc_private.h |
||
1015 | @@ -0,0 +1,55 @@ |
||
1016 | +/* |
||
1017 | + * Freescale Management Complex VFIO private declarations |
||
1018 | + * |
||
1019 | + * Copyright (C) 2013-2016 Freescale Semiconductor, Inc. |
||
1020 | + * Copyright 2016 NXP |
||
1021 | + * Author: Bharat Bhushan <bharat.bhushan@nxp.com> |
||
1022 | + * |
||
1023 | + * This file is licensed under the terms of the GNU General Public |
||
1024 | + * License version 2. This program is licensed "as is" without any |
||
1025 | + * warranty of any kind, whether express or implied. |
||
1026 | + */ |
||
1027 | + |
||
1028 | +#ifndef VFIO_FSL_MC_PRIVATE_H |
||
1029 | +#define VFIO_FSL_MC_PRIVATE_H |
||
1030 | + |
||
1031 | +#define VFIO_FSL_MC_OFFSET_SHIFT 40 |
||
1032 | +#define VFIO_FSL_MC_OFFSET_MASK (((u64)(1) << VFIO_FSL_MC_OFFSET_SHIFT) - 1) |
||
1033 | + |
||
1034 | +#define VFIO_FSL_MC_OFFSET_TO_INDEX(off) (off >> VFIO_FSL_MC_OFFSET_SHIFT) |
||
1035 | + |
||
1036 | +#define VFIO_FSL_MC_INDEX_TO_OFFSET(index) \ |
||
1037 | + ((u64)(index) << VFIO_FSL_MC_OFFSET_SHIFT) |
||
1038 | + |
||
1039 | +struct vfio_fsl_mc_irq { |
||
1040 | + u32 flags; |
||
1041 | + u32 count; |
||
1042 | + struct eventfd_ctx *trigger; |
||
1043 | + char *name; |
||
1044 | +}; |
||
1045 | + |
||
1046 | +struct vfio_fsl_mc_region { |
||
1047 | + u32 flags; |
||
1048 | +#define VFIO_FSL_MC_REGION_TYPE_MMIO 1 |
||
1049 | +#define VFIO_FSL_MC_REGION_TYPE_CACHEABLE 2 |
||
1050 | + u32 type; |
||
1051 | + u64 addr; |
||
1052 | + resource_size_t size; |
||
1053 | + void __iomem *ioaddr; |
||
1054 | +}; |
||
1055 | + |
||
1056 | +struct vfio_fsl_mc_device { |
||
1057 | + struct fsl_mc_device *mc_dev; |
||
1058 | + int refcnt; |
||
1059 | + u32 num_regions; |
||
1060 | + struct vfio_fsl_mc_region *regions; |
||
1061 | + struct vfio_fsl_mc_irq *mc_irqs; |
||
1062 | +}; |
||
1063 | + |
||
1064 | +int vfio_fsl_mc_irqs_init(struct vfio_fsl_mc_device *vdev); |
||
1065 | +void vfio_fsl_mc_irqs_cleanup(struct vfio_fsl_mc_device *vdev); |
||
1066 | +int vfio_fsl_mc_set_irqs_ioctl(struct vfio_fsl_mc_device *vdev, |
||
1067 | + uint32_t flags, unsigned int index, |
||
1068 | + unsigned int start, unsigned int count, |
||
1069 | + void *data); |
||
1070 | +#endif /* VFIO_PCI_PRIVATE_H */ |
||
1071 | --- a/include/uapi/linux/vfio.h |
||
1072 | +++ b/include/uapi/linux/vfio.h |
||
1073 | @@ -200,6 +200,7 @@ struct vfio_device_info { |
||
1074 | #define VFIO_DEVICE_FLAGS_PLATFORM (1 << 2) /* vfio-platform device */ |
||
1075 | #define VFIO_DEVICE_FLAGS_AMBA (1 << 3) /* vfio-amba device */ |
||
1076 | #define VFIO_DEVICE_FLAGS_CCW (1 << 4) /* vfio-ccw device */ |
||
1077 | +#define VFIO_DEVICE_FLAGS_FSL_MC (1 << 5) /* vfio-fsl-mc device */ |
||
1078 | __u32 num_regions; /* Max region index + 1 */ |
||
1079 | __u32 num_irqs; /* Max IRQ index + 1 */ |
||
1080 | }; |