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 = virtio_device_started(vdev, status); 94 95 if (vhost_dev_is_started(&rng->vhost_dev) == should_start) { 96 return; 97 } 98 99 if (should_start) { 100 vu_rng_start(vdev); 101 } else { 102 vu_rng_stop(vdev); 103 } 104 } 105 106 static uint64_t vu_rng_get_features(VirtIODevice *vdev, 107 uint64_t requested_features, Error **errp) 108 { 109 /* No feature bits used yet */ 110 return requested_features; 111 } 112 113 static void vu_rng_handle_output(VirtIODevice *vdev, VirtQueue *vq) 114 { 115 /* 116 * Not normally called; it's the daemon that handles the queue; 117 * however virtio's cleanup path can call this. 118 */ 119 } 120 121 static void vu_rng_guest_notifier_mask(VirtIODevice *vdev, int idx, bool mask) 122 { 123 VHostUserRNG *rng = VHOST_USER_RNG(vdev); 124 125 vhost_virtqueue_mask(&rng->vhost_dev, vdev, idx, mask); 126 } 127 128 static bool vu_rng_guest_notifier_pending(VirtIODevice *vdev, int idx) 129 { 130 VHostUserRNG *rng = VHOST_USER_RNG(vdev); 131 132 return vhost_virtqueue_pending(&rng->vhost_dev, idx); 133 } 134 135 static void vu_rng_connect(DeviceState *dev) 136 { 137 VirtIODevice *vdev = VIRTIO_DEVICE(dev); 138 VHostUserRNG *rng = VHOST_USER_RNG(vdev); 139 140 if (rng->connected) { 141 return; 142 } 143 144 rng->connected = true; 145 146 /* restore vhost state */ 147 if (virtio_device_started(vdev, vdev->status)) { 148 vu_rng_start(vdev); 149 } 150 } 151 152 static void vu_rng_disconnect(DeviceState *dev) 153 { 154 VirtIODevice *vdev = VIRTIO_DEVICE(dev); 155 VHostUserRNG *rng = VHOST_USER_RNG(vdev); 156 157 if (!rng->connected) { 158 return; 159 } 160 161 rng->connected = false; 162 163 if (vhost_dev_is_started(&rng->vhost_dev)) { 164 vu_rng_stop(vdev); 165 } 166 } 167 168 static void vu_rng_event(void *opaque, QEMUChrEvent event) 169 { 170 DeviceState *dev = opaque; 171 172 switch (event) { 173 case CHR_EVENT_OPENED: 174 vu_rng_connect(dev); 175 break; 176 case CHR_EVENT_CLOSED: 177 vu_rng_disconnect(dev); 178 break; 179 case CHR_EVENT_BREAK: 180 case CHR_EVENT_MUX_IN: 181 case CHR_EVENT_MUX_OUT: 182 /* Ignore */ 183 break; 184 } 185 } 186 187 static void vu_rng_device_realize(DeviceState *dev, Error **errp) 188 { 189 VirtIODevice *vdev = VIRTIO_DEVICE(dev); 190 VHostUserRNG *rng = VHOST_USER_RNG(dev); 191 int ret; 192 193 if (!rng->chardev.chr) { 194 error_setg(errp, "missing chardev"); 195 return; 196 } 197 198 if (!vhost_user_init(&rng->vhost_user, &rng->chardev, errp)) { 199 return; 200 } 201 202 virtio_init(vdev, VIRTIO_ID_RNG, 0); 203 204 rng->req_vq = virtio_add_queue(vdev, 4, vu_rng_handle_output); 205 if (!rng->req_vq) { 206 error_setg_errno(errp, -1, "virtio_add_queue() failed"); 207 goto virtio_add_queue_failed; 208 } 209 210 rng->vhost_dev.nvqs = 1; 211 rng->vhost_dev.vqs = g_new0(struct vhost_virtqueue, rng->vhost_dev.nvqs); 212 ret = vhost_dev_init(&rng->vhost_dev, &rng->vhost_user, 213 VHOST_BACKEND_TYPE_USER, 0, errp); 214 if (ret < 0) { 215 error_setg_errno(errp, -ret, "vhost_dev_init() failed"); 216 goto vhost_dev_init_failed; 217 } 218 219 qemu_chr_fe_set_handlers(&rng->chardev, NULL, NULL, vu_rng_event, NULL, 220 dev, NULL, true); 221 222 return; 223 224 vhost_dev_init_failed: 225 virtio_delete_queue(rng->req_vq); 226 virtio_add_queue_failed: 227 virtio_cleanup(vdev); 228 vhost_user_cleanup(&rng->vhost_user); 229 } 230 231 static void vu_rng_device_unrealize(DeviceState *dev) 232 { 233 VirtIODevice *vdev = VIRTIO_DEVICE(dev); 234 VHostUserRNG *rng = VHOST_USER_RNG(dev); 235 236 vu_rng_set_status(vdev, 0); 237 238 vhost_dev_cleanup(&rng->vhost_dev); 239 g_free(rng->vhost_dev.vqs); 240 rng->vhost_dev.vqs = NULL; 241 virtio_delete_queue(rng->req_vq); 242 virtio_cleanup(vdev); 243 vhost_user_cleanup(&rng->vhost_user); 244 } 245 246 static struct vhost_dev *vu_rng_get_vhost(VirtIODevice *vdev) 247 { 248 VHostUserRNG *rng = VHOST_USER_RNG(vdev); 249 return &rng->vhost_dev; 250 } 251 252 static const VMStateDescription vu_rng_vmstate = { 253 .name = "vhost-user-rng", 254 .unmigratable = 1, 255 }; 256 257 static Property vu_rng_properties[] = { 258 DEFINE_PROP_CHR("chardev", VHostUserRNG, chardev), 259 DEFINE_PROP_END_OF_LIST(), 260 }; 261 262 static void vu_rng_class_init(ObjectClass *klass, void *data) 263 { 264 DeviceClass *dc = DEVICE_CLASS(klass); 265 VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass); 266 267 device_class_set_props(dc, vu_rng_properties); 268 dc->vmsd = &vu_rng_vmstate; 269 set_bit(DEVICE_CATEGORY_INPUT, dc->categories); 270 271 vdc->realize = vu_rng_device_realize; 272 vdc->unrealize = vu_rng_device_unrealize; 273 vdc->get_features = vu_rng_get_features; 274 vdc->set_status = vu_rng_set_status; 275 vdc->guest_notifier_mask = vu_rng_guest_notifier_mask; 276 vdc->guest_notifier_pending = vu_rng_guest_notifier_pending; 277 vdc->get_vhost = vu_rng_get_vhost; 278 } 279 280 static const TypeInfo vu_rng_info = { 281 .name = TYPE_VHOST_USER_RNG, 282 .parent = TYPE_VIRTIO_DEVICE, 283 .instance_size = sizeof(VHostUserRNG), 284 .class_init = vu_rng_class_init, 285 }; 286 287 static void vu_rng_register_types(void) 288 { 289 type_register_static(&vu_rng_info); 290 } 291 292 type_init(vu_rng_register_types) 293