1 /* 2 * Vhost-user RNG virtio device 3 * 4 * Copyright (c) 2021 Mathieu Poirier <mathieu.poirier@linaro.org> 5 * 6 * Implementation seriously tailored on vhost-user-i2c.c 7 * 8 * SPDX-License-Identifier: GPL-2.0-or-later 9 */ 10 11 #include "qemu/osdep.h" 12 #include "qapi/error.h" 13 #include "hw/qdev-properties.h" 14 #include "hw/virtio/virtio-bus.h" 15 #include "hw/virtio/vhost-user-rng.h" 16 #include "qemu/error-report.h" 17 #include "standard-headers/linux/virtio_ids.h" 18 19 static void vu_rng_start(VirtIODevice *vdev) 20 { 21 VHostUserRNG *rng = VHOST_USER_RNG(vdev); 22 BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(vdev))); 23 VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus); 24 int ret; 25 int i; 26 27 if (!k->set_guest_notifiers) { 28 error_report("binding does not support guest notifiers"); 29 return; 30 } 31 32 ret = vhost_dev_enable_notifiers(&rng->vhost_dev, vdev); 33 if (ret < 0) { 34 error_report("Error enabling host notifiers: %d", -ret); 35 return; 36 } 37 38 ret = k->set_guest_notifiers(qbus->parent, rng->vhost_dev.nvqs, true); 39 if (ret < 0) { 40 error_report("Error binding guest notifier: %d", -ret); 41 goto err_host_notifiers; 42 } 43 44 rng->vhost_dev.acked_features = vdev->guest_features; 45 ret = vhost_dev_start(&rng->vhost_dev, vdev); 46 if (ret < 0) { 47 error_report("Error starting vhost-user-rng: %d", -ret); 48 goto err_guest_notifiers; 49 } 50 51 /* 52 * guest_notifier_mask/pending not used yet, so just unmask 53 * everything here. virtio-pci will do the right thing by 54 * enabling/disabling irqfd. 55 */ 56 for (i = 0; i < rng->vhost_dev.nvqs; i++) { 57 vhost_virtqueue_mask(&rng->vhost_dev, vdev, i, false); 58 } 59 60 return; 61 62 err_guest_notifiers: 63 k->set_guest_notifiers(qbus->parent, rng->vhost_dev.nvqs, false); 64 err_host_notifiers: 65 vhost_dev_disable_notifiers(&rng->vhost_dev, vdev); 66 } 67 68 static void vu_rng_stop(VirtIODevice *vdev) 69 { 70 VHostUserRNG *rng = VHOST_USER_RNG(vdev); 71 BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(vdev))); 72 VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus); 73 int ret; 74 75 if (!k->set_guest_notifiers) { 76 return; 77 } 78 79 vhost_dev_stop(&rng->vhost_dev, vdev); 80 81 ret = k->set_guest_notifiers(qbus->parent, rng->vhost_dev.nvqs, false); 82 if (ret < 0) { 83 error_report("vhost guest notifier cleanup failed: %d", ret); 84 return; 85 } 86 87 vhost_dev_disable_notifiers(&rng->vhost_dev, vdev); 88 } 89 90 static void vu_rng_set_status(VirtIODevice *vdev, uint8_t status) 91 { 92 VHostUserRNG *rng = VHOST_USER_RNG(vdev); 93 bool should_start = status & VIRTIO_CONFIG_S_DRIVER_OK; 94 95 if (!vdev->vm_running) { 96 should_start = false; 97 } 98 99 if (rng->vhost_dev.started == should_start) { 100 return; 101 } 102 103 if (should_start) { 104 vu_rng_start(vdev); 105 } else { 106 vu_rng_stop(vdev); 107 } 108 } 109 110 static uint64_t vu_rng_get_features(VirtIODevice *vdev, 111 uint64_t requested_features, Error **errp) 112 { 113 /* No feature bits used yet */ 114 return requested_features; 115 } 116 117 static void vu_rng_handle_output(VirtIODevice *vdev, VirtQueue *vq) 118 { 119 /* 120 * Not normally called; it's the daemon that handles the queue; 121 * however virtio's cleanup path can call this. 122 */ 123 } 124 125 static void vu_rng_guest_notifier_mask(VirtIODevice *vdev, int idx, bool mask) 126 { 127 VHostUserRNG *rng = VHOST_USER_RNG(vdev); 128 129 vhost_virtqueue_mask(&rng->vhost_dev, vdev, idx, mask); 130 } 131 132 static bool vu_rng_guest_notifier_pending(VirtIODevice *vdev, int idx) 133 { 134 VHostUserRNG *rng = VHOST_USER_RNG(vdev); 135 136 return vhost_virtqueue_pending(&rng->vhost_dev, idx); 137 } 138 139 static void vu_rng_connect(DeviceState *dev) 140 { 141 VirtIODevice *vdev = VIRTIO_DEVICE(dev); 142 VHostUserRNG *rng = VHOST_USER_RNG(vdev); 143 144 if (rng->connected) { 145 return; 146 } 147 148 rng->connected = true; 149 150 /* restore vhost state */ 151 if (virtio_device_started(vdev, vdev->status)) { 152 vu_rng_start(vdev); 153 } 154 } 155 156 static void vu_rng_disconnect(DeviceState *dev) 157 { 158 VirtIODevice *vdev = VIRTIO_DEVICE(dev); 159 VHostUserRNG *rng = VHOST_USER_RNG(vdev); 160 161 if (!rng->connected) { 162 return; 163 } 164 165 rng->connected = false; 166 167 if (rng->vhost_dev.started) { 168 vu_rng_stop(vdev); 169 } 170 } 171 172 static void vu_rng_event(void *opaque, QEMUChrEvent event) 173 { 174 DeviceState *dev = opaque; 175 176 switch (event) { 177 case CHR_EVENT_OPENED: 178 vu_rng_connect(dev); 179 break; 180 case CHR_EVENT_CLOSED: 181 vu_rng_disconnect(dev); 182 break; 183 case CHR_EVENT_BREAK: 184 case CHR_EVENT_MUX_IN: 185 case CHR_EVENT_MUX_OUT: 186 /* Ignore */ 187 break; 188 } 189 } 190 191 static void vu_rng_device_realize(DeviceState *dev, Error **errp) 192 { 193 VirtIODevice *vdev = VIRTIO_DEVICE(dev); 194 VHostUserRNG *rng = VHOST_USER_RNG(dev); 195 int ret; 196 197 if (!rng->chardev.chr) { 198 error_setg(errp, "missing chardev"); 199 return; 200 } 201 202 if (!vhost_user_init(&rng->vhost_user, &rng->chardev, errp)) { 203 return; 204 } 205 206 virtio_init(vdev, "vhost-user-rng", VIRTIO_ID_RNG, 0); 207 208 rng->req_vq = virtio_add_queue(vdev, 4, vu_rng_handle_output); 209 if (!rng->req_vq) { 210 error_setg_errno(errp, -1, "virtio_add_queue() failed"); 211 goto virtio_add_queue_failed; 212 } 213 214 rng->vhost_dev.nvqs = 1; 215 rng->vhost_dev.vqs = g_new0(struct vhost_virtqueue, rng->vhost_dev.nvqs); 216 ret = vhost_dev_init(&rng->vhost_dev, &rng->vhost_user, 217 VHOST_BACKEND_TYPE_USER, 0, errp); 218 if (ret < 0) { 219 error_setg_errno(errp, -ret, "vhost_dev_init() failed"); 220 goto vhost_dev_init_failed; 221 } 222 223 qemu_chr_fe_set_handlers(&rng->chardev, NULL, NULL, vu_rng_event, NULL, 224 dev, NULL, true); 225 226 return; 227 228 vhost_dev_init_failed: 229 virtio_delete_queue(rng->req_vq); 230 virtio_add_queue_failed: 231 virtio_cleanup(vdev); 232 vhost_user_cleanup(&rng->vhost_user); 233 } 234 235 static void vu_rng_device_unrealize(DeviceState *dev) 236 { 237 VirtIODevice *vdev = VIRTIO_DEVICE(dev); 238 VHostUserRNG *rng = VHOST_USER_RNG(dev); 239 240 vu_rng_set_status(vdev, 0); 241 242 vhost_dev_cleanup(&rng->vhost_dev); 243 g_free(rng->vhost_dev.vqs); 244 rng->vhost_dev.vqs = NULL; 245 virtio_delete_queue(rng->req_vq); 246 virtio_cleanup(vdev); 247 vhost_user_cleanup(&rng->vhost_user); 248 } 249 250 static const VMStateDescription vu_rng_vmstate = { 251 .name = "vhost-user-rng", 252 .unmigratable = 1, 253 }; 254 255 static Property vu_rng_properties[] = { 256 DEFINE_PROP_CHR("chardev", VHostUserRNG, chardev), 257 DEFINE_PROP_END_OF_LIST(), 258 }; 259 260 static void vu_rng_class_init(ObjectClass *klass, void *data) 261 { 262 DeviceClass *dc = DEVICE_CLASS(klass); 263 VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass); 264 265 device_class_set_props(dc, vu_rng_properties); 266 dc->vmsd = &vu_rng_vmstate; 267 set_bit(DEVICE_CATEGORY_INPUT, dc->categories); 268 269 vdc->realize = vu_rng_device_realize; 270 vdc->unrealize = vu_rng_device_unrealize; 271 vdc->get_features = vu_rng_get_features; 272 vdc->set_status = vu_rng_set_status; 273 vdc->guest_notifier_mask = vu_rng_guest_notifier_mask; 274 vdc->guest_notifier_pending = vu_rng_guest_notifier_pending; 275 } 276 277 static const TypeInfo vu_rng_info = { 278 .name = TYPE_VHOST_USER_RNG, 279 .parent = TYPE_VIRTIO_DEVICE, 280 .instance_size = sizeof(VHostUserRNG), 281 .class_init = vu_rng_class_init, 282 }; 283 284 static void vu_rng_register_types(void) 285 { 286 type_register_static(&vu_rng_info); 287 } 288 289 type_init(vu_rng_register_types) 290