virtio-rng: hardware random number generator device
[qemu.git] / hw / virtio-rng.c
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 "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 VirtQueueElement elem;
26
27 /* Config data for the device -- currently only chardev */
28 VirtIORNGConf *conf;
29
30 /* Whether we've popped a vq element into 'elem' above */
31 bool popped;
32
33 RngBackend *rng;
34 } VirtIORNG;
35
36 static bool is_guest_ready(VirtIORNG *vrng)
37 {
38 if (virtio_queue_ready(vrng->vq)
39 && (vrng->vdev.status & VIRTIO_CONFIG_S_DRIVER_OK)) {
40 return true;
41 }
42 return false;
43 }
44
45 static size_t pop_an_elem(VirtIORNG *vrng)
46 {
47 size_t size;
48
49 if (!vrng->popped && !virtqueue_pop(vrng->vq, &vrng->elem)) {
50 return 0;
51 }
52 vrng->popped = true;
53
54 size = iov_size(vrng->elem.in_sg, vrng->elem.in_num);
55 return size;
56 }
57
58 /* Send data from a char device over to the guest */
59 static void chr_read(void *opaque, const void *buf, size_t size)
60 {
61 VirtIORNG *vrng = opaque;
62 size_t len;
63 int offset;
64
65 if (!is_guest_ready(vrng)) {
66 return;
67 }
68
69 offset = 0;
70 while (offset < size) {
71 if (!pop_an_elem(vrng)) {
72 break;
73 }
74 len = iov_from_buf(vrng->elem.in_sg, vrng->elem.in_num,
75 0, buf + offset, size - offset);
76 offset += len;
77
78 virtqueue_push(vrng->vq, &vrng->elem, len);
79 vrng->popped = false;
80 }
81 virtio_notify(&vrng->vdev, vrng->vq);
82
83 /*
84 * Lastly, if we had multiple elems queued by the guest, and we
85 * didn't have enough data to fill them all, indicate we want more
86 * data.
87 */
88 len = pop_an_elem(vrng);
89 if (len) {
90 rng_backend_request_entropy(vrng->rng, size, chr_read, vrng);
91 }
92 }
93
94 static void handle_input(VirtIODevice *vdev, VirtQueue *vq)
95 {
96 VirtIORNG *vrng = DO_UPCAST(VirtIORNG, vdev, vdev);
97 size_t size;
98
99 size = pop_an_elem(vrng);
100 if (size) {
101 rng_backend_request_entropy(vrng->rng, size, chr_read, vrng);
102 }
103 }
104
105 static uint32_t get_features(VirtIODevice *vdev, uint32_t f)
106 {
107 return f;
108 }
109
110 static void virtio_rng_save(QEMUFile *f, void *opaque)
111 {
112 VirtIORNG *vrng = opaque;
113
114 virtio_save(&vrng->vdev, f);
115
116 qemu_put_byte(f, vrng->popped);
117 if (vrng->popped) {
118 int i;
119
120 qemu_put_be32(f, vrng->elem.index);
121
122 qemu_put_be32(f, vrng->elem.in_num);
123 for (i = 0; i < vrng->elem.in_num; i++) {
124 qemu_put_be64(f, vrng->elem.in_addr[i]);
125 }
126
127 qemu_put_be32(f, vrng->elem.out_num);
128 for (i = 0; i < vrng->elem.out_num; i++) {
129 qemu_put_be64(f, vrng->elem.out_addr[i]);
130 }
131 }
132 }
133
134 static int virtio_rng_load(QEMUFile *f, void *opaque, int version_id)
135 {
136 VirtIORNG *vrng = opaque;
137
138 if (version_id != 1) {
139 return -EINVAL;
140 }
141 virtio_load(&vrng->vdev, f);
142
143 vrng->popped = qemu_get_byte(f);
144 if (vrng->popped) {
145 int i;
146
147 vrng->elem.index = qemu_get_be32(f);
148
149 vrng->elem.in_num = qemu_get_be32(f);
150 g_assert(vrng->elem.in_num < VIRTQUEUE_MAX_SIZE);
151 for (i = 0; i < vrng->elem.in_num; i++) {
152 vrng->elem.in_addr[i] = qemu_get_be64(f);
153 }
154
155 vrng->elem.out_num = qemu_get_be32(f);
156 g_assert(vrng->elem.out_num < VIRTQUEUE_MAX_SIZE);
157 for (i = 0; i < vrng->elem.out_num; i++) {
158 vrng->elem.out_addr[i] = qemu_get_be64(f);
159 }
160
161 virtqueue_map_sg(vrng->elem.in_sg, vrng->elem.in_addr,
162 vrng->elem.in_num, 1);
163 virtqueue_map_sg(vrng->elem.out_sg, vrng->elem.out_addr,
164 vrng->elem.out_num, 0);
165 }
166 return 0;
167 }
168
169 VirtIODevice *virtio_rng_init(DeviceState *dev, VirtIORNGConf *conf)
170 {
171 VirtIORNG *vrng;
172 VirtIODevice *vdev;
173 Error *local_err = NULL;
174
175 vdev = virtio_common_init("virtio-rng", VIRTIO_ID_RNG, 0,
176 sizeof(VirtIORNG));
177
178 vrng = DO_UPCAST(VirtIORNG, vdev, vdev);
179
180 vrng->rng = conf->rng;
181 if (vrng->rng == NULL) {
182 qerror_report(QERR_INVALID_PARAMETER_VALUE, "rng", "a valid object");
183 return NULL;
184 }
185
186 rng_backend_open(vrng->rng, &local_err);
187 if (local_err) {
188 qerror_report_err(local_err);
189 error_free(local_err);
190 return NULL;
191 }
192
193 vrng->vq = virtio_add_queue(vdev, 8, handle_input);
194 vrng->vdev.get_features = get_features;
195
196 vrng->qdev = dev;
197 vrng->conf = conf;
198 vrng->popped = false;
199 register_savevm(dev, "virtio-rng", -1, 1, virtio_rng_save,
200 virtio_rng_load, vrng);
201
202 return vdev;
203 }
204
205 void virtio_rng_exit(VirtIODevice *vdev)
206 {
207 VirtIORNG *vrng = DO_UPCAST(VirtIORNG, vdev, vdev);
208
209 unregister_savevm(vrng->qdev, "virtio-rng", vrng);
210 virtio_cleanup(vdev);
211 }