xref: /qemu/hw/virtio/vhost-user-rng.c (revision 821d28b88f850e5fbc66ee62bdd155eb2d474a29)
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