xref: /kvmtool/virtio/rng.c (revision c75b037f399498723bffc62b439b3beefca8408c)
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"
14*c75b037fSSasha Levin #include "kvm/guest_compat.h"
15376ac44cSSasha Levin 
16376ac44cSSasha Levin #include <linux/virtio_ring.h>
17376ac44cSSasha Levin #include <linux/virtio_rng.h>
18376ac44cSSasha Levin 
1980ac1d05SSasha Levin #include <linux/list.h>
20376ac44cSSasha Levin #include <fcntl.h>
21376ac44cSSasha Levin #include <sys/types.h>
22376ac44cSSasha Levin #include <sys/stat.h>
23376ac44cSSasha Levin #include <pthread.h>
24376ac44cSSasha Levin 
25376ac44cSSasha Levin #define NUM_VIRT_QUEUES		1
26376ac44cSSasha Levin #define VIRTIO_RNG_QUEUE_SIZE	128
27376ac44cSSasha Levin 
2880ac1d05SSasha Levin struct rng_dev_job {
2980ac1d05SSasha Levin 	struct virt_queue	*vq;
3080ac1d05SSasha Levin 	struct rng_dev		*rdev;
31df0c7f57SSasha Levin 	struct thread_pool__job	job_id;
322449f6e3SSasha Levin };
332449f6e3SSasha Levin 
3480ffe4d1SSasha Levin struct rng_dev {
3580ac1d05SSasha Levin 	struct pci_device_header pci_hdr;
3680ac1d05SSasha Levin 	struct list_head	list;
3780ac1d05SSasha Levin 
3880ac1d05SSasha Levin 	u16			base_addr;
393fdf659dSSasha Levin 	u8			status;
40d2963622SAsias He 	u8			isr;
413fdf659dSSasha Levin 	u16			config_vector;
4280ffe4d1SSasha Levin 	int			fd;
43bc485053SSasha Levin 	u32			vq_vector[NUM_VIRT_QUEUES];
44bc485053SSasha Levin 	u32			msix_io_block;
45*c75b037fSSasha Levin 	int			compat_id;
46376ac44cSSasha Levin 
47376ac44cSSasha Levin 	/* virtio queue */
483fdf659dSSasha Levin 	u16			queue_selector;
49376ac44cSSasha Levin 	struct virt_queue	vqs[NUM_VIRT_QUEUES];
5080ac1d05SSasha Levin 	struct rng_dev_job	jobs[NUM_VIRT_QUEUES];
51376ac44cSSasha Levin };
52376ac44cSSasha Levin 
5380ac1d05SSasha Levin static LIST_HEAD(rdevs);
54376ac44cSSasha Levin 
55c9f6a037SXiao Guangrong static bool virtio_rng_pci_io_in(struct ioport *ioport, struct kvm *kvm, u16 port, void *data, int size)
56376ac44cSSasha Levin {
57376ac44cSSasha Levin 	unsigned long offset;
58376ac44cSSasha Levin 	bool ret = true;
5980ac1d05SSasha Levin 	struct rng_dev *rdev;
60376ac44cSSasha Levin 
6180ac1d05SSasha Levin 	rdev = ioport->priv;
6280ac1d05SSasha Levin 	offset = port - rdev->base_addr;
63376ac44cSSasha Levin 
64376ac44cSSasha Levin 	switch (offset) {
65376ac44cSSasha Levin 	case VIRTIO_PCI_HOST_FEATURES:
66376ac44cSSasha Levin 	case VIRTIO_PCI_GUEST_FEATURES:
67376ac44cSSasha Levin 	case VIRTIO_PCI_QUEUE_SEL:
68376ac44cSSasha Levin 	case VIRTIO_PCI_QUEUE_NOTIFY:
69376ac44cSSasha Levin 		ret		= false;
70376ac44cSSasha Levin 		break;
71376ac44cSSasha Levin 	case VIRTIO_PCI_QUEUE_PFN:
7280ac1d05SSasha Levin 		ioport__write32(data, rdev->vqs[rdev->queue_selector].pfn);
73376ac44cSSasha Levin 		break;
74376ac44cSSasha Levin 	case VIRTIO_PCI_QUEUE_NUM:
75376ac44cSSasha Levin 		ioport__write16(data, VIRTIO_RNG_QUEUE_SIZE);
76376ac44cSSasha Levin 		break;
77376ac44cSSasha Levin 	case VIRTIO_PCI_STATUS:
7880ac1d05SSasha Levin 		ioport__write8(data, rdev->status);
79376ac44cSSasha Levin 		break;
80376ac44cSSasha Levin 	case VIRTIO_PCI_ISR:
8180ac1d05SSasha Levin 		ioport__write8(data, rdev->isr);
8280ac1d05SSasha Levin 		kvm__irq_line(kvm, rdev->pci_hdr.irq_line, VIRTIO_IRQ_LOW);
8380ac1d05SSasha Levin 		rdev->isr = VIRTIO_IRQ_LOW;
84376ac44cSSasha Levin 		break;
85376ac44cSSasha Levin 	case VIRTIO_MSI_CONFIG_VECTOR:
8680ac1d05SSasha Levin 		ioport__write16(data, rdev->config_vector);
87376ac44cSSasha Levin 		break;
88bc485053SSasha Levin 	case VIRTIO_MSI_QUEUE_VECTOR:
89bc485053SSasha Levin 		ioport__write16(data, rdev->vq_vector[rdev->queue_selector]);
90bc485053SSasha Levin 		break;
91376ac44cSSasha Levin 	default:
92376ac44cSSasha Levin 		ret		= false;
93407475bfSPekka Enberg 		break;
94376ac44cSSasha Levin 	};
95376ac44cSSasha Levin 
96376ac44cSSasha Levin 	return ret;
97376ac44cSSasha Levin }
98376ac44cSSasha Levin 
9980ac1d05SSasha Levin static bool virtio_rng_do_io_request(struct kvm *kvm, struct rng_dev *rdev, struct virt_queue *queue)
100376ac44cSSasha Levin {
101376ac44cSSasha Levin 	struct iovec iov[VIRTIO_RNG_QUEUE_SIZE];
102376ac44cSSasha Levin 	unsigned int len = 0;
103407475bfSPekka Enberg 	u16 out, in, head;
104376ac44cSSasha Levin 
10580ffe4d1SSasha Levin 	head		= virt_queue__get_iov(queue, iov, &out, &in, kvm);
10680ac1d05SSasha Levin 	len		= readv(rdev->fd, iov, in);
107407475bfSPekka Enberg 
108376ac44cSSasha Levin 	virt_queue__set_used_elem(queue, head, len);
109376ac44cSSasha Levin 
110376ac44cSSasha Levin 	return true;
111376ac44cSSasha Levin }
112376ac44cSSasha Levin 
113376ac44cSSasha Levin static void virtio_rng_do_io(struct kvm *kvm, void *param)
114376ac44cSSasha Levin {
11580ac1d05SSasha Levin 	struct rng_dev_job *job = param;
11680ac1d05SSasha Levin 	struct virt_queue *vq = job->vq;
11780ac1d05SSasha Levin 	struct rng_dev *rdev = job->rdev;
118376ac44cSSasha Levin 
119bc485053SSasha Levin 	while (virt_queue__available(vq))
12080ac1d05SSasha Levin 		virtio_rng_do_io_request(kvm, rdev, vq);
121bc485053SSasha Levin 
122bc485053SSasha Levin 	kvm__irq_line(kvm, rdev->pci_hdr.irq_line, VIRTIO_IRQ_HIGH);
123376ac44cSSasha Levin }
124376ac44cSSasha Levin 
125c9f6a037SXiao Guangrong static bool virtio_rng_pci_io_out(struct ioport *ioport, struct kvm *kvm, u16 port, void *data, int size)
126376ac44cSSasha Levin {
127376ac44cSSasha Levin 	unsigned long offset;
128376ac44cSSasha Levin 	bool ret = true;
12980ac1d05SSasha Levin 	struct rng_dev *rdev;
130376ac44cSSasha Levin 
13180ac1d05SSasha Levin 	rdev = ioport->priv;
13280ac1d05SSasha Levin 	offset = port - rdev->base_addr;
133376ac44cSSasha Levin 
134376ac44cSSasha Levin 	switch (offset) {
135376ac44cSSasha Levin 	case VIRTIO_PCI_GUEST_FEATURES:
136376ac44cSSasha Levin 		break;
137376ac44cSSasha Levin 	case VIRTIO_PCI_QUEUE_PFN: {
138376ac44cSSasha Levin 		struct virt_queue *queue;
13980ac1d05SSasha Levin 		struct rng_dev_job *job;
140376ac44cSSasha Levin 		void *p;
141376ac44cSSasha Levin 
142*c75b037fSSasha Levin 		compat__remove_message(rdev->compat_id);
143*c75b037fSSasha Levin 
14480ac1d05SSasha Levin 		queue			= &rdev->vqs[rdev->queue_selector];
145376ac44cSSasha Levin 		queue->pfn		= ioport__read32(data);
146aaf0b445SSasha Levin 		p			= guest_pfn_to_host(kvm, queue->pfn);
147376ac44cSSasha Levin 
14880ac1d05SSasha Levin 		job = &rdev->jobs[rdev->queue_selector];
14980ac1d05SSasha Levin 
150b8f43678SSasha Levin 		vring_init(&queue->vring, VIRTIO_RNG_QUEUE_SIZE, p, VIRTIO_PCI_VRING_ALIGN);
151376ac44cSSasha Levin 
15280ac1d05SSasha Levin 		*job			= (struct rng_dev_job) {
15380ac1d05SSasha Levin 			.vq			= queue,
15480ac1d05SSasha Levin 			.rdev			= rdev,
15580ac1d05SSasha Levin 		};
15680ac1d05SSasha Levin 
157df0c7f57SSasha Levin 		thread_pool__init_job(&job->job_id, kvm, virtio_rng_do_io, job);
158376ac44cSSasha Levin 
159376ac44cSSasha Levin 		break;
160376ac44cSSasha Levin 	}
161376ac44cSSasha Levin 	case VIRTIO_PCI_QUEUE_SEL:
16280ac1d05SSasha Levin 		rdev->queue_selector	= ioport__read16(data);
163376ac44cSSasha Levin 		break;
164376ac44cSSasha Levin 	case VIRTIO_PCI_QUEUE_NOTIFY: {
1653fdf659dSSasha Levin 		u16 queue_index;
166376ac44cSSasha Levin 		queue_index		= ioport__read16(data);
167df0c7f57SSasha Levin 		thread_pool__do_job(&rdev->jobs[queue_index].job_id);
168376ac44cSSasha Levin 		break;
169376ac44cSSasha Levin 	}
170376ac44cSSasha Levin 	case VIRTIO_PCI_STATUS:
17180ac1d05SSasha Levin 		rdev->status		= ioport__read8(data);
172376ac44cSSasha Levin 		break;
173376ac44cSSasha Levin 	case VIRTIO_MSI_CONFIG_VECTOR:
174bc485053SSasha Levin 		rdev->config_vector	= ioport__read16(data);
175376ac44cSSasha Levin 		break;
176bc485053SSasha Levin 	case VIRTIO_MSI_QUEUE_VECTOR: {
177bc485053SSasha Levin 		u32 gsi;
178bc485053SSasha Levin 		u32 vec;
179bc485053SSasha Levin 
180bc485053SSasha Levin 		vec = rdev->vq_vector[rdev->queue_selector] = ioport__read16(data);
181bc485053SSasha Levin 
182bc485053SSasha Levin 		gsi = irq__add_msix_route(kvm,
183bc485053SSasha Levin 					  rdev->pci_hdr.msix.table[vec].low,
184bc485053SSasha Levin 					  rdev->pci_hdr.msix.table[vec].high,
185bc485053SSasha Levin 					  rdev->pci_hdr.msix.table[vec].data);
186bc485053SSasha Levin 		rdev->pci_hdr.irq_line = gsi;
187bc485053SSasha Levin 		break;
188bc485053SSasha Levin 	}
189376ac44cSSasha Levin 	default:
190376ac44cSSasha Levin 		ret			= false;
191407475bfSPekka Enberg 		break;
192376ac44cSSasha Levin 	};
193376ac44cSSasha Levin 
194376ac44cSSasha Levin 	return ret;
195376ac44cSSasha Levin }
196376ac44cSSasha Levin 
197376ac44cSSasha Levin static struct ioport_operations virtio_rng_io_ops = {
198376ac44cSSasha Levin 	.io_in				= virtio_rng_pci_io_in,
199376ac44cSSasha Levin 	.io_out				= virtio_rng_pci_io_out,
200376ac44cSSasha Levin };
201376ac44cSSasha Levin 
202b2533581SSasha Levin static void ioevent_callback(struct kvm *kvm, void *param)
203b2533581SSasha Levin {
204b2533581SSasha Levin 	struct rng_dev_job *job = param;
205b2533581SSasha Levin 
206df0c7f57SSasha Levin 	thread_pool__do_job(&job->job_id);
207b2533581SSasha Levin }
208b2533581SSasha Levin 
209bc485053SSasha Levin static void callback_mmio(u64 addr, u8 *data, u32 len, u8 is_write, void *ptr)
210bc485053SSasha Levin {
211bc485053SSasha Levin 	struct rng_dev *rdev = ptr;
212bc485053SSasha Levin 	void *table = &rdev->pci_hdr.msix.table;
213bc485053SSasha Levin 	if (is_write)
214bc485053SSasha Levin 		memcpy(table + addr - rdev->msix_io_block, data, len);
215bc485053SSasha Levin 	else
216bc485053SSasha Levin 		memcpy(data, table + addr - rdev->msix_io_block, len);
217bc485053SSasha Levin }
218bc485053SSasha Levin 
219376ac44cSSasha Levin void virtio_rng__init(struct kvm *kvm)
220376ac44cSSasha Levin {
221b2533581SSasha Levin 	u8 pin, line, dev, i;
22280ac1d05SSasha Levin 	u16 rdev_base_addr;
22380ac1d05SSasha Levin 	struct rng_dev *rdev;
224b2533581SSasha Levin 	struct ioevent ioevent;
2252449f6e3SSasha Levin 
22680ac1d05SSasha Levin 	rdev = malloc(sizeof(*rdev));
22780ac1d05SSasha Levin 	if (rdev == NULL)
22880ac1d05SSasha Levin 		return;
22980ac1d05SSasha Levin 
230bc485053SSasha Levin 	rdev->msix_io_block = pci_get_io_space_block();
231bc485053SSasha Levin 
23280ac1d05SSasha Levin 	rdev_base_addr = ioport__register(IOPORT_EMPTY, &virtio_rng_io_ops, IOPORT_SIZE, rdev);
233bc485053SSasha Levin 	kvm__register_mmio(kvm, rdev->msix_io_block, 0x100, callback_mmio, rdev);
23480ac1d05SSasha Levin 
23580ac1d05SSasha Levin 	rdev->pci_hdr = (struct pci_device_header) {
23680ac1d05SSasha Levin 		.vendor_id		= PCI_VENDOR_ID_REDHAT_QUMRANET,
23780ac1d05SSasha Levin 		.device_id		= PCI_DEVICE_ID_VIRTIO_RNG,
23880ac1d05SSasha Levin 		.header_type		= PCI_HEADER_TYPE_NORMAL,
23980ac1d05SSasha Levin 		.revision_id		= 0,
24080ac1d05SSasha Levin 		.class			= 0x010000,
24180ac1d05SSasha Levin 		.subsys_vendor_id	= PCI_SUBSYSTEM_VENDOR_ID_REDHAT_QUMRANET,
24280ac1d05SSasha Levin 		.subsys_id		= VIRTIO_ID_RNG,
24380ac1d05SSasha Levin 		.bar[0]			= rdev_base_addr | PCI_BASE_ADDRESS_SPACE_IO,
244bc485053SSasha Levin 		.bar[1]			= rdev->msix_io_block |
245bc485053SSasha Levin 					PCI_BASE_ADDRESS_SPACE_MEMORY |
246bc485053SSasha Levin 					PCI_BASE_ADDRESS_MEM_TYPE_64,
247bc485053SSasha Levin 		/* bar[2] is the continuation of bar[1] for 64bit addressing */
248bc485053SSasha Levin 		.bar[2]			= 0,
249bc485053SSasha Levin 		.status			= PCI_STATUS_CAP_LIST,
250bc485053SSasha Levin 		.capabilities		= (void *)&rdev->pci_hdr.msix - (void *)&rdev->pci_hdr,
25180ac1d05SSasha Levin 	};
25280ac1d05SSasha Levin 
253bc485053SSasha Levin 	rdev->pci_hdr.msix.cap = PCI_CAP_ID_MSIX;
254bc485053SSasha Levin 	rdev->pci_hdr.msix.next = 0;
255bc485053SSasha Levin 	rdev->pci_hdr.msix.table_size = (NUM_VIRT_QUEUES + 1) | PCI_MSIX_FLAGS_ENABLE;
256bc485053SSasha Levin 	rdev->pci_hdr.msix.table_offset = 1; /* Use BAR 1 */
257bc485053SSasha Levin 
258bc485053SSasha Levin 	rdev->config_vector = 0;
25980ac1d05SSasha Levin 	rdev->base_addr = rdev_base_addr;
26080ac1d05SSasha Levin 	rdev->fd = open("/dev/urandom", O_RDONLY);
26180ac1d05SSasha Levin 	if (rdev->fd < 0)
262376ac44cSSasha Levin 		die("Failed initializing RNG");
263376ac44cSSasha Levin 
2640a7ab0c6SSasha Levin 	if (irq__register_device(VIRTIO_ID_RNG, &dev, &pin, &line) < 0)
2652449f6e3SSasha Levin 		return;
2662449f6e3SSasha Levin 
26780ac1d05SSasha Levin 	rdev->pci_hdr.irq_pin	= pin;
26880ac1d05SSasha Levin 	rdev->pci_hdr.irq_line	= line;
26980ac1d05SSasha Levin 	pci__register(&rdev->pci_hdr, dev);
270376ac44cSSasha Levin 
27180ac1d05SSasha Levin 	list_add_tail(&rdev->list, &rdevs);
272b2533581SSasha Levin 
273b2533581SSasha Levin 	for (i = 0; i < NUM_VIRT_QUEUES; i++) {
274b2533581SSasha Levin 		ioevent = (struct ioevent) {
275b2533581SSasha Levin 			.io_addr		= rdev_base_addr + VIRTIO_PCI_QUEUE_NOTIFY,
276b2533581SSasha Levin 			.io_len			= sizeof(u16),
277b2533581SSasha Levin 			.fn			= ioevent_callback,
278b2533581SSasha Levin 			.fn_ptr			= &rdev->jobs[i],
279b2533581SSasha Levin 			.datamatch		= i,
280b2533581SSasha Levin 			.fn_kvm			= kvm,
281b2533581SSasha Levin 			.fd			= eventfd(0, 0),
282b2533581SSasha Levin 		};
283b2533581SSasha Levin 
284b2533581SSasha Levin 		ioeventfd__add_event(&ioevent);
285b2533581SSasha Levin 	}
286*c75b037fSSasha Levin 
287*c75b037fSSasha Levin 	rdev->compat_id = compat__add_message("virtio-rng device was not detected",
288*c75b037fSSasha Levin 						"While you have requested a virtio-rng device, "
289*c75b037fSSasha Levin 						"the guest kernel didn't seem to detect it.\n"
290*c75b037fSSasha Levin 						"Please make sure that the kernel was compiled"
291*c75b037fSSasha Levin 						"with CONFIG_HW_RANDOM_VIRTIO.");
29280ac1d05SSasha Levin }
29380ac1d05SSasha Levin 
29480ac1d05SSasha Levin void virtio_rng__delete_all(struct kvm *kvm)
29580ac1d05SSasha Levin {
29680ac1d05SSasha Levin 	while (!list_empty(&rdevs)) {
29780ac1d05SSasha Levin 		struct rng_dev *rdev;
29880ac1d05SSasha Levin 
29980ac1d05SSasha Levin 		rdev = list_first_entry(&rdevs, struct rng_dev, list);
30080ac1d05SSasha Levin 		list_del(&rdev->list);
301b2533581SSasha Levin 		ioeventfd__del_event(rdev->base_addr + VIRTIO_PCI_QUEUE_NOTIFY, 0);
30280ac1d05SSasha Levin 		free(rdev);
30380ac1d05SSasha Levin 	}
304376ac44cSSasha Levin }
305