xref: /openbmc/qemu/hw/virtio/vhost-user-rng.c (revision a495eba0)
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, 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 struct vhost_dev *vu_rng_get_vhost(VirtIODevice *vdev)
251 {
252     VHostUserRNG *rng = VHOST_USER_RNG(vdev);
253     return &rng->vhost_dev;
254 }
255 
256 static const VMStateDescription vu_rng_vmstate = {
257     .name = "vhost-user-rng",
258     .unmigratable = 1,
259 };
260 
261 static Property vu_rng_properties[] = {
262     DEFINE_PROP_CHR("chardev", VHostUserRNG, chardev),
263     DEFINE_PROP_END_OF_LIST(),
264 };
265 
266 static void vu_rng_class_init(ObjectClass *klass, void *data)
267 {
268     DeviceClass *dc = DEVICE_CLASS(klass);
269     VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass);
270 
271     device_class_set_props(dc, vu_rng_properties);
272     dc->vmsd = &vu_rng_vmstate;
273     set_bit(DEVICE_CATEGORY_INPUT, dc->categories);
274 
275     vdc->realize = vu_rng_device_realize;
276     vdc->unrealize = vu_rng_device_unrealize;
277     vdc->get_features = vu_rng_get_features;
278     vdc->set_status = vu_rng_set_status;
279     vdc->guest_notifier_mask = vu_rng_guest_notifier_mask;
280     vdc->guest_notifier_pending = vu_rng_guest_notifier_pending;
281     vdc->get_vhost = vu_rng_get_vhost;
282 }
283 
284 static const TypeInfo vu_rng_info = {
285     .name = TYPE_VHOST_USER_RNG,
286     .parent = TYPE_VIRTIO_DEVICE,
287     .instance_size = sizeof(VHostUserRNG),
288     .class_init = vu_rng_class_init,
289 };
290 
291 static void vu_rng_register_types(void)
292 {
293     type_register_static(&vu_rng_info);
294 }
295 
296 type_init(vu_rng_register_types)
297