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