1 | /* |
2 | * VFIO based AP matrix device assignment |
3 | * |
4 | * Copyright 2018 IBM Corp. |
5 | * Author(s): Tony Krowiak <akrowiak@linux.ibm.com> |
6 | * Halil Pasic <pasic@linux.ibm.com> |
7 | * |
8 | * This work is licensed under the terms of the GNU GPL, version 2 or (at |
9 | * your option) any later version. See the COPYING file in the top-level |
10 | * directory. |
11 | */ |
12 | |
13 | #include "qemu/osdep.h" |
14 | #include <linux/vfio.h> |
15 | #include <sys/ioctl.h> |
16 | #include "qapi/error.h" |
17 | #include "hw/sysbus.h" |
18 | #include "hw/vfio/vfio.h" |
19 | #include "hw/vfio/vfio-common.h" |
20 | #include "hw/s390x/ap-device.h" |
21 | #include "qemu/error-report.h" |
22 | #include "qemu/module.h" |
23 | #include "qemu/option.h" |
24 | #include "qemu/config-file.h" |
25 | #include "cpu.h" |
26 | #include "kvm_s390x.h" |
27 | #include "migration/vmstate.h" |
28 | #include "hw/qdev-properties.h" |
29 | #include "hw/s390x/ap-bridge.h" |
30 | #include "exec/address-spaces.h" |
31 | |
32 | #define VFIO_AP_DEVICE_TYPE "vfio-ap" |
33 | |
34 | typedef struct VFIOAPDevice { |
35 | APDevice apdev; |
36 | VFIODevice vdev; |
37 | } VFIOAPDevice; |
38 | |
39 | #define VFIO_AP_DEVICE(obj) \ |
40 | OBJECT_CHECK(VFIOAPDevice, (obj), VFIO_AP_DEVICE_TYPE) |
41 | |
42 | static void vfio_ap_compute_needs_reset(VFIODevice *vdev) |
43 | { |
44 | vdev->needs_reset = false; |
45 | } |
46 | |
47 | /* |
48 | * We don't need vfio_hot_reset_multi and vfio_eoi operations for |
49 | * vfio-ap device now. |
50 | */ |
51 | struct VFIODeviceOps vfio_ap_ops = { |
52 | .vfio_compute_needs_reset = vfio_ap_compute_needs_reset, |
53 | }; |
54 | |
55 | static void vfio_ap_put_device(VFIOAPDevice *vapdev) |
56 | { |
57 | g_free(vapdev->vdev.name); |
58 | vfio_put_base_device(&vapdev->vdev); |
59 | } |
60 | |
61 | static VFIOGroup *vfio_ap_get_group(VFIOAPDevice *vapdev, Error **errp) |
62 | { |
63 | GError *gerror = NULL; |
64 | char *symlink, *group_path; |
65 | int groupid; |
66 | |
67 | symlink = g_strdup_printf("%s/iommu_group" , vapdev->vdev.sysfsdev); |
68 | group_path = g_file_read_link(symlink, &gerror); |
69 | g_free(symlink); |
70 | |
71 | if (!group_path) { |
72 | error_setg(errp, "%s: no iommu_group found for %s: %s" , |
73 | VFIO_AP_DEVICE_TYPE, vapdev->vdev.sysfsdev, gerror->message); |
74 | return NULL; |
75 | } |
76 | |
77 | if (sscanf(basename(group_path), "%d" , &groupid) != 1) { |
78 | error_setg(errp, "vfio: failed to read %s" , group_path); |
79 | g_free(group_path); |
80 | return NULL; |
81 | } |
82 | |
83 | g_free(group_path); |
84 | |
85 | return vfio_get_group(groupid, &address_space_memory, errp); |
86 | } |
87 | |
88 | static void vfio_ap_realize(DeviceState *dev, Error **errp) |
89 | { |
90 | int ret; |
91 | char *mdevid; |
92 | Error *local_err = NULL; |
93 | VFIOGroup *vfio_group; |
94 | APDevice *apdev = AP_DEVICE(dev); |
95 | VFIOAPDevice *vapdev = VFIO_AP_DEVICE(apdev); |
96 | |
97 | vfio_group = vfio_ap_get_group(vapdev, &local_err); |
98 | if (!vfio_group) { |
99 | goto out_err; |
100 | } |
101 | |
102 | vapdev->vdev.ops = &vfio_ap_ops; |
103 | vapdev->vdev.type = VFIO_DEVICE_TYPE_AP; |
104 | mdevid = basename(vapdev->vdev.sysfsdev); |
105 | vapdev->vdev.name = g_strdup_printf("%s" , mdevid); |
106 | vapdev->vdev.dev = dev; |
107 | |
108 | /* |
109 | * vfio-ap devices operate in a way compatible with |
110 | * memory ballooning, as no pages are pinned in the host. |
111 | * This needs to be set before vfio_get_device() for vfio common to |
112 | * handle the balloon inhibitor. |
113 | */ |
114 | vapdev->vdev.balloon_allowed = true; |
115 | |
116 | ret = vfio_get_device(vfio_group, mdevid, &vapdev->vdev, &local_err); |
117 | if (ret) { |
118 | goto out_get_dev_err; |
119 | } |
120 | |
121 | return; |
122 | |
123 | out_get_dev_err: |
124 | vfio_ap_put_device(vapdev); |
125 | vfio_put_group(vfio_group); |
126 | out_err: |
127 | error_propagate(errp, local_err); |
128 | } |
129 | |
130 | static void vfio_ap_unrealize(DeviceState *dev, Error **errp) |
131 | { |
132 | APDevice *apdev = AP_DEVICE(dev); |
133 | VFIOAPDevice *vapdev = VFIO_AP_DEVICE(apdev); |
134 | VFIOGroup *group = vapdev->vdev.group; |
135 | |
136 | vfio_ap_put_device(vapdev); |
137 | vfio_put_group(group); |
138 | } |
139 | |
140 | static Property vfio_ap_properties[] = { |
141 | DEFINE_PROP_STRING("sysfsdev" , VFIOAPDevice, vdev.sysfsdev), |
142 | DEFINE_PROP_END_OF_LIST(), |
143 | }; |
144 | |
145 | static void vfio_ap_reset(DeviceState *dev) |
146 | { |
147 | int ret; |
148 | APDevice *apdev = AP_DEVICE(dev); |
149 | VFIOAPDevice *vapdev = VFIO_AP_DEVICE(apdev); |
150 | |
151 | ret = ioctl(vapdev->vdev.fd, VFIO_DEVICE_RESET); |
152 | if (ret) { |
153 | error_report("%s: failed to reset %s device: %s" , __func__, |
154 | vapdev->vdev.name, strerror(errno)); |
155 | } |
156 | } |
157 | |
158 | static const VMStateDescription vfio_ap_vmstate = { |
159 | .name = "vfio-ap" , |
160 | .unmigratable = 1, |
161 | }; |
162 | |
163 | static void vfio_ap_class_init(ObjectClass *klass, void *data) |
164 | { |
165 | DeviceClass *dc = DEVICE_CLASS(klass); |
166 | |
167 | dc->props = vfio_ap_properties; |
168 | dc->vmsd = &vfio_ap_vmstate; |
169 | dc->desc = "VFIO-based AP device assignment" ; |
170 | set_bit(DEVICE_CATEGORY_MISC, dc->categories); |
171 | dc->realize = vfio_ap_realize; |
172 | dc->unrealize = vfio_ap_unrealize; |
173 | dc->hotpluggable = true; |
174 | dc->reset = vfio_ap_reset; |
175 | dc->bus_type = TYPE_AP_BUS; |
176 | } |
177 | |
178 | static const TypeInfo vfio_ap_info = { |
179 | .name = VFIO_AP_DEVICE_TYPE, |
180 | .parent = AP_DEVICE_TYPE, |
181 | .instance_size = sizeof(VFIOAPDevice), |
182 | .class_init = vfio_ap_class_init, |
183 | }; |
184 | |
185 | static void vfio_ap_type_init(void) |
186 | { |
187 | type_register_static(&vfio_ap_info); |
188 | } |
189 | |
190 | type_init(vfio_ap_type_init) |
191 | |