root / hw / virtio / virtio-rng.c @ 49ab747f
History | View | Annotate | Download (4.5 kB)
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 "hw/qdev.h" |
14 |
#include "qapi/qmp/qerror.h" |
15 |
#include "hw/virtio/virtio.h" |
16 |
#include "hw/virtio/virtio-rng.h" |
17 |
#include "qemu/rng.h" |
18 |
|
19 |
static bool is_guest_ready(VirtIORNG *vrng) |
20 |
{ |
21 |
if (virtio_queue_ready(vrng->vq)
|
22 |
&& (vrng->vdev.status & VIRTIO_CONFIG_S_DRIVER_OK)) { |
23 |
return true; |
24 |
} |
25 |
return false; |
26 |
} |
27 |
|
28 |
static size_t get_request_size(VirtQueue *vq, unsigned quota) |
29 |
{ |
30 |
unsigned int in, out; |
31 |
|
32 |
virtqueue_get_avail_bytes(vq, &in, &out, quota, 0);
|
33 |
return in;
|
34 |
} |
35 |
|
36 |
static void virtio_rng_process(VirtIORNG *vrng); |
37 |
|
38 |
/* Send data from a char device over to the guest */
|
39 |
static void chr_read(void *opaque, const void *buf, size_t size) |
40 |
{ |
41 |
VirtIORNG *vrng = opaque; |
42 |
VirtQueueElement elem; |
43 |
size_t len; |
44 |
int offset;
|
45 |
|
46 |
if (!is_guest_ready(vrng)) {
|
47 |
return;
|
48 |
} |
49 |
|
50 |
vrng->quota_remaining -= size; |
51 |
|
52 |
offset = 0;
|
53 |
while (offset < size) {
|
54 |
if (!virtqueue_pop(vrng->vq, &elem)) {
|
55 |
break;
|
56 |
} |
57 |
len = iov_from_buf(elem.in_sg, elem.in_num, |
58 |
0, buf + offset, size - offset);
|
59 |
offset += len; |
60 |
|
61 |
virtqueue_push(vrng->vq, &elem, len); |
62 |
} |
63 |
virtio_notify(&vrng->vdev, vrng->vq); |
64 |
} |
65 |
|
66 |
static void virtio_rng_process(VirtIORNG *vrng) |
67 |
{ |
68 |
size_t size; |
69 |
unsigned quota;
|
70 |
|
71 |
if (!is_guest_ready(vrng)) {
|
72 |
return;
|
73 |
} |
74 |
|
75 |
if (vrng->quota_remaining < 0) { |
76 |
quota = 0;
|
77 |
} else {
|
78 |
quota = MIN((uint64_t)vrng->quota_remaining, (uint64_t)UINT32_MAX); |
79 |
} |
80 |
size = get_request_size(vrng->vq, quota); |
81 |
size = MIN(vrng->quota_remaining, size); |
82 |
if (size) {
|
83 |
rng_backend_request_entropy(vrng->rng, size, chr_read, vrng); |
84 |
} |
85 |
} |
86 |
|
87 |
static void handle_input(VirtIODevice *vdev, VirtQueue *vq) |
88 |
{ |
89 |
VirtIORNG *vrng = DO_UPCAST(VirtIORNG, vdev, vdev); |
90 |
virtio_rng_process(vrng); |
91 |
} |
92 |
|
93 |
static uint32_t get_features(VirtIODevice *vdev, uint32_t f)
|
94 |
{ |
95 |
return f;
|
96 |
} |
97 |
|
98 |
static void virtio_rng_save(QEMUFile *f, void *opaque) |
99 |
{ |
100 |
VirtIORNG *vrng = opaque; |
101 |
|
102 |
virtio_save(&vrng->vdev, f); |
103 |
} |
104 |
|
105 |
static int virtio_rng_load(QEMUFile *f, void *opaque, int version_id) |
106 |
{ |
107 |
VirtIORNG *vrng = opaque; |
108 |
|
109 |
if (version_id != 1) { |
110 |
return -EINVAL;
|
111 |
} |
112 |
virtio_load(&vrng->vdev, f); |
113 |
|
114 |
/* We may have an element ready but couldn't process it due to a quota
|
115 |
* limit. Make sure to try again after live migration when the quota may
|
116 |
* have been reset.
|
117 |
*/
|
118 |
virtio_rng_process(vrng); |
119 |
|
120 |
return 0; |
121 |
} |
122 |
|
123 |
static void check_rate_limit(void *opaque) |
124 |
{ |
125 |
VirtIORNG *s = opaque; |
126 |
|
127 |
s->quota_remaining = s->conf->max_bytes; |
128 |
virtio_rng_process(s); |
129 |
qemu_mod_timer(s->rate_limit_timer, |
130 |
qemu_get_clock_ms(vm_clock) + s->conf->period_ms); |
131 |
} |
132 |
|
133 |
|
134 |
VirtIODevice *virtio_rng_init(DeviceState *dev, VirtIORNGConf *conf) |
135 |
{ |
136 |
VirtIORNG *vrng; |
137 |
VirtIODevice *vdev; |
138 |
Error *local_err = NULL;
|
139 |
|
140 |
vdev = virtio_common_init("virtio-rng", VIRTIO_ID_RNG, 0, |
141 |
sizeof(VirtIORNG));
|
142 |
|
143 |
vrng = DO_UPCAST(VirtIORNG, vdev, vdev); |
144 |
|
145 |
vrng->rng = conf->rng; |
146 |
if (vrng->rng == NULL) { |
147 |
qerror_report(QERR_INVALID_PARAMETER_VALUE, "rng", "a valid object"); |
148 |
return NULL; |
149 |
} |
150 |
|
151 |
rng_backend_open(vrng->rng, &local_err); |
152 |
if (local_err) {
|
153 |
qerror_report_err(local_err); |
154 |
error_free(local_err); |
155 |
return NULL; |
156 |
} |
157 |
|
158 |
vrng->vq = virtio_add_queue(vdev, 8, handle_input);
|
159 |
vrng->vdev.get_features = get_features; |
160 |
|
161 |
vrng->qdev = dev; |
162 |
vrng->conf = conf; |
163 |
|
164 |
assert(vrng->conf->max_bytes <= INT64_MAX); |
165 |
vrng->quota_remaining = vrng->conf->max_bytes; |
166 |
|
167 |
vrng->rate_limit_timer = qemu_new_timer_ms(vm_clock, |
168 |
check_rate_limit, vrng); |
169 |
|
170 |
qemu_mod_timer(vrng->rate_limit_timer, |
171 |
qemu_get_clock_ms(vm_clock) + vrng->conf->period_ms); |
172 |
|
173 |
register_savevm(dev, "virtio-rng", -1, 1, virtio_rng_save, |
174 |
virtio_rng_load, vrng); |
175 |
|
176 |
return vdev;
|
177 |
} |
178 |
|
179 |
void virtio_rng_exit(VirtIODevice *vdev)
|
180 |
{ |
181 |
VirtIORNG *vrng = DO_UPCAST(VirtIORNG, vdev, vdev); |
182 |
|
183 |
qemu_del_timer(vrng->rate_limit_timer); |
184 |
qemu_free_timer(vrng->rate_limit_timer); |
185 |
unregister_savevm(vrng->qdev, "virtio-rng", vrng);
|
186 |
virtio_cleanup(vdev); |
187 |
} |