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