xref: /kvmtool/virtio/rng.c (revision aaf0b4453e15de9f86ea31a5758b2b8957a2102a)
1376ac44cSSasha Levin #include "kvm/virtio-rng.h"
2376ac44cSSasha Levin 
3376ac44cSSasha Levin #include "kvm/virtio-pci.h"
431638bcaSCyrill Gorcunov #include "kvm/virtio-pci-dev.h"
5376ac44cSSasha Levin 
6376ac44cSSasha Levin #include "kvm/disk-image.h"
7376ac44cSSasha Levin #include "kvm/virtio.h"
8376ac44cSSasha Levin #include "kvm/ioport.h"
9376ac44cSSasha Levin #include "kvm/mutex.h"
10376ac44cSSasha Levin #include "kvm/util.h"
11376ac44cSSasha Levin #include "kvm/kvm.h"
12376ac44cSSasha Levin #include "kvm/pci.h"
13376ac44cSSasha Levin #include "kvm/threadpool.h"
142449f6e3SSasha Levin #include "kvm/irq.h"
15376ac44cSSasha Levin 
16376ac44cSSasha Levin #include <linux/virtio_ring.h>
17376ac44cSSasha Levin #include <linux/virtio_rng.h>
18376ac44cSSasha Levin 
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 
272449f6e3SSasha Levin static struct pci_device_header virtio_rng_pci_device = {
282449f6e3SSasha Levin 	.vendor_id		= PCI_VENDOR_ID_REDHAT_QUMRANET,
292449f6e3SSasha Levin 	.device_id		= PCI_DEVICE_ID_VIRTIO_RNG,
302449f6e3SSasha Levin 	.header_type		= PCI_HEADER_TYPE_NORMAL,
312449f6e3SSasha Levin 	.revision_id		= 0,
322449f6e3SSasha Levin 	.class			= 0x010000,
332449f6e3SSasha Levin 	.subsys_vendor_id	= PCI_SUBSYSTEM_VENDOR_ID_REDHAT_QUMRANET,
342449f6e3SSasha Levin 	.subsys_id		= PCI_SUBSYSTEM_ID_VIRTIO_RNG,
352449f6e3SSasha Levin 	.bar[0]			= IOPORT_VIRTIO_RNG | PCI_BASE_ADDRESS_SPACE_IO,
362449f6e3SSasha Levin };
372449f6e3SSasha Levin 
3880ffe4d1SSasha Levin struct rng_dev {
393fdf659dSSasha Levin 	u8			status;
40d2963622SAsias He 	u8			isr;
413fdf659dSSasha Levin 	u16			config_vector;
4280ffe4d1SSasha Levin 	int			fd;
43376ac44cSSasha Levin 
44376ac44cSSasha Levin 	/* virtio queue */
453fdf659dSSasha Levin 	u16			queue_selector;
46376ac44cSSasha Levin 	struct virt_queue	vqs[NUM_VIRT_QUEUES];
47376ac44cSSasha Levin 	void			*jobs[NUM_VIRT_QUEUES];
48376ac44cSSasha Levin };
49376ac44cSSasha Levin 
5080ffe4d1SSasha Levin static struct rng_dev rdev;
51376ac44cSSasha Levin 
523fdf659dSSasha Levin static bool virtio_rng_pci_io_in(struct kvm *kvm, u16 port, void *data, int size, u32 count)
53376ac44cSSasha Levin {
54376ac44cSSasha Levin 	unsigned long offset;
55376ac44cSSasha Levin 	bool ret = true;
56376ac44cSSasha Levin 
57376ac44cSSasha Levin 	offset = port - IOPORT_VIRTIO_RNG;
58376ac44cSSasha Levin 
59376ac44cSSasha Levin 	switch (offset) {
60376ac44cSSasha Levin 	case VIRTIO_PCI_HOST_FEATURES:
61376ac44cSSasha Levin 	case VIRTIO_PCI_GUEST_FEATURES:
62376ac44cSSasha Levin 	case VIRTIO_PCI_QUEUE_SEL:
63376ac44cSSasha Levin 	case VIRTIO_PCI_QUEUE_NOTIFY:
64376ac44cSSasha Levin 		ret		= false;
65376ac44cSSasha Levin 		break;
66376ac44cSSasha Levin 	case VIRTIO_PCI_QUEUE_PFN:
6780ffe4d1SSasha Levin 		ioport__write32(data, rdev.vqs[rdev.queue_selector].pfn);
68376ac44cSSasha Levin 		break;
69376ac44cSSasha Levin 	case VIRTIO_PCI_QUEUE_NUM:
70376ac44cSSasha Levin 		ioport__write16(data, VIRTIO_RNG_QUEUE_SIZE);
71376ac44cSSasha Levin 		break;
72376ac44cSSasha Levin 	case VIRTIO_PCI_STATUS:
7380ffe4d1SSasha Levin 		ioport__write8(data, rdev.status);
74376ac44cSSasha Levin 		break;
75376ac44cSSasha Levin 	case VIRTIO_PCI_ISR:
76d2963622SAsias He 		ioport__write8(data, rdev.isr);
77d2963622SAsias He 		kvm__irq_line(kvm, virtio_rng_pci_device.irq_line, VIRTIO_IRQ_LOW);
78d2963622SAsias He 		rdev.isr = VIRTIO_IRQ_LOW;
79376ac44cSSasha Levin 		break;
80376ac44cSSasha Levin 	case VIRTIO_MSI_CONFIG_VECTOR:
8180ffe4d1SSasha Levin 		ioport__write16(data, rdev.config_vector);
82376ac44cSSasha Levin 		break;
83376ac44cSSasha Levin 	default:
84376ac44cSSasha Levin 		ret		= false;
85407475bfSPekka Enberg 		break;
86376ac44cSSasha Levin 	};
87376ac44cSSasha Levin 
88376ac44cSSasha Levin 	return ret;
89376ac44cSSasha Levin }
90376ac44cSSasha Levin 
9180ffe4d1SSasha Levin static bool virtio_rng_do_io_request(struct kvm *kvm, struct virt_queue *queue)
92376ac44cSSasha Levin {
93376ac44cSSasha Levin 	struct iovec iov[VIRTIO_RNG_QUEUE_SIZE];
94376ac44cSSasha Levin 	unsigned int len = 0;
95407475bfSPekka Enberg 	u16 out, in, head;
96376ac44cSSasha Levin 
9780ffe4d1SSasha Levin 	head		= virt_queue__get_iov(queue, iov, &out, &in, kvm);
9880ffe4d1SSasha Levin 	len		= readv(rdev.fd, iov, in);
99407475bfSPekka Enberg 
100376ac44cSSasha Levin 	virt_queue__set_used_elem(queue, head, len);
101376ac44cSSasha Levin 
102376ac44cSSasha Levin 	return true;
103376ac44cSSasha Levin }
104376ac44cSSasha Levin 
105376ac44cSSasha Levin static void virtio_rng_do_io(struct kvm *kvm, void *param)
106376ac44cSSasha Levin {
107376ac44cSSasha Levin 	struct virt_queue *vq = param;
108376ac44cSSasha Levin 
109376ac44cSSasha Levin 	while (virt_queue__available(vq)) {
110376ac44cSSasha Levin 		virtio_rng_do_io_request(kvm, vq);
111d2963622SAsias He 		virt_queue__trigger_irq(vq, virtio_rng_pci_device.irq_line, &rdev.isr, kvm);
112376ac44cSSasha Levin 	}
113376ac44cSSasha Levin }
114376ac44cSSasha Levin 
1153fdf659dSSasha Levin static bool virtio_rng_pci_io_out(struct kvm *kvm, u16 port, void *data, int size, u32 count)
116376ac44cSSasha Levin {
117376ac44cSSasha Levin 	unsigned long offset;
118376ac44cSSasha Levin 	bool ret = true;
119376ac44cSSasha Levin 
120376ac44cSSasha Levin 	offset		= port - IOPORT_VIRTIO_RNG;
121376ac44cSSasha Levin 
122376ac44cSSasha Levin 	switch (offset) {
123376ac44cSSasha Levin 	case VIRTIO_MSI_QUEUE_VECTOR:
124376ac44cSSasha Levin 	case VIRTIO_PCI_GUEST_FEATURES:
125376ac44cSSasha Levin 		break;
126376ac44cSSasha Levin 	case VIRTIO_PCI_QUEUE_PFN: {
127376ac44cSSasha Levin 		struct virt_queue *queue;
128376ac44cSSasha Levin 		void *p;
129376ac44cSSasha Levin 
13080ffe4d1SSasha Levin 		queue			= &rdev.vqs[rdev.queue_selector];
131376ac44cSSasha Levin 		queue->pfn		= ioport__read32(data);
132*aaf0b445SSasha Levin 		p			= guest_pfn_to_host(kvm, queue->pfn);
133376ac44cSSasha Levin 
134376ac44cSSasha Levin 		vring_init(&queue->vring, VIRTIO_RNG_QUEUE_SIZE, p, 4096);
135376ac44cSSasha Levin 
136407475bfSPekka Enberg 		rdev.jobs[rdev.queue_selector] = thread_pool__add_job(kvm, virtio_rng_do_io, queue);
137376ac44cSSasha Levin 
138376ac44cSSasha Levin 		break;
139376ac44cSSasha Levin 	}
140376ac44cSSasha Levin 	case VIRTIO_PCI_QUEUE_SEL:
14180ffe4d1SSasha Levin 		rdev.queue_selector	= ioport__read16(data);
142376ac44cSSasha Levin 		break;
143376ac44cSSasha Levin 	case VIRTIO_PCI_QUEUE_NOTIFY: {
1443fdf659dSSasha Levin 		u16 queue_index;
145376ac44cSSasha Levin 		queue_index		= ioport__read16(data);
14680ffe4d1SSasha Levin 		thread_pool__do_job(rdev.jobs[queue_index]);
147376ac44cSSasha Levin 		break;
148376ac44cSSasha Levin 	}
149376ac44cSSasha Levin 	case VIRTIO_PCI_STATUS:
15080ffe4d1SSasha Levin 		rdev.status		= ioport__read8(data);
151376ac44cSSasha Levin 		break;
152376ac44cSSasha Levin 	case VIRTIO_MSI_CONFIG_VECTOR:
15380ffe4d1SSasha Levin 		rdev.config_vector	= VIRTIO_MSI_NO_VECTOR;
154376ac44cSSasha Levin 		break;
155376ac44cSSasha Levin 	default:
156376ac44cSSasha Levin 		ret			= false;
157407475bfSPekka Enberg 		break;
158376ac44cSSasha Levin 	};
159376ac44cSSasha Levin 
160376ac44cSSasha Levin 	return ret;
161376ac44cSSasha Levin }
162376ac44cSSasha Levin 
163376ac44cSSasha Levin static struct ioport_operations virtio_rng_io_ops = {
164376ac44cSSasha Levin 	.io_in				= virtio_rng_pci_io_in,
165376ac44cSSasha Levin 	.io_out				= virtio_rng_pci_io_out,
166376ac44cSSasha Levin };
167376ac44cSSasha Levin 
168376ac44cSSasha Levin void virtio_rng__init(struct kvm *kvm)
169376ac44cSSasha Levin {
1702449f6e3SSasha Levin 	u8 pin, line, dev;
1712449f6e3SSasha Levin 
17280ffe4d1SSasha Levin 	rdev.fd = open("/dev/urandom", O_RDONLY);
17380ffe4d1SSasha Levin 	if (rdev.fd < 0)
174376ac44cSSasha Levin 		die("Failed initializing RNG");
175376ac44cSSasha Levin 
1762449f6e3SSasha Levin 	if (irq__register_device(PCI_DEVICE_ID_VIRTIO_RNG, &dev, &pin, &line) < 0)
1772449f6e3SSasha Levin 		return;
1782449f6e3SSasha Levin 
1792449f6e3SSasha Levin 	virtio_rng_pci_device.irq_pin	= pin;
1802449f6e3SSasha Levin 	virtio_rng_pci_device.irq_line	= line;
1812449f6e3SSasha Levin 	pci__register(&virtio_rng_pci_device, dev);
182376ac44cSSasha Levin 
183376ac44cSSasha Levin 	ioport__register(IOPORT_VIRTIO_RNG, &virtio_rng_io_ops, IOPORT_VIRTIO_RNG_SIZE);
184376ac44cSSasha Levin }
185