xref: /kvmtool/virtio/rng.c (revision 53cbeb9b7c9e9d26bca2f256f87cc83ed9a44159)
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"
9b2533581SSasha Levin #include "kvm/ioeventfd.h"
10c75b037fSSasha Levin #include "kvm/guest_compat.h"
11*53cbeb9bSSasha Levin #include "kvm/virtio-pci.h"
12376ac44cSSasha Levin 
13376ac44cSSasha Levin #include <linux/virtio_ring.h>
14376ac44cSSasha Levin #include <linux/virtio_rng.h>
15376ac44cSSasha Levin 
1680ac1d05SSasha Levin #include <linux/list.h>
17376ac44cSSasha Levin #include <fcntl.h>
18376ac44cSSasha Levin #include <sys/types.h>
19376ac44cSSasha Levin #include <sys/stat.h>
20376ac44cSSasha Levin #include <pthread.h>
21*53cbeb9bSSasha Levin #include <linux/kernel.h>
22376ac44cSSasha Levin 
23376ac44cSSasha Levin #define NUM_VIRT_QUEUES		1
24376ac44cSSasha Levin #define VIRTIO_RNG_QUEUE_SIZE	128
25376ac44cSSasha Levin 
2680ac1d05SSasha Levin struct rng_dev_job {
2780ac1d05SSasha Levin 	struct virt_queue	*vq;
2880ac1d05SSasha Levin 	struct rng_dev		*rdev;
29df0c7f57SSasha Levin 	struct thread_pool__job	job_id;
302449f6e3SSasha Levin };
312449f6e3SSasha Levin 
3280ffe4d1SSasha Levin struct rng_dev {
3380ac1d05SSasha Levin 	struct list_head	list;
34*53cbeb9bSSasha Levin 	struct virtio_pci	vpci;
3580ac1d05SSasha Levin 
3680ffe4d1SSasha Levin 	int			fd;
37c75b037fSSasha Levin 	int			compat_id;
38376ac44cSSasha Levin 
39376ac44cSSasha Levin 	/* virtio queue */
40376ac44cSSasha Levin 	struct virt_queue	vqs[NUM_VIRT_QUEUES];
4180ac1d05SSasha Levin 	struct rng_dev_job	jobs[NUM_VIRT_QUEUES];
42376ac44cSSasha Levin };
43376ac44cSSasha Levin 
4480ac1d05SSasha Levin static LIST_HEAD(rdevs);
45376ac44cSSasha Levin 
46*53cbeb9bSSasha Levin static void set_config(struct kvm *kvm, void *dev, u8 data, u32 offset)
47376ac44cSSasha Levin {
48*53cbeb9bSSasha Levin 	/* Unused */
49*53cbeb9bSSasha Levin }
50376ac44cSSasha Levin 
51*53cbeb9bSSasha Levin static u8 get_config(struct kvm *kvm, void *dev, u32 offset)
52*53cbeb9bSSasha Levin {
53*53cbeb9bSSasha Levin 	/* Unused */
54*53cbeb9bSSasha Levin 	return 0;
55*53cbeb9bSSasha Levin }
56376ac44cSSasha Levin 
57*53cbeb9bSSasha Levin static u32 get_host_features(struct kvm *kvm, void *dev)
58*53cbeb9bSSasha Levin {
59*53cbeb9bSSasha Levin 	/* Unused */
60*53cbeb9bSSasha Levin 	return 0;
61*53cbeb9bSSasha Levin }
62376ac44cSSasha Levin 
63*53cbeb9bSSasha Levin static void set_guest_features(struct kvm *kvm, void *dev, u32 features)
64*53cbeb9bSSasha Levin {
65*53cbeb9bSSasha Levin 	/* Unused */
66376ac44cSSasha Levin }
67376ac44cSSasha Levin 
6880ac1d05SSasha Levin static bool virtio_rng_do_io_request(struct kvm *kvm, struct rng_dev *rdev, struct virt_queue *queue)
69376ac44cSSasha Levin {
70376ac44cSSasha Levin 	struct iovec iov[VIRTIO_RNG_QUEUE_SIZE];
71376ac44cSSasha Levin 	unsigned int len = 0;
72407475bfSPekka Enberg 	u16 out, in, head;
73376ac44cSSasha Levin 
7480ffe4d1SSasha Levin 	head		= virt_queue__get_iov(queue, iov, &out, &in, kvm);
7580ac1d05SSasha Levin 	len		= readv(rdev->fd, iov, in);
76407475bfSPekka Enberg 
77376ac44cSSasha Levin 	virt_queue__set_used_elem(queue, head, len);
78376ac44cSSasha Levin 
79376ac44cSSasha Levin 	return true;
80376ac44cSSasha Levin }
81376ac44cSSasha Levin 
82376ac44cSSasha Levin static void virtio_rng_do_io(struct kvm *kvm, void *param)
83376ac44cSSasha Levin {
8480ac1d05SSasha Levin 	struct rng_dev_job *job = param;
8580ac1d05SSasha Levin 	struct virt_queue *vq = job->vq;
8680ac1d05SSasha Levin 	struct rng_dev *rdev = job->rdev;
87376ac44cSSasha Levin 
88bc485053SSasha Levin 	while (virt_queue__available(vq))
8980ac1d05SSasha Levin 		virtio_rng_do_io_request(kvm, rdev, vq);
90bc485053SSasha Levin 
91*53cbeb9bSSasha Levin 	virtio_pci__signal_vq(kvm, &rdev->vpci, vq - rdev->vqs);
92376ac44cSSasha Levin }
93376ac44cSSasha Levin 
94*53cbeb9bSSasha Levin static int init_vq(struct kvm *kvm, void *dev, u32 vq, u32 pfn)
95376ac44cSSasha Levin {
96*53cbeb9bSSasha Levin 	struct rng_dev *rdev = dev;
97376ac44cSSasha Levin 	struct virt_queue *queue;
9880ac1d05SSasha Levin 	struct rng_dev_job *job;
99376ac44cSSasha Levin 	void *p;
100*53cbeb9bSSasha Levin 	struct ioevent ioevent;
101376ac44cSSasha Levin 
102c75b037fSSasha Levin 	compat__remove_message(rdev->compat_id);
103c75b037fSSasha Levin 
104*53cbeb9bSSasha Levin 	queue			= &rdev->vqs[vq];
105*53cbeb9bSSasha Levin 	queue->pfn		= pfn;
106aaf0b445SSasha Levin 	p			= guest_pfn_to_host(kvm, queue->pfn);
107376ac44cSSasha Levin 
108*53cbeb9bSSasha Levin 	job = &rdev->jobs[vq];
10980ac1d05SSasha Levin 
110b8f43678SSasha Levin 	vring_init(&queue->vring, VIRTIO_RNG_QUEUE_SIZE, p, VIRTIO_PCI_VRING_ALIGN);
111376ac44cSSasha Levin 
11280ac1d05SSasha Levin 	*job		= (struct rng_dev_job) {
11380ac1d05SSasha Levin 		.vq		= queue,
11480ac1d05SSasha Levin 		.rdev		= rdev,
11580ac1d05SSasha Levin 	};
11680ac1d05SSasha Levin 
117b2533581SSasha Levin 	ioevent = (struct ioevent) {
118*53cbeb9bSSasha Levin 		.io_addr	= rdev->vpci.base_addr + VIRTIO_PCI_QUEUE_NOTIFY,
119b2533581SSasha Levin 		.io_len		= sizeof(u16),
120*53cbeb9bSSasha Levin 		.fn		= virtio_rng_do_io,
121*53cbeb9bSSasha Levin 		.fn_ptr		= &rdev->jobs[vq],
122*53cbeb9bSSasha Levin 		.datamatch	= vq,
123b2533581SSasha Levin 		.fn_kvm		= kvm,
124b2533581SSasha Levin 		.fd		= eventfd(0, 0),
125b2533581SSasha Levin 	};
126b2533581SSasha Levin 
127b2533581SSasha Levin 	ioeventfd__add_event(&ioevent);
128*53cbeb9bSSasha Levin 
129*53cbeb9bSSasha Levin 	thread_pool__init_job(&job->job_id, kvm, virtio_rng_do_io, job);
130*53cbeb9bSSasha Levin 
131*53cbeb9bSSasha Levin 	return 0;
132b2533581SSasha Levin }
133c75b037fSSasha Levin 
134*53cbeb9bSSasha Levin static int notify_vq(struct kvm *kvm, void *dev, u32 vq)
135*53cbeb9bSSasha Levin {
136*53cbeb9bSSasha Levin 	struct rng_dev *rdev = dev;
137*53cbeb9bSSasha Levin 
138*53cbeb9bSSasha Levin 	thread_pool__do_job(&rdev->jobs[vq].job_id);
139*53cbeb9bSSasha Levin 
140*53cbeb9bSSasha Levin 	return 0;
141*53cbeb9bSSasha Levin }
142*53cbeb9bSSasha Levin 
143*53cbeb9bSSasha Levin static int get_pfn_vq(struct kvm *kvm, void *dev, u32 vq)
144*53cbeb9bSSasha Levin {
145*53cbeb9bSSasha Levin 	struct rng_dev *rdev = dev;
146*53cbeb9bSSasha Levin 
147*53cbeb9bSSasha Levin 	return rdev->vqs[vq].pfn;
148*53cbeb9bSSasha Levin }
149*53cbeb9bSSasha Levin 
150*53cbeb9bSSasha Levin static int get_size_vq(struct kvm *kvm, void *dev, u32 vq)
151*53cbeb9bSSasha Levin {
152*53cbeb9bSSasha Levin 	return VIRTIO_RNG_QUEUE_SIZE;
153*53cbeb9bSSasha Levin }
154*53cbeb9bSSasha Levin 
155*53cbeb9bSSasha Levin void virtio_rng__init(struct kvm *kvm)
156*53cbeb9bSSasha Levin {
157*53cbeb9bSSasha Levin 	struct rng_dev *rdev;
158*53cbeb9bSSasha Levin 
159*53cbeb9bSSasha Levin 	rdev = malloc(sizeof(*rdev));
160*53cbeb9bSSasha Levin 	if (rdev == NULL)
161*53cbeb9bSSasha Levin 		return;
162*53cbeb9bSSasha Levin 
163*53cbeb9bSSasha Levin 	rdev->fd = open("/dev/urandom", O_RDONLY);
164*53cbeb9bSSasha Levin 	if (rdev->fd < 0)
165*53cbeb9bSSasha Levin 		die("Failed initializing RNG");
166*53cbeb9bSSasha Levin 
167*53cbeb9bSSasha Levin 	virtio_pci__init(kvm, &rdev->vpci, rdev, PCI_DEVICE_ID_VIRTIO_RNG, VIRTIO_ID_RNG);
168*53cbeb9bSSasha Levin 	rdev->vpci.ops = (struct virtio_pci_ops) {
169*53cbeb9bSSasha Levin 		.set_config		= set_config,
170*53cbeb9bSSasha Levin 		.get_config		= get_config,
171*53cbeb9bSSasha Levin 		.get_host_features	= get_host_features,
172*53cbeb9bSSasha Levin 		.set_guest_features	= set_guest_features,
173*53cbeb9bSSasha Levin 		.init_vq		= init_vq,
174*53cbeb9bSSasha Levin 		.notify_vq		= notify_vq,
175*53cbeb9bSSasha Levin 		.get_pfn_vq		= get_pfn_vq,
176*53cbeb9bSSasha Levin 		.get_size_vq		= get_size_vq,
177*53cbeb9bSSasha Levin 	};
178*53cbeb9bSSasha Levin 
179*53cbeb9bSSasha Levin 	list_add_tail(&rdev->list, &rdevs);
180*53cbeb9bSSasha Levin 
181c75b037fSSasha Levin 	rdev->compat_id = compat__add_message("virtio-rng device was not detected",
182c75b037fSSasha Levin 						"While you have requested a virtio-rng device, "
183c75b037fSSasha Levin 						"the guest kernel didn't seem to detect it.\n"
184c75b037fSSasha Levin 						"Please make sure that the kernel was compiled"
185c75b037fSSasha Levin 						"with CONFIG_HW_RANDOM_VIRTIO.");
18680ac1d05SSasha Levin }
18780ac1d05SSasha Levin 
18880ac1d05SSasha Levin void virtio_rng__delete_all(struct kvm *kvm)
18980ac1d05SSasha Levin {
19080ac1d05SSasha Levin 	while (!list_empty(&rdevs)) {
19180ac1d05SSasha Levin 		struct rng_dev *rdev;
19280ac1d05SSasha Levin 
19380ac1d05SSasha Levin 		rdev = list_first_entry(&rdevs, struct rng_dev, list);
19480ac1d05SSasha Levin 		list_del(&rdev->list);
195*53cbeb9bSSasha Levin 		ioeventfd__del_event(rdev->vpci.base_addr + VIRTIO_PCI_QUEUE_NOTIFY, 0);
19680ac1d05SSasha Levin 		free(rdev);
19780ac1d05SSasha Levin 	}
198376ac44cSSasha Levin }
199