]>
Commit | Line | Data |
---|---|---|
2fe2942c TK |
1 | /* |
2 | * VFIO based AP matrix device assignment | |
3 | * | |
4 | * Copyright 2018 IBM Corp. | |
5 | * Author(s): Tony Krowiak <[email protected]> | |
6 | * Halil Pasic <[email protected]> | |
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" |
a27bd6c7 | 28 | #include "hw/qdev-properties.h" |
2fe2942c TK |
29 | #include "hw/s390x/ap-bridge.h" |
30 | #include "exec/address-spaces.h" | |
db1015e9 | 31 | #include "qom/object.h" |
2fe2942c | 32 | |
8b3a1ee5 | 33 | #define TYPE_VFIO_AP_DEVICE "vfio-ap" |
2fe2942c | 34 | |
db1015e9 | 35 | struct VFIOAPDevice { |
2fe2942c TK |
36 | APDevice apdev; |
37 | VFIODevice vdev; | |
db1015e9 | 38 | }; |
2fe2942c | 39 | |
8063396b | 40 | OBJECT_DECLARE_SIMPLE_TYPE(VFIOAPDevice, VFIO_AP_DEVICE) |
2fe2942c TK |
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", | |
8b3a1ee5 | 73 | TYPE_VFIO_AP_DEVICE, vapdev->vdev.sysfsdev, gerror->message); |
0216b18b | 74 | g_error_free(gerror); |
2fe2942c TK |
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; | |
2fe2942c TK |
93 | VFIOGroup *vfio_group; |
94 | APDevice *apdev = AP_DEVICE(dev); | |
95 | VFIOAPDevice *vapdev = VFIO_AP_DEVICE(apdev); | |
96 | ||
b5e45b0f | 97 | vfio_group = vfio_ap_get_group(vapdev, errp); |
2fe2942c | 98 | if (!vfio_group) { |
b5e45b0f | 99 | return; |
2fe2942c TK |
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 | ||
1883e8fc | 108 | /* |
aff92b82 DH |
109 | * vfio-ap devices operate in a way compatible with discarding of |
110 | * memory in RAM blocks, as no pages are pinned in the host. | |
1883e8fc | 111 | * This needs to be set before vfio_get_device() for vfio common to |
aff92b82 | 112 | * handle ram_block_discard_disable(). |
1883e8fc | 113 | */ |
aff92b82 | 114 | vapdev->vdev.ram_block_discard_allowed = true; |
1883e8fc | 115 | |
b5e45b0f | 116 | ret = vfio_get_device(vfio_group, mdevid, &vapdev->vdev, errp); |
2fe2942c TK |
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); | |
2fe2942c TK |
126 | } |
127 | ||
b69c3c21 | 128 | static void vfio_ap_unrealize(DeviceState *dev) |
2fe2942c TK |
129 | { |
130 | APDevice *apdev = AP_DEVICE(dev); | |
131 | VFIOAPDevice *vapdev = VFIO_AP_DEVICE(apdev); | |
132 | VFIOGroup *group = vapdev->vdev.group; | |
133 | ||
134 | vfio_ap_put_device(vapdev); | |
135 | vfio_put_group(group); | |
136 | } | |
137 | ||
138 | static Property vfio_ap_properties[] = { | |
139 | DEFINE_PROP_STRING("sysfsdev", VFIOAPDevice, vdev.sysfsdev), | |
140 | DEFINE_PROP_END_OF_LIST(), | |
141 | }; | |
142 | ||
143 | static void vfio_ap_reset(DeviceState *dev) | |
144 | { | |
145 | int ret; | |
146 | APDevice *apdev = AP_DEVICE(dev); | |
147 | VFIOAPDevice *vapdev = VFIO_AP_DEVICE(apdev); | |
148 | ||
149 | ret = ioctl(vapdev->vdev.fd, VFIO_DEVICE_RESET); | |
150 | if (ret) { | |
151 | error_report("%s: failed to reset %s device: %s", __func__, | |
ab17b1fc | 152 | vapdev->vdev.name, strerror(errno)); |
2fe2942c TK |
153 | } |
154 | } | |
155 | ||
156 | static const VMStateDescription vfio_ap_vmstate = { | |
da56e330 | 157 | .name = "vfio-ap", |
2fe2942c TK |
158 | .unmigratable = 1, |
159 | }; | |
160 | ||
161 | static void vfio_ap_class_init(ObjectClass *klass, void *data) | |
162 | { | |
163 | DeviceClass *dc = DEVICE_CLASS(klass); | |
164 | ||
4f67d30b | 165 | device_class_set_props(dc, vfio_ap_properties); |
2fe2942c TK |
166 | dc->vmsd = &vfio_ap_vmstate; |
167 | dc->desc = "VFIO-based AP device assignment"; | |
168 | set_bit(DEVICE_CATEGORY_MISC, dc->categories); | |
169 | dc->realize = vfio_ap_realize; | |
170 | dc->unrealize = vfio_ap_unrealize; | |
374b78e3 | 171 | dc->hotpluggable = true; |
2fe2942c TK |
172 | dc->reset = vfio_ap_reset; |
173 | dc->bus_type = TYPE_AP_BUS; | |
174 | } | |
175 | ||
176 | static const TypeInfo vfio_ap_info = { | |
8b3a1ee5 | 177 | .name = TYPE_VFIO_AP_DEVICE, |
fab2afff | 178 | .parent = TYPE_AP_DEVICE, |
2fe2942c TK |
179 | .instance_size = sizeof(VFIOAPDevice), |
180 | .class_init = vfio_ap_class_init, | |
181 | }; | |
182 | ||
183 | static void vfio_ap_type_init(void) | |
184 | { | |
185 | type_register_static(&vfio_ap_info); | |
186 | } | |
187 | ||
188 | type_init(vfio_ap_type_init) |