xref: /kvmtool/virtio/rng.c (revision 376ac44c19094ca604357ea825d5147b187848b5)
1*376ac44cSSasha Levin #include "kvm/virtio-rng.h"
2*376ac44cSSasha Levin 
3*376ac44cSSasha Levin #include "kvm/virtio-pci.h"
4*376ac44cSSasha Levin 
5*376ac44cSSasha Levin #include "kvm/disk-image.h"
6*376ac44cSSasha Levin #include "kvm/virtio.h"
7*376ac44cSSasha Levin #include "kvm/ioport.h"
8*376ac44cSSasha Levin #include "kvm/mutex.h"
9*376ac44cSSasha Levin #include "kvm/util.h"
10*376ac44cSSasha Levin #include "kvm/kvm.h"
11*376ac44cSSasha Levin #include "kvm/pci.h"
12*376ac44cSSasha Levin #include "kvm/threadpool.h"
13*376ac44cSSasha Levin 
14*376ac44cSSasha Levin #include <linux/virtio_ring.h>
15*376ac44cSSasha Levin #include <linux/virtio_rng.h>
16*376ac44cSSasha Levin 
17*376ac44cSSasha Levin #include <fcntl.h>
18*376ac44cSSasha Levin #include <sys/types.h>
19*376ac44cSSasha Levin #include <sys/stat.h>
20*376ac44cSSasha Levin #include <inttypes.h>
21*376ac44cSSasha Levin #include <pthread.h>
22*376ac44cSSasha Levin 
23*376ac44cSSasha Levin #define PCI_VENDOR_ID_REDHAT_QUMRANET			0x1af4
24*376ac44cSSasha Levin #define PCI_DEVICE_ID_VIRTIO_RNG				0x1004
25*376ac44cSSasha Levin #define PCI_SUBSYSTEM_VENDOR_ID_REDHAT_QUMRANET	0x1af4
26*376ac44cSSasha Levin #define PCI_SUBSYSTEM_ID_VIRTIO_RNG				0x0004
27*376ac44cSSasha Levin #define PCI_VIRTIO_RNG_DEVNUM 4
28*376ac44cSSasha Levin 
29*376ac44cSSasha Levin #define VIRTIO_RNG_IRQ		11
30*376ac44cSSasha Levin #define VIRTIO_RNG_PIN		1
31*376ac44cSSasha Levin 
32*376ac44cSSasha Levin #define NUM_VIRT_QUEUES		1
33*376ac44cSSasha Levin 
34*376ac44cSSasha Levin #define VIRTIO_RNG_QUEUE_SIZE	128
35*376ac44cSSasha Levin 
36*376ac44cSSasha Levin struct rng_device {
37*376ac44cSSasha Levin 	uint8_t				status;
38*376ac44cSSasha Levin 	uint16_t			config_vector;
39*376ac44cSSasha Levin 	int					fd_rng;
40*376ac44cSSasha Levin 
41*376ac44cSSasha Levin 	/* virtio queue */
42*376ac44cSSasha Levin 	uint16_t			queue_selector;
43*376ac44cSSasha Levin 	struct virt_queue	vqs[NUM_VIRT_QUEUES];
44*376ac44cSSasha Levin 	void				*jobs[NUM_VIRT_QUEUES];
45*376ac44cSSasha Levin };
46*376ac44cSSasha Levin 
47*376ac44cSSasha Levin static struct rng_device rng_device;
48*376ac44cSSasha Levin 
49*376ac44cSSasha Levin static bool virtio_rng_pci_io_in(struct kvm *kvm, uint16_t port, void *data, int size, uint32_t count)
50*376ac44cSSasha Levin {
51*376ac44cSSasha Levin 	unsigned long offset;
52*376ac44cSSasha Levin 	bool ret = true;
53*376ac44cSSasha Levin 
54*376ac44cSSasha Levin 	offset = port - IOPORT_VIRTIO_RNG;
55*376ac44cSSasha Levin 
56*376ac44cSSasha Levin 	switch (offset) {
57*376ac44cSSasha Levin 	case VIRTIO_PCI_HOST_FEATURES:
58*376ac44cSSasha Levin 	case VIRTIO_PCI_GUEST_FEATURES:
59*376ac44cSSasha Levin 	case VIRTIO_PCI_QUEUE_SEL:
60*376ac44cSSasha Levin 	case VIRTIO_PCI_QUEUE_NOTIFY:
61*376ac44cSSasha Levin 		ret		= false;
62*376ac44cSSasha Levin 		break;
63*376ac44cSSasha Levin 	case VIRTIO_PCI_QUEUE_PFN:
64*376ac44cSSasha Levin 		ioport__write32(data, rng_device.vqs[rng_device.queue_selector].pfn);
65*376ac44cSSasha Levin 		break;
66*376ac44cSSasha Levin 	case VIRTIO_PCI_QUEUE_NUM:
67*376ac44cSSasha Levin 		ioport__write16(data, VIRTIO_RNG_QUEUE_SIZE);
68*376ac44cSSasha Levin 		break;
69*376ac44cSSasha Levin 	case VIRTIO_PCI_STATUS:
70*376ac44cSSasha Levin 		ioport__write8(data, rng_device.status);
71*376ac44cSSasha Levin 		break;
72*376ac44cSSasha Levin 	case VIRTIO_PCI_ISR:
73*376ac44cSSasha Levin 		ioport__write8(data, 0x1);
74*376ac44cSSasha Levin 		kvm__irq_line(kvm, VIRTIO_RNG_IRQ, 0);
75*376ac44cSSasha Levin 		break;
76*376ac44cSSasha Levin 	case VIRTIO_MSI_CONFIG_VECTOR:
77*376ac44cSSasha Levin 		ioport__write16(data, rng_device.config_vector);
78*376ac44cSSasha Levin 		break;
79*376ac44cSSasha Levin 	default:
80*376ac44cSSasha Levin 		ret		= false;
81*376ac44cSSasha Levin 	};
82*376ac44cSSasha Levin 
83*376ac44cSSasha Levin 	return ret;
84*376ac44cSSasha Levin }
85*376ac44cSSasha Levin 
86*376ac44cSSasha Levin static bool virtio_rng_do_io_request(struct kvm *self, struct virt_queue *queue)
87*376ac44cSSasha Levin {
88*376ac44cSSasha Levin 	struct iovec iov[VIRTIO_RNG_QUEUE_SIZE];
89*376ac44cSSasha Levin 	uint16_t out, in, head;
90*376ac44cSSasha Levin 	unsigned int len = 0;
91*376ac44cSSasha Levin 
92*376ac44cSSasha Levin 	head = virt_queue__get_iov(queue, iov, &out, &in, self);
93*376ac44cSSasha Levin 	len = readv(rng_device.fd_rng, iov, in);
94*376ac44cSSasha Levin 	virt_queue__set_used_elem(queue, head, len);
95*376ac44cSSasha Levin 
96*376ac44cSSasha Levin 	return true;
97*376ac44cSSasha Levin }
98*376ac44cSSasha Levin 
99*376ac44cSSasha Levin static void virtio_rng_do_io(struct kvm *kvm, void *param)
100*376ac44cSSasha Levin {
101*376ac44cSSasha Levin 	struct virt_queue *vq = param;
102*376ac44cSSasha Levin 
103*376ac44cSSasha Levin 	while (virt_queue__available(vq)) {
104*376ac44cSSasha Levin 		virtio_rng_do_io_request(kvm, vq);
105*376ac44cSSasha Levin 		kvm__irq_line(kvm, VIRTIO_RNG_IRQ, 1);
106*376ac44cSSasha Levin 	}
107*376ac44cSSasha Levin }
108*376ac44cSSasha Levin 
109*376ac44cSSasha Levin static bool virtio_rng_pci_io_out(struct kvm *kvm, uint16_t port, void *data, int size, uint32_t count)
110*376ac44cSSasha Levin {
111*376ac44cSSasha Levin 	unsigned long offset;
112*376ac44cSSasha Levin 	bool ret = true;
113*376ac44cSSasha Levin 
114*376ac44cSSasha Levin 	offset = port - IOPORT_VIRTIO_RNG;
115*376ac44cSSasha Levin 
116*376ac44cSSasha Levin 	switch (offset) {
117*376ac44cSSasha Levin 	case VIRTIO_MSI_QUEUE_VECTOR:
118*376ac44cSSasha Levin 	case VIRTIO_PCI_GUEST_FEATURES:
119*376ac44cSSasha Levin 		break;
120*376ac44cSSasha Levin 	case VIRTIO_PCI_QUEUE_PFN: {
121*376ac44cSSasha Levin 		struct virt_queue *queue;
122*376ac44cSSasha Levin 		void *p;
123*376ac44cSSasha Levin 
124*376ac44cSSasha Levin 		queue			= &rng_device.vqs[rng_device.queue_selector];
125*376ac44cSSasha Levin 		queue->pfn		= ioport__read32(data);
126*376ac44cSSasha Levin 		p				= guest_flat_to_host(kvm, queue->pfn << 12);
127*376ac44cSSasha Levin 
128*376ac44cSSasha Levin 		vring_init(&queue->vring, VIRTIO_RNG_QUEUE_SIZE, p, 4096);
129*376ac44cSSasha Levin 
130*376ac44cSSasha Levin 		rng_device.jobs[rng_device.queue_selector] =
131*376ac44cSSasha Levin 			thread_pool__add_jobtype(kvm, virtio_rng_do_io, queue);
132*376ac44cSSasha Levin 
133*376ac44cSSasha Levin 		break;
134*376ac44cSSasha Levin 	}
135*376ac44cSSasha Levin 	case VIRTIO_PCI_QUEUE_SEL:
136*376ac44cSSasha Levin 		rng_device.queue_selector	= ioport__read16(data);
137*376ac44cSSasha Levin 		break;
138*376ac44cSSasha Levin 	case VIRTIO_PCI_QUEUE_NOTIFY: {
139*376ac44cSSasha Levin 		uint16_t queue_index;
140*376ac44cSSasha Levin 		queue_index		= ioport__read16(data);
141*376ac44cSSasha Levin 		thread_pool__signal_work(rng_device.jobs[queue_index]);
142*376ac44cSSasha Levin 		break;
143*376ac44cSSasha Levin 	}
144*376ac44cSSasha Levin 	case VIRTIO_PCI_STATUS:
145*376ac44cSSasha Levin 		rng_device.status		= ioport__read8(data);
146*376ac44cSSasha Levin 		break;
147*376ac44cSSasha Levin 	case VIRTIO_MSI_CONFIG_VECTOR:
148*376ac44cSSasha Levin 		rng_device.config_vector	= VIRTIO_MSI_NO_VECTOR;
149*376ac44cSSasha Levin 		break;
150*376ac44cSSasha Levin 	default:
151*376ac44cSSasha Levin 		ret		= false;
152*376ac44cSSasha Levin 	};
153*376ac44cSSasha Levin 
154*376ac44cSSasha Levin 	return ret;
155*376ac44cSSasha Levin }
156*376ac44cSSasha Levin 
157*376ac44cSSasha Levin static struct ioport_operations virtio_rng_io_ops = {
158*376ac44cSSasha Levin 	.io_in		= virtio_rng_pci_io_in,
159*376ac44cSSasha Levin 	.io_out		= virtio_rng_pci_io_out,
160*376ac44cSSasha Levin };
161*376ac44cSSasha Levin 
162*376ac44cSSasha Levin static struct pci_device_header virtio_rng_pci_device = {
163*376ac44cSSasha Levin 	.vendor_id			= PCI_VENDOR_ID_REDHAT_QUMRANET,
164*376ac44cSSasha Levin 	.device_id			= PCI_DEVICE_ID_VIRTIO_RNG,
165*376ac44cSSasha Levin 	.header_type		= PCI_HEADER_TYPE_NORMAL,
166*376ac44cSSasha Levin 	.revision_id		= 0,
167*376ac44cSSasha Levin 	.class				= 0x010000,
168*376ac44cSSasha Levin 	.subsys_vendor_id	= PCI_SUBSYSTEM_VENDOR_ID_REDHAT_QUMRANET,
169*376ac44cSSasha Levin 	.subsys_id			= PCI_SUBSYSTEM_ID_VIRTIO_RNG,
170*376ac44cSSasha Levin 	.bar[0]				= IOPORT_VIRTIO_RNG | PCI_BASE_ADDRESS_SPACE_IO,
171*376ac44cSSasha Levin 	.irq_pin			= VIRTIO_RNG_PIN,
172*376ac44cSSasha Levin 	.irq_line			= VIRTIO_RNG_IRQ,
173*376ac44cSSasha Levin };
174*376ac44cSSasha Levin 
175*376ac44cSSasha Levin void virtio_rng__init(struct kvm *kvm)
176*376ac44cSSasha Levin {
177*376ac44cSSasha Levin 	rng_device.fd_rng = open("/dev/urandom", O_RDONLY);
178*376ac44cSSasha Levin 	if (rng_device.fd_rng < 0)
179*376ac44cSSasha Levin 		die("Failed initializing RNG");
180*376ac44cSSasha Levin 
181*376ac44cSSasha Levin 	pci__register(&virtio_rng_pci_device, PCI_VIRTIO_RNG_DEVNUM);
182*376ac44cSSasha Levin 
183*376ac44cSSasha Levin 	ioport__register(IOPORT_VIRTIO_RNG, &virtio_rng_io_ops, IOPORT_VIRTIO_RNG_SIZE);
184*376ac44cSSasha Levin }
185