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