xref: /openbmc/qemu/hw/vfio/ccw.c (revision 80adf54e)
1 /*
2  * vfio based subchannel assignment support
3  *
4  * Copyright 2017 IBM Corp.
5  * Author(s): Dong Jia Shi <bjsdjshi@linux.vnet.ibm.com>
6  *            Xiao Feng Ren <renxiaof@linux.vnet.ibm.com>
7  *            Pierre Morel <pmorel@linux.vnet.ibm.com>
8  *
9  * This work is licensed under the terms of the GNU GPL, version 2 or(at
10  * your option) any version. See the COPYING file in the top-level
11  * directory.
12  */
13 
14 #include <linux/vfio.h>
15 #include <linux/vfio_ccw.h>
16 #include <sys/ioctl.h>
17 
18 #include "qemu/osdep.h"
19 #include "qapi/error.h"
20 #include "hw/sysbus.h"
21 #include "hw/vfio/vfio.h"
22 #include "hw/vfio/vfio-common.h"
23 #include "hw/s390x/s390-ccw.h"
24 #include "hw/s390x/ccw-device.h"
25 #include "qemu/error-report.h"
26 
27 #define TYPE_VFIO_CCW "vfio-ccw"
28 typedef struct VFIOCCWDevice {
29     S390CCWDevice cdev;
30     VFIODevice vdev;
31     uint64_t io_region_size;
32     uint64_t io_region_offset;
33     struct ccw_io_region *io_region;
34     EventNotifier io_notifier;
35 } VFIOCCWDevice;
36 
37 static void vfio_ccw_compute_needs_reset(VFIODevice *vdev)
38 {
39     vdev->needs_reset = false;
40 }
41 
42 /*
43  * We don't need vfio_hot_reset_multi and vfio_eoi operations for
44  * vfio_ccw device now.
45  */
46 struct VFIODeviceOps vfio_ccw_ops = {
47     .vfio_compute_needs_reset = vfio_ccw_compute_needs_reset,
48 };
49 
50 static int vfio_ccw_handle_request(ORB *orb, SCSW *scsw, void *data)
51 {
52     S390CCWDevice *cdev = data;
53     VFIOCCWDevice *vcdev = DO_UPCAST(VFIOCCWDevice, cdev, cdev);
54     struct ccw_io_region *region = vcdev->io_region;
55     int ret;
56 
57     QEMU_BUILD_BUG_ON(sizeof(region->orb_area) != sizeof(ORB));
58     QEMU_BUILD_BUG_ON(sizeof(region->scsw_area) != sizeof(SCSW));
59     QEMU_BUILD_BUG_ON(sizeof(region->irb_area) != sizeof(IRB));
60 
61     memset(region, 0, sizeof(*region));
62 
63     memcpy(region->orb_area, orb, sizeof(ORB));
64     memcpy(region->scsw_area, scsw, sizeof(SCSW));
65 
66 again:
67     ret = pwrite(vcdev->vdev.fd, region,
68                  vcdev->io_region_size, vcdev->io_region_offset);
69     if (ret != vcdev->io_region_size) {
70         if (errno == EAGAIN) {
71             goto again;
72         }
73         error_report("vfio-ccw: wirte I/O region failed with errno=%d", errno);
74         return -errno;
75     }
76 
77     return region->ret_code;
78 }
79 
80 static void vfio_ccw_reset(DeviceState *dev)
81 {
82     CcwDevice *ccw_dev = DO_UPCAST(CcwDevice, parent_obj, dev);
83     S390CCWDevice *cdev = DO_UPCAST(S390CCWDevice, parent_obj, ccw_dev);
84     VFIOCCWDevice *vcdev = DO_UPCAST(VFIOCCWDevice, cdev, cdev);
85 
86     ioctl(vcdev->vdev.fd, VFIO_DEVICE_RESET);
87 }
88 
89 static void vfio_ccw_io_notifier_handler(void *opaque)
90 {
91     VFIOCCWDevice *vcdev = opaque;
92     struct ccw_io_region *region = vcdev->io_region;
93     S390CCWDevice *cdev = S390_CCW_DEVICE(vcdev);
94     CcwDevice *ccw_dev = CCW_DEVICE(cdev);
95     SubchDev *sch = ccw_dev->sch;
96     SCSW *s = &sch->curr_status.scsw;
97     PMCW *p = &sch->curr_status.pmcw;
98     IRB irb;
99     int size;
100 
101     if (!event_notifier_test_and_clear(&vcdev->io_notifier)) {
102         return;
103     }
104 
105     size = pread(vcdev->vdev.fd, region, vcdev->io_region_size,
106                  vcdev->io_region_offset);
107     if (size == -1) {
108         switch (errno) {
109         case ENODEV:
110             /* Generate a deferred cc 3 condition. */
111             s->flags |= SCSW_FLAGS_MASK_CC;
112             s->ctrl &= ~SCSW_CTRL_MASK_STCTL;
113             s->ctrl |= (SCSW_STCTL_ALERT | SCSW_STCTL_STATUS_PEND);
114             goto read_err;
115         case EFAULT:
116             /* Memory problem, generate channel data check. */
117             s->ctrl &= ~SCSW_ACTL_START_PEND;
118             s->cstat = SCSW_CSTAT_DATA_CHECK;
119             s->ctrl &= ~SCSW_CTRL_MASK_STCTL;
120             s->ctrl |= SCSW_STCTL_PRIMARY | SCSW_STCTL_SECONDARY |
121                        SCSW_STCTL_ALERT | SCSW_STCTL_STATUS_PEND;
122             goto read_err;
123         default:
124             /* Error, generate channel program check. */
125             s->ctrl &= ~SCSW_ACTL_START_PEND;
126             s->cstat = SCSW_CSTAT_PROG_CHECK;
127             s->ctrl &= ~SCSW_CTRL_MASK_STCTL;
128             s->ctrl |= SCSW_STCTL_PRIMARY | SCSW_STCTL_SECONDARY |
129                        SCSW_STCTL_ALERT | SCSW_STCTL_STATUS_PEND;
130             goto read_err;
131         }
132     } else if (size != vcdev->io_region_size) {
133         /* Information transfer error, generate channel-control check. */
134         s->ctrl &= ~SCSW_ACTL_START_PEND;
135         s->cstat = SCSW_CSTAT_CHN_CTRL_CHK;
136         s->ctrl &= ~SCSW_CTRL_MASK_STCTL;
137         s->ctrl |= SCSW_STCTL_PRIMARY | SCSW_STCTL_SECONDARY |
138                    SCSW_STCTL_ALERT | SCSW_STCTL_STATUS_PEND;
139         goto read_err;
140     }
141 
142     memcpy(&irb, region->irb_area, sizeof(IRB));
143 
144     /* Update control block via irb. */
145     copy_scsw_to_guest(s, &irb.scsw);
146 
147     /* If a uint check is pending, copy sense data. */
148     if ((s->dstat & SCSW_DSTAT_UNIT_CHECK) &&
149         (p->chars & PMCW_CHARS_MASK_CSENSE)) {
150         memcpy(sch->sense_data, irb.ecw, sizeof(irb.ecw));
151     }
152 
153 read_err:
154     css_inject_io_interrupt(sch);
155 }
156 
157 static void vfio_ccw_register_io_notifier(VFIOCCWDevice *vcdev, Error **errp)
158 {
159     VFIODevice *vdev = &vcdev->vdev;
160     struct vfio_irq_info *irq_info;
161     struct vfio_irq_set *irq_set;
162     size_t argsz;
163     int32_t *pfd;
164 
165     if (vdev->num_irqs < VFIO_CCW_IO_IRQ_INDEX + 1) {
166         error_setg(errp, "vfio: unexpected number of io irqs %u",
167                    vdev->num_irqs);
168         return;
169     }
170 
171     argsz = sizeof(*irq_info);
172     irq_info = g_malloc0(argsz);
173     irq_info->index = VFIO_CCW_IO_IRQ_INDEX;
174     irq_info->argsz = argsz;
175     if (ioctl(vdev->fd, VFIO_DEVICE_GET_IRQ_INFO,
176               irq_info) < 0 || irq_info->count < 1) {
177         error_setg_errno(errp, errno, "vfio: Error getting irq info");
178         goto out_free_info;
179     }
180 
181     if (event_notifier_init(&vcdev->io_notifier, 0)) {
182         error_setg_errno(errp, errno,
183                          "vfio: Unable to init event notifier for IO");
184         goto out_free_info;
185     }
186 
187     argsz = sizeof(*irq_set) + sizeof(*pfd);
188     irq_set = g_malloc0(argsz);
189     irq_set->argsz = argsz;
190     irq_set->flags = VFIO_IRQ_SET_DATA_EVENTFD |
191                      VFIO_IRQ_SET_ACTION_TRIGGER;
192     irq_set->index = VFIO_CCW_IO_IRQ_INDEX;
193     irq_set->start = 0;
194     irq_set->count = 1;
195     pfd = (int32_t *) &irq_set->data;
196 
197     *pfd = event_notifier_get_fd(&vcdev->io_notifier);
198     qemu_set_fd_handler(*pfd, vfio_ccw_io_notifier_handler, NULL, vcdev);
199     if (ioctl(vdev->fd, VFIO_DEVICE_SET_IRQS, irq_set)) {
200         error_setg(errp, "vfio: Failed to set up io notification");
201         qemu_set_fd_handler(*pfd, NULL, NULL, vcdev);
202         event_notifier_cleanup(&vcdev->io_notifier);
203     }
204 
205     g_free(irq_set);
206 
207 out_free_info:
208     g_free(irq_info);
209 }
210 
211 static void vfio_ccw_unregister_io_notifier(VFIOCCWDevice *vcdev)
212 {
213     struct vfio_irq_set *irq_set;
214     size_t argsz;
215     int32_t *pfd;
216 
217     argsz = sizeof(*irq_set) + sizeof(*pfd);
218     irq_set = g_malloc0(argsz);
219     irq_set->argsz = argsz;
220     irq_set->flags = VFIO_IRQ_SET_DATA_EVENTFD |
221                      VFIO_IRQ_SET_ACTION_TRIGGER;
222     irq_set->index = VFIO_CCW_IO_IRQ_INDEX;
223     irq_set->start = 0;
224     irq_set->count = 1;
225     pfd = (int32_t *) &irq_set->data;
226     *pfd = -1;
227 
228     if (ioctl(vcdev->vdev.fd, VFIO_DEVICE_SET_IRQS, irq_set)) {
229         error_report("vfio: Failed to de-assign device io fd: %m");
230     }
231 
232     qemu_set_fd_handler(event_notifier_get_fd(&vcdev->io_notifier),
233                         NULL, NULL, vcdev);
234     event_notifier_cleanup(&vcdev->io_notifier);
235 
236     g_free(irq_set);
237 }
238 
239 static void vfio_ccw_get_region(VFIOCCWDevice *vcdev, Error **errp)
240 {
241     VFIODevice *vdev = &vcdev->vdev;
242     struct vfio_region_info *info;
243     int ret;
244 
245     /* Sanity check device */
246     if (!(vdev->flags & VFIO_DEVICE_FLAGS_CCW)) {
247         error_setg(errp, "vfio: Um, this isn't a vfio-ccw device");
248         return;
249     }
250 
251     if (vdev->num_regions < VFIO_CCW_CONFIG_REGION_INDEX + 1) {
252         error_setg(errp, "vfio: Unexpected number of the I/O region %u",
253                    vdev->num_regions);
254         return;
255     }
256 
257     ret = vfio_get_region_info(vdev, VFIO_CCW_CONFIG_REGION_INDEX, &info);
258     if (ret) {
259         error_setg_errno(errp, -ret, "vfio: Error getting config info");
260         return;
261     }
262 
263     vcdev->io_region_size = info->size;
264     if (sizeof(*vcdev->io_region) != vcdev->io_region_size) {
265         error_setg(errp, "vfio: Unexpected size of the I/O region");
266         g_free(info);
267         return;
268     }
269 
270     vcdev->io_region_offset = info->offset;
271     vcdev->io_region = g_malloc0(info->size);
272 
273     g_free(info);
274 }
275 
276 static void vfio_ccw_put_region(VFIOCCWDevice *vcdev)
277 {
278     g_free(vcdev->io_region);
279 }
280 
281 static void vfio_put_device(VFIOCCWDevice *vcdev)
282 {
283     g_free(vcdev->vdev.name);
284     vfio_put_base_device(&vcdev->vdev);
285 }
286 
287 static VFIOGroup *vfio_ccw_get_group(S390CCWDevice *cdev, Error **errp)
288 {
289     char *tmp, group_path[PATH_MAX];
290     ssize_t len;
291     int groupid;
292 
293     tmp = g_strdup_printf("/sys/bus/css/devices/%x.%x.%04x/%s/iommu_group",
294                           cdev->hostid.cssid, cdev->hostid.ssid,
295                           cdev->hostid.devid, cdev->mdevid);
296     len = readlink(tmp, group_path, sizeof(group_path));
297     g_free(tmp);
298 
299     if (len <= 0 || len >= sizeof(group_path)) {
300         error_setg(errp, "vfio: no iommu_group found");
301         return NULL;
302     }
303 
304     group_path[len] = 0;
305 
306     if (sscanf(basename(group_path), "%d", &groupid) != 1) {
307         error_setg(errp, "vfio: failed to read %s", group_path);
308         return NULL;
309     }
310 
311     return vfio_get_group(groupid, &address_space_memory, errp);
312 }
313 
314 static void vfio_ccw_realize(DeviceState *dev, Error **errp)
315 {
316     VFIODevice *vbasedev;
317     VFIOGroup *group;
318     CcwDevice *ccw_dev = DO_UPCAST(CcwDevice, parent_obj, dev);
319     S390CCWDevice *cdev = DO_UPCAST(S390CCWDevice, parent_obj, ccw_dev);
320     VFIOCCWDevice *vcdev = DO_UPCAST(VFIOCCWDevice, cdev, cdev);
321     S390CCWDeviceClass *cdc = S390_CCW_DEVICE_GET_CLASS(cdev);
322     Error *err = NULL;
323 
324     /* Call the class init function for subchannel. */
325     if (cdc->realize) {
326         cdc->realize(cdev, vcdev->vdev.sysfsdev, &err);
327         if (err) {
328             goto out_err_propagate;
329         }
330     }
331 
332     group = vfio_ccw_get_group(cdev, &err);
333     if (!group) {
334         goto out_group_err;
335     }
336 
337     vcdev->vdev.ops = &vfio_ccw_ops;
338     vcdev->vdev.type = VFIO_DEVICE_TYPE_CCW;
339     vcdev->vdev.name = g_strdup_printf("%x.%x.%04x", cdev->hostid.cssid,
340                                        cdev->hostid.ssid, cdev->hostid.devid);
341     vcdev->vdev.dev = dev;
342     QLIST_FOREACH(vbasedev, &group->device_list, next) {
343         if (strcmp(vbasedev->name, vcdev->vdev.name) == 0) {
344             error_setg(&err, "vfio: subchannel %s has already been attached",
345                        vcdev->vdev.name);
346             goto out_device_err;
347         }
348     }
349 
350     if (vfio_get_device(group, cdev->mdevid, &vcdev->vdev, &err)) {
351         goto out_device_err;
352     }
353 
354     vfio_ccw_get_region(vcdev, &err);
355     if (err) {
356         goto out_region_err;
357     }
358 
359     vfio_ccw_register_io_notifier(vcdev, &err);
360     if (err) {
361         goto out_notifier_err;
362     }
363 
364     return;
365 
366 out_notifier_err:
367     vfio_ccw_put_region(vcdev);
368 out_region_err:
369     vfio_put_device(vcdev);
370 out_device_err:
371     vfio_put_group(group);
372 out_group_err:
373     if (cdc->unrealize) {
374         cdc->unrealize(cdev, NULL);
375     }
376 out_err_propagate:
377     error_propagate(errp, err);
378 }
379 
380 static void vfio_ccw_unrealize(DeviceState *dev, Error **errp)
381 {
382     CcwDevice *ccw_dev = DO_UPCAST(CcwDevice, parent_obj, dev);
383     S390CCWDevice *cdev = DO_UPCAST(S390CCWDevice, parent_obj, ccw_dev);
384     VFIOCCWDevice *vcdev = DO_UPCAST(VFIOCCWDevice, cdev, cdev);
385     S390CCWDeviceClass *cdc = S390_CCW_DEVICE_GET_CLASS(cdev);
386     VFIOGroup *group = vcdev->vdev.group;
387 
388     vfio_ccw_unregister_io_notifier(vcdev);
389     vfio_ccw_put_region(vcdev);
390     vfio_put_device(vcdev);
391     vfio_put_group(group);
392 
393     if (cdc->unrealize) {
394         cdc->unrealize(cdev, errp);
395     }
396 }
397 
398 static Property vfio_ccw_properties[] = {
399     DEFINE_PROP_STRING("sysfsdev", VFIOCCWDevice, vdev.sysfsdev),
400     DEFINE_PROP_END_OF_LIST(),
401 };
402 
403 static const VMStateDescription vfio_ccw_vmstate = {
404     .name = TYPE_VFIO_CCW,
405     .unmigratable = 1,
406 };
407 
408 static void vfio_ccw_class_init(ObjectClass *klass, void *data)
409 {
410     DeviceClass *dc = DEVICE_CLASS(klass);
411     S390CCWDeviceClass *cdc = S390_CCW_DEVICE_CLASS(klass);
412 
413     dc->props = vfio_ccw_properties;
414     dc->vmsd = &vfio_ccw_vmstate;
415     dc->desc = "VFIO-based subchannel assignment";
416     dc->realize = vfio_ccw_realize;
417     dc->unrealize = vfio_ccw_unrealize;
418     dc->reset = vfio_ccw_reset;
419 
420     cdc->handle_request = vfio_ccw_handle_request;
421 }
422 
423 static const TypeInfo vfio_ccw_info = {
424     .name = TYPE_VFIO_CCW,
425     .parent = TYPE_S390_CCW,
426     .instance_size = sizeof(VFIOCCWDevice),
427     .class_init = vfio_ccw_class_init,
428 };
429 
430 static void register_vfio_ccw_type(void)
431 {
432     type_register_static(&vfio_ccw_info);
433 }
434 
435 type_init(register_vfio_ccw_type)
436