hc
2024-01-03 2f7c68cb55ecb7331f2381deb497c27155f32faf
kernel/drivers/s390/cio/vfio_ccw_ops.c
....@@ -3,13 +3,17 @@
33 * Physical device callbacks for vfio_ccw
44 *
55 * Copyright IBM Corp. 2017
6
+ * Copyright Red Hat, Inc. 2019
67 *
78 * Author(s): Dong Jia Shi <bjsdjshi@linux.vnet.ibm.com>
89 * Xiao Feng Ren <renxiaof@linux.vnet.ibm.com>
10
+ * Cornelia Huck <cohuck@redhat.com>
911 */
1012
1113 #include <linux/vfio.h>
1214 #include <linux/mdev.h>
15
+#include <linux/nospec.h>
16
+#include <linux/slab.h>
1317
1418 #include "vfio_ccw_private.h"
1519
....@@ -120,6 +124,11 @@
120124 private->mdev = mdev;
121125 private->state = VFIO_CCW_STATE_IDLE;
122126
127
+ VFIO_CCW_MSG_EVENT(2, "mdev %pUl, sch %x.%x.%04x: create\n",
128
+ mdev_uuid(mdev), private->sch->schid.cssid,
129
+ private->sch->schid.ssid,
130
+ private->sch->schid.sch_no);
131
+
123132 return 0;
124133 }
125134
....@@ -127,6 +136,11 @@
127136 {
128137 struct vfio_ccw_private *private =
129138 dev_get_drvdata(mdev_parent_dev(mdev));
139
+
140
+ VFIO_CCW_MSG_EVENT(2, "mdev %pUl, sch %x.%x.%04x: remove\n",
141
+ mdev_uuid(mdev), private->sch->schid.cssid,
142
+ private->sch->schid.ssid,
143
+ private->sch->schid.sch_no);
130144
131145 if ((private->state != VFIO_CCW_STATE_NOT_OPER) &&
132146 (private->state != VFIO_CCW_STATE_STANDBY)) {
....@@ -147,11 +161,34 @@
147161 struct vfio_ccw_private *private =
148162 dev_get_drvdata(mdev_parent_dev(mdev));
149163 unsigned long events = VFIO_IOMMU_NOTIFY_DMA_UNMAP;
164
+ int ret;
150165
151166 private->nb.notifier_call = vfio_ccw_mdev_notifier;
152167
153
- return vfio_register_notifier(mdev_dev(mdev), VFIO_IOMMU_NOTIFY,
154
- &events, &private->nb);
168
+ ret = vfio_register_notifier(mdev_dev(mdev), VFIO_IOMMU_NOTIFY,
169
+ &events, &private->nb);
170
+ if (ret)
171
+ return ret;
172
+
173
+ ret = vfio_ccw_register_async_dev_regions(private);
174
+ if (ret)
175
+ goto out_unregister;
176
+
177
+ ret = vfio_ccw_register_schib_dev_regions(private);
178
+ if (ret)
179
+ goto out_unregister;
180
+
181
+ ret = vfio_ccw_register_crw_dev_regions(private);
182
+ if (ret)
183
+ goto out_unregister;
184
+
185
+ return ret;
186
+
187
+out_unregister:
188
+ vfio_ccw_unregister_dev_regions(private);
189
+ vfio_unregister_notifier(mdev_dev(mdev), VFIO_IOMMU_NOTIFY,
190
+ &private->nb);
191
+ return ret;
155192 }
156193
157194 static void vfio_ccw_mdev_release(struct mdev_device *mdev)
....@@ -167,8 +204,30 @@
167204 }
168205
169206 cp_free(&private->cp);
207
+ vfio_ccw_unregister_dev_regions(private);
170208 vfio_unregister_notifier(mdev_dev(mdev), VFIO_IOMMU_NOTIFY,
171209 &private->nb);
210
+}
211
+
212
+static ssize_t vfio_ccw_mdev_read_io_region(struct vfio_ccw_private *private,
213
+ char __user *buf, size_t count,
214
+ loff_t *ppos)
215
+{
216
+ loff_t pos = *ppos & VFIO_CCW_OFFSET_MASK;
217
+ struct ccw_io_region *region;
218
+ int ret;
219
+
220
+ if (pos + count > sizeof(*region))
221
+ return -EINVAL;
222
+
223
+ mutex_lock(&private->io_mutex);
224
+ region = private->io_region;
225
+ if (copy_to_user(buf, (void *)region + pos, count))
226
+ ret = -EFAULT;
227
+ else
228
+ ret = count;
229
+ mutex_unlock(&private->io_mutex);
230
+ return ret;
172231 }
173232
174233 static ssize_t vfio_ccw_mdev_read(struct mdev_device *mdev,
....@@ -176,18 +235,52 @@
176235 size_t count,
177236 loff_t *ppos)
178237 {
238
+ unsigned int index = VFIO_CCW_OFFSET_TO_INDEX(*ppos);
179239 struct vfio_ccw_private *private;
180
- struct ccw_io_region *region;
181
-
182
- if (*ppos + count > sizeof(*region))
183
- return -EINVAL;
184240
185241 private = dev_get_drvdata(mdev_parent_dev(mdev));
186
- region = private->io_region;
187
- if (copy_to_user(buf, (void *)region + *ppos, count))
188
- return -EFAULT;
189242
190
- return count;
243
+ if (index >= VFIO_CCW_NUM_REGIONS + private->num_regions)
244
+ return -EINVAL;
245
+
246
+ switch (index) {
247
+ case VFIO_CCW_CONFIG_REGION_INDEX:
248
+ return vfio_ccw_mdev_read_io_region(private, buf, count, ppos);
249
+ default:
250
+ index -= VFIO_CCW_NUM_REGIONS;
251
+ return private->region[index].ops->read(private, buf, count,
252
+ ppos);
253
+ }
254
+
255
+ return -EINVAL;
256
+}
257
+
258
+static ssize_t vfio_ccw_mdev_write_io_region(struct vfio_ccw_private *private,
259
+ const char __user *buf,
260
+ size_t count, loff_t *ppos)
261
+{
262
+ loff_t pos = *ppos & VFIO_CCW_OFFSET_MASK;
263
+ struct ccw_io_region *region;
264
+ int ret;
265
+
266
+ if (pos + count > sizeof(*region))
267
+ return -EINVAL;
268
+
269
+ if (!mutex_trylock(&private->io_mutex))
270
+ return -EAGAIN;
271
+
272
+ region = private->io_region;
273
+ if (copy_from_user((void *)region + pos, buf, count)) {
274
+ ret = -EFAULT;
275
+ goto out_unlock;
276
+ }
277
+
278
+ vfio_ccw_fsm_event(private, VFIO_CCW_EVENT_IO_REQ);
279
+ ret = (region->ret_code != 0) ? region->ret_code : count;
280
+
281
+out_unlock:
282
+ mutex_unlock(&private->io_mutex);
283
+ return ret;
191284 }
192285
193286 static ssize_t vfio_ccw_mdev_write(struct mdev_device *mdev,
....@@ -195,42 +288,47 @@
195288 size_t count,
196289 loff_t *ppos)
197290 {
291
+ unsigned int index = VFIO_CCW_OFFSET_TO_INDEX(*ppos);
198292 struct vfio_ccw_private *private;
199
- struct ccw_io_region *region;
200
-
201
- if (*ppos + count > sizeof(*region))
202
- return -EINVAL;
203293
204294 private = dev_get_drvdata(mdev_parent_dev(mdev));
205
- if (private->state != VFIO_CCW_STATE_IDLE)
206
- return -EACCES;
207295
208
- region = private->io_region;
209
- if (copy_from_user((void *)region + *ppos, buf, count))
210
- return -EFAULT;
296
+ if (index >= VFIO_CCW_NUM_REGIONS + private->num_regions)
297
+ return -EINVAL;
211298
212
- vfio_ccw_fsm_event(private, VFIO_CCW_EVENT_IO_REQ);
213
- if (region->ret_code != 0) {
214
- private->state = VFIO_CCW_STATE_IDLE;
215
- return region->ret_code;
299
+ switch (index) {
300
+ case VFIO_CCW_CONFIG_REGION_INDEX:
301
+ return vfio_ccw_mdev_write_io_region(private, buf, count, ppos);
302
+ default:
303
+ index -= VFIO_CCW_NUM_REGIONS;
304
+ return private->region[index].ops->write(private, buf, count,
305
+ ppos);
216306 }
217307
218
- return count;
308
+ return -EINVAL;
219309 }
220310
221
-static int vfio_ccw_mdev_get_device_info(struct vfio_device_info *info)
311
+static int vfio_ccw_mdev_get_device_info(struct vfio_device_info *info,
312
+ struct mdev_device *mdev)
222313 {
314
+ struct vfio_ccw_private *private;
315
+
316
+ private = dev_get_drvdata(mdev_parent_dev(mdev));
223317 info->flags = VFIO_DEVICE_FLAGS_CCW | VFIO_DEVICE_FLAGS_RESET;
224
- info->num_regions = VFIO_CCW_NUM_REGIONS;
318
+ info->num_regions = VFIO_CCW_NUM_REGIONS + private->num_regions;
225319 info->num_irqs = VFIO_CCW_NUM_IRQS;
226320
227321 return 0;
228322 }
229323
230324 static int vfio_ccw_mdev_get_region_info(struct vfio_region_info *info,
231
- u16 *cap_type_id,
232
- void **cap_type)
325
+ struct mdev_device *mdev,
326
+ unsigned long arg)
233327 {
328
+ struct vfio_ccw_private *private;
329
+ int i;
330
+
331
+ private = dev_get_drvdata(mdev_parent_dev(mdev));
234332 switch (info->index) {
235333 case VFIO_CCW_CONFIG_REGION_INDEX:
236334 info->offset = 0;
....@@ -238,24 +336,75 @@
238336 info->flags = VFIO_REGION_INFO_FLAG_READ
239337 | VFIO_REGION_INFO_FLAG_WRITE;
240338 return 0;
241
- default:
242
- return -EINVAL;
339
+ default: /* all other regions are handled via capability chain */
340
+ {
341
+ struct vfio_info_cap caps = { .buf = NULL, .size = 0 };
342
+ struct vfio_region_info_cap_type cap_type = {
343
+ .header.id = VFIO_REGION_INFO_CAP_TYPE,
344
+ .header.version = 1 };
345
+ int ret;
346
+
347
+ if (info->index >=
348
+ VFIO_CCW_NUM_REGIONS + private->num_regions)
349
+ return -EINVAL;
350
+
351
+ info->index = array_index_nospec(info->index,
352
+ VFIO_CCW_NUM_REGIONS +
353
+ private->num_regions);
354
+
355
+ i = info->index - VFIO_CCW_NUM_REGIONS;
356
+
357
+ info->offset = VFIO_CCW_INDEX_TO_OFFSET(info->index);
358
+ info->size = private->region[i].size;
359
+ info->flags = private->region[i].flags;
360
+
361
+ cap_type.type = private->region[i].type;
362
+ cap_type.subtype = private->region[i].subtype;
363
+
364
+ ret = vfio_info_add_capability(&caps, &cap_type.header,
365
+ sizeof(cap_type));
366
+ if (ret)
367
+ return ret;
368
+
369
+ info->flags |= VFIO_REGION_INFO_FLAG_CAPS;
370
+ if (info->argsz < sizeof(*info) + caps.size) {
371
+ info->argsz = sizeof(*info) + caps.size;
372
+ info->cap_offset = 0;
373
+ } else {
374
+ vfio_info_cap_shift(&caps, sizeof(*info));
375
+ if (copy_to_user((void __user *)arg + sizeof(*info),
376
+ caps.buf, caps.size)) {
377
+ kfree(caps.buf);
378
+ return -EFAULT;
379
+ }
380
+ info->cap_offset = sizeof(*info);
381
+ }
382
+
383
+ kfree(caps.buf);
384
+
243385 }
386
+ }
387
+ return 0;
244388 }
245389
246390 static int vfio_ccw_mdev_get_irq_info(struct vfio_irq_info *info)
247391 {
248
- if (info->index != VFIO_CCW_IO_IRQ_INDEX)
392
+ switch (info->index) {
393
+ case VFIO_CCW_IO_IRQ_INDEX:
394
+ case VFIO_CCW_CRW_IRQ_INDEX:
395
+ info->count = 1;
396
+ info->flags = VFIO_IRQ_INFO_EVENTFD;
397
+ break;
398
+ default:
249399 return -EINVAL;
250
-
251
- info->count = 1;
252
- info->flags = VFIO_IRQ_INFO_EVENTFD;
400
+ }
253401
254402 return 0;
255403 }
256404
257405 static int vfio_ccw_mdev_set_irqs(struct mdev_device *mdev,
258406 uint32_t flags,
407
+ uint32_t index,
259408 void __user *data)
260409 {
261410 struct vfio_ccw_private *private;
....@@ -265,7 +414,17 @@
265414 return -EINVAL;
266415
267416 private = dev_get_drvdata(mdev_parent_dev(mdev));
268
- ctx = &private->io_trigger;
417
+
418
+ switch (index) {
419
+ case VFIO_CCW_IO_IRQ_INDEX:
420
+ ctx = &private->io_trigger;
421
+ break;
422
+ case VFIO_CCW_CRW_IRQ_INDEX:
423
+ ctx = &private->crw_trigger;
424
+ break;
425
+ default:
426
+ return -EINVAL;
427
+ }
269428
270429 switch (flags & VFIO_IRQ_SET_DATA_TYPE_MASK) {
271430 case VFIO_IRQ_SET_DATA_NONE:
....@@ -317,6 +476,43 @@
317476 }
318477 }
319478
479
+int vfio_ccw_register_dev_region(struct vfio_ccw_private *private,
480
+ unsigned int subtype,
481
+ const struct vfio_ccw_regops *ops,
482
+ size_t size, u32 flags, void *data)
483
+{
484
+ struct vfio_ccw_region *region;
485
+
486
+ region = krealloc(private->region,
487
+ (private->num_regions + 1) * sizeof(*region),
488
+ GFP_KERNEL);
489
+ if (!region)
490
+ return -ENOMEM;
491
+
492
+ private->region = region;
493
+ private->region[private->num_regions].type = VFIO_REGION_TYPE_CCW;
494
+ private->region[private->num_regions].subtype = subtype;
495
+ private->region[private->num_regions].ops = ops;
496
+ private->region[private->num_regions].size = size;
497
+ private->region[private->num_regions].flags = flags;
498
+ private->region[private->num_regions].data = data;
499
+
500
+ private->num_regions++;
501
+
502
+ return 0;
503
+}
504
+
505
+void vfio_ccw_unregister_dev_regions(struct vfio_ccw_private *private)
506
+{
507
+ int i;
508
+
509
+ for (i = 0; i < private->num_regions; i++)
510
+ private->region[i].ops->release(private, &private->region[i]);
511
+ private->num_regions = 0;
512
+ kfree(private->region);
513
+ private->region = NULL;
514
+}
515
+
320516 static ssize_t vfio_ccw_mdev_ioctl(struct mdev_device *mdev,
321517 unsigned int cmd,
322518 unsigned long arg)
....@@ -337,7 +533,7 @@
337533 if (info.argsz < minsz)
338534 return -EINVAL;
339535
340
- ret = vfio_ccw_mdev_get_device_info(&info);
536
+ ret = vfio_ccw_mdev_get_device_info(&info, mdev);
341537 if (ret)
342538 return ret;
343539
....@@ -346,8 +542,6 @@
346542 case VFIO_DEVICE_GET_REGION_INFO:
347543 {
348544 struct vfio_region_info info;
349
- u16 cap_type_id = 0;
350
- void *cap_type = NULL;
351545
352546 minsz = offsetofend(struct vfio_region_info, offset);
353547
....@@ -357,8 +551,7 @@
357551 if (info.argsz < minsz)
358552 return -EINVAL;
359553
360
- ret = vfio_ccw_mdev_get_region_info(&info, &cap_type_id,
361
- &cap_type);
554
+ ret = vfio_ccw_mdev_get_region_info(&info, mdev, arg);
362555 if (ret)
363556 return ret;
364557
....@@ -403,7 +596,7 @@
403596 return ret;
404597
405598 data = (void __user *)(arg + minsz);
406
- return vfio_ccw_mdev_set_irqs(mdev, hdr.flags, data);
599
+ return vfio_ccw_mdev_set_irqs(mdev, hdr.flags, hdr.index, data);
407600 }
408601 case VFIO_DEVICE_RESET:
409602 return vfio_ccw_mdev_reset(mdev);