root / hw / virtio-rng.c @ 5e22c276
History | View | Annotate | Download (4.9 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 "qdev.h" |
14 |
#include "virtio.h" |
15 |
#include "virtio-rng.h" |
16 |
#include "qemu/rng.h" |
17 |
|
18 |
typedef 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 |
|
37 |
static 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 |
|
46 |
static 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 |
|
54 |
static void virtio_rng_process(VirtIORNG *vrng); |
55 |
|
56 |
/* Send data from a char device over to the guest */
|
57 |
static 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 |
|
84 |
static 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 |
|
105 |
static void handle_input(VirtIODevice *vdev, VirtQueue *vq) |
106 |
{ |
107 |
VirtIORNG *vrng = DO_UPCAST(VirtIORNG, vdev, vdev); |
108 |
virtio_rng_process(vrng); |
109 |
} |
110 |
|
111 |
static uint32_t get_features(VirtIODevice *vdev, uint32_t f)
|
112 |
{ |
113 |
return f;
|
114 |
} |
115 |
|
116 |
static void virtio_rng_save(QEMUFile *f, void *opaque) |
117 |
{ |
118 |
VirtIORNG *vrng = opaque; |
119 |
|
120 |
virtio_save(&vrng->vdev, f); |
121 |
} |
122 |
|
123 |
static 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 |
|
141 |
static 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 |
|
152 |
VirtIODevice *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 |
|
197 |
void 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 |
} |