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 "sysemu/rng.h"
  14#include "sysemu/char.h"
  15#include "qapi/qmp/qerror.h"
  16#include "hw/qdev.h" /* just for DEFINE_PROP_CHR */
  17
  18#define TYPE_RNG_EGD "rng-egd"
  19#define RNG_EGD(obj) OBJECT_CHECK(RngEgd, (obj), TYPE_RNG_EGD)
  20
  21typedef struct RngEgd
  22{
  23    RngBackend parent;
  24
  25    CharDriverState *chr;
  26    char *chr_name;
  27
  28    GSList *requests;
  29} RngEgd;
  30
  31typedef struct RngRequest
  32{
  33    EntropyReceiveFunc *receive_entropy;
  34    uint8_t *data;
  35    void *opaque;
  36    size_t offset;
  37    size_t size;
  38} RngRequest;
  39
  40static void rng_egd_request_entropy(RngBackend *b, size_t size,
  41                                    EntropyReceiveFunc *receive_entropy,
  42                                    void *opaque)
  43{
  44    RngEgd *s = RNG_EGD(b);
  45    RngRequest *req;
  46
  47    req = g_malloc(sizeof(*req));
  48
  49    req->offset = 0;
  50    req->size = size;
  51    req->receive_entropy = receive_entropy;
  52    req->opaque = opaque;
  53    req->data = g_malloc(req->size);
  54
  55    while (size > 0) {
  56        uint8_t header[2];
  57        uint8_t len = MIN(size, 255);
  58
  59        /* synchronous entropy request */
  60        header[0] = 0x02;
  61        header[1] = len;
  62
  63        qemu_chr_fe_write(s->chr, header, sizeof(header));
  64
  65        size -= len;
  66    }
  67
  68    s->requests = g_slist_append(s->requests, req);
  69}
  70
  71static void rng_egd_free_request(RngRequest *req)
  72{
  73    g_free(req->data);
  74    g_free(req);
  75}
  76
  77static int rng_egd_chr_can_read(void *opaque)
  78{
  79    RngEgd *s = RNG_EGD(opaque);
  80    GSList *i;
  81    int size = 0;
  82
  83    for (i = s->requests; i; i = i->next) {
  84        RngRequest *req = i->data;
  85        size += req->size - req->offset;
  86    }
  87
  88    return size;
  89}
  90
  91static void rng_egd_chr_read(void *opaque, const uint8_t *buf, int size)
  92{
  93    RngEgd *s = RNG_EGD(opaque);
  94    size_t buf_offset = 0;
  95
  96    while (size > 0 && s->requests) {
  97        RngRequest *req = s->requests->data;
  98        int len = MIN(size, req->size - req->offset);
  99
 100        memcpy(req->data + req->offset, buf + buf_offset, len);
 101        buf_offset += len;
 102        req->offset += len;
 103        size -= len;
 104
 105        if (req->offset == req->size) {
 106            s->requests = g_slist_remove_link(s->requests, s->requests);
 107
 108            req->receive_entropy(req->opaque, req->data, req->size);
 109
 110            rng_egd_free_request(req);
 111        }
 112    }
 113}
 114
 115static void rng_egd_free_requests(RngEgd *s)
 116{
 117    GSList *i;
 118
 119    for (i = s->requests; i; i = i->next) {
 120        rng_egd_free_request(i->data);
 121    }
 122
 123    g_slist_free(s->requests);
 124    s->requests = NULL;
 125}
 126
 127static void rng_egd_cancel_requests(RngBackend *b)
 128{
 129    RngEgd *s = RNG_EGD(b);
 130
 131    /* We simply delete the list of pending requests.  If there is data in the 
 132     * queue waiting to be read, this is okay, because there will always be
 133     * more data than we requested originally
 134     */
 135    rng_egd_free_requests(s);
 136}
 137
 138static void rng_egd_opened(RngBackend *b, Error **errp)
 139{
 140    RngEgd *s = RNG_EGD(b);
 141
 142    if (s->chr_name == NULL) {
 143        error_set(errp, QERR_INVALID_PARAMETER_VALUE,
 144                  "chardev", "a valid character device");
 145        return;
 146    }
 147
 148    s->chr = qemu_chr_find(s->chr_name);
 149    if (s->chr == NULL) {
 150        error_set(errp, QERR_DEVICE_NOT_FOUND, s->chr_name);
 151        return;
 152    }
 153
 154    if (qemu_chr_fe_claim(s->chr) != 0) {
 155        error_set(errp, QERR_DEVICE_IN_USE, s->chr_name);
 156        return;
 157    }
 158
 159    /* FIXME we should resubmit pending requests when the CDS reconnects. */
 160    qemu_chr_add_handlers(s->chr, rng_egd_chr_can_read, rng_egd_chr_read,
 161                          NULL, s);
 162}
 163
 164static void rng_egd_set_chardev(Object *obj, const char *value, Error **errp)
 165{
 166    RngBackend *b = RNG_BACKEND(obj);
 167    RngEgd *s = RNG_EGD(b);
 168
 169    if (b->opened) {
 170        error_set(errp, QERR_PERMISSION_DENIED);
 171    } else {
 172        s->chr_name = g_strdup(value);
 173    }
 174}
 175
 176static char *rng_egd_get_chardev(Object *obj, Error **errp)
 177{
 178    RngEgd *s = RNG_EGD(obj);
 179
 180    if (s->chr && s->chr->label) {
 181        return g_strdup(s->chr->label);
 182    }
 183
 184    return NULL;
 185}
 186
 187static void rng_egd_init(Object *obj)
 188{
 189    object_property_add_str(obj, "chardev",
 190                            rng_egd_get_chardev, rng_egd_set_chardev,
 191                            NULL);
 192}
 193
 194static void rng_egd_finalize(Object *obj)
 195{
 196    RngEgd *s = RNG_EGD(obj);
 197
 198    if (s->chr) {
 199        qemu_chr_add_handlers(s->chr, NULL, NULL, NULL, NULL);
 200        qemu_chr_fe_release(s->chr);
 201    }
 202
 203    g_free(s->chr_name);
 204
 205    rng_egd_free_requests(s);
 206}
 207
 208static void rng_egd_class_init(ObjectClass *klass, void *data)
 209{
 210    RngBackendClass *rbc = RNG_BACKEND_CLASS(klass);
 211
 212    rbc->request_entropy = rng_egd_request_entropy;
 213    rbc->cancel_requests = rng_egd_cancel_requests;
 214    rbc->opened = rng_egd_opened;
 215}
 216
 217static const TypeInfo rng_egd_info = {
 218    .name = TYPE_RNG_EGD,
 219    .parent = TYPE_RNG_BACKEND,
 220    .instance_size = sizeof(RngEgd),
 221    .class_init = rng_egd_class_init,
 222    .instance_init = rng_egd_init,
 223    .instance_finalize = rng_egd_finalize,
 224};
 225
 226static void register_types(void)
 227{
 228    type_register_static(&rng_egd_info);
 229}
 230
 231type_init(register_types);
 232