xref: /kvmtool/virtio/rng.c (revision 52f34d2c8bc1c54046a308830799f4ac3f58c81e)
1376ac44cSSasha Levin #include "kvm/virtio-rng.h"
2376ac44cSSasha Levin 
331638bcaSCyrill Gorcunov #include "kvm/virtio-pci-dev.h"
4376ac44cSSasha Levin 
5376ac44cSSasha Levin #include "kvm/virtio.h"
6376ac44cSSasha Levin #include "kvm/util.h"
7376ac44cSSasha Levin #include "kvm/kvm.h"
8376ac44cSSasha Levin #include "kvm/threadpool.h"
9c75b037fSSasha Levin #include "kvm/guest_compat.h"
10376ac44cSSasha Levin 
11376ac44cSSasha Levin #include <linux/virtio_ring.h>
12376ac44cSSasha Levin #include <linux/virtio_rng.h>
13376ac44cSSasha Levin 
1480ac1d05SSasha Levin #include <linux/list.h>
15376ac44cSSasha Levin #include <fcntl.h>
16376ac44cSSasha Levin #include <sys/types.h>
17376ac44cSSasha Levin #include <sys/stat.h>
18376ac44cSSasha Levin #include <pthread.h>
1953cbeb9bSSasha Levin #include <linux/kernel.h>
20376ac44cSSasha Levin 
21376ac44cSSasha Levin #define NUM_VIRT_QUEUES		1
22376ac44cSSasha Levin #define VIRTIO_RNG_QUEUE_SIZE	128
23376ac44cSSasha Levin 
2480ac1d05SSasha Levin struct rng_dev_job {
2580ac1d05SSasha Levin 	struct virt_queue	*vq;
2680ac1d05SSasha Levin 	struct rng_dev		*rdev;
27df0c7f57SSasha Levin 	struct thread_pool__job	job_id;
282449f6e3SSasha Levin };
292449f6e3SSasha Levin 
3080ffe4d1SSasha Levin struct rng_dev {
3180ac1d05SSasha Levin 	struct list_head	list;
3202eca50cSAsias He 	struct virtio_device	vdev;
3380ac1d05SSasha Levin 
3480ffe4d1SSasha Levin 	int			fd;
35376ac44cSSasha Levin 
36376ac44cSSasha Levin 	/* virtio queue */
37376ac44cSSasha Levin 	struct virt_queue	vqs[NUM_VIRT_QUEUES];
3880ac1d05SSasha Levin 	struct rng_dev_job	jobs[NUM_VIRT_QUEUES];
39376ac44cSSasha Levin };
40376ac44cSSasha Levin 
4180ac1d05SSasha Levin static LIST_HEAD(rdevs);
42312c62d1SSasha Levin static int compat_id = -1;
43376ac44cSSasha Levin 
4453cbeb9bSSasha Levin static void set_config(struct kvm *kvm, void *dev, u8 data, u32 offset)
45376ac44cSSasha Levin {
4653cbeb9bSSasha Levin 	/* Unused */
4753cbeb9bSSasha Levin }
48376ac44cSSasha Levin 
4953cbeb9bSSasha Levin static u8 get_config(struct kvm *kvm, void *dev, u32 offset)
5053cbeb9bSSasha Levin {
5153cbeb9bSSasha Levin 	/* Unused */
5253cbeb9bSSasha Levin 	return 0;
5353cbeb9bSSasha Levin }
54376ac44cSSasha Levin 
5553cbeb9bSSasha Levin static u32 get_host_features(struct kvm *kvm, void *dev)
5653cbeb9bSSasha Levin {
5753cbeb9bSSasha Levin 	/* Unused */
5853cbeb9bSSasha Levin 	return 0;
5953cbeb9bSSasha Levin }
60376ac44cSSasha Levin 
6153cbeb9bSSasha Levin static void set_guest_features(struct kvm *kvm, void *dev, u32 features)
6253cbeb9bSSasha Levin {
6353cbeb9bSSasha Levin 	/* Unused */
64376ac44cSSasha Levin }
65376ac44cSSasha Levin 
6680ac1d05SSasha Levin static bool virtio_rng_do_io_request(struct kvm *kvm, struct rng_dev *rdev, struct virt_queue *queue)
67376ac44cSSasha Levin {
68376ac44cSSasha Levin 	struct iovec iov[VIRTIO_RNG_QUEUE_SIZE];
69376ac44cSSasha Levin 	unsigned int len = 0;
70407475bfSPekka Enberg 	u16 out, in, head;
71376ac44cSSasha Levin 
7280ffe4d1SSasha Levin 	head	= virt_queue__get_iov(queue, iov, &out, &in, kvm);
7380ac1d05SSasha Levin 	len	= readv(rdev->fd, iov, in);
74407475bfSPekka Enberg 
75376ac44cSSasha Levin 	virt_queue__set_used_elem(queue, head, len);
76376ac44cSSasha Levin 
77376ac44cSSasha Levin 	return true;
78376ac44cSSasha Levin }
79376ac44cSSasha Levin 
80376ac44cSSasha Levin static void virtio_rng_do_io(struct kvm *kvm, void *param)
81376ac44cSSasha Levin {
8280ac1d05SSasha Levin 	struct rng_dev_job *job	= param;
8380ac1d05SSasha Levin 	struct virt_queue *vq	= job->vq;
8480ac1d05SSasha Levin 	struct rng_dev *rdev	= job->rdev;
85376ac44cSSasha Levin 
86bc485053SSasha Levin 	while (virt_queue__available(vq))
8780ac1d05SSasha Levin 		virtio_rng_do_io_request(kvm, rdev, vq);
88bc485053SSasha Levin 
8902eca50cSAsias He 	rdev->vdev.ops->signal_vq(kvm, &rdev->vdev, vq - rdev->vqs);
90376ac44cSSasha Levin }
91376ac44cSSasha Levin 
9253cbeb9bSSasha Levin static int init_vq(struct kvm *kvm, void *dev, u32 vq, u32 pfn)
93376ac44cSSasha Levin {
9453cbeb9bSSasha Levin 	struct rng_dev *rdev = dev;
95376ac44cSSasha Levin 	struct virt_queue *queue;
9680ac1d05SSasha Levin 	struct rng_dev_job *job;
97376ac44cSSasha Levin 	void *p;
98376ac44cSSasha Levin 
99312c62d1SSasha Levin 	compat__remove_message(compat_id);
100c75b037fSSasha Levin 
10153cbeb9bSSasha Levin 	queue		= &rdev->vqs[vq];
10253cbeb9bSSasha Levin 	queue->pfn	= pfn;
103aaf0b445SSasha Levin 	p		= guest_pfn_to_host(kvm, queue->pfn);
104376ac44cSSasha Levin 
10553cbeb9bSSasha Levin 	job = &rdev->jobs[vq];
10680ac1d05SSasha Levin 
107b8f43678SSasha Levin 	vring_init(&queue->vring, VIRTIO_RNG_QUEUE_SIZE, p, VIRTIO_PCI_VRING_ALIGN);
108376ac44cSSasha Levin 
10980ac1d05SSasha Levin 	*job = (struct rng_dev_job) {
11080ac1d05SSasha Levin 		.vq	= queue,
11180ac1d05SSasha Levin 		.rdev	= rdev,
11280ac1d05SSasha Levin 	};
11380ac1d05SSasha Levin 
11453cbeb9bSSasha Levin 	thread_pool__init_job(&job->job_id, kvm, virtio_rng_do_io, job);
11553cbeb9bSSasha Levin 
11653cbeb9bSSasha Levin 	return 0;
117b2533581SSasha Levin }
118c75b037fSSasha Levin 
11953cbeb9bSSasha Levin static int notify_vq(struct kvm *kvm, void *dev, u32 vq)
12053cbeb9bSSasha Levin {
12153cbeb9bSSasha Levin 	struct rng_dev *rdev = dev;
12253cbeb9bSSasha Levin 
12353cbeb9bSSasha Levin 	thread_pool__do_job(&rdev->jobs[vq].job_id);
12453cbeb9bSSasha Levin 
12553cbeb9bSSasha Levin 	return 0;
12653cbeb9bSSasha Levin }
12753cbeb9bSSasha Levin 
12853cbeb9bSSasha Levin static int get_pfn_vq(struct kvm *kvm, void *dev, u32 vq)
12953cbeb9bSSasha Levin {
13053cbeb9bSSasha Levin 	struct rng_dev *rdev = dev;
13153cbeb9bSSasha Levin 
13253cbeb9bSSasha Levin 	return rdev->vqs[vq].pfn;
13353cbeb9bSSasha Levin }
13453cbeb9bSSasha Levin 
13553cbeb9bSSasha Levin static int get_size_vq(struct kvm *kvm, void *dev, u32 vq)
13653cbeb9bSSasha Levin {
13753cbeb9bSSasha Levin 	return VIRTIO_RNG_QUEUE_SIZE;
13853cbeb9bSSasha Levin }
13953cbeb9bSSasha Levin 
1401c47ce69SSasha Levin static struct virtio_ops rng_dev_virtio_ops = (struct virtio_ops) {
1411c47ce69SSasha Levin 	.set_config		= set_config,
1421c47ce69SSasha Levin 	.get_config		= get_config,
1431c47ce69SSasha Levin 	.get_host_features	= get_host_features,
1441c47ce69SSasha Levin 	.set_guest_features	= set_guest_features,
1451c47ce69SSasha Levin 	.init_vq		= init_vq,
1461c47ce69SSasha Levin 	.notify_vq		= notify_vq,
1471c47ce69SSasha Levin 	.get_pfn_vq		= get_pfn_vq,
1481c47ce69SSasha Levin 	.get_size_vq		= get_size_vq,
1491c47ce69SSasha Levin };
1501c47ce69SSasha Levin 
151495fbd4eSSasha Levin int virtio_rng__init(struct kvm *kvm)
15253cbeb9bSSasha Levin {
15353cbeb9bSSasha Levin 	struct rng_dev *rdev;
154495fbd4eSSasha Levin 	int r;
15553cbeb9bSSasha Levin 
15653cbeb9bSSasha Levin 	rdev = malloc(sizeof(*rdev));
15753cbeb9bSSasha Levin 	if (rdev == NULL)
158495fbd4eSSasha Levin 		return -ENOMEM;
15953cbeb9bSSasha Levin 
16053cbeb9bSSasha Levin 	rdev->fd = open("/dev/urandom", O_RDONLY);
161495fbd4eSSasha Levin 	if (rdev->fd < 0) {
162495fbd4eSSasha Levin 		r = rdev->fd;
163495fbd4eSSasha Levin 		goto cleanup;
164495fbd4eSSasha Levin 	}
16553cbeb9bSSasha Levin 
16602eca50cSAsias He 	r = virtio_init(kvm, rdev, &rdev->vdev, &rng_dev_virtio_ops,
16702eca50cSAsias He 			VIRTIO_PCI, PCI_DEVICE_ID_VIRTIO_RNG, VIRTIO_ID_RNG, PCI_CLASS_RNG);
168495fbd4eSSasha Levin 	if (r < 0)
169495fbd4eSSasha Levin 		goto cleanup;
170495fbd4eSSasha Levin 
17153cbeb9bSSasha Levin 	list_add_tail(&rdev->list, &rdevs);
17253cbeb9bSSasha Levin 
173312c62d1SSasha Levin 	if (compat_id != -1)
174*52f34d2cSAsias He 		compat_id = virtio_compat_add_message("virtio-rng", "CONFIG_HW_RANDOM_VIRTIO");
175495fbd4eSSasha Levin 	return 0;
176495fbd4eSSasha Levin cleanup:
177495fbd4eSSasha Levin 	close(rdev->fd);
178495fbd4eSSasha Levin 	free(rdev);
179495fbd4eSSasha Levin 
180495fbd4eSSasha Levin 	return r;
18180ac1d05SSasha Levin }
18280ac1d05SSasha Levin 
183495fbd4eSSasha Levin int virtio_rng__exit(struct kvm *kvm)
18480ac1d05SSasha Levin {
1853a60be06SSasha Levin 	struct rng_dev *rdev, *tmp;
18680ac1d05SSasha Levin 
1873a60be06SSasha Levin 	list_for_each_entry_safe(rdev, tmp, &rdevs, list) {
18880ac1d05SSasha Levin 		list_del(&rdev->list);
18902eca50cSAsias He 		rdev->vdev.ops->exit(kvm, &rdev->vdev);
19080ac1d05SSasha Levin 		free(rdev);
19180ac1d05SSasha Levin 	}
192495fbd4eSSasha Levin 
193495fbd4eSSasha Levin 	return 0;
194376ac44cSSasha Levin }
195