qemu/hw/virtio-rng.c
<<
>>
Prefs
   1/*
   2 * A virtio device implementing a hardware random number generator.
   3 *
   4 * Copyright 2012 Red Hat, Inc.
   5 * Copyright 2012 Amit Shah <amit.shah@redhat.com>
   6 *
   7 * This work is licensed under the terms of the GNU GPL, version 2 or
   8 * (at your option) any later version.  See the COPYING file in the
   9 * top-level directory.
  10 */
  11
  12#include "qemu/iov.h"
  13#include "qdev.h"
  14#include "virtio.h"
  15#include "virtio-rng.h"
  16#include "qemu/rng.h"
  17
  18typedef struct VirtIORNG {
  19    VirtIODevice vdev;
  20
  21    DeviceState *qdev;
  22
  23    /* Only one vq - guest puts buffer(s) on it when it needs entropy */
  24    VirtQueue *vq;
  25
  26    VirtIORNGConf *conf;
  27
  28    RngBackend *rng;
  29
  30    /* We purposefully don't migrate this state.  The quota will reset on the
  31     * destination as a result.  Rate limiting is host state, not guest state.
  32     */
  33    QEMUTimer *rate_limit_timer;
  34    int64_t quota_remaining;
  35} VirtIORNG;
  36
  37static bool is_guest_ready(VirtIORNG *vrng)
  38{
  39    if (virtio_queue_ready(vrng->vq)
  40        && (vrng->vdev.status & VIRTIO_CONFIG_S_DRIVER_OK)) {
  41        return true;
  42    }
  43    return false;
  44}
  45
  46static size_t get_request_size(VirtQueue *vq, unsigned quota)
  47{
  48    unsigned int in, out;
  49
  50    virtqueue_get_avail_bytes(vq, &in, &out, quota, 0);
  51    return in;
  52}
  53
  54static void virtio_rng_process(VirtIORNG *vrng);
  55
  56/* Send data from a char device over to the guest */
  57static void chr_read(void *opaque, const void *buf, size_t size)
  58{
  59    VirtIORNG *vrng = opaque;
  60    VirtQueueElement elem;
  61    size_t len;
  62    int offset;
  63
  64    if (!is_guest_ready(vrng)) {
  65        return;
  66    }
  67
  68    vrng->quota_remaining -= size;
  69
  70    offset = 0;
  71    while (offset < size) {
  72        if (!virtqueue_pop(vrng->vq, &elem)) {
  73            break;
  74        }
  75        len = iov_from_buf(elem.in_sg, elem.in_num,
  76                           0, buf + offset, size - offset);
  77        offset += len;
  78
  79        virtqueue_push(vrng->vq, &elem, len);
  80    }
  81    virtio_notify(&vrng->vdev, vrng->vq);
  82}
  83
  84static void virtio_rng_process(VirtIORNG *vrng)
  85{
  86    size_t size;
  87    unsigned quota;
  88
  89    if (!is_guest_ready(vrng)) {
  90        return;
  91    }
  92
  93    if (vrng->quota_remaining < 0) {
  94        quota = 0;
  95    } else {
  96        quota = MIN((uint64_t)vrng->quota_remaining, (uint64_t)UINT32_MAX);
  97    }
  98    size = get_request_size(vrng->vq, quota);
  99    size = MIN(vrng->quota_remaining, size);
 100    if (size) {
 101        rng_backend_request_entropy(vrng->rng, size, chr_read, vrng);
 102    }
 103}
 104
 105static void handle_input(VirtIODevice *vdev, VirtQueue *vq)
 106{
 107    VirtIORNG *vrng = DO_UPCAST(VirtIORNG, vdev, vdev);
 108    virtio_rng_process(vrng);
 109}
 110
 111static uint32_t get_features(VirtIODevice *vdev, uint32_t f)
 112{
 113    return f;
 114}
 115
 116static void virtio_rng_save(QEMUFile *f, void *opaque)
 117{
 118    VirtIORNG *vrng = opaque;
 119
 120    virtio_save(&vrng->vdev, f);
 121}
 122
 123static int virtio_rng_load(QEMUFile *f, void *opaque, int version_id)
 124{
 125    VirtIORNG *vrng = opaque;
 126
 127    if (version_id != 1) {
 128        return -EINVAL;
 129    }
 130    virtio_load(&vrng->vdev, f);
 131
 132    /* We may have an element ready but couldn't process it due to a quota
 133     * limit.  Make sure to try again after live migration when the quota may
 134     * have been reset.
 135     */
 136    virtio_rng_process(vrng);
 137
 138    return 0;
 139}
 140
 141static void check_rate_limit(void *opaque)
 142{
 143    VirtIORNG *s = opaque;
 144
 145    s->quota_remaining = s->conf->max_bytes;
 146    virtio_rng_process(s);
 147    qemu_mod_timer(s->rate_limit_timer,
 148                   qemu_get_clock_ms(vm_clock) + s->conf->period_ms);
 149}
 150
 151
 152VirtIODevice *virtio_rng_init(DeviceState *dev, VirtIORNGConf *conf)
 153{
 154    VirtIORNG *vrng;
 155    VirtIODevice *vdev;
 156    Error *local_err = NULL;
 157
 158    vdev = virtio_common_init("virtio-rng", VIRTIO_ID_RNG, 0,
 159                              sizeof(VirtIORNG));
 160
 161    vrng = DO_UPCAST(VirtIORNG, vdev, vdev);
 162
 163    vrng->rng = conf->rng;
 164    if (vrng->rng == NULL) {
 165        qerror_report(QERR_INVALID_PARAMETER_VALUE, "rng", "a valid object");
 166        return NULL;
 167    }
 168
 169    rng_backend_open(vrng->rng, &local_err);
 170    if (local_err) {
 171        qerror_report_err(local_err);
 172        error_free(local_err);
 173        return NULL;
 174    }
 175
 176    vrng->vq = virtio_add_queue(vdev, 8, handle_input);
 177    vrng->vdev.get_features = get_features;
 178
 179    vrng->qdev = dev;
 180    vrng->conf = conf;
 181
 182    assert(vrng->conf->max_bytes <= INT64_MAX);
 183    vrng->quota_remaining = vrng->conf->max_bytes;
 184
 185    vrng->rate_limit_timer = qemu_new_timer_ms(vm_clock,
 186                                               check_rate_limit, vrng);
 187
 188    qemu_mod_timer(vrng->rate_limit_timer,
 189                   qemu_get_clock_ms(vm_clock) + vrng->conf->period_ms);
 190
 191    register_savevm(dev, "virtio-rng", -1, 1, virtio_rng_save,
 192                    virtio_rng_load, vrng);
 193
 194    return vdev;
 195}
 196
 197void virtio_rng_exit(VirtIODevice *vdev)
 198{
 199    VirtIORNG *vrng = DO_UPCAST(VirtIORNG, vdev, vdev);
 200
 201    qemu_del_timer(vrng->rate_limit_timer);
 202    qemu_free_timer(vrng->rate_limit_timer);
 203    unregister_savevm(vrng->qdev, "virtio-rng", vrng);
 204    virtio_cleanup(vdev);
 205}
 206