blob: b2e6154040344a323a412dca46caede26abd2023 [file] [log] [blame]
Dong Jia Shi84cd8fc2017-03-17 04:17:33 +01001/*
2 * Physical device callbacks for vfio_ccw
3 *
4 * Copyright IBM Corp. 2017
5 *
6 * Author(s): Dong Jia Shi <bjsdjshi@linux.vnet.ibm.com>
7 * Xiao Feng Ren <renxiaof@linux.vnet.ibm.com>
8 */
9
10#include <linux/vfio.h>
11#include <linux/mdev.h>
12
13#include "vfio_ccw_private.h"
14
Dong Jia Shi83d11932017-03-17 04:17:37 +010015static int vfio_ccw_mdev_reset(struct mdev_device *mdev)
16{
17 struct vfio_ccw_private *private;
18 struct subchannel *sch;
19 int ret;
20
21 private = dev_get_drvdata(mdev_parent_dev(mdev));
22 if (!private)
23 return -ENODEV;
24
25 sch = private->sch;
26 /*
27 * TODO:
28 * In the cureent stage, some things like "no I/O running" and "no
29 * interrupt pending" are clear, but we are not sure what other state
30 * we need to care about.
31 * There are still a lot more instructions need to be handled. We
32 * should come back here later.
33 */
34 ret = vfio_ccw_sch_quiesce(sch);
35 if (ret)
36 return ret;
37
Dong Jia Shibbe37e42017-03-17 04:17:40 +010038 ret = cio_enable_subchannel(sch, (u32)(unsigned long)sch);
39 if (!ret)
40 private->state = VFIO_CCW_STATE_IDLE;
41
42 return ret;
Dong Jia Shi83d11932017-03-17 04:17:37 +010043}
44
Dong Jia Shi84cd8fc2017-03-17 04:17:33 +010045static int vfio_ccw_mdev_notifier(struct notifier_block *nb,
46 unsigned long action,
47 void *data)
48{
49 struct vfio_ccw_private *private =
50 container_of(nb, struct vfio_ccw_private, nb);
51
52 if (!private)
53 return NOTIFY_STOP;
54
55 /*
Dong Jia Shi84cd8fc2017-03-17 04:17:33 +010056 * Vendor drivers MUST unpin pages in response to an
57 * invalidation.
58 */
Dong Jia Shi4e149e42017-03-17 04:17:35 +010059 if (action == VFIO_IOMMU_NOTIFY_DMA_UNMAP) {
60 struct vfio_iommu_type1_dma_unmap *unmap = data;
Dong Jia Shi4e149e42017-03-17 04:17:35 +010061
62 if (!cp_iova_pinned(&private->cp, unmap->iova))
63 return NOTIFY_OK;
64
Dong Jia Shi83d11932017-03-17 04:17:37 +010065 if (vfio_ccw_mdev_reset(private->mdev))
Dong Jia Shi4e149e42017-03-17 04:17:35 +010066 return NOTIFY_BAD;
67
68 cp_free(&private->cp);
69 return NOTIFY_OK;
70 }
Dong Jia Shi84cd8fc2017-03-17 04:17:33 +010071
72 return NOTIFY_DONE;
73}
74
75static ssize_t name_show(struct kobject *kobj, struct device *dev, char *buf)
76{
77 return sprintf(buf, "I/O subchannel (Non-QDIO)\n");
78}
79MDEV_TYPE_ATTR_RO(name);
80
81static ssize_t device_api_show(struct kobject *kobj, struct device *dev,
82 char *buf)
83{
84 return sprintf(buf, "%s\n", VFIO_DEVICE_API_CCW_STRING);
85}
86MDEV_TYPE_ATTR_RO(device_api);
87
88static ssize_t available_instances_show(struct kobject *kobj,
89 struct device *dev, char *buf)
90{
91 struct vfio_ccw_private *private = dev_get_drvdata(dev);
92
93 return sprintf(buf, "%d\n", atomic_read(&private->avail));
94}
95MDEV_TYPE_ATTR_RO(available_instances);
96
97static struct attribute *mdev_types_attrs[] = {
98 &mdev_type_attr_name.attr,
99 &mdev_type_attr_device_api.attr,
100 &mdev_type_attr_available_instances.attr,
101 NULL,
102};
103
104static struct attribute_group mdev_type_group = {
105 .name = "io",
106 .attrs = mdev_types_attrs,
107};
108
109struct attribute_group *mdev_type_groups[] = {
110 &mdev_type_group,
111 NULL,
112};
113
114static int vfio_ccw_mdev_create(struct kobject *kobj, struct mdev_device *mdev)
115{
116 struct vfio_ccw_private *private =
117 dev_get_drvdata(mdev_parent_dev(mdev));
118
Dong Jia Shibbe37e42017-03-17 04:17:40 +0100119 if (private->state == VFIO_CCW_STATE_NOT_OPER)
120 return -ENODEV;
121
Dong Jia Shi84cd8fc2017-03-17 04:17:33 +0100122 if (atomic_dec_if_positive(&private->avail) < 0)
123 return -EPERM;
124
125 private->mdev = mdev;
Dong Jia Shibbe37e42017-03-17 04:17:40 +0100126 private->state = VFIO_CCW_STATE_IDLE;
Dong Jia Shi84cd8fc2017-03-17 04:17:33 +0100127
128 return 0;
129}
130
131static int vfio_ccw_mdev_remove(struct mdev_device *mdev)
132{
Dong Jia Shi83d11932017-03-17 04:17:37 +0100133 struct vfio_ccw_private *private =
134 dev_get_drvdata(mdev_parent_dev(mdev));
Dong Jia Shi84cd8fc2017-03-17 04:17:33 +0100135 int ret;
136
Dong Jia Shibbe37e42017-03-17 04:17:40 +0100137 if (!private)
138 goto out;
139
140 if ((private->state == VFIO_CCW_STATE_NOT_OPER) ||
141 (private->state == VFIO_CCW_STATE_STANDBY))
142 goto out;
143
Dong Jia Shi83d11932017-03-17 04:17:37 +0100144 ret = vfio_ccw_mdev_reset(mdev);
Dong Jia Shi84cd8fc2017-03-17 04:17:33 +0100145 if (ret)
146 return ret;
147
Dong Jia Shibbe37e42017-03-17 04:17:40 +0100148 private->state = VFIO_CCW_STATE_STANDBY;
149
150out:
Dong Jia Shi84cd8fc2017-03-17 04:17:33 +0100151 private->mdev = NULL;
152 atomic_inc(&private->avail);
153
154 return 0;
155}
156
157static int vfio_ccw_mdev_open(struct mdev_device *mdev)
158{
159 struct vfio_ccw_private *private =
160 dev_get_drvdata(mdev_parent_dev(mdev));
161 unsigned long events = VFIO_IOMMU_NOTIFY_DMA_UNMAP;
162
163 private->nb.notifier_call = vfio_ccw_mdev_notifier;
164
165 return vfio_register_notifier(mdev_dev(mdev), VFIO_IOMMU_NOTIFY,
166 &events, &private->nb);
167}
168
169void vfio_ccw_mdev_release(struct mdev_device *mdev)
170{
171 struct vfio_ccw_private *private =
172 dev_get_drvdata(mdev_parent_dev(mdev));
173
174 vfio_unregister_notifier(mdev_dev(mdev), VFIO_IOMMU_NOTIFY,
175 &private->nb);
176}
177
Dong Jia Shi060d2b52017-03-17 04:17:34 +0100178static ssize_t vfio_ccw_mdev_read(struct mdev_device *mdev,
179 char __user *buf,
180 size_t count,
181 loff_t *ppos)
182{
183 struct vfio_ccw_private *private;
184 struct ccw_io_region *region;
185
186 if (*ppos + count > sizeof(*region))
187 return -EINVAL;
188
189 private = dev_get_drvdata(mdev_parent_dev(mdev));
190 if (!private)
191 return -ENODEV;
192
193 region = &private->io_region;
194 if (copy_to_user(buf, (void *)region + *ppos, count))
195 return -EFAULT;
196
197 return count;
198}
199
200static ssize_t vfio_ccw_mdev_write(struct mdev_device *mdev,
201 const char __user *buf,
202 size_t count,
203 loff_t *ppos)
204{
205 struct vfio_ccw_private *private;
206 struct ccw_io_region *region;
207
208 if (*ppos + count > sizeof(*region))
209 return -EINVAL;
210
211 private = dev_get_drvdata(mdev_parent_dev(mdev));
212 if (!private)
213 return -ENODEV;
Dong Jia Shibbe37e42017-03-17 04:17:40 +0100214 if (private->state != VFIO_CCW_STATE_IDLE)
215 return -EACCES;
Dong Jia Shi060d2b52017-03-17 04:17:34 +0100216
217 region = &private->io_region;
218 if (copy_from_user((void *)region + *ppos, buf, count))
219 return -EFAULT;
Dong Jia Shi4e149e42017-03-17 04:17:35 +0100220
Dong Jia Shibbe37e42017-03-17 04:17:40 +0100221 vfio_ccw_fsm_event(private, VFIO_CCW_EVENT_IO_REQ);
222 if (region->ret_code != 0) {
223 private->state = VFIO_CCW_STATE_IDLE;
Dong Jia Shi4e149e42017-03-17 04:17:35 +0100224 return region->ret_code;
Dong Jia Shibbe37e42017-03-17 04:17:40 +0100225 }
Dong Jia Shi060d2b52017-03-17 04:17:34 +0100226
227 return count;
228}
229
Dong Jia Shie01bcdd2017-03-17 04:17:36 +0100230static int vfio_ccw_mdev_get_device_info(struct vfio_device_info *info)
231{
Dong Jia Shi83d11932017-03-17 04:17:37 +0100232 info->flags = VFIO_DEVICE_FLAGS_CCW | VFIO_DEVICE_FLAGS_RESET;
Dong Jia Shie01bcdd2017-03-17 04:17:36 +0100233 info->num_regions = VFIO_CCW_NUM_REGIONS;
Dong Jia Shi120e2142017-03-17 04:17:38 +0100234 info->num_irqs = VFIO_CCW_NUM_IRQS;
Dong Jia Shie01bcdd2017-03-17 04:17:36 +0100235
236 return 0;
237}
238
239static int vfio_ccw_mdev_get_region_info(struct vfio_region_info *info,
240 u16 *cap_type_id,
241 void **cap_type)
242{
243 switch (info->index) {
244 case VFIO_CCW_CONFIG_REGION_INDEX:
245 info->offset = 0;
246 info->size = sizeof(struct ccw_io_region);
247 info->flags = VFIO_REGION_INFO_FLAG_READ
248 | VFIO_REGION_INFO_FLAG_WRITE;
249 return 0;
250 default:
251 return -EINVAL;
252 }
253}
254
Dong Jia Shi120e2142017-03-17 04:17:38 +0100255int vfio_ccw_mdev_get_irq_info(struct vfio_irq_info *info)
256{
257 if (info->index != VFIO_CCW_IO_IRQ_INDEX)
258 return -EINVAL;
259
260 info->count = 1;
261 info->flags = VFIO_IRQ_INFO_EVENTFD;
262
263 return 0;
264}
265
266static int vfio_ccw_mdev_set_irqs(struct mdev_device *mdev,
267 uint32_t flags,
268 void __user *data)
269{
270 struct vfio_ccw_private *private;
271 struct eventfd_ctx **ctx;
272
273 if (!(flags & VFIO_IRQ_SET_ACTION_TRIGGER))
274 return -EINVAL;
275
276 private = dev_get_drvdata(mdev_parent_dev(mdev));
277 if (!private)
278 return -ENODEV;
279
280 ctx = &private->io_trigger;
281
282 switch (flags & VFIO_IRQ_SET_DATA_TYPE_MASK) {
283 case VFIO_IRQ_SET_DATA_NONE:
284 {
285 if (*ctx)
286 eventfd_signal(*ctx, 1);
287 return 0;
288 }
289 case VFIO_IRQ_SET_DATA_BOOL:
290 {
291 uint8_t trigger;
292
293 if (get_user(trigger, (uint8_t __user *)data))
294 return -EFAULT;
295
296 if (trigger && *ctx)
297 eventfd_signal(*ctx, 1);
298 return 0;
299 }
300 case VFIO_IRQ_SET_DATA_EVENTFD:
301 {
302 int32_t fd;
303
304 if (get_user(fd, (int32_t __user *)data))
305 return -EFAULT;
306
307 if (fd == -1) {
308 if (*ctx)
309 eventfd_ctx_put(*ctx);
310 *ctx = NULL;
311 } else if (fd >= 0) {
312 struct eventfd_ctx *efdctx;
313
314 efdctx = eventfd_ctx_fdget(fd);
315 if (IS_ERR(efdctx))
316 return PTR_ERR(efdctx);
317
318 if (*ctx)
319 eventfd_ctx_put(*ctx);
320
321 *ctx = efdctx;
322 } else
323 return -EINVAL;
324
325 return 0;
326 }
327 default:
328 return -EINVAL;
329 }
330}
331
Dong Jia Shie01bcdd2017-03-17 04:17:36 +0100332static ssize_t vfio_ccw_mdev_ioctl(struct mdev_device *mdev,
333 unsigned int cmd,
334 unsigned long arg)
335{
336 int ret = 0;
337 unsigned long minsz;
338
339 switch (cmd) {
340 case VFIO_DEVICE_GET_INFO:
341 {
342 struct vfio_device_info info;
343
344 minsz = offsetofend(struct vfio_device_info, num_irqs);
345
346 if (copy_from_user(&info, (void __user *)arg, minsz))
347 return -EFAULT;
348
349 if (info.argsz < minsz)
350 return -EINVAL;
351
352 ret = vfio_ccw_mdev_get_device_info(&info);
353 if (ret)
354 return ret;
355
356 return copy_to_user((void __user *)arg, &info, minsz);
357 }
358 case VFIO_DEVICE_GET_REGION_INFO:
359 {
360 struct vfio_region_info info;
361 u16 cap_type_id = 0;
362 void *cap_type = NULL;
363
364 minsz = offsetofend(struct vfio_region_info, offset);
365
366 if (copy_from_user(&info, (void __user *)arg, minsz))
367 return -EFAULT;
368
369 if (info.argsz < minsz)
370 return -EINVAL;
371
372 ret = vfio_ccw_mdev_get_region_info(&info, &cap_type_id,
373 &cap_type);
374 if (ret)
375 return ret;
376
377 return copy_to_user((void __user *)arg, &info, minsz);
378 }
Dong Jia Shi120e2142017-03-17 04:17:38 +0100379 case VFIO_DEVICE_GET_IRQ_INFO:
380 {
381 struct vfio_irq_info info;
382
383 minsz = offsetofend(struct vfio_irq_info, count);
384
385 if (copy_from_user(&info, (void __user *)arg, minsz))
386 return -EFAULT;
387
388 if (info.argsz < minsz || info.index >= VFIO_CCW_NUM_IRQS)
389 return -EINVAL;
390
391 ret = vfio_ccw_mdev_get_irq_info(&info);
392 if (ret)
393 return ret;
394
395 if (info.count == -1)
396 return -EINVAL;
397
398 return copy_to_user((void __user *)arg, &info, minsz);
399 }
400 case VFIO_DEVICE_SET_IRQS:
401 {
402 struct vfio_irq_set hdr;
403 size_t data_size;
404 void __user *data;
405
406 minsz = offsetofend(struct vfio_irq_set, count);
407
408 if (copy_from_user(&hdr, (void __user *)arg, minsz))
409 return -EFAULT;
410
411 ret = vfio_set_irqs_validate_and_prepare(&hdr, 1,
412 VFIO_CCW_NUM_IRQS,
413 &data_size);
414 if (ret)
415 return ret;
416
417 data = (void __user *)(arg + minsz);
418 return vfio_ccw_mdev_set_irqs(mdev, hdr.flags, data);
419 }
Dong Jia Shi83d11932017-03-17 04:17:37 +0100420 case VFIO_DEVICE_RESET:
421 return vfio_ccw_mdev_reset(mdev);
Dong Jia Shie01bcdd2017-03-17 04:17:36 +0100422 default:
423 return -ENOTTY;
424 }
425}
426
Dong Jia Shi84cd8fc2017-03-17 04:17:33 +0100427static const struct mdev_parent_ops vfio_ccw_mdev_ops = {
428 .owner = THIS_MODULE,
429 .supported_type_groups = mdev_type_groups,
430 .create = vfio_ccw_mdev_create,
431 .remove = vfio_ccw_mdev_remove,
432 .open = vfio_ccw_mdev_open,
433 .release = vfio_ccw_mdev_release,
Dong Jia Shi060d2b52017-03-17 04:17:34 +0100434 .read = vfio_ccw_mdev_read,
435 .write = vfio_ccw_mdev_write,
Dong Jia Shie01bcdd2017-03-17 04:17:36 +0100436 .ioctl = vfio_ccw_mdev_ioctl,
Dong Jia Shi84cd8fc2017-03-17 04:17:33 +0100437};
438
439int vfio_ccw_mdev_reg(struct subchannel *sch)
440{
441 return mdev_register_device(&sch->dev, &vfio_ccw_mdev_ops);
442}
443
444void vfio_ccw_mdev_unreg(struct subchannel *sch)
445{
446 mdev_unregister_device(&sch->dev);
447}