qemu/backends/rng-egd.c
<<
>>
Prefs
   1/*
   2 * QEMU Random Number Generator Backend
   3 *
   4 * Copyright IBM, Corp. 2012
   5 *
   6 * Authors:
   7 *  Anthony Liguori   <aliguori@us.ibm.com>
   8 *
   9 * This work is licensed under the terms of the GNU GPL, version 2 or later.
  10 * See the COPYING file in the top-level directory.
  11 */
  12
  13#include "qemu/osdep.h"
  14#include "sysemu/rng.h"
  15#include "sysemu/char.h"
  16#include "qapi/error.h"
  17#include "qapi/qmp/qerror.h"
  18#include "hw/qdev.h" /* just for DEFINE_PROP_CHR */
  19
  20#define TYPE_RNG_EGD "rng-egd"
  21#define RNG_EGD(obj) OBJECT_CHECK(RngEgd, (obj), TYPE_RNG_EGD)
  22
  23typedef struct RngEgd
  24{
  25    RngBackend parent;
  26
  27    CharDriverState *chr;
  28    char *chr_name;
  29} RngEgd;
  30
  31static void rng_egd_request_entropy(RngBackend *b, RngRequest *req)
  32{
  33    RngEgd *s = RNG_EGD(b);
  34    size_t size = req->size;
  35
  36    while (size > 0) {
  37        uint8_t header[2];
  38        uint8_t len = MIN(size, 255);
  39
  40        /* synchronous entropy request */
  41        header[0] = 0x02;
  42        header[1] = len;
  43
  44        qemu_chr_fe_write(s->chr, header, sizeof(header));
  45
  46        size -= len;
  47    }
  48}
  49
  50static int rng_egd_chr_can_read(void *opaque)
  51{
  52    RngEgd *s = RNG_EGD(opaque);
  53    RngRequest *req;
  54    int size = 0;
  55
  56    QSIMPLEQ_FOREACH(req, &s->parent.requests, next) {
  57        size += req->size - req->offset;
  58    }
  59
  60    return size;
  61}
  62
  63static void rng_egd_chr_read(void *opaque, const uint8_t *buf, int size)
  64{
  65    RngEgd *s = RNG_EGD(opaque);
  66    size_t buf_offset = 0;
  67
  68    while (size > 0 && !QSIMPLEQ_EMPTY(&s->parent.requests)) {
  69        RngRequest *req = QSIMPLEQ_FIRST(&s->parent.requests);
  70        int len = MIN(size, req->size - req->offset);
  71
  72        memcpy(req->data + req->offset, buf + buf_offset, len);
  73        buf_offset += len;
  74        req->offset += len;
  75        size -= len;
  76
  77        if (req->offset == req->size) {
  78            req->receive_entropy(req->opaque, req->data, req->size);
  79
  80            rng_backend_finalize_request(&s->parent, req);
  81        }
  82    }
  83}
  84
  85static void rng_egd_opened(RngBackend *b, Error **errp)
  86{
  87    RngEgd *s = RNG_EGD(b);
  88
  89    if (s->chr_name == NULL) {
  90        error_setg(errp, QERR_INVALID_PARAMETER_VALUE,
  91                   "chardev", "a valid character device");
  92        return;
  93    }
  94
  95    s->chr = qemu_chr_find(s->chr_name);
  96    if (s->chr == NULL) {
  97        error_set(errp, ERROR_CLASS_DEVICE_NOT_FOUND,
  98                  "Device '%s' not found", s->chr_name);
  99        return;
 100    }
 101
 102    if (qemu_chr_fe_claim(s->chr) != 0) {
 103        error_setg(errp, QERR_DEVICE_IN_USE, s->chr_name);
 104        return;
 105    }
 106
 107    /* FIXME we should resubmit pending requests when the CDS reconnects. */
 108    qemu_chr_add_handlers(s->chr, rng_egd_chr_can_read, rng_egd_chr_read,
 109                          NULL, s);
 110}
 111
 112static void rng_egd_set_chardev(Object *obj, const char *value, Error **errp)
 113{
 114    RngBackend *b = RNG_BACKEND(obj);
 115    RngEgd *s = RNG_EGD(b);
 116
 117    if (b->opened) {
 118        error_setg(errp, QERR_PERMISSION_DENIED);
 119    } else {
 120        g_free(s->chr_name);
 121        s->chr_name = g_strdup(value);
 122    }
 123}
 124
 125static char *rng_egd_get_chardev(Object *obj, Error **errp)
 126{
 127    RngEgd *s = RNG_EGD(obj);
 128
 129    if (s->chr && s->chr->label) {
 130        return g_strdup(s->chr->label);
 131    }
 132
 133    return NULL;
 134}
 135
 136static void rng_egd_init(Object *obj)
 137{
 138    object_property_add_str(obj, "chardev",
 139                            rng_egd_get_chardev, rng_egd_set_chardev,
 140                            NULL);
 141}
 142
 143static void rng_egd_finalize(Object *obj)
 144{
 145    RngEgd *s = RNG_EGD(obj);
 146
 147    if (s->chr) {
 148        qemu_chr_add_handlers(s->chr, NULL, NULL, NULL, NULL);
 149        qemu_chr_fe_release(s->chr);
 150    }
 151
 152    g_free(s->chr_name);
 153}
 154
 155static void rng_egd_class_init(ObjectClass *klass, void *data)
 156{
 157    RngBackendClass *rbc = RNG_BACKEND_CLASS(klass);
 158
 159    rbc->request_entropy = rng_egd_request_entropy;
 160    rbc->opened = rng_egd_opened;
 161}
 162
 163static const TypeInfo rng_egd_info = {
 164    .name = TYPE_RNG_EGD,
 165    .parent = TYPE_RNG_BACKEND,
 166    .instance_size = sizeof(RngEgd),
 167    .class_init = rng_egd_class_init,
 168    .instance_init = rng_egd_init,
 169    .instance_finalize = rng_egd_finalize,
 170};
 171
 172static void register_types(void)
 173{
 174    type_register_static(&rng_egd_info);
 175}
 176
 177type_init(register_types);
 178