]>
Commit | Line | Data |
---|---|---|
ff8eca55 FK |
1 | /* |
2 | * VirtioBus | |
3 | * | |
4 | * Copyright (C) 2012 : GreenSocs Ltd | |
5 | * http://www.greensocs.com/ , email: [email protected] | |
6 | * | |
7 | * Developed by : | |
8 | * Frederic Konrad <[email protected]> | |
9 | * | |
10 | * This program is free software; you can redistribute it and/or modify | |
11 | * it under the terms of the GNU General Public License as published by | |
12 | * the Free Software Foundation, either version 2 of the License, or | |
13 | * (at your option) any later version. | |
14 | * | |
15 | * This program is distributed in the hope that it will be useful, | |
16 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
18 | * GNU General Public License for more details. | |
19 | * | |
20 | * You should have received a copy of the GNU General Public License along | |
21 | * with this program; if not, see <http://www.gnu.org/licenses/>. | |
22 | * | |
23 | */ | |
24 | ||
9b8bfe21 | 25 | #include "qemu/osdep.h" |
ff8eca55 | 26 | #include "qemu/error-report.h" |
0b8fa32f | 27 | #include "qemu/module.h" |
a77690c4 | 28 | #include "qapi/error.h" |
83c9f4ca | 29 | #include "hw/qdev.h" |
0d09e41a PB |
30 | #include "hw/virtio/virtio-bus.h" |
31 | #include "hw/virtio/virtio.h" | |
8607f5c3 | 32 | #include "exec/address-spaces.h" |
ff8eca55 FK |
33 | |
34 | /* #define DEBUG_VIRTIO_BUS */ | |
35 | ||
36 | #ifdef DEBUG_VIRTIO_BUS | |
37 | #define DPRINTF(fmt, ...) \ | |
38 | do { printf("virtio_bus: " fmt , ## __VA_ARGS__); } while (0) | |
39 | #else | |
40 | #define DPRINTF(fmt, ...) do { } while (0) | |
41 | #endif | |
42 | ||
5e96f5d2 | 43 | /* A VirtIODevice is being plugged */ |
e8398045 | 44 | void virtio_bus_device_plugged(VirtIODevice *vdev, Error **errp) |
ff8eca55 FK |
45 | { |
46 | DeviceState *qdev = DEVICE(vdev); | |
47 | BusState *qbus = BUS(qdev_get_parent_bus(qdev)); | |
48 | VirtioBusState *bus = VIRTIO_BUS(qbus); | |
49 | VirtioBusClass *klass = VIRTIO_BUS_GET_CLASS(bus); | |
6b8f1020 | 50 | VirtioDeviceClass *vdc = VIRTIO_DEVICE_GET_CLASS(vdev); |
2943b53f | 51 | bool has_iommu = virtio_host_has_feature(vdev, VIRTIO_F_IOMMU_PLATFORM); |
a77690c4 | 52 | Error *local_err = NULL; |
6b8f1020 | 53 | |
ff8eca55 FK |
54 | DPRINTF("%s: plug device.\n", qbus->name); |
55 | ||
d1b4259f | 56 | if (klass->pre_plugged != NULL) { |
a77690c4 FZ |
57 | klass->pre_plugged(qbus->parent, &local_err); |
58 | if (local_err) { | |
59 | error_propagate(errp, local_err); | |
60 | return; | |
61 | } | |
ff8eca55 FK |
62 | } |
63 | ||
6b8f1020 CH |
64 | /* Get the features of the plugged device. */ |
65 | assert(vdc->get_features != NULL); | |
9d5b731d | 66 | vdev->host_features = vdc->get_features(vdev, vdev->host_features, |
a77690c4 FZ |
67 | &local_err); |
68 | if (local_err) { | |
69 | error_propagate(errp, local_err); | |
70 | return; | |
71 | } | |
d1b4259f MC |
72 | |
73 | if (klass->device_plugged != NULL) { | |
a77690c4 FZ |
74 | klass->device_plugged(qbus->parent, &local_err); |
75 | } | |
76 | if (local_err) { | |
77 | error_propagate(errp, local_err); | |
78 | return; | |
11380b36 | 79 | } |
8607f5c3 | 80 | |
2943b53f JW |
81 | if (klass->get_dma_as != NULL && has_iommu) { |
82 | virtio_add_feature(&vdev->host_features, VIRTIO_F_IOMMU_PLATFORM); | |
8607f5c3 JW |
83 | vdev->dma_as = klass->get_dma_as(qbus->parent); |
84 | } else { | |
85 | vdev->dma_as = &address_space_memory; | |
86 | } | |
ff8eca55 FK |
87 | } |
88 | ||
89 | /* Reset the virtio_bus */ | |
90 | void virtio_bus_reset(VirtioBusState *bus) | |
91 | { | |
06d3dff0 PB |
92 | VirtIODevice *vdev = virtio_bus_get_device(bus); |
93 | ||
2c80ab15 | 94 | DPRINTF("%s: reset device.\n", BUS(bus)->name); |
06d3dff0 PB |
95 | if (vdev != NULL) { |
96 | virtio_reset(vdev); | |
ff8eca55 FK |
97 | } |
98 | } | |
99 | ||
5e96f5d2 PB |
100 | /* A VirtIODevice is being unplugged */ |
101 | void virtio_bus_device_unplugged(VirtIODevice *vdev) | |
ff8eca55 | 102 | { |
5e96f5d2 PB |
103 | DeviceState *qdev = DEVICE(vdev); |
104 | BusState *qbus = BUS(qdev_get_parent_bus(qdev)); | |
105 | VirtioBusClass *klass = VIRTIO_BUS_GET_CLASS(qbus); | |
06d3dff0 | 106 | |
ff8eca55 FK |
107 | DPRINTF("%s: remove device.\n", qbus->name); |
108 | ||
06d3dff0 | 109 | if (vdev != NULL) { |
5e96f5d2 PB |
110 | if (klass->device_unplugged != NULL) { |
111 | klass->device_unplugged(qbus->parent); | |
ff8eca55 | 112 | } |
ff8eca55 FK |
113 | } |
114 | } | |
115 | ||
116 | /* Get the device id of the plugged device. */ | |
117 | uint16_t virtio_bus_get_vdev_id(VirtioBusState *bus) | |
118 | { | |
06d3dff0 PB |
119 | VirtIODevice *vdev = virtio_bus_get_device(bus); |
120 | assert(vdev != NULL); | |
121 | return vdev->device_id; | |
ff8eca55 FK |
122 | } |
123 | ||
124 | /* Get the config_len field of the plugged device. */ | |
125 | size_t virtio_bus_get_vdev_config_len(VirtioBusState *bus) | |
126 | { | |
06d3dff0 PB |
127 | VirtIODevice *vdev = virtio_bus_get_device(bus); |
128 | assert(vdev != NULL); | |
129 | return vdev->config_len; | |
ff8eca55 FK |
130 | } |
131 | ||
8e05db92 FK |
132 | /* Get bad features of the plugged device. */ |
133 | uint32_t virtio_bus_get_vdev_bad_features(VirtioBusState *bus) | |
134 | { | |
06d3dff0 | 135 | VirtIODevice *vdev = virtio_bus_get_device(bus); |
8e05db92 | 136 | VirtioDeviceClass *k; |
06d3dff0 PB |
137 | |
138 | assert(vdev != NULL); | |
139 | k = VIRTIO_DEVICE_GET_CLASS(vdev); | |
8e05db92 | 140 | if (k->bad_features != NULL) { |
06d3dff0 | 141 | return k->bad_features(vdev); |
8e05db92 FK |
142 | } else { |
143 | return 0; | |
144 | } | |
145 | } | |
146 | ||
147 | /* Get config of the plugged device. */ | |
148 | void virtio_bus_get_vdev_config(VirtioBusState *bus, uint8_t *config) | |
149 | { | |
06d3dff0 | 150 | VirtIODevice *vdev = virtio_bus_get_device(bus); |
8e05db92 | 151 | VirtioDeviceClass *k; |
06d3dff0 PB |
152 | |
153 | assert(vdev != NULL); | |
154 | k = VIRTIO_DEVICE_GET_CLASS(vdev); | |
8e05db92 | 155 | if (k->get_config != NULL) { |
06d3dff0 | 156 | k->get_config(vdev, config); |
8e05db92 FK |
157 | } |
158 | } | |
159 | ||
5d448f9d FK |
160 | /* Set config of the plugged device. */ |
161 | void virtio_bus_set_vdev_config(VirtioBusState *bus, uint8_t *config) | |
162 | { | |
06d3dff0 | 163 | VirtIODevice *vdev = virtio_bus_get_device(bus); |
5d448f9d | 164 | VirtioDeviceClass *k; |
06d3dff0 PB |
165 | |
166 | assert(vdev != NULL); | |
167 | k = VIRTIO_DEVICE_GET_CLASS(vdev); | |
5d448f9d | 168 | if (k->set_config != NULL) { |
06d3dff0 | 169 | k->set_config(vdev, config); |
5d448f9d FK |
170 | } |
171 | } | |
172 | ||
310837de PB |
173 | /* On success, ioeventfd ownership belongs to the caller. */ |
174 | int virtio_bus_grab_ioeventfd(VirtioBusState *bus) | |
175 | { | |
176 | VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(bus); | |
177 | ||
178 | /* vhost can be used even if ioeventfd=off in the proxy device, | |
179 | * so do not check k->ioeventfd_enabled. | |
180 | */ | |
181 | if (!k->ioeventfd_assign) { | |
182 | return -ENOSYS; | |
183 | } | |
184 | ||
185 | if (bus->ioeventfd_grabbed == 0 && bus->ioeventfd_started) { | |
186 | virtio_bus_stop_ioeventfd(bus); | |
187 | /* Remember that we need to restart ioeventfd | |
188 | * when ioeventfd_grabbed becomes zero. | |
189 | */ | |
190 | bus->ioeventfd_started = true; | |
191 | } | |
192 | bus->ioeventfd_grabbed++; | |
193 | return 0; | |
194 | } | |
195 | ||
196 | void virtio_bus_release_ioeventfd(VirtioBusState *bus) | |
197 | { | |
198 | assert(bus->ioeventfd_grabbed != 0); | |
199 | if (--bus->ioeventfd_grabbed == 0 && bus->ioeventfd_started) { | |
200 | /* Force virtio_bus_start_ioeventfd to act. */ | |
201 | bus->ioeventfd_started = false; | |
202 | virtio_bus_start_ioeventfd(bus); | |
203 | } | |
204 | } | |
205 | ||
ff4c07df | 206 | int virtio_bus_start_ioeventfd(VirtioBusState *bus) |
6798e245 CH |
207 | { |
208 | VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(bus); | |
209 | DeviceState *proxy = DEVICE(BUS(bus)->parent); | |
ff4c07df PB |
210 | VirtIODevice *vdev = virtio_bus_get_device(bus); |
211 | VirtioDeviceClass *vdc = VIRTIO_DEVICE_GET_CLASS(vdev); | |
212 | int r; | |
6798e245 | 213 | |
8e93cef1 | 214 | if (!k->ioeventfd_assign || !k->ioeventfd_enabled(proxy)) { |
ff4c07df | 215 | return -ENOSYS; |
6798e245 | 216 | } |
e616c2f3 | 217 | if (bus->ioeventfd_started) { |
ff4c07df | 218 | return 0; |
6798e245 | 219 | } |
310837de PB |
220 | |
221 | /* Only set our notifier if we have ownership. */ | |
222 | if (!bus->ioeventfd_grabbed) { | |
223 | r = vdc->start_ioeventfd(vdev); | |
224 | if (r < 0) { | |
225 | error_report("%s: failed. Fallback to userspace (slower).", __func__); | |
226 | return r; | |
227 | } | |
6798e245 | 228 | } |
b13d3962 | 229 | bus->ioeventfd_started = true; |
ff4c07df | 230 | return 0; |
6798e245 CH |
231 | } |
232 | ||
233 | void virtio_bus_stop_ioeventfd(VirtioBusState *bus) | |
234 | { | |
6798e245 | 235 | VirtIODevice *vdev; |
ff4c07df | 236 | VirtioDeviceClass *vdc; |
6798e245 | 237 | |
b13d3962 | 238 | if (!bus->ioeventfd_started) { |
6798e245 CH |
239 | return; |
240 | } | |
ff4c07df | 241 | |
310837de PB |
242 | /* Only remove our notifier if we have ownership. */ |
243 | if (!bus->ioeventfd_grabbed) { | |
244 | vdev = virtio_bus_get_device(bus); | |
245 | vdc = VIRTIO_DEVICE_GET_CLASS(vdev); | |
246 | vdc->stop_ioeventfd(vdev); | |
247 | } | |
b13d3962 | 248 | bus->ioeventfd_started = false; |
6798e245 CH |
249 | } |
250 | ||
8e93cef1 PB |
251 | bool virtio_bus_ioeventfd_enabled(VirtioBusState *bus) |
252 | { | |
253 | VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(bus); | |
254 | DeviceState *proxy = DEVICE(BUS(bus)->parent); | |
255 | ||
256 | return k->ioeventfd_assign && k->ioeventfd_enabled(proxy); | |
257 | } | |
258 | ||
6798e245 | 259 | /* |
e616c2f3 PB |
260 | * This function switches ioeventfd on/off in the device. |
261 | * The caller must set or clear the handlers for the EventNotifier. | |
6798e245 CH |
262 | */ |
263 | int virtio_bus_set_host_notifier(VirtioBusState *bus, int n, bool assign) | |
264 | { | |
2bd3c31a | 265 | VirtIODevice *vdev = virtio_bus_get_device(bus); |
6798e245 CH |
266 | VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(bus); |
267 | DeviceState *proxy = DEVICE(BUS(bus)->parent); | |
2bd3c31a PB |
268 | VirtQueue *vq = virtio_get_queue(vdev, n); |
269 | EventNotifier *notifier = virtio_queue_get_host_notifier(vq); | |
270 | int r = 0; | |
6798e245 | 271 | |
b13d3962 | 272 | if (!k->ioeventfd_assign) { |
6798e245 CH |
273 | return -ENOSYS; |
274 | } | |
2bd3c31a | 275 | |
6798e245 | 276 | if (assign) { |
2bd3c31a PB |
277 | r = event_notifier_init(notifier, 1); |
278 | if (r < 0) { | |
279 | error_report("%s: unable to init event notifier: %s (%d)", | |
280 | __func__, strerror(-r), r); | |
281 | return r; | |
282 | } | |
283 | r = k->ioeventfd_assign(proxy, notifier, n, true); | |
284 | if (r < 0) { | |
285 | error_report("%s: unable to assign ioeventfd: %d", __func__, r); | |
76143618 | 286 | virtio_bus_cleanup_host_notifier(bus, n); |
2bd3c31a | 287 | } |
e616c2f3 | 288 | } else { |
2bd3c31a | 289 | k->ioeventfd_assign(proxy, notifier, n, false); |
6798e245 | 290 | } |
2bd3c31a | 291 | |
76143618 GH |
292 | return r; |
293 | } | |
294 | ||
295 | void virtio_bus_cleanup_host_notifier(VirtioBusState *bus, int n) | |
296 | { | |
297 | VirtIODevice *vdev = virtio_bus_get_device(bus); | |
298 | VirtQueue *vq = virtio_get_queue(vdev, n); | |
299 | EventNotifier *notifier = virtio_queue_get_host_notifier(vq); | |
300 | ||
1ef8185a MT |
301 | /* Test and clear notifier after disabling event, |
302 | * in case poll callback didn't have time to run. | |
303 | */ | |
304 | virtio_queue_host_notifier_read(notifier); | |
305 | event_notifier_cleanup(notifier); | |
6798e245 CH |
306 | } |
307 | ||
6d46895b FK |
308 | static char *virtio_bus_get_dev_path(DeviceState *dev) |
309 | { | |
310 | BusState *bus = qdev_get_parent_bus(dev); | |
311 | DeviceState *proxy = DEVICE(bus->parent); | |
312 | return qdev_get_dev_path(proxy); | |
313 | } | |
314 | ||
bbfa18fc AK |
315 | static char *virtio_bus_get_fw_dev_path(DeviceState *dev) |
316 | { | |
317 | return NULL; | |
318 | } | |
319 | ||
6d46895b FK |
320 | static void virtio_bus_class_init(ObjectClass *klass, void *data) |
321 | { | |
322 | BusClass *bus_class = BUS_CLASS(klass); | |
323 | bus_class->get_dev_path = virtio_bus_get_dev_path; | |
bbfa18fc | 324 | bus_class->get_fw_dev_path = virtio_bus_get_fw_dev_path; |
6d46895b FK |
325 | } |
326 | ||
ff8eca55 FK |
327 | static const TypeInfo virtio_bus_info = { |
328 | .name = TYPE_VIRTIO_BUS, | |
329 | .parent = TYPE_BUS, | |
330 | .instance_size = sizeof(VirtioBusState), | |
331 | .abstract = true, | |
332 | .class_size = sizeof(VirtioBusClass), | |
6d46895b | 333 | .class_init = virtio_bus_class_init |
ff8eca55 FK |
334 | }; |
335 | ||
336 | static void virtio_register_types(void) | |
337 | { | |
338 | type_register_static(&virtio_bus_info); | |
339 | } | |
340 | ||
341 | type_init(virtio_register_types) |