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