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