]>
Commit | Line | Data |
---|---|---|
ff8eca55 FK |
1 | /* |
2 | * VirtioBus | |
3 | * | |
4 | * Copyright (C) 2012 : GreenSocs Ltd | |
5 | * http://www.greensocs.com/ , email: info@greensocs.com | |
6 | * | |
7 | * Developed by : | |
8 | * Frederic Konrad <fred.konrad@greensocs.com> | |
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" |
83c9f4ca | 26 | #include "hw/hw.h" |
ff8eca55 | 27 | #include "qemu/error-report.h" |
83c9f4ca | 28 | #include "hw/qdev.h" |
0d09e41a PB |
29 | #include "hw/virtio/virtio-bus.h" |
30 | #include "hw/virtio/virtio.h" | |
ff8eca55 FK |
31 | |
32 | /* #define DEBUG_VIRTIO_BUS */ | |
33 | ||
34 | #ifdef DEBUG_VIRTIO_BUS | |
35 | #define DPRINTF(fmt, ...) \ | |
36 | do { printf("virtio_bus: " fmt , ## __VA_ARGS__); } while (0) | |
37 | #else | |
38 | #define DPRINTF(fmt, ...) do { } while (0) | |
39 | #endif | |
40 | ||
5e96f5d2 | 41 | /* A VirtIODevice is being plugged */ |
e8398045 | 42 | void virtio_bus_device_plugged(VirtIODevice *vdev, Error **errp) |
ff8eca55 FK |
43 | { |
44 | DeviceState *qdev = DEVICE(vdev); | |
45 | BusState *qbus = BUS(qdev_get_parent_bus(qdev)); | |
46 | VirtioBusState *bus = VIRTIO_BUS(qbus); | |
47 | VirtioBusClass *klass = VIRTIO_BUS_GET_CLASS(bus); | |
6b8f1020 CH |
48 | VirtioDeviceClass *vdc = VIRTIO_DEVICE_GET_CLASS(vdev); |
49 | ||
ff8eca55 FK |
50 | DPRINTF("%s: plug device.\n", qbus->name); |
51 | ||
ff8eca55 | 52 | if (klass->device_plugged != NULL) { |
e8398045 | 53 | klass->device_plugged(qbus->parent, errp); |
ff8eca55 FK |
54 | } |
55 | ||
6b8f1020 CH |
56 | /* Get the features of the plugged device. */ |
57 | assert(vdc->get_features != NULL); | |
9d5b731d JW |
58 | vdev->host_features = vdc->get_features(vdev, vdev->host_features, |
59 | errp); | |
11380b36 CH |
60 | if (klass->post_plugged != NULL) { |
61 | klass->post_plugged(qbus->parent, errp); | |
62 | } | |
ff8eca55 FK |
63 | } |
64 | ||
65 | /* Reset the virtio_bus */ | |
66 | void virtio_bus_reset(VirtioBusState *bus) | |
67 | { | |
06d3dff0 PB |
68 | VirtIODevice *vdev = virtio_bus_get_device(bus); |
69 | ||
2c80ab15 | 70 | DPRINTF("%s: reset device.\n", BUS(bus)->name); |
06d3dff0 PB |
71 | if (vdev != NULL) { |
72 | virtio_reset(vdev); | |
ff8eca55 FK |
73 | } |
74 | } | |
75 | ||
5e96f5d2 PB |
76 | /* A VirtIODevice is being unplugged */ |
77 | void virtio_bus_device_unplugged(VirtIODevice *vdev) | |
ff8eca55 | 78 | { |
5e96f5d2 PB |
79 | DeviceState *qdev = DEVICE(vdev); |
80 | BusState *qbus = BUS(qdev_get_parent_bus(qdev)); | |
81 | VirtioBusClass *klass = VIRTIO_BUS_GET_CLASS(qbus); | |
06d3dff0 | 82 | |
ff8eca55 FK |
83 | DPRINTF("%s: remove device.\n", qbus->name); |
84 | ||
06d3dff0 | 85 | if (vdev != NULL) { |
5e96f5d2 PB |
86 | if (klass->device_unplugged != NULL) { |
87 | klass->device_unplugged(qbus->parent); | |
ff8eca55 | 88 | } |
ff8eca55 FK |
89 | } |
90 | } | |
91 | ||
92 | /* Get the device id of the plugged device. */ | |
93 | uint16_t virtio_bus_get_vdev_id(VirtioBusState *bus) | |
94 | { | |
06d3dff0 PB |
95 | VirtIODevice *vdev = virtio_bus_get_device(bus); |
96 | assert(vdev != NULL); | |
97 | return vdev->device_id; | |
ff8eca55 FK |
98 | } |
99 | ||
100 | /* Get the config_len field of the plugged device. */ | |
101 | size_t virtio_bus_get_vdev_config_len(VirtioBusState *bus) | |
102 | { | |
06d3dff0 PB |
103 | VirtIODevice *vdev = virtio_bus_get_device(bus); |
104 | assert(vdev != NULL); | |
105 | return vdev->config_len; | |
ff8eca55 FK |
106 | } |
107 | ||
8e05db92 FK |
108 | /* Get bad features of the plugged device. */ |
109 | uint32_t virtio_bus_get_vdev_bad_features(VirtioBusState *bus) | |
110 | { | |
06d3dff0 | 111 | VirtIODevice *vdev = virtio_bus_get_device(bus); |
8e05db92 | 112 | VirtioDeviceClass *k; |
06d3dff0 PB |
113 | |
114 | assert(vdev != NULL); | |
115 | k = VIRTIO_DEVICE_GET_CLASS(vdev); | |
8e05db92 | 116 | if (k->bad_features != NULL) { |
06d3dff0 | 117 | return k->bad_features(vdev); |
8e05db92 FK |
118 | } else { |
119 | return 0; | |
120 | } | |
121 | } | |
122 | ||
123 | /* Get config of the plugged device. */ | |
124 | void virtio_bus_get_vdev_config(VirtioBusState *bus, uint8_t *config) | |
125 | { | |
06d3dff0 | 126 | VirtIODevice *vdev = virtio_bus_get_device(bus); |
8e05db92 | 127 | VirtioDeviceClass *k; |
06d3dff0 PB |
128 | |
129 | assert(vdev != NULL); | |
130 | k = VIRTIO_DEVICE_GET_CLASS(vdev); | |
8e05db92 | 131 | if (k->get_config != NULL) { |
06d3dff0 | 132 | k->get_config(vdev, config); |
8e05db92 FK |
133 | } |
134 | } | |
135 | ||
5d448f9d FK |
136 | /* Set config of the plugged device. */ |
137 | void virtio_bus_set_vdev_config(VirtioBusState *bus, uint8_t *config) | |
138 | { | |
06d3dff0 | 139 | VirtIODevice *vdev = virtio_bus_get_device(bus); |
5d448f9d | 140 | VirtioDeviceClass *k; |
06d3dff0 PB |
141 | |
142 | assert(vdev != NULL); | |
143 | k = VIRTIO_DEVICE_GET_CLASS(vdev); | |
5d448f9d | 144 | if (k->set_config != NULL) { |
06d3dff0 | 145 | k->set_config(vdev, config); |
5d448f9d FK |
146 | } |
147 | } | |
148 | ||
6798e245 CH |
149 | /* |
150 | * This function handles both assigning the ioeventfd handler and | |
151 | * registering it with the kernel. | |
152 | * assign: register/deregister ioeventfd with the kernel | |
153 | * set_handler: use the generic ioeventfd handler | |
154 | */ | |
155 | static int set_host_notifier_internal(DeviceState *proxy, VirtioBusState *bus, | |
156 | int n, bool assign, bool set_handler) | |
157 | { | |
158 | VirtIODevice *vdev = virtio_bus_get_device(bus); | |
159 | VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(bus); | |
160 | VirtQueue *vq = virtio_get_queue(vdev, n); | |
161 | EventNotifier *notifier = virtio_queue_get_host_notifier(vq); | |
162 | int r = 0; | |
163 | ||
164 | if (assign) { | |
165 | r = event_notifier_init(notifier, 1); | |
166 | if (r < 0) { | |
167 | error_report("%s: unable to init event notifier: %d", __func__, r); | |
168 | return r; | |
169 | } | |
170 | virtio_queue_set_host_notifier_fd_handler(vq, true, set_handler); | |
171 | r = k->ioeventfd_assign(proxy, notifier, n, assign); | |
172 | if (r < 0) { | |
173 | error_report("%s: unable to assign ioeventfd: %d", __func__, r); | |
174 | virtio_queue_set_host_notifier_fd_handler(vq, false, false); | |
175 | event_notifier_cleanup(notifier); | |
176 | return r; | |
177 | } | |
178 | } else { | |
6798e245 | 179 | k->ioeventfd_assign(proxy, notifier, n, assign); |
0830c96d | 180 | virtio_queue_set_host_notifier_fd_handler(vq, false, false); |
6798e245 CH |
181 | event_notifier_cleanup(notifier); |
182 | } | |
183 | return r; | |
184 | } | |
185 | ||
186 | void virtio_bus_start_ioeventfd(VirtioBusState *bus) | |
187 | { | |
188 | VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(bus); | |
189 | DeviceState *proxy = DEVICE(BUS(bus)->parent); | |
190 | VirtIODevice *vdev; | |
191 | int n, r; | |
192 | ||
193 | if (!k->ioeventfd_started || k->ioeventfd_started(proxy)) { | |
194 | return; | |
195 | } | |
196 | if (k->ioeventfd_disabled(proxy)) { | |
197 | return; | |
198 | } | |
199 | vdev = virtio_bus_get_device(bus); | |
200 | for (n = 0; n < VIRTIO_QUEUE_MAX; n++) { | |
201 | if (!virtio_queue_get_num(vdev, n)) { | |
202 | continue; | |
203 | } | |
204 | r = set_host_notifier_internal(proxy, bus, n, true, true); | |
205 | if (r < 0) { | |
206 | goto assign_error; | |
207 | } | |
208 | } | |
209 | k->ioeventfd_set_started(proxy, true, false); | |
210 | return; | |
211 | ||
212 | assign_error: | |
213 | while (--n >= 0) { | |
214 | if (!virtio_queue_get_num(vdev, n)) { | |
215 | continue; | |
216 | } | |
217 | ||
218 | r = set_host_notifier_internal(proxy, bus, n, false, false); | |
219 | assert(r >= 0); | |
220 | } | |
221 | k->ioeventfd_set_started(proxy, false, true); | |
222 | error_report("%s: failed. Fallback to userspace (slower).", __func__); | |
223 | } | |
224 | ||
225 | void virtio_bus_stop_ioeventfd(VirtioBusState *bus) | |
226 | { | |
227 | VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(bus); | |
228 | DeviceState *proxy = DEVICE(BUS(bus)->parent); | |
229 | VirtIODevice *vdev; | |
230 | int n, r; | |
231 | ||
232 | if (!k->ioeventfd_started || !k->ioeventfd_started(proxy)) { | |
233 | return; | |
234 | } | |
235 | vdev = virtio_bus_get_device(bus); | |
236 | for (n = 0; n < VIRTIO_QUEUE_MAX; n++) { | |
237 | if (!virtio_queue_get_num(vdev, n)) { | |
238 | continue; | |
239 | } | |
240 | r = set_host_notifier_internal(proxy, bus, n, false, false); | |
241 | assert(r >= 0); | |
242 | } | |
243 | k->ioeventfd_set_started(proxy, false, false); | |
244 | } | |
245 | ||
246 | /* | |
247 | * This function switches from/to the generic ioeventfd handler. | |
248 | * assign==false means 'use generic ioeventfd handler'. | |
249 | */ | |
250 | int virtio_bus_set_host_notifier(VirtioBusState *bus, int n, bool assign) | |
251 | { | |
252 | VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(bus); | |
253 | DeviceState *proxy = DEVICE(BUS(bus)->parent); | |
6798e245 CH |
254 | |
255 | if (!k->ioeventfd_started) { | |
256 | return -ENOSYS; | |
257 | } | |
0830c96d | 258 | k->ioeventfd_set_disabled(proxy, assign); |
6798e245 CH |
259 | if (assign) { |
260 | /* | |
261 | * Stop using the generic ioeventfd, we are doing eventfd handling | |
262 | * ourselves below | |
0830c96d CH |
263 | * |
264 | * FIXME: We should just switch the handler and not deassign the | |
265 | * ioeventfd. | |
266 | * Otherwise, there's a window where we don't have an | |
267 | * ioeventfd and we may end up with a notification where | |
268 | * we don't expect one. | |
6798e245 | 269 | */ |
0830c96d | 270 | virtio_bus_stop_ioeventfd(bus); |
6798e245 | 271 | } |
0830c96d | 272 | return set_host_notifier_internal(proxy, bus, n, assign, false); |
6798e245 CH |
273 | } |
274 | ||
6d46895b FK |
275 | static char *virtio_bus_get_dev_path(DeviceState *dev) |
276 | { | |
277 | BusState *bus = qdev_get_parent_bus(dev); | |
278 | DeviceState *proxy = DEVICE(bus->parent); | |
279 | return qdev_get_dev_path(proxy); | |
280 | } | |
281 | ||
bbfa18fc AK |
282 | static char *virtio_bus_get_fw_dev_path(DeviceState *dev) |
283 | { | |
284 | return NULL; | |
285 | } | |
286 | ||
6d46895b FK |
287 | static void virtio_bus_class_init(ObjectClass *klass, void *data) |
288 | { | |
289 | BusClass *bus_class = BUS_CLASS(klass); | |
290 | bus_class->get_dev_path = virtio_bus_get_dev_path; | |
bbfa18fc | 291 | bus_class->get_fw_dev_path = virtio_bus_get_fw_dev_path; |
6d46895b FK |
292 | } |
293 | ||
ff8eca55 FK |
294 | static const TypeInfo virtio_bus_info = { |
295 | .name = TYPE_VIRTIO_BUS, | |
296 | .parent = TYPE_BUS, | |
297 | .instance_size = sizeof(VirtioBusState), | |
298 | .abstract = true, | |
299 | .class_size = sizeof(VirtioBusClass), | |
6d46895b | 300 | .class_init = virtio_bus_class_init |
ff8eca55 FK |
301 | }; |
302 | ||
303 | static void virtio_register_types(void) | |
304 | { | |
305 | type_register_static(&virtio_bus_info); | |
306 | } | |
307 | ||
308 | type_init(virtio_register_types) |