xref: /openbmc/qemu/hw/virtio/virtio-rng.c (revision 5e25c93ccb8ddc8dda6845d6c09334ca44cbef17)
149ab747fSPaolo Bonzini /*
249ab747fSPaolo Bonzini  * A virtio device implementing a hardware random number generator.
349ab747fSPaolo Bonzini  *
449ab747fSPaolo Bonzini  * Copyright 2012 Red Hat, Inc.
549ab747fSPaolo Bonzini  * Copyright 2012 Amit Shah <amit.shah@redhat.com>
649ab747fSPaolo Bonzini  *
749ab747fSPaolo Bonzini  * This work is licensed under the terms of the GNU GPL, version 2 or
849ab747fSPaolo Bonzini  * (at your option) any later version.  See the COPYING file in the
949ab747fSPaolo Bonzini  * top-level directory.
1049ab747fSPaolo Bonzini  */
1149ab747fSPaolo Bonzini 
129b8bfe21SPeter Maydell #include "qemu/osdep.h"
13da34e65cSMarkus Armbruster #include "qapi/error.h"
1449ab747fSPaolo Bonzini #include "qemu/iov.h"
150b8fa32fSMarkus Armbruster #include "qemu/module.h"
1654d31236SMarkus Armbruster #include "qemu/timer.h"
1749ab747fSPaolo Bonzini #include "hw/virtio/virtio.h"
18a27bd6c7SMarkus Armbruster #include "hw/qdev-properties.h"
1949ab747fSPaolo Bonzini #include "hw/virtio/virtio-rng.h"
20dccfcd0eSPaolo Bonzini #include "sysemu/rng.h"
2154d31236SMarkus Armbruster #include "sysemu/runstate.h"
2257d3e1b3SIgor Mammedov #include "qom/object_interfaces.h"
234ac44580SAmit Shah #include "trace.h"
2449ab747fSPaolo Bonzini 
is_guest_ready(VirtIORNG * vrng)2549ab747fSPaolo Bonzini static bool is_guest_ready(VirtIORNG *vrng)
2649ab747fSPaolo Bonzini {
27611aa333SKONRAD Frederic     VirtIODevice *vdev = VIRTIO_DEVICE(vrng);
2849ab747fSPaolo Bonzini     if (virtio_queue_ready(vrng->vq)
29611aa333SKONRAD Frederic         && (vdev->status & VIRTIO_CONFIG_S_DRIVER_OK)) {
3049ab747fSPaolo Bonzini         return true;
3149ab747fSPaolo Bonzini     }
324ac44580SAmit Shah     trace_virtio_rng_guest_not_ready(vrng);
3349ab747fSPaolo Bonzini     return false;
3449ab747fSPaolo Bonzini }
3549ab747fSPaolo Bonzini 
get_request_size(VirtQueue * vq,unsigned quota)3649ab747fSPaolo Bonzini static size_t get_request_size(VirtQueue *vq, unsigned quota)
3749ab747fSPaolo Bonzini {
3849ab747fSPaolo Bonzini     unsigned int in, out;
3949ab747fSPaolo Bonzini 
4049ab747fSPaolo Bonzini     virtqueue_get_avail_bytes(vq, &in, &out, quota, 0);
4149ab747fSPaolo Bonzini     return in;
4249ab747fSPaolo Bonzini }
4349ab747fSPaolo Bonzini 
4449ab747fSPaolo Bonzini static void virtio_rng_process(VirtIORNG *vrng);
4549ab747fSPaolo Bonzini 
4649ab747fSPaolo Bonzini /* Send data from a char device over to the guest */
chr_read(void * opaque,const void * buf,size_t size)4749ab747fSPaolo Bonzini static void chr_read(void *opaque, const void *buf, size_t size)
4849ab747fSPaolo Bonzini {
4949ab747fSPaolo Bonzini     VirtIORNG *vrng = opaque;
50611aa333SKONRAD Frederic     VirtIODevice *vdev = VIRTIO_DEVICE(vrng);
5151b19ebeSPaolo Bonzini     VirtQueueElement *elem;
5249ab747fSPaolo Bonzini     size_t len;
5349ab747fSPaolo Bonzini     int offset;
5449ab747fSPaolo Bonzini 
5549ab747fSPaolo Bonzini     if (!is_guest_ready(vrng)) {
5649ab747fSPaolo Bonzini         return;
5749ab747fSPaolo Bonzini     }
5849ab747fSPaolo Bonzini 
59a23a6d18SLaurent Vivier     /* we can't modify the virtqueue until
60a23a6d18SLaurent Vivier      * our state is fully synced
61a23a6d18SLaurent Vivier      */
62a23a6d18SLaurent Vivier 
63a23a6d18SLaurent Vivier     if (!runstate_check(RUN_STATE_RUNNING)) {
64a23a6d18SLaurent Vivier         trace_virtio_rng_cpu_is_stopped(vrng, size);
65a23a6d18SLaurent Vivier         return;
66a23a6d18SLaurent Vivier     }
67a23a6d18SLaurent Vivier 
6849ab747fSPaolo Bonzini     vrng->quota_remaining -= size;
6949ab747fSPaolo Bonzini 
7049ab747fSPaolo Bonzini     offset = 0;
7149ab747fSPaolo Bonzini     while (offset < size) {
7251b19ebeSPaolo Bonzini         elem = virtqueue_pop(vrng->vq, sizeof(VirtQueueElement));
7351b19ebeSPaolo Bonzini         if (!elem) {
7449ab747fSPaolo Bonzini             break;
7549ab747fSPaolo Bonzini         }
76a23a6d18SLaurent Vivier         trace_virtio_rng_popped(vrng);
7751b19ebeSPaolo Bonzini         len = iov_from_buf(elem->in_sg, elem->in_num,
7849ab747fSPaolo Bonzini                            0, buf + offset, size - offset);
7949ab747fSPaolo Bonzini         offset += len;
8049ab747fSPaolo Bonzini 
8151b19ebeSPaolo Bonzini         virtqueue_push(vrng->vq, elem, len);
824ac44580SAmit Shah         trace_virtio_rng_pushed(vrng, len);
8351b19ebeSPaolo Bonzini         g_free(elem);
8449ab747fSPaolo Bonzini     }
85611aa333SKONRAD Frederic     virtio_notify(vdev, vrng->vq);
86f8693c2cSLadi Prosek 
87f8693c2cSLadi Prosek     if (!virtio_queue_empty(vrng->vq)) {
88f8693c2cSLadi Prosek         /* If we didn't drain the queue, call virtio_rng_process
89f8693c2cSLadi Prosek          * to take care of asking for more data as appropriate.
90f8693c2cSLadi Prosek          */
91f8693c2cSLadi Prosek         virtio_rng_process(vrng);
92f8693c2cSLadi Prosek     }
9349ab747fSPaolo Bonzini }
9449ab747fSPaolo Bonzini 
virtio_rng_process(VirtIORNG * vrng)9549ab747fSPaolo Bonzini static void virtio_rng_process(VirtIORNG *vrng)
9649ab747fSPaolo Bonzini {
9749ab747fSPaolo Bonzini     size_t size;
9849ab747fSPaolo Bonzini     unsigned quota;
9949ab747fSPaolo Bonzini 
10049ab747fSPaolo Bonzini     if (!is_guest_ready(vrng)) {
10149ab747fSPaolo Bonzini         return;
10249ab747fSPaolo Bonzini     }
10349ab747fSPaolo Bonzini 
104621a20e0SPankaj Gupta     if (vrng->activate_timer) {
105621a20e0SPankaj Gupta         timer_mod(vrng->rate_limit_timer,
106621a20e0SPankaj Gupta                   qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) + vrng->conf.period_ms);
107621a20e0SPankaj Gupta         vrng->activate_timer = false;
108621a20e0SPankaj Gupta     }
109621a20e0SPankaj Gupta 
11049ab747fSPaolo Bonzini     if (vrng->quota_remaining < 0) {
11149ab747fSPaolo Bonzini         quota = 0;
11249ab747fSPaolo Bonzini     } else {
11349ab747fSPaolo Bonzini         quota = MIN((uint64_t)vrng->quota_remaining, (uint64_t)UINT32_MAX);
11449ab747fSPaolo Bonzini     }
11549ab747fSPaolo Bonzini     size = get_request_size(vrng->vq, quota);
1164ac44580SAmit Shah 
1174ac44580SAmit Shah     trace_virtio_rng_request(vrng, size, quota);
1184ac44580SAmit Shah 
11949ab747fSPaolo Bonzini     size = MIN(vrng->quota_remaining, size);
12049ab747fSPaolo Bonzini     if (size) {
12149ab747fSPaolo Bonzini         rng_backend_request_entropy(vrng->rng, size, chr_read, vrng);
12249ab747fSPaolo Bonzini     }
12349ab747fSPaolo Bonzini }
12449ab747fSPaolo Bonzini 
handle_input(VirtIODevice * vdev,VirtQueue * vq)12549ab747fSPaolo Bonzini static void handle_input(VirtIODevice *vdev, VirtQueue *vq)
12649ab747fSPaolo Bonzini {
127611aa333SKONRAD Frederic     VirtIORNG *vrng = VIRTIO_RNG(vdev);
12849ab747fSPaolo Bonzini     virtio_rng_process(vrng);
12949ab747fSPaolo Bonzini }
13049ab747fSPaolo Bonzini 
get_features(VirtIODevice * vdev,uint64_t f,Error ** errp)1319d5b731dSJason Wang static uint64_t get_features(VirtIODevice *vdev, uint64_t f, Error **errp)
13249ab747fSPaolo Bonzini {
13349ab747fSPaolo Bonzini     return f;
13449ab747fSPaolo Bonzini }
13549ab747fSPaolo Bonzini 
virtio_rng_vm_state_change(void * opaque,bool running,RunState state)136538f0497SPhilippe Mathieu-Daudé static void virtio_rng_vm_state_change(void *opaque, bool running,
137a23a6d18SLaurent Vivier                                        RunState state)
13849ab747fSPaolo Bonzini {
139db12451dSDavid Gibson     VirtIORNG *vrng = opaque;
14049ab747fSPaolo Bonzini 
141a23a6d18SLaurent Vivier     trace_virtio_rng_vm_state_change(vrng, running, state);
14249ab747fSPaolo Bonzini 
143a23a6d18SLaurent Vivier     /* We may have an element ready but couldn't process it due to a quota
144a23a6d18SLaurent Vivier      * limit or because CPU was stopped.  Make sure to try again when the
145a23a6d18SLaurent Vivier      * CPU restart.
146a23a6d18SLaurent Vivier      */
147a23a6d18SLaurent Vivier 
148a23a6d18SLaurent Vivier     if (running && is_guest_ready(vrng)) {
149a23a6d18SLaurent Vivier         virtio_rng_process(vrng);
150a23a6d18SLaurent Vivier     }
15149ab747fSPaolo Bonzini }
15249ab747fSPaolo Bonzini 
check_rate_limit(void * opaque)15349ab747fSPaolo Bonzini static void check_rate_limit(void *opaque)
15449ab747fSPaolo Bonzini {
155611aa333SKONRAD Frederic     VirtIORNG *vrng = opaque;
15649ab747fSPaolo Bonzini 
157611aa333SKONRAD Frederic     vrng->quota_remaining = vrng->conf.max_bytes;
158611aa333SKONRAD Frederic     virtio_rng_process(vrng);
159621a20e0SPankaj Gupta     vrng->activate_timer = true;
16049ab747fSPaolo Bonzini }
16149ab747fSPaolo Bonzini 
virtio_rng_set_status(VirtIODevice * vdev,uint8_t status)1625d9c9ea2SPankaj Gupta static void virtio_rng_set_status(VirtIODevice *vdev, uint8_t status)
1635d9c9ea2SPankaj Gupta {
1645d9c9ea2SPankaj Gupta     VirtIORNG *vrng = VIRTIO_RNG(vdev);
1655d9c9ea2SPankaj Gupta 
1665d9c9ea2SPankaj Gupta     if (!vdev->vm_running) {
1675d9c9ea2SPankaj Gupta         return;
1685d9c9ea2SPankaj Gupta     }
1695d9c9ea2SPankaj Gupta     vdev->status = status;
1705d9c9ea2SPankaj Gupta 
1715d9c9ea2SPankaj Gupta     /* Something changed, try to process buffers */
1725d9c9ea2SPankaj Gupta     virtio_rng_process(vrng);
1735d9c9ea2SPankaj Gupta }
1745d9c9ea2SPankaj Gupta 
virtio_rng_device_realize(DeviceState * dev,Error ** errp)175a8d57dfbSAndreas Färber static void virtio_rng_device_realize(DeviceState *dev, Error **errp)
1766eac8aecSKONRAD Frederic {
177a8d57dfbSAndreas Färber     VirtIODevice *vdev = VIRTIO_DEVICE(dev);
178af7671fdSAndreas Färber     VirtIORNG *vrng = VIRTIO_RNG(dev);
1796eac8aecSKONRAD Frederic 
180a3a292c4SAmit Shah     if (vrng->conf.period_ms <= 0) {
181c617dd3bSJohn Snow         error_setg(errp, "'period' parameter expects a positive integer");
182a8d57dfbSAndreas Färber         return;
183d44bb860SAmos Kong     }
184d44bb860SAmos Kong 
1851efd6e07SJohn Snow     /* Workaround: Property parsing does not enforce unsigned integers,
1861efd6e07SJohn Snow      * So this is a hack to reject such numbers. */
187*024d046bSMichael S. Tsirkin     if (vrng->conf.max_bytes == 0 ||
188*024d046bSMichael S. Tsirkin         vrng->conf.max_bytes > INT64_MAX) {
189*024d046bSMichael S. Tsirkin         error_setg(errp, "'max-bytes' parameter must be positive, "
190c617dd3bSJohn Snow                    "and less than 2^63");
1911efd6e07SJohn Snow         return;
1921efd6e07SJohn Snow     }
1931efd6e07SJohn Snow 
1946eac8aecSKONRAD Frederic     if (vrng->conf.rng == NULL) {
1950198c262SLaurent Vivier         Object *default_backend = object_new(TYPE_RNG_BUILTIN);
1966eac8aecSKONRAD Frederic 
197778a2dc5SMarkus Armbruster         if (!user_creatable_complete(USER_CREATABLE(default_backend),
198668f62ecSMarkus Armbruster                                      errp)) {
1995f7655f6SMarkus Armbruster             object_unref(default_backend);
20057d3e1b3SIgor Mammedov             return;
20157d3e1b3SIgor Mammedov         }
20257d3e1b3SIgor Mammedov 
2035f7655f6SMarkus Armbruster         object_property_add_child(OBJECT(dev), "default-backend",
204d2623129SMarkus Armbruster                                   default_backend);
2056eac8aecSKONRAD Frederic 
206abdffd1fSStefan Hajnoczi         /* The child property took a reference, we can safely drop ours now */
2075f7655f6SMarkus Armbruster         object_unref(default_backend);
208abdffd1fSStefan Hajnoczi 
2095325cc34SMarkus Armbruster         object_property_set_link(OBJECT(dev), "rng", default_backend,
2105325cc34SMarkus Armbruster                                  &error_abort);
2116eac8aecSKONRAD Frederic     }
2126eac8aecSKONRAD Frederic 
21346a5a89dSKONRAD Frederic     vrng->rng = vrng->conf.rng;
21446a5a89dSKONRAD Frederic     if (vrng->rng == NULL) {
215c617dd3bSJohn Snow         error_setg(errp, "'rng' parameter expects a valid object");
216a8d57dfbSAndreas Färber         return;
2176eac8aecSKONRAD Frederic     }
21846a5a89dSKONRAD Frederic 
2193857cd5cSJonah Palmer     virtio_init(vdev, VIRTIO_ID_RNG, 0);
22046a5a89dSKONRAD Frederic 
2211efd6e07SJohn Snow     vrng->vq = virtio_add_queue(vdev, 8, handle_input);
22246a5a89dSKONRAD Frederic     vrng->quota_remaining = vrng->conf.max_bytes;
223bc72ad67SAlex Bligh     vrng->rate_limit_timer = timer_new_ms(QEMU_CLOCK_VIRTUAL,
22446a5a89dSKONRAD Frederic                                                check_rate_limit, vrng);
225621a20e0SPankaj Gupta     vrng->activate_timer = true;
226a23a6d18SLaurent Vivier 
227a23a6d18SLaurent Vivier     vrng->vmstate = qemu_add_vm_change_state_handler(virtio_rng_vm_state_change,
228a23a6d18SLaurent Vivier                                                      vrng);
2296eac8aecSKONRAD Frederic }
2306eac8aecSKONRAD Frederic 
virtio_rng_device_unrealize(DeviceState * dev)231b69c3c21SMarkus Armbruster static void virtio_rng_device_unrealize(DeviceState *dev)
2326eac8aecSKONRAD Frederic {
233306ec6c3SAndreas Färber     VirtIODevice *vdev = VIRTIO_DEVICE(dev);
234306ec6c3SAndreas Färber     VirtIORNG *vrng = VIRTIO_RNG(dev);
2356eac8aecSKONRAD Frederic 
236a23a6d18SLaurent Vivier     qemu_del_vm_change_state_handler(vrng->vmstate);
237bc72ad67SAlex Bligh     timer_free(vrng->rate_limit_timer);
238522bbb19SEugenio Pérez     virtio_del_queue(vdev, 0);
2396a1a8cc7SKONRAD Frederic     virtio_cleanup(vdev);
2406eac8aecSKONRAD Frederic }
2416eac8aecSKONRAD Frederic 
242b7de81f6SHalil Pasic static const VMStateDescription vmstate_virtio_rng = {
243b7de81f6SHalil Pasic     .name = "virtio-rng",
244b7de81f6SHalil Pasic     .minimum_version_id = 1,
245b7de81f6SHalil Pasic     .version_id = 1,
246ca02a170SRichard Henderson     .fields = (const VMStateField[]) {
247b7de81f6SHalil Pasic         VMSTATE_VIRTIO_DEVICE,
248b7de81f6SHalil Pasic         VMSTATE_END_OF_LIST()
249b7de81f6SHalil Pasic     },
250b7de81f6SHalil Pasic };
251b6075793SDr. David Alan Gilbert 
2526eac8aecSKONRAD Frederic static Property virtio_rng_properties[] = {
253fe704809SShannon Zhao     /* Set a default rate limit of 2^47 bytes per minute or roughly 2TB/s.  If
254fe704809SShannon Zhao      * you have an entropy source capable of generating more entropy than this
255fe704809SShannon Zhao      * and you can pass it through via virtio-rng, then hats off to you.  Until
256fe704809SShannon Zhao      * then, this is unlimited for all practical purposes.
257fe704809SShannon Zhao      */
258fe704809SShannon Zhao     DEFINE_PROP_UINT64("max-bytes", VirtIORNG, conf.max_bytes, INT64_MAX),
259fe704809SShannon Zhao     DEFINE_PROP_UINT32("period", VirtIORNG, conf.period_ms, 1 << 16),
260d1fd7f77SFam Zheng     DEFINE_PROP_LINK("rng", VirtIORNG, conf.rng, TYPE_RNG_BACKEND, RngBackend *),
2616eac8aecSKONRAD Frederic     DEFINE_PROP_END_OF_LIST(),
2626eac8aecSKONRAD Frederic };
2636eac8aecSKONRAD Frederic 
virtio_rng_class_init(ObjectClass * klass,void * data)2646eac8aecSKONRAD Frederic static void virtio_rng_class_init(ObjectClass *klass, void *data)
2656eac8aecSKONRAD Frederic {
2666eac8aecSKONRAD Frederic     DeviceClass *dc = DEVICE_CLASS(klass);
2676eac8aecSKONRAD Frederic     VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass);
268a8d57dfbSAndreas Färber 
2694f67d30bSMarc-André Lureau     device_class_set_props(dc, virtio_rng_properties);
270b6075793SDr. David Alan Gilbert     dc->vmsd = &vmstate_virtio_rng;
271125ee0edSMarcel Apfelbaum     set_bit(DEVICE_CATEGORY_MISC, dc->categories);
272a8d57dfbSAndreas Färber     vdc->realize = virtio_rng_device_realize;
273306ec6c3SAndreas Färber     vdc->unrealize = virtio_rng_device_unrealize;
2746eac8aecSKONRAD Frederic     vdc->get_features = get_features;
2755d9c9ea2SPankaj Gupta     vdc->set_status = virtio_rng_set_status;
2766eac8aecSKONRAD Frederic }
2776eac8aecSKONRAD Frederic 
2786eac8aecSKONRAD Frederic static const TypeInfo virtio_rng_info = {
2796eac8aecSKONRAD Frederic     .name = TYPE_VIRTIO_RNG,
2806eac8aecSKONRAD Frederic     .parent = TYPE_VIRTIO_DEVICE,
2816eac8aecSKONRAD Frederic     .instance_size = sizeof(VirtIORNG),
2826eac8aecSKONRAD Frederic     .class_init = virtio_rng_class_init,
2836eac8aecSKONRAD Frederic };
2846eac8aecSKONRAD Frederic 
virtio_register_types(void)2856eac8aecSKONRAD Frederic static void virtio_register_types(void)
2866eac8aecSKONRAD Frederic {
2876eac8aecSKONRAD Frederic     type_register_static(&virtio_rng_info);
2886eac8aecSKONRAD Frederic }
2896eac8aecSKONRAD Frederic 
2906eac8aecSKONRAD Frederic type_init(virtio_register_types)
291