2 * Vhost-user RNG virtio device
4 * Copyright (c) 2021 Mathieu Poirier <mathieu.poirier@linaro.org>
6 * Implementation seriously tailored on vhost-user-i2c.c
8 * SPDX-License-Identifier: GPL-2.0-or-later
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"
19 static void vu_rng_start(VirtIODevice
*vdev
)
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
);
27 if (!k
->set_guest_notifiers
) {
28 error_report("binding does not support guest notifiers");
32 ret
= vhost_dev_enable_notifiers(&rng
->vhost_dev
, vdev
);
34 error_report("Error enabling host notifiers: %d", -ret
);
38 ret
= k
->set_guest_notifiers(qbus
->parent
, rng
->vhost_dev
.nvqs
, true);
40 error_report("Error binding guest notifier: %d", -ret
);
41 goto err_host_notifiers
;
44 rng
->vhost_dev
.acked_features
= vdev
->guest_features
;
45 ret
= vhost_dev_start(&rng
->vhost_dev
, vdev
);
47 error_report("Error starting vhost-user-rng: %d", -ret
);
48 goto err_guest_notifiers
;
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.
56 for (i
= 0; i
< rng
->vhost_dev
.nvqs
; i
++) {
57 vhost_virtqueue_mask(&rng
->vhost_dev
, vdev
, i
, false);
63 k
->set_guest_notifiers(qbus
->parent
, rng
->vhost_dev
.nvqs
, false);
65 vhost_dev_disable_notifiers(&rng
->vhost_dev
, vdev
);
68 static void vu_rng_stop(VirtIODevice
*vdev
)
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
);
75 if (!k
->set_guest_notifiers
) {
79 vhost_dev_stop(&rng
->vhost_dev
, vdev
);
81 ret
= k
->set_guest_notifiers(qbus
->parent
, rng
->vhost_dev
.nvqs
, false);
83 error_report("vhost guest notifier cleanup failed: %d", ret
);
87 vhost_dev_disable_notifiers(&rng
->vhost_dev
, vdev
);
90 static void vu_rng_set_status(VirtIODevice
*vdev
, uint8_t status
)
92 VHostUserRNG
*rng
= VHOST_USER_RNG(vdev
);
93 bool should_start
= status
& VIRTIO_CONFIG_S_DRIVER_OK
;
95 if (!vdev
->vm_running
) {
99 if (rng
->vhost_dev
.started
== should_start
) {
110 static uint64_t vu_rng_get_features(VirtIODevice
*vdev
,
111 uint64_t requested_features
, Error
**errp
)
113 /* No feature bits used yet */
114 return requested_features
;
117 static void vu_rng_handle_output(VirtIODevice
*vdev
, VirtQueue
*vq
)
120 * Not normally called; it's the daemon that handles the queue;
121 * however virtio's cleanup path can call this.
125 static void vu_rng_guest_notifier_mask(VirtIODevice
*vdev
, int idx
, bool mask
)
127 VHostUserRNG
*rng
= VHOST_USER_RNG(vdev
);
129 vhost_virtqueue_mask(&rng
->vhost_dev
, vdev
, idx
, mask
);
132 static bool vu_rng_guest_notifier_pending(VirtIODevice
*vdev
, int idx
)
134 VHostUserRNG
*rng
= VHOST_USER_RNG(vdev
);
136 return vhost_virtqueue_pending(&rng
->vhost_dev
, idx
);
139 static void vu_rng_connect(DeviceState
*dev
)
141 VirtIODevice
*vdev
= VIRTIO_DEVICE(dev
);
142 VHostUserRNG
*rng
= VHOST_USER_RNG(vdev
);
144 if (rng
->connected
) {
148 rng
->connected
= true;
150 /* restore vhost state */
151 if (virtio_device_started(vdev
, vdev
->status
)) {
156 static void vu_rng_disconnect(DeviceState
*dev
)
158 VirtIODevice
*vdev
= VIRTIO_DEVICE(dev
);
159 VHostUserRNG
*rng
= VHOST_USER_RNG(vdev
);
161 if (!rng
->connected
) {
165 rng
->connected
= false;
167 if (rng
->vhost_dev
.started
) {
172 static void vu_rng_event(void *opaque
, QEMUChrEvent event
)
174 DeviceState
*dev
= opaque
;
177 case CHR_EVENT_OPENED
:
180 case CHR_EVENT_CLOSED
:
181 vu_rng_disconnect(dev
);
183 case CHR_EVENT_BREAK
:
184 case CHR_EVENT_MUX_IN
:
185 case CHR_EVENT_MUX_OUT
:
191 static void vu_rng_device_realize(DeviceState
*dev
, Error
**errp
)
193 VirtIODevice
*vdev
= VIRTIO_DEVICE(dev
);
194 VHostUserRNG
*rng
= VHOST_USER_RNG(dev
);
197 if (!rng
->chardev
.chr
) {
198 error_setg(errp
, "missing chardev");
202 if (!vhost_user_init(&rng
->vhost_user
, &rng
->chardev
, errp
)) {
206 virtio_init(vdev
, "vhost-user-rng", VIRTIO_ID_RNG
, 0);
208 rng
->req_vq
= virtio_add_queue(vdev
, 4, vu_rng_handle_output
);
210 error_setg_errno(errp
, -1, "virtio_add_queue() failed");
211 goto virtio_add_queue_failed
;
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
);
219 error_setg_errno(errp
, -ret
, "vhost_dev_init() failed");
220 goto vhost_dev_init_failed
;
223 qemu_chr_fe_set_handlers(&rng
->chardev
, NULL
, NULL
, vu_rng_event
, NULL
,
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
);
235 static void vu_rng_device_unrealize(DeviceState
*dev
)
237 VirtIODevice
*vdev
= VIRTIO_DEVICE(dev
);
238 VHostUserRNG
*rng
= VHOST_USER_RNG(dev
);
240 vu_rng_set_status(vdev
, 0);
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
);
250 static const VMStateDescription vu_rng_vmstate
= {
251 .name
= "vhost-user-rng",
255 static Property vu_rng_properties
[] = {
256 DEFINE_PROP_CHR("chardev", VHostUserRNG
, chardev
),
257 DEFINE_PROP_END_OF_LIST(),
260 static void vu_rng_class_init(ObjectClass
*klass
, void *data
)
262 DeviceClass
*dc
= DEVICE_CLASS(klass
);
263 VirtioDeviceClass
*vdc
= VIRTIO_DEVICE_CLASS(klass
);
265 device_class_set_props(dc
, vu_rng_properties
);
266 dc
->vmsd
= &vu_rng_vmstate
;
267 set_bit(DEVICE_CATEGORY_INPUT
, dc
->categories
);
269 vdc
->realize
= vu_rng_device_realize
;
270 vdc
->unrealize
= vu_rng_device_unrealize
;
271 vdc
->get_features
= vu_rng_get_features
;
272 vdc
->set_status
= vu_rng_set_status
;
273 vdc
->guest_notifier_mask
= vu_rng_guest_notifier_mask
;
274 vdc
->guest_notifier_pending
= vu_rng_guest_notifier_pending
;
277 static const TypeInfo vu_rng_info
= {
278 .name
= TYPE_VHOST_USER_RNG
,
279 .parent
= TYPE_VIRTIO_DEVICE
,
280 .instance_size
= sizeof(VHostUserRNG
),
281 .class_init
= vu_rng_class_init
,
284 static void vu_rng_register_types(void)
286 type_register_static(&vu_rng_info
);
289 type_init(vu_rng_register_types
)