]>
Commit | Line | Data |
---|---|---|
16c915ba AS |
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 | ||
1de7afc9 | 12 | #include "qemu/iov.h" |
83c9f4ca | 13 | #include "hw/qdev.h" |
b4a42f81 | 14 | #include "qapi/qmp/qerror.h" |
0d09e41a PB |
15 | #include "hw/virtio/virtio.h" |
16 | #include "hw/virtio/virtio-rng.h" | |
dccfcd0e | 17 | #include "sysemu/rng.h" |
16c915ba | 18 | |
16c915ba AS |
19 | static bool is_guest_ready(VirtIORNG *vrng) |
20 | { | |
611aa333 | 21 | VirtIODevice *vdev = VIRTIO_DEVICE(vrng); |
16c915ba | 22 | if (virtio_queue_ready(vrng->vq) |
611aa333 | 23 | && (vdev->status & VIRTIO_CONFIG_S_DRIVER_OK)) { |
16c915ba AS |
24 | return true; |
25 | } | |
26 | return false; | |
27 | } | |
28 | ||
e1f7b481 | 29 | static size_t get_request_size(VirtQueue *vq, unsigned quota) |
16c915ba | 30 | { |
14417039 | 31 | unsigned int in, out; |
16c915ba | 32 | |
e1f7b481 | 33 | virtqueue_get_avail_bytes(vq, &in, &out, quota, 0); |
14417039 | 34 | return in; |
16c915ba AS |
35 | } |
36 | ||
904d6f58 AL |
37 | static void virtio_rng_process(VirtIORNG *vrng); |
38 | ||
16c915ba AS |
39 | /* Send data from a char device over to the guest */ |
40 | static void chr_read(void *opaque, const void *buf, size_t size) | |
41 | { | |
42 | VirtIORNG *vrng = opaque; | |
611aa333 | 43 | VirtIODevice *vdev = VIRTIO_DEVICE(vrng); |
14417039 | 44 | VirtQueueElement elem; |
16c915ba AS |
45 | size_t len; |
46 | int offset; | |
47 | ||
48 | if (!is_guest_ready(vrng)) { | |
49 | return; | |
50 | } | |
51 | ||
904d6f58 AL |
52 | vrng->quota_remaining -= size; |
53 | ||
16c915ba AS |
54 | offset = 0; |
55 | while (offset < size) { | |
14417039 | 56 | if (!virtqueue_pop(vrng->vq, &elem)) { |
16c915ba AS |
57 | break; |
58 | } | |
14417039 | 59 | len = iov_from_buf(elem.in_sg, elem.in_num, |
16c915ba AS |
60 | 0, buf + offset, size - offset); |
61 | offset += len; | |
62 | ||
14417039 | 63 | virtqueue_push(vrng->vq, &elem, len); |
16c915ba | 64 | } |
611aa333 | 65 | virtio_notify(vdev, vrng->vq); |
16c915ba AS |
66 | } |
67 | ||
904d6f58 | 68 | static void virtio_rng_process(VirtIORNG *vrng) |
16c915ba | 69 | { |
14417039 | 70 | size_t size; |
e1f7b481 | 71 | unsigned quota; |
904d6f58 AL |
72 | |
73 | if (!is_guest_ready(vrng)) { | |
74 | return; | |
75 | } | |
16c915ba | 76 | |
e1f7b481 MT |
77 | if (vrng->quota_remaining < 0) { |
78 | quota = 0; | |
79 | } else { | |
80 | quota = MIN((uint64_t)vrng->quota_remaining, (uint64_t)UINT32_MAX); | |
81 | } | |
82 | size = get_request_size(vrng->vq, quota); | |
904d6f58 | 83 | size = MIN(vrng->quota_remaining, size); |
14417039 | 84 | if (size) { |
16c915ba AS |
85 | rng_backend_request_entropy(vrng->rng, size, chr_read, vrng); |
86 | } | |
87 | } | |
88 | ||
904d6f58 AL |
89 | static void handle_input(VirtIODevice *vdev, VirtQueue *vq) |
90 | { | |
611aa333 | 91 | VirtIORNG *vrng = VIRTIO_RNG(vdev); |
904d6f58 AL |
92 | virtio_rng_process(vrng); |
93 | } | |
94 | ||
16c915ba AS |
95 | static uint32_t get_features(VirtIODevice *vdev, uint32_t f) |
96 | { | |
97 | return f; | |
98 | } | |
99 | ||
100 | static void virtio_rng_save(QEMUFile *f, void *opaque) | |
101 | { | |
611aa333 | 102 | VirtIODevice *vdev = opaque; |
16c915ba | 103 | |
611aa333 | 104 | virtio_save(vdev, f); |
16c915ba AS |
105 | } |
106 | ||
107 | static int virtio_rng_load(QEMUFile *f, void *opaque, int version_id) | |
108 | { | |
109 | VirtIORNG *vrng = opaque; | |
611aa333 | 110 | VirtIODevice *vdev = VIRTIO_DEVICE(vrng); |
16c915ba AS |
111 | |
112 | if (version_id != 1) { | |
113 | return -EINVAL; | |
114 | } | |
611aa333 | 115 | virtio_load(vdev, f); |
16c915ba | 116 | |
904d6f58 | 117 | /* We may have an element ready but couldn't process it due to a quota |
42015c9a AS |
118 | * limit. Make sure to try again after live migration when the quota may |
119 | * have been reset. | |
120 | */ | |
904d6f58 AL |
121 | virtio_rng_process(vrng); |
122 | ||
16c915ba AS |
123 | return 0; |
124 | } | |
125 | ||
904d6f58 AL |
126 | static void check_rate_limit(void *opaque) |
127 | { | |
611aa333 | 128 | VirtIORNG *vrng = opaque; |
904d6f58 | 129 | |
611aa333 KF |
130 | vrng->quota_remaining = vrng->conf.max_bytes; |
131 | virtio_rng_process(vrng); | |
bc72ad67 AB |
132 | timer_mod(vrng->rate_limit_timer, |
133 | qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) + vrng->conf.period_ms); | |
904d6f58 AL |
134 | } |
135 | ||
46a5a89d | 136 | static int virtio_rng_device_init(VirtIODevice *vdev) |
16c915ba | 137 | { |
46a5a89d KF |
138 | DeviceState *qdev = DEVICE(vdev); |
139 | VirtIORNG *vrng = VIRTIO_RNG(vdev); | |
16c915ba AS |
140 | Error *local_err = NULL; |
141 | ||
46a5a89d KF |
142 | if (vrng->conf.rng == NULL) { |
143 | vrng->conf.default_backend = RNG_RANDOM(object_new(TYPE_RNG_RANDOM)); | |
144 | ||
145 | object_property_add_child(OBJECT(qdev), | |
146 | "default-backend", | |
147 | OBJECT(vrng->conf.default_backend), | |
148 | NULL); | |
149 | ||
150 | object_property_set_link(OBJECT(qdev), | |
151 | OBJECT(vrng->conf.default_backend), | |
152 | "rng", NULL); | |
6eac8aec | 153 | } |
16c915ba | 154 | |
46a5a89d KF |
155 | virtio_init(vdev, "virtio-rng", VIRTIO_ID_RNG, 0); |
156 | ||
157 | vrng->rng = vrng->conf.rng; | |
16c915ba AS |
158 | if (vrng->rng == NULL) { |
159 | qerror_report(QERR_INVALID_PARAMETER_VALUE, "rng", "a valid object"); | |
46a5a89d | 160 | return -1; |
16c915ba AS |
161 | } |
162 | ||
163 | rng_backend_open(vrng->rng, &local_err); | |
164 | if (local_err) { | |
165 | qerror_report_err(local_err); | |
166 | error_free(local_err); | |
46a5a89d | 167 | return -1; |
16c915ba AS |
168 | } |
169 | ||
170 | vrng->vq = virtio_add_queue(vdev, 8, handle_input); | |
6eac8aec | 171 | |
af1a8ad6 KF |
172 | assert(vrng->conf.max_bytes <= INT64_MAX); |
173 | vrng->quota_remaining = vrng->conf.max_bytes; | |
904d6f58 | 174 | |
bc72ad67 | 175 | vrng->rate_limit_timer = timer_new_ms(QEMU_CLOCK_VIRTUAL, |
904d6f58 AL |
176 | check_rate_limit, vrng); |
177 | ||
bc72ad67 AB |
178 | timer_mod(vrng->rate_limit_timer, |
179 | qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) + vrng->conf.period_ms); | |
904d6f58 | 180 | |
46a5a89d | 181 | register_savevm(qdev, "virtio-rng", -1, 1, virtio_rng_save, |
16c915ba AS |
182 | virtio_rng_load, vrng); |
183 | ||
6eac8aec KF |
184 | return 0; |
185 | } | |
186 | ||
187 | static int virtio_rng_device_exit(DeviceState *qdev) | |
188 | { | |
189 | VirtIORNG *vrng = VIRTIO_RNG(qdev); | |
190 | VirtIODevice *vdev = VIRTIO_DEVICE(qdev); | |
191 | ||
bc72ad67 AB |
192 | timer_del(vrng->rate_limit_timer); |
193 | timer_free(vrng->rate_limit_timer); | |
6eac8aec | 194 | unregister_savevm(qdev, "virtio-rng", vrng); |
6a1a8cc7 | 195 | virtio_cleanup(vdev); |
6eac8aec KF |
196 | return 0; |
197 | } | |
198 | ||
199 | static Property virtio_rng_properties[] = { | |
200 | DEFINE_VIRTIO_RNG_PROPERTIES(VirtIORNG, conf), | |
201 | DEFINE_PROP_END_OF_LIST(), | |
202 | }; | |
203 | ||
204 | static void virtio_rng_class_init(ObjectClass *klass, void *data) | |
205 | { | |
206 | DeviceClass *dc = DEVICE_CLASS(klass); | |
207 | VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass); | |
208 | dc->exit = virtio_rng_device_exit; | |
209 | dc->props = virtio_rng_properties; | |
125ee0ed | 210 | set_bit(DEVICE_CATEGORY_MISC, dc->categories); |
6eac8aec KF |
211 | vdc->init = virtio_rng_device_init; |
212 | vdc->get_features = get_features; | |
213 | } | |
214 | ||
215 | static void virtio_rng_initfn(Object *obj) | |
216 | { | |
217 | VirtIORNG *vrng = VIRTIO_RNG(obj); | |
218 | ||
219 | object_property_add_link(obj, "rng", TYPE_RNG_BACKEND, | |
220 | (Object **)&vrng->conf.rng, NULL); | |
221 | } | |
222 | ||
223 | static const TypeInfo virtio_rng_info = { | |
224 | .name = TYPE_VIRTIO_RNG, | |
225 | .parent = TYPE_VIRTIO_DEVICE, | |
226 | .instance_size = sizeof(VirtIORNG), | |
227 | .instance_init = virtio_rng_initfn, | |
228 | .class_init = virtio_rng_class_init, | |
229 | }; | |
230 | ||
231 | static void virtio_register_types(void) | |
232 | { | |
233 | type_register_static(&virtio_rng_info); | |
234 | } | |
235 | ||
236 | type_init(virtio_register_types) |