xref: /kvmtool/virtio/rng.c (revision df0c7f571eb7fad7895e0930cd695eb039b1fe57)
1376ac44cSSasha Levin #include "kvm/virtio-rng.h"
2376ac44cSSasha Levin 
331638bcaSCyrill Gorcunov #include "kvm/virtio-pci-dev.h"
4376ac44cSSasha Levin 
5376ac44cSSasha Levin #include "kvm/disk-image.h"
6376ac44cSSasha Levin #include "kvm/virtio.h"
7376ac44cSSasha Levin #include "kvm/ioport.h"
8376ac44cSSasha Levin #include "kvm/util.h"
9376ac44cSSasha Levin #include "kvm/kvm.h"
10376ac44cSSasha Levin #include "kvm/pci.h"
11376ac44cSSasha Levin #include "kvm/threadpool.h"
122449f6e3SSasha Levin #include "kvm/irq.h"
13b2533581SSasha Levin #include "kvm/ioeventfd.h"
14376ac44cSSasha Levin 
15376ac44cSSasha Levin #include <linux/virtio_ring.h>
16376ac44cSSasha Levin #include <linux/virtio_rng.h>
17376ac44cSSasha Levin 
1880ac1d05SSasha Levin #include <linux/list.h>
19376ac44cSSasha Levin #include <fcntl.h>
20376ac44cSSasha Levin #include <sys/types.h>
21376ac44cSSasha Levin #include <sys/stat.h>
22376ac44cSSasha Levin #include <pthread.h>
23376ac44cSSasha Levin 
24376ac44cSSasha Levin #define NUM_VIRT_QUEUES		1
25376ac44cSSasha Levin #define VIRTIO_RNG_QUEUE_SIZE	128
26376ac44cSSasha Levin 
2780ac1d05SSasha Levin struct rng_dev_job {
2880ac1d05SSasha Levin 	struct virt_queue	*vq;
2980ac1d05SSasha Levin 	struct rng_dev		*rdev;
30*df0c7f57SSasha Levin 	struct thread_pool__job	job_id;
312449f6e3SSasha Levin };
322449f6e3SSasha Levin 
3380ffe4d1SSasha Levin struct rng_dev {
3480ac1d05SSasha Levin 	struct pci_device_header pci_hdr;
3580ac1d05SSasha Levin 	struct list_head	list;
3680ac1d05SSasha Levin 
3780ac1d05SSasha Levin 	u16			base_addr;
383fdf659dSSasha Levin 	u8			status;
39d2963622SAsias He 	u8			isr;
403fdf659dSSasha Levin 	u16			config_vector;
4180ffe4d1SSasha Levin 	int			fd;
42376ac44cSSasha Levin 
43376ac44cSSasha Levin 	/* virtio queue */
443fdf659dSSasha Levin 	u16			queue_selector;
45376ac44cSSasha Levin 	struct virt_queue	vqs[NUM_VIRT_QUEUES];
4680ac1d05SSasha Levin 	struct rng_dev_job	jobs[NUM_VIRT_QUEUES];
47376ac44cSSasha Levin };
48376ac44cSSasha Levin 
4980ac1d05SSasha Levin static LIST_HEAD(rdevs);
50376ac44cSSasha Levin 
513d62dea6SSasha Levin static bool virtio_rng_pci_io_in(struct ioport *ioport, struct kvm *kvm, u16 port, void *data, int size, u32 count)
52376ac44cSSasha Levin {
53376ac44cSSasha Levin 	unsigned long offset;
54376ac44cSSasha Levin 	bool ret = true;
5580ac1d05SSasha Levin 	struct rng_dev *rdev;
56376ac44cSSasha Levin 
5780ac1d05SSasha Levin 	rdev = ioport->priv;
5880ac1d05SSasha Levin 	offset = port - rdev->base_addr;
59376ac44cSSasha Levin 
60376ac44cSSasha Levin 	switch (offset) {
61376ac44cSSasha Levin 	case VIRTIO_PCI_HOST_FEATURES:
62376ac44cSSasha Levin 	case VIRTIO_PCI_GUEST_FEATURES:
63376ac44cSSasha Levin 	case VIRTIO_PCI_QUEUE_SEL:
64376ac44cSSasha Levin 	case VIRTIO_PCI_QUEUE_NOTIFY:
65376ac44cSSasha Levin 		ret		= false;
66376ac44cSSasha Levin 		break;
67376ac44cSSasha Levin 	case VIRTIO_PCI_QUEUE_PFN:
6880ac1d05SSasha Levin 		ioport__write32(data, rdev->vqs[rdev->queue_selector].pfn);
69376ac44cSSasha Levin 		break;
70376ac44cSSasha Levin 	case VIRTIO_PCI_QUEUE_NUM:
71376ac44cSSasha Levin 		ioport__write16(data, VIRTIO_RNG_QUEUE_SIZE);
72376ac44cSSasha Levin 		break;
73376ac44cSSasha Levin 	case VIRTIO_PCI_STATUS:
7480ac1d05SSasha Levin 		ioport__write8(data, rdev->status);
75376ac44cSSasha Levin 		break;
76376ac44cSSasha Levin 	case VIRTIO_PCI_ISR:
7780ac1d05SSasha Levin 		ioport__write8(data, rdev->isr);
7880ac1d05SSasha Levin 		kvm__irq_line(kvm, rdev->pci_hdr.irq_line, VIRTIO_IRQ_LOW);
7980ac1d05SSasha Levin 		rdev->isr = VIRTIO_IRQ_LOW;
80376ac44cSSasha Levin 		break;
81376ac44cSSasha Levin 	case VIRTIO_MSI_CONFIG_VECTOR:
8280ac1d05SSasha Levin 		ioport__write16(data, rdev->config_vector);
83376ac44cSSasha Levin 		break;
84376ac44cSSasha Levin 	default:
85376ac44cSSasha Levin 		ret		= false;
86407475bfSPekka Enberg 		break;
87376ac44cSSasha Levin 	};
88376ac44cSSasha Levin 
89376ac44cSSasha Levin 	return ret;
90376ac44cSSasha Levin }
91376ac44cSSasha Levin 
9280ac1d05SSasha Levin static bool virtio_rng_do_io_request(struct kvm *kvm, struct rng_dev *rdev, struct virt_queue *queue)
93376ac44cSSasha Levin {
94376ac44cSSasha Levin 	struct iovec iov[VIRTIO_RNG_QUEUE_SIZE];
95376ac44cSSasha Levin 	unsigned int len = 0;
96407475bfSPekka Enberg 	u16 out, in, head;
97376ac44cSSasha Levin 
9880ffe4d1SSasha Levin 	head		= virt_queue__get_iov(queue, iov, &out, &in, kvm);
9980ac1d05SSasha Levin 	len		= readv(rdev->fd, iov, in);
100407475bfSPekka Enberg 
101376ac44cSSasha Levin 	virt_queue__set_used_elem(queue, head, len);
102376ac44cSSasha Levin 
103376ac44cSSasha Levin 	return true;
104376ac44cSSasha Levin }
105376ac44cSSasha Levin 
106376ac44cSSasha Levin static void virtio_rng_do_io(struct kvm *kvm, void *param)
107376ac44cSSasha Levin {
10880ac1d05SSasha Levin 	struct rng_dev_job *job = param;
10980ac1d05SSasha Levin 	struct virt_queue *vq = job->vq;
11080ac1d05SSasha Levin 	struct rng_dev *rdev = job->rdev;
111376ac44cSSasha Levin 
112376ac44cSSasha Levin 	while (virt_queue__available(vq)) {
11380ac1d05SSasha Levin 		virtio_rng_do_io_request(kvm, rdev, vq);
11480ac1d05SSasha Levin 		virt_queue__trigger_irq(vq, rdev->pci_hdr.irq_line, &rdev->isr, kvm);
115376ac44cSSasha Levin 	}
116376ac44cSSasha Levin }
117376ac44cSSasha Levin 
1183d62dea6SSasha Levin static bool virtio_rng_pci_io_out(struct ioport *ioport, struct kvm *kvm, u16 port, void *data, int size, u32 count)
119376ac44cSSasha Levin {
120376ac44cSSasha Levin 	unsigned long offset;
121376ac44cSSasha Levin 	bool ret = true;
12280ac1d05SSasha Levin 	struct rng_dev *rdev;
123376ac44cSSasha Levin 
12480ac1d05SSasha Levin 	rdev = ioport->priv;
12580ac1d05SSasha Levin 	offset = port - rdev->base_addr;
126376ac44cSSasha Levin 
127376ac44cSSasha Levin 	switch (offset) {
128376ac44cSSasha Levin 	case VIRTIO_MSI_QUEUE_VECTOR:
129376ac44cSSasha Levin 	case VIRTIO_PCI_GUEST_FEATURES:
130376ac44cSSasha Levin 		break;
131376ac44cSSasha Levin 	case VIRTIO_PCI_QUEUE_PFN: {
132376ac44cSSasha Levin 		struct virt_queue *queue;
13380ac1d05SSasha Levin 		struct rng_dev_job *job;
134376ac44cSSasha Levin 		void *p;
135376ac44cSSasha Levin 
13680ac1d05SSasha Levin 		queue			= &rdev->vqs[rdev->queue_selector];
137376ac44cSSasha Levin 		queue->pfn		= ioport__read32(data);
138aaf0b445SSasha Levin 		p			= guest_pfn_to_host(kvm, queue->pfn);
139376ac44cSSasha Levin 
14080ac1d05SSasha Levin 		job = &rdev->jobs[rdev->queue_selector];
14180ac1d05SSasha Levin 
142b8f43678SSasha Levin 		vring_init(&queue->vring, VIRTIO_RNG_QUEUE_SIZE, p, VIRTIO_PCI_VRING_ALIGN);
143376ac44cSSasha Levin 
14480ac1d05SSasha Levin 		*job			= (struct rng_dev_job) {
14580ac1d05SSasha Levin 			.vq			= queue,
14680ac1d05SSasha Levin 			.rdev			= rdev,
14780ac1d05SSasha Levin 		};
14880ac1d05SSasha Levin 
149*df0c7f57SSasha Levin 		thread_pool__init_job(&job->job_id, kvm, virtio_rng_do_io, job);
150376ac44cSSasha Levin 
151376ac44cSSasha Levin 		break;
152376ac44cSSasha Levin 	}
153376ac44cSSasha Levin 	case VIRTIO_PCI_QUEUE_SEL:
15480ac1d05SSasha Levin 		rdev->queue_selector	= ioport__read16(data);
155376ac44cSSasha Levin 		break;
156376ac44cSSasha Levin 	case VIRTIO_PCI_QUEUE_NOTIFY: {
1573fdf659dSSasha Levin 		u16 queue_index;
158376ac44cSSasha Levin 		queue_index		= ioport__read16(data);
159*df0c7f57SSasha Levin 		thread_pool__do_job(&rdev->jobs[queue_index].job_id);
160376ac44cSSasha Levin 		break;
161376ac44cSSasha Levin 	}
162376ac44cSSasha Levin 	case VIRTIO_PCI_STATUS:
16380ac1d05SSasha Levin 		rdev->status		= ioport__read8(data);
164376ac44cSSasha Levin 		break;
165376ac44cSSasha Levin 	case VIRTIO_MSI_CONFIG_VECTOR:
16680ac1d05SSasha Levin 		rdev->config_vector	= VIRTIO_MSI_NO_VECTOR;
167376ac44cSSasha Levin 		break;
168376ac44cSSasha Levin 	default:
169376ac44cSSasha Levin 		ret			= false;
170407475bfSPekka Enberg 		break;
171376ac44cSSasha Levin 	};
172376ac44cSSasha Levin 
173376ac44cSSasha Levin 	return ret;
174376ac44cSSasha Levin }
175376ac44cSSasha Levin 
176376ac44cSSasha Levin static struct ioport_operations virtio_rng_io_ops = {
177376ac44cSSasha Levin 	.io_in				= virtio_rng_pci_io_in,
178376ac44cSSasha Levin 	.io_out				= virtio_rng_pci_io_out,
179376ac44cSSasha Levin };
180376ac44cSSasha Levin 
181b2533581SSasha Levin static void ioevent_callback(struct kvm *kvm, void *param)
182b2533581SSasha Levin {
183b2533581SSasha Levin 	struct rng_dev_job *job = param;
184b2533581SSasha Levin 
185*df0c7f57SSasha Levin 	thread_pool__do_job(&job->job_id);
186b2533581SSasha Levin }
187b2533581SSasha Levin 
188376ac44cSSasha Levin void virtio_rng__init(struct kvm *kvm)
189376ac44cSSasha Levin {
190b2533581SSasha Levin 	u8 pin, line, dev, i;
19180ac1d05SSasha Levin 	u16 rdev_base_addr;
19280ac1d05SSasha Levin 	struct rng_dev *rdev;
193b2533581SSasha Levin 	struct ioevent ioevent;
1942449f6e3SSasha Levin 
19580ac1d05SSasha Levin 	rdev = malloc(sizeof(*rdev));
19680ac1d05SSasha Levin 	if (rdev == NULL)
19780ac1d05SSasha Levin 		return;
19880ac1d05SSasha Levin 
19980ac1d05SSasha Levin 	rdev_base_addr = ioport__register(IOPORT_EMPTY, &virtio_rng_io_ops, IOPORT_SIZE, rdev);
20080ac1d05SSasha Levin 
20180ac1d05SSasha Levin 	rdev->pci_hdr = (struct pci_device_header) {
20280ac1d05SSasha Levin 		.vendor_id		= PCI_VENDOR_ID_REDHAT_QUMRANET,
20380ac1d05SSasha Levin 		.device_id		= PCI_DEVICE_ID_VIRTIO_RNG,
20480ac1d05SSasha Levin 		.header_type		= PCI_HEADER_TYPE_NORMAL,
20580ac1d05SSasha Levin 		.revision_id		= 0,
20680ac1d05SSasha Levin 		.class			= 0x010000,
20780ac1d05SSasha Levin 		.subsys_vendor_id	= PCI_SUBSYSTEM_VENDOR_ID_REDHAT_QUMRANET,
20880ac1d05SSasha Levin 		.subsys_id		= VIRTIO_ID_RNG,
20980ac1d05SSasha Levin 		.bar[0]			= rdev_base_addr | PCI_BASE_ADDRESS_SPACE_IO,
21080ac1d05SSasha Levin 	};
21180ac1d05SSasha Levin 
21280ac1d05SSasha Levin 	rdev->base_addr = rdev_base_addr;
21380ac1d05SSasha Levin 	rdev->fd = open("/dev/urandom", O_RDONLY);
21480ac1d05SSasha Levin 	if (rdev->fd < 0)
215376ac44cSSasha Levin 		die("Failed initializing RNG");
216376ac44cSSasha Levin 
2170a7ab0c6SSasha Levin 	if (irq__register_device(VIRTIO_ID_RNG, &dev, &pin, &line) < 0)
2182449f6e3SSasha Levin 		return;
2192449f6e3SSasha Levin 
22080ac1d05SSasha Levin 	rdev->pci_hdr.irq_pin	= pin;
22180ac1d05SSasha Levin 	rdev->pci_hdr.irq_line	= line;
22280ac1d05SSasha Levin 	pci__register(&rdev->pci_hdr, dev);
223376ac44cSSasha Levin 
22480ac1d05SSasha Levin 	list_add_tail(&rdev->list, &rdevs);
225b2533581SSasha Levin 
226b2533581SSasha Levin 	for (i = 0; i < NUM_VIRT_QUEUES; i++) {
227b2533581SSasha Levin 		ioevent = (struct ioevent) {
228b2533581SSasha Levin 			.io_addr		= rdev_base_addr + VIRTIO_PCI_QUEUE_NOTIFY,
229b2533581SSasha Levin 			.io_len			= sizeof(u16),
230b2533581SSasha Levin 			.fn			= ioevent_callback,
231b2533581SSasha Levin 			.fn_ptr			= &rdev->jobs[i],
232b2533581SSasha Levin 			.datamatch		= i,
233b2533581SSasha Levin 			.fn_kvm			= kvm,
234b2533581SSasha Levin 			.fd			= eventfd(0, 0),
235b2533581SSasha Levin 		};
236b2533581SSasha Levin 
237b2533581SSasha Levin 		ioeventfd__add_event(&ioevent);
238b2533581SSasha Levin 	}
23980ac1d05SSasha Levin }
24080ac1d05SSasha Levin 
24180ac1d05SSasha Levin void virtio_rng__delete_all(struct kvm *kvm)
24280ac1d05SSasha Levin {
24380ac1d05SSasha Levin 	while (!list_empty(&rdevs)) {
24480ac1d05SSasha Levin 		struct rng_dev *rdev;
24580ac1d05SSasha Levin 
24680ac1d05SSasha Levin 		rdev = list_first_entry(&rdevs, struct rng_dev, list);
24780ac1d05SSasha Levin 		list_del(&rdev->list);
248b2533581SSasha Levin 		ioeventfd__del_event(rdev->base_addr + VIRTIO_PCI_QUEUE_NOTIFY, 0);
24980ac1d05SSasha Levin 		free(rdev);
25080ac1d05SSasha Levin 	}
251376ac44cSSasha Levin }
252