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 25 #include "qemu/osdep.h" 26 #include "qemu/error-report.h" 27 #include "qemu/module.h" 28 #include "qapi/error.h" 29 #include "hw/virtio/virtio-bus.h" 30 #include "hw/virtio/virtio.h" 31 #include "exec/address-spaces.h" 32 33 /* #define DEBUG_VIRTIO_BUS */ 34 35 #ifdef DEBUG_VIRTIO_BUS 36 #define DPRINTF(fmt, ...) \ 37 do { printf("virtio_bus: " fmt , ## __VA_ARGS__); } while (0) 38 #else 39 #define DPRINTF(fmt, ...) do { } while (0) 40 #endif 41 42 /* A VirtIODevice is being plugged */ 43 void virtio_bus_device_plugged(VirtIODevice *vdev, Error **errp) 44 { 45 DeviceState *qdev = DEVICE(vdev); 46 BusState *qbus = BUS(qdev_get_parent_bus(qdev)); 47 VirtioBusState *bus = VIRTIO_BUS(qbus); 48 VirtioBusClass *klass = VIRTIO_BUS_GET_CLASS(bus); 49 VirtioDeviceClass *vdc = VIRTIO_DEVICE_GET_CLASS(vdev); 50 bool has_iommu = virtio_host_has_feature(vdev, VIRTIO_F_IOMMU_PLATFORM); 51 Error *local_err = NULL; 52 53 DPRINTF("%s: plug device.\n", qbus->name); 54 55 if (klass->pre_plugged != NULL) { 56 klass->pre_plugged(qbus->parent, &local_err); 57 if (local_err) { 58 error_propagate(errp, local_err); 59 return; 60 } 61 } 62 63 /* Get the features of the plugged device. */ 64 assert(vdc->get_features != NULL); 65 vdev->host_features = vdc->get_features(vdev, vdev->host_features, 66 &local_err); 67 if (local_err) { 68 error_propagate(errp, local_err); 69 return; 70 } 71 72 if (klass->device_plugged != NULL) { 73 klass->device_plugged(qbus->parent, &local_err); 74 } 75 if (local_err) { 76 error_propagate(errp, local_err); 77 return; 78 } 79 80 if (klass->get_dma_as != NULL && has_iommu) { 81 virtio_add_feature(&vdev->host_features, VIRTIO_F_IOMMU_PLATFORM); 82 vdev->dma_as = klass->get_dma_as(qbus->parent); 83 } else { 84 vdev->dma_as = &address_space_memory; 85 } 86 } 87 88 /* Reset the virtio_bus */ 89 void virtio_bus_reset(VirtioBusState *bus) 90 { 91 VirtIODevice *vdev = virtio_bus_get_device(bus); 92 93 DPRINTF("%s: reset device.\n", BUS(bus)->name); 94 if (vdev != NULL) { 95 virtio_reset(vdev); 96 } 97 } 98 99 /* A VirtIODevice is being unplugged */ 100 void virtio_bus_device_unplugged(VirtIODevice *vdev) 101 { 102 DeviceState *qdev = DEVICE(vdev); 103 BusState *qbus = BUS(qdev_get_parent_bus(qdev)); 104 VirtioBusClass *klass = VIRTIO_BUS_GET_CLASS(qbus); 105 106 DPRINTF("%s: remove device.\n", qbus->name); 107 108 if (vdev != NULL) { 109 if (klass->device_unplugged != NULL) { 110 klass->device_unplugged(qbus->parent); 111 } 112 } 113 } 114 115 /* Get the device id of the plugged device. */ 116 uint16_t virtio_bus_get_vdev_id(VirtioBusState *bus) 117 { 118 VirtIODevice *vdev = virtio_bus_get_device(bus); 119 assert(vdev != NULL); 120 return vdev->device_id; 121 } 122 123 /* Get the config_len field of the plugged device. */ 124 size_t virtio_bus_get_vdev_config_len(VirtioBusState *bus) 125 { 126 VirtIODevice *vdev = virtio_bus_get_device(bus); 127 assert(vdev != NULL); 128 return vdev->config_len; 129 } 130 131 /* Get bad features of the plugged device. */ 132 uint32_t virtio_bus_get_vdev_bad_features(VirtioBusState *bus) 133 { 134 VirtIODevice *vdev = virtio_bus_get_device(bus); 135 VirtioDeviceClass *k; 136 137 assert(vdev != NULL); 138 k = VIRTIO_DEVICE_GET_CLASS(vdev); 139 if (k->bad_features != NULL) { 140 return k->bad_features(vdev); 141 } else { 142 return 0; 143 } 144 } 145 146 /* Get config of the plugged device. */ 147 void virtio_bus_get_vdev_config(VirtioBusState *bus, uint8_t *config) 148 { 149 VirtIODevice *vdev = virtio_bus_get_device(bus); 150 VirtioDeviceClass *k; 151 152 assert(vdev != NULL); 153 k = VIRTIO_DEVICE_GET_CLASS(vdev); 154 if (k->get_config != NULL) { 155 k->get_config(vdev, config); 156 } 157 } 158 159 /* Set config of the plugged device. */ 160 void virtio_bus_set_vdev_config(VirtioBusState *bus, uint8_t *config) 161 { 162 VirtIODevice *vdev = virtio_bus_get_device(bus); 163 VirtioDeviceClass *k; 164 165 assert(vdev != NULL); 166 k = VIRTIO_DEVICE_GET_CLASS(vdev); 167 if (k->set_config != NULL) { 168 k->set_config(vdev, config); 169 } 170 } 171 172 /* On success, ioeventfd ownership belongs to the caller. */ 173 int virtio_bus_grab_ioeventfd(VirtioBusState *bus) 174 { 175 VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(bus); 176 177 /* vhost can be used even if ioeventfd=off in the proxy device, 178 * so do not check k->ioeventfd_enabled. 179 */ 180 if (!k->ioeventfd_assign) { 181 return -ENOSYS; 182 } 183 184 if (bus->ioeventfd_grabbed == 0 && bus->ioeventfd_started) { 185 virtio_bus_stop_ioeventfd(bus); 186 /* Remember that we need to restart ioeventfd 187 * when ioeventfd_grabbed becomes zero. 188 */ 189 bus->ioeventfd_started = true; 190 } 191 bus->ioeventfd_grabbed++; 192 return 0; 193 } 194 195 void virtio_bus_release_ioeventfd(VirtioBusState *bus) 196 { 197 assert(bus->ioeventfd_grabbed != 0); 198 if (--bus->ioeventfd_grabbed == 0 && bus->ioeventfd_started) { 199 /* Force virtio_bus_start_ioeventfd to act. */ 200 bus->ioeventfd_started = false; 201 virtio_bus_start_ioeventfd(bus); 202 } 203 } 204 205 int virtio_bus_start_ioeventfd(VirtioBusState *bus) 206 { 207 VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(bus); 208 DeviceState *proxy = DEVICE(BUS(bus)->parent); 209 VirtIODevice *vdev = virtio_bus_get_device(bus); 210 VirtioDeviceClass *vdc = VIRTIO_DEVICE_GET_CLASS(vdev); 211 int r; 212 213 if (!k->ioeventfd_assign || !k->ioeventfd_enabled(proxy)) { 214 return -ENOSYS; 215 } 216 if (bus->ioeventfd_started) { 217 return 0; 218 } 219 220 /* Only set our notifier if we have ownership. */ 221 if (!bus->ioeventfd_grabbed) { 222 r = vdc->start_ioeventfd(vdev); 223 if (r < 0) { 224 error_report("%s: failed. Fallback to userspace (slower).", __func__); 225 return r; 226 } 227 } 228 bus->ioeventfd_started = true; 229 return 0; 230 } 231 232 void virtio_bus_stop_ioeventfd(VirtioBusState *bus) 233 { 234 VirtIODevice *vdev; 235 VirtioDeviceClass *vdc; 236 237 if (!bus->ioeventfd_started) { 238 return; 239 } 240 241 /* Only remove our notifier if we have ownership. */ 242 if (!bus->ioeventfd_grabbed) { 243 vdev = virtio_bus_get_device(bus); 244 vdc = VIRTIO_DEVICE_GET_CLASS(vdev); 245 vdc->stop_ioeventfd(vdev); 246 } 247 bus->ioeventfd_started = false; 248 } 249 250 bool virtio_bus_ioeventfd_enabled(VirtioBusState *bus) 251 { 252 VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(bus); 253 DeviceState *proxy = DEVICE(BUS(bus)->parent); 254 255 return k->ioeventfd_assign && k->ioeventfd_enabled(proxy); 256 } 257 258 /* 259 * This function switches ioeventfd on/off in the device. 260 * The caller must set or clear the handlers for the EventNotifier. 261 */ 262 int virtio_bus_set_host_notifier(VirtioBusState *bus, int n, bool assign) 263 { 264 VirtIODevice *vdev = virtio_bus_get_device(bus); 265 VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(bus); 266 DeviceState *proxy = DEVICE(BUS(bus)->parent); 267 VirtQueue *vq = virtio_get_queue(vdev, n); 268 EventNotifier *notifier = virtio_queue_get_host_notifier(vq); 269 int r = 0; 270 271 if (!k->ioeventfd_assign) { 272 return -ENOSYS; 273 } 274 275 if (assign) { 276 r = event_notifier_init(notifier, 1); 277 if (r < 0) { 278 error_report("%s: unable to init event notifier: %s (%d)", 279 __func__, strerror(-r), r); 280 return r; 281 } 282 r = k->ioeventfd_assign(proxy, notifier, n, true); 283 if (r < 0) { 284 error_report("%s: unable to assign ioeventfd: %d", __func__, r); 285 virtio_bus_cleanup_host_notifier(bus, n); 286 } 287 } else { 288 k->ioeventfd_assign(proxy, notifier, n, false); 289 } 290 291 if (r == 0) { 292 virtio_queue_set_host_notifier_enabled(vq, assign); 293 } 294 295 return r; 296 } 297 298 void virtio_bus_cleanup_host_notifier(VirtioBusState *bus, int n) 299 { 300 VirtIODevice *vdev = virtio_bus_get_device(bus); 301 VirtQueue *vq = virtio_get_queue(vdev, n); 302 EventNotifier *notifier = virtio_queue_get_host_notifier(vq); 303 304 /* Test and clear notifier after disabling event, 305 * in case poll callback didn't have time to run. 306 */ 307 virtio_queue_host_notifier_read(notifier); 308 event_notifier_cleanup(notifier); 309 } 310 311 static char *virtio_bus_get_dev_path(DeviceState *dev) 312 { 313 BusState *bus = qdev_get_parent_bus(dev); 314 DeviceState *proxy = DEVICE(bus->parent); 315 return qdev_get_dev_path(proxy); 316 } 317 318 static char *virtio_bus_get_fw_dev_path(DeviceState *dev) 319 { 320 return NULL; 321 } 322 323 static void virtio_bus_class_init(ObjectClass *klass, void *data) 324 { 325 BusClass *bus_class = BUS_CLASS(klass); 326 bus_class->get_dev_path = virtio_bus_get_dev_path; 327 bus_class->get_fw_dev_path = virtio_bus_get_fw_dev_path; 328 } 329 330 static const TypeInfo virtio_bus_info = { 331 .name = TYPE_VIRTIO_BUS, 332 .parent = TYPE_BUS, 333 .instance_size = sizeof(VirtioBusState), 334 .abstract = true, 335 .class_size = sizeof(VirtioBusClass), 336 .class_init = virtio_bus_class_init 337 }; 338 339 static void virtio_register_types(void) 340 { 341 type_register_static(&virtio_bus_info); 342 } 343 344 type_init(virtio_register_types) 345