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 "chardev/char-fe.h"
  16#include "qapi/error.h"
  17#include "qapi/qmp/qerror.h"
  18
  19#define TYPE_RNG_EGD "rng-egd"
  20#define RNG_EGD(obj) OBJECT_CHECK(RngEgd, (obj), TYPE_RNG_EGD)
  21
  22typedef struct RngEgd
  23{
  24    RngBackend parent;
  25
  26    CharBackend chr;
  27    char *chr_name;
  28} RngEgd;
  29
  30static void rng_egd_request_entropy(RngBackend *b, RngRequest *req)
  31{
  32    RngEgd *s = RNG_EGD(b);
  33    size_t size = req->size;
  34
  35    while (size > 0) {
  36        uint8_t header[2];
  37        uint8_t len = MIN(size, 255);
  38
  39        /* synchronous entropy request */
  40        header[0] = 0x02;
  41        header[1] = len;
  42
  43        /* XXX this blocks entire thread. Rewrite to use
  44         * qemu_chr_fe_write and background I/O callbacks */
  45        qemu_chr_fe_write_all(&s->chr, header, sizeof(header));
  46
  47        size -= len;
  48    }
  49}
  50
  51static int rng_egd_chr_can_read(void *opaque)
  52{
  53    RngEgd *s = RNG_EGD(opaque);
  54    RngRequest *req;
  55    int size = 0;
  56
  57    QSIMPLEQ_FOREACH(req, &s->parent.requests, next) {
  58        size += req->size - req->offset;
  59    }
  60
  61    return size;
  62}
  63
  64static void rng_egd_chr_read(void *opaque, const uint8_t *buf, int size)
  65{
  66    RngEgd *s = RNG_EGD(opaque);
  67    size_t buf_offset = 0;
  68
  69    while (size > 0 && !QSIMPLEQ_EMPTY(&s->parent.requests)) {
  70        RngRequest *req = QSIMPLEQ_FIRST(&s->parent.requests);
  71        int len = MIN(size, req->size - req->offset);
  72
  73        memcpy(req->data + req->offset, buf + buf_offset, len);
  74        buf_offset += len;
  75        req->offset += len;
  76        size -= len;
  77
  78        if (req->offset == req->size) {
  79            req->receive_entropy(req->opaque, req->data, req->size);
  80
  81            rng_backend_finalize_request(&s->parent, req);
  82        }
  83    }
  84}
  85
  86static void rng_egd_opened(RngBackend *b, Error **errp)
  87{
  88    RngEgd *s = RNG_EGD(b);
  89    Chardev *chr;
  90
  91    if (s->chr_name == NULL) {
  92        error_setg(errp, QERR_INVALID_PARAMETER_VALUE,
  93                   "chardev", "a valid character device");
  94        return;
  95    }
  96
  97    chr = qemu_chr_find(s->chr_name);
  98    if (chr == NULL) {
  99        error_set(errp, ERROR_CLASS_DEVICE_NOT_FOUND,
 100                  "Device '%s' not found", s->chr_name);
 101        return;
 102    }
 103    if (!qemu_chr_fe_init(&s->chr, chr, errp)) {
 104        return;
 105    }
 106
 107    /* FIXME we should resubmit pending requests when the CDS reconnects. */
 108    qemu_chr_fe_set_handlers(&s->chr, rng_egd_chr_can_read,
 109                             rng_egd_chr_read, NULL, NULL, s, NULL, true);
 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    Chardev *chr = qemu_chr_fe_get_driver(&s->chr);
 129
 130    if (chr && chr->label) {
 131        return g_strdup(chr->label);
 132    }
 133
 134    return NULL;
 135}
 136
 137static void rng_egd_init(Object *obj)
 138{
 139    object_property_add_str(obj, "chardev",
 140                            rng_egd_get_chardev, rng_egd_set_chardev,
 141                            NULL);
 142}
 143
 144static void rng_egd_finalize(Object *obj)
 145{
 146    RngEgd *s = RNG_EGD(obj);
 147
 148    qemu_chr_fe_deinit(&s->chr, false);
 149    g_free(s->chr_name);
 150}
 151
 152static void rng_egd_class_init(ObjectClass *klass, void *data)
 153{
 154    RngBackendClass *rbc = RNG_BACKEND_CLASS(klass);
 155
 156    rbc->request_entropy = rng_egd_request_entropy;
 157    rbc->opened = rng_egd_opened;
 158}
 159
 160static const TypeInfo rng_egd_info = {
 161    .name = TYPE_RNG_EGD,
 162    .parent = TYPE_RNG_BACKEND,
 163    .instance_size = sizeof(RngEgd),
 164    .class_init = rng_egd_class_init,
 165    .instance_init = rng_egd_init,
 166    .instance_finalize = rng_egd_finalize,
 167};
 168
 169static void register_types(void)
 170{
 171    type_register_static(&rng_egd_info);
 172}
 173
 174type_init(register_types);
 175