xref: /kvmtool/virtio/rng.c (revision 1c47ce695a0c853fcca0dccf558c7f2d62d23715)
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"
10*1c47ce69SSasha Levin #include "kvm/virtio-trans.h"
11376ac44cSSasha Levin 
12376ac44cSSasha Levin #include <linux/virtio_ring.h>
13376ac44cSSasha Levin #include <linux/virtio_rng.h>
14376ac44cSSasha Levin 
1580ac1d05SSasha Levin #include <linux/list.h>
16376ac44cSSasha Levin #include <fcntl.h>
17376ac44cSSasha Levin #include <sys/types.h>
18376ac44cSSasha Levin #include <sys/stat.h>
19376ac44cSSasha Levin #include <pthread.h>
2053cbeb9bSSasha Levin #include <linux/kernel.h>
21376ac44cSSasha Levin 
22376ac44cSSasha Levin #define NUM_VIRT_QUEUES		1
23376ac44cSSasha Levin #define VIRTIO_RNG_QUEUE_SIZE	128
24376ac44cSSasha Levin 
2580ac1d05SSasha Levin struct rng_dev_job {
2680ac1d05SSasha Levin 	struct virt_queue	*vq;
2780ac1d05SSasha Levin 	struct rng_dev		*rdev;
28df0c7f57SSasha Levin 	struct thread_pool__job	job_id;
292449f6e3SSasha Levin };
302449f6e3SSasha Levin 
3180ffe4d1SSasha Levin struct rng_dev {
3280ac1d05SSasha Levin 	struct list_head	list;
33*1c47ce69SSasha Levin 	struct virtio_trans	vtrans;
3480ac1d05SSasha Levin 
3580ffe4d1SSasha Levin 	int			fd;
36376ac44cSSasha Levin 
37376ac44cSSasha Levin 	/* virtio queue */
38376ac44cSSasha Levin 	struct virt_queue	vqs[NUM_VIRT_QUEUES];
3980ac1d05SSasha Levin 	struct rng_dev_job	jobs[NUM_VIRT_QUEUES];
40376ac44cSSasha Levin };
41376ac44cSSasha Levin 
4280ac1d05SSasha Levin static LIST_HEAD(rdevs);
43312c62d1SSasha Levin static int compat_id = -1;
44376ac44cSSasha Levin 
4553cbeb9bSSasha Levin static void set_config(struct kvm *kvm, void *dev, u8 data, u32 offset)
46376ac44cSSasha Levin {
4753cbeb9bSSasha Levin 	/* Unused */
4853cbeb9bSSasha Levin }
49376ac44cSSasha Levin 
5053cbeb9bSSasha Levin static u8 get_config(struct kvm *kvm, void *dev, u32 offset)
5153cbeb9bSSasha Levin {
5253cbeb9bSSasha Levin 	/* Unused */
5353cbeb9bSSasha Levin 	return 0;
5453cbeb9bSSasha Levin }
55376ac44cSSasha Levin 
5653cbeb9bSSasha Levin static u32 get_host_features(struct kvm *kvm, void *dev)
5753cbeb9bSSasha Levin {
5853cbeb9bSSasha Levin 	/* Unused */
5953cbeb9bSSasha Levin 	return 0;
6053cbeb9bSSasha Levin }
61376ac44cSSasha Levin 
6253cbeb9bSSasha Levin static void set_guest_features(struct kvm *kvm, void *dev, u32 features)
6353cbeb9bSSasha Levin {
6453cbeb9bSSasha Levin 	/* Unused */
65376ac44cSSasha Levin }
66376ac44cSSasha Levin 
6780ac1d05SSasha Levin static bool virtio_rng_do_io_request(struct kvm *kvm, struct rng_dev *rdev, struct virt_queue *queue)
68376ac44cSSasha Levin {
69376ac44cSSasha Levin 	struct iovec iov[VIRTIO_RNG_QUEUE_SIZE];
70376ac44cSSasha Levin 	unsigned int len = 0;
71407475bfSPekka Enberg 	u16 out, in, head;
72376ac44cSSasha Levin 
7380ffe4d1SSasha Levin 	head		= virt_queue__get_iov(queue, iov, &out, &in, kvm);
7480ac1d05SSasha Levin 	len		= readv(rdev->fd, iov, in);
75407475bfSPekka Enberg 
76376ac44cSSasha Levin 	virt_queue__set_used_elem(queue, head, len);
77376ac44cSSasha Levin 
78376ac44cSSasha Levin 	return true;
79376ac44cSSasha Levin }
80376ac44cSSasha Levin 
81376ac44cSSasha Levin static void virtio_rng_do_io(struct kvm *kvm, void *param)
82376ac44cSSasha Levin {
8380ac1d05SSasha Levin 	struct rng_dev_job *job = param;
8480ac1d05SSasha Levin 	struct virt_queue *vq = job->vq;
8580ac1d05SSasha Levin 	struct rng_dev *rdev = job->rdev;
86376ac44cSSasha Levin 
87bc485053SSasha Levin 	while (virt_queue__available(vq))
8880ac1d05SSasha Levin 		virtio_rng_do_io_request(kvm, rdev, vq);
89bc485053SSasha Levin 
90*1c47ce69SSasha Levin 	rdev->vtrans.trans_ops->signal_vq(kvm, &rdev->vtrans, vq - rdev->vqs);
91376ac44cSSasha Levin }
92376ac44cSSasha Levin 
9353cbeb9bSSasha Levin static int init_vq(struct kvm *kvm, void *dev, u32 vq, u32 pfn)
94376ac44cSSasha Levin {
9553cbeb9bSSasha Levin 	struct rng_dev *rdev = dev;
96376ac44cSSasha Levin 	struct virt_queue *queue;
9780ac1d05SSasha Levin 	struct rng_dev_job *job;
98376ac44cSSasha Levin 	void *p;
99376ac44cSSasha Levin 
100312c62d1SSasha Levin 	compat__remove_message(compat_id);
101c75b037fSSasha Levin 
10253cbeb9bSSasha Levin 	queue			= &rdev->vqs[vq];
10353cbeb9bSSasha Levin 	queue->pfn		= pfn;
104aaf0b445SSasha Levin 	p			= guest_pfn_to_host(kvm, queue->pfn);
105376ac44cSSasha Levin 
10653cbeb9bSSasha Levin 	job = &rdev->jobs[vq];
10780ac1d05SSasha Levin 
108b8f43678SSasha Levin 	vring_init(&queue->vring, VIRTIO_RNG_QUEUE_SIZE, p, VIRTIO_PCI_VRING_ALIGN);
109376ac44cSSasha Levin 
11080ac1d05SSasha Levin 	*job		= (struct rng_dev_job) {
11180ac1d05SSasha Levin 		.vq		= queue,
11280ac1d05SSasha Levin 		.rdev		= rdev,
11380ac1d05SSasha Levin 	};
11480ac1d05SSasha Levin 
11553cbeb9bSSasha Levin 	thread_pool__init_job(&job->job_id, kvm, virtio_rng_do_io, job);
11653cbeb9bSSasha Levin 
11753cbeb9bSSasha Levin 	return 0;
118b2533581SSasha Levin }
119c75b037fSSasha Levin 
12053cbeb9bSSasha Levin static int notify_vq(struct kvm *kvm, void *dev, u32 vq)
12153cbeb9bSSasha Levin {
12253cbeb9bSSasha Levin 	struct rng_dev *rdev = dev;
12353cbeb9bSSasha Levin 
12453cbeb9bSSasha Levin 	thread_pool__do_job(&rdev->jobs[vq].job_id);
12553cbeb9bSSasha Levin 
12653cbeb9bSSasha Levin 	return 0;
12753cbeb9bSSasha Levin }
12853cbeb9bSSasha Levin 
12953cbeb9bSSasha Levin static int get_pfn_vq(struct kvm *kvm, void *dev, u32 vq)
13053cbeb9bSSasha Levin {
13153cbeb9bSSasha Levin 	struct rng_dev *rdev = dev;
13253cbeb9bSSasha Levin 
13353cbeb9bSSasha Levin 	return rdev->vqs[vq].pfn;
13453cbeb9bSSasha Levin }
13553cbeb9bSSasha Levin 
13653cbeb9bSSasha Levin static int get_size_vq(struct kvm *kvm, void *dev, u32 vq)
13753cbeb9bSSasha Levin {
13853cbeb9bSSasha Levin 	return VIRTIO_RNG_QUEUE_SIZE;
13953cbeb9bSSasha Levin }
14053cbeb9bSSasha Levin 
141*1c47ce69SSasha Levin static struct virtio_ops rng_dev_virtio_ops = (struct virtio_ops) {
142*1c47ce69SSasha Levin 	.set_config		= set_config,
143*1c47ce69SSasha Levin 	.get_config		= get_config,
144*1c47ce69SSasha Levin 	.get_host_features	= get_host_features,
145*1c47ce69SSasha Levin 	.set_guest_features	= set_guest_features,
146*1c47ce69SSasha Levin 	.init_vq		= init_vq,
147*1c47ce69SSasha Levin 	.notify_vq		= notify_vq,
148*1c47ce69SSasha Levin 	.get_pfn_vq		= get_pfn_vq,
149*1c47ce69SSasha Levin 	.get_size_vq		= get_size_vq,
150*1c47ce69SSasha Levin };
151*1c47ce69SSasha Levin 
15253cbeb9bSSasha Levin void virtio_rng__init(struct kvm *kvm)
15353cbeb9bSSasha Levin {
15453cbeb9bSSasha Levin 	struct rng_dev *rdev;
15553cbeb9bSSasha Levin 
15653cbeb9bSSasha Levin 	rdev = malloc(sizeof(*rdev));
15753cbeb9bSSasha Levin 	if (rdev == NULL)
15853cbeb9bSSasha Levin 		return;
15953cbeb9bSSasha Levin 
16053cbeb9bSSasha Levin 	rdev->fd = open("/dev/urandom", O_RDONLY);
16153cbeb9bSSasha Levin 	if (rdev->fd < 0)
16253cbeb9bSSasha Levin 		die("Failed initializing RNG");
16353cbeb9bSSasha Levin 
164*1c47ce69SSasha Levin 	virtio_trans_init(&rdev->vtrans, VIRTIO_PCI);
165*1c47ce69SSasha Levin 	rdev->vtrans.trans_ops->init(kvm, &rdev->vtrans, rdev, PCI_DEVICE_ID_VIRTIO_RNG,
166*1c47ce69SSasha Levin 					VIRTIO_ID_RNG, PCI_CLASS_RNG);
167*1c47ce69SSasha Levin 	rdev->vtrans.virtio_ops = &rng_dev_virtio_ops;
16853cbeb9bSSasha Levin 
16953cbeb9bSSasha Levin 	list_add_tail(&rdev->list, &rdevs);
17053cbeb9bSSasha Levin 
171312c62d1SSasha Levin 	if (compat_id != -1)
172312c62d1SSasha Levin 		compat_id = compat__add_message("virtio-rng device was not detected",
173c75b037fSSasha Levin 						"While you have requested a virtio-rng device, "
174fc835ab3SSasha Levin 						"the guest kernel did not initialize it.\n"
175fc835ab3SSasha Levin 						"Please make sure that the guest kernel was "
176fc835ab3SSasha Levin 						"compiled with CONFIG_HW_RANDOM_VIRTIO=y enabled "
177fc835ab3SSasha Levin 						"in its .config");
17880ac1d05SSasha Levin }
17980ac1d05SSasha Levin 
18080ac1d05SSasha Levin void virtio_rng__delete_all(struct kvm *kvm)
18180ac1d05SSasha Levin {
18280ac1d05SSasha Levin 	while (!list_empty(&rdevs)) {
18380ac1d05SSasha Levin 		struct rng_dev *rdev;
18480ac1d05SSasha Levin 
18580ac1d05SSasha Levin 		rdev = list_first_entry(&rdevs, struct rng_dev, list);
18680ac1d05SSasha Levin 		list_del(&rdev->list);
18780ac1d05SSasha Levin 		free(rdev);
18880ac1d05SSasha Levin 	}
189376ac44cSSasha Levin }
190