xref: /kvmtool/virtio/rng.c (revision c9ad1e3c72be64501c14a6cdc583d4f4bbf4e329)
1376ac44cSSasha Levin #include "kvm/virtio-rng.h"
2376ac44cSSasha Levin 
3376ac44cSSasha Levin #include "kvm/virtio-pci.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/mutex.h"
9376ac44cSSasha Levin #include "kvm/util.h"
10376ac44cSSasha Levin #include "kvm/kvm.h"
11376ac44cSSasha Levin #include "kvm/pci.h"
12376ac44cSSasha Levin #include "kvm/threadpool.h"
13376ac44cSSasha Levin 
14376ac44cSSasha Levin #include <linux/virtio_ring.h>
15376ac44cSSasha Levin #include <linux/virtio_rng.h>
16376ac44cSSasha Levin 
17376ac44cSSasha Levin #include <fcntl.h>
18376ac44cSSasha Levin #include <sys/types.h>
19376ac44cSSasha Levin #include <sys/stat.h>
20376ac44cSSasha Levin #include <inttypes.h>
21376ac44cSSasha Levin #include <pthread.h>
22376ac44cSSasha Levin 
23376ac44cSSasha Levin #define PCI_VENDOR_ID_REDHAT_QUMRANET			0x1af4
24376ac44cSSasha Levin #define PCI_DEVICE_ID_VIRTIO_RNG				0x1004
25376ac44cSSasha Levin #define PCI_SUBSYSTEM_VENDOR_ID_REDHAT_QUMRANET	0x1af4
26376ac44cSSasha Levin #define PCI_SUBSYSTEM_ID_VIRTIO_RNG				0x0004
27376ac44cSSasha Levin #define PCI_VIRTIO_RNG_DEVNUM 4
28376ac44cSSasha Levin 
29376ac44cSSasha Levin #define VIRTIO_RNG_IRQ		11
30376ac44cSSasha Levin #define VIRTIO_RNG_PIN		1
31376ac44cSSasha Levin 
32376ac44cSSasha Levin #define NUM_VIRT_QUEUES		1
33376ac44cSSasha Levin 
34376ac44cSSasha Levin #define VIRTIO_RNG_QUEUE_SIZE	128
35376ac44cSSasha Levin 
36376ac44cSSasha Levin struct rng_device {
37376ac44cSSasha Levin 	uint8_t				status;
38376ac44cSSasha Levin 	uint16_t			config_vector;
39376ac44cSSasha Levin 	int					fd_rng;
40376ac44cSSasha Levin 
41376ac44cSSasha Levin 	/* virtio queue */
42376ac44cSSasha Levin 	uint16_t			queue_selector;
43376ac44cSSasha Levin 	struct virt_queue	vqs[NUM_VIRT_QUEUES];
44376ac44cSSasha Levin 	void				*jobs[NUM_VIRT_QUEUES];
45376ac44cSSasha Levin };
46376ac44cSSasha Levin 
47376ac44cSSasha Levin static struct rng_device rng_device;
48376ac44cSSasha Levin 
49376ac44cSSasha Levin static bool virtio_rng_pci_io_in(struct kvm *kvm, uint16_t port, void *data, int size, uint32_t count)
50376ac44cSSasha Levin {
51376ac44cSSasha Levin 	unsigned long offset;
52376ac44cSSasha Levin 	bool ret = true;
53376ac44cSSasha Levin 
54376ac44cSSasha Levin 	offset = port - IOPORT_VIRTIO_RNG;
55376ac44cSSasha Levin 
56376ac44cSSasha Levin 	switch (offset) {
57376ac44cSSasha Levin 	case VIRTIO_PCI_HOST_FEATURES:
58376ac44cSSasha Levin 	case VIRTIO_PCI_GUEST_FEATURES:
59376ac44cSSasha Levin 	case VIRTIO_PCI_QUEUE_SEL:
60376ac44cSSasha Levin 	case VIRTIO_PCI_QUEUE_NOTIFY:
61376ac44cSSasha Levin 		ret		= false;
62376ac44cSSasha Levin 		break;
63376ac44cSSasha Levin 	case VIRTIO_PCI_QUEUE_PFN:
64376ac44cSSasha Levin 		ioport__write32(data, rng_device.vqs[rng_device.queue_selector].pfn);
65376ac44cSSasha Levin 		break;
66376ac44cSSasha Levin 	case VIRTIO_PCI_QUEUE_NUM:
67376ac44cSSasha Levin 		ioport__write16(data, VIRTIO_RNG_QUEUE_SIZE);
68376ac44cSSasha Levin 		break;
69376ac44cSSasha Levin 	case VIRTIO_PCI_STATUS:
70376ac44cSSasha Levin 		ioport__write8(data, rng_device.status);
71376ac44cSSasha Levin 		break;
72376ac44cSSasha Levin 	case VIRTIO_PCI_ISR:
73376ac44cSSasha Levin 		ioport__write8(data, 0x1);
74376ac44cSSasha Levin 		kvm__irq_line(kvm, VIRTIO_RNG_IRQ, 0);
75376ac44cSSasha Levin 		break;
76376ac44cSSasha Levin 	case VIRTIO_MSI_CONFIG_VECTOR:
77376ac44cSSasha Levin 		ioport__write16(data, rng_device.config_vector);
78376ac44cSSasha Levin 		break;
79376ac44cSSasha Levin 	default:
80376ac44cSSasha Levin 		ret		= false;
81376ac44cSSasha Levin 	};
82376ac44cSSasha Levin 
83376ac44cSSasha Levin 	return ret;
84376ac44cSSasha Levin }
85376ac44cSSasha Levin 
86376ac44cSSasha Levin static bool virtio_rng_do_io_request(struct kvm *self, struct virt_queue *queue)
87376ac44cSSasha Levin {
88376ac44cSSasha Levin 	struct iovec iov[VIRTIO_RNG_QUEUE_SIZE];
89376ac44cSSasha Levin 	uint16_t out, in, head;
90376ac44cSSasha Levin 	unsigned int len = 0;
91376ac44cSSasha Levin 
92376ac44cSSasha Levin 	head = virt_queue__get_iov(queue, iov, &out, &in, self);
93376ac44cSSasha Levin 	len = readv(rng_device.fd_rng, iov, in);
94376ac44cSSasha Levin 	virt_queue__set_used_elem(queue, head, len);
95376ac44cSSasha Levin 
96376ac44cSSasha Levin 	return true;
97376ac44cSSasha Levin }
98376ac44cSSasha Levin 
99376ac44cSSasha Levin static void virtio_rng_do_io(struct kvm *kvm, void *param)
100376ac44cSSasha Levin {
101376ac44cSSasha Levin 	struct virt_queue *vq = param;
102376ac44cSSasha Levin 
103376ac44cSSasha Levin 	while (virt_queue__available(vq)) {
104376ac44cSSasha Levin 		virtio_rng_do_io_request(kvm, vq);
105376ac44cSSasha Levin 		kvm__irq_line(kvm, VIRTIO_RNG_IRQ, 1);
106376ac44cSSasha Levin 	}
107376ac44cSSasha Levin }
108376ac44cSSasha Levin 
109376ac44cSSasha Levin static bool virtio_rng_pci_io_out(struct kvm *kvm, uint16_t port, void *data, int size, uint32_t count)
110376ac44cSSasha Levin {
111376ac44cSSasha Levin 	unsigned long offset;
112376ac44cSSasha Levin 	bool ret = true;
113376ac44cSSasha Levin 
114376ac44cSSasha Levin 	offset = port - IOPORT_VIRTIO_RNG;
115376ac44cSSasha Levin 
116376ac44cSSasha Levin 	switch (offset) {
117376ac44cSSasha Levin 	case VIRTIO_MSI_QUEUE_VECTOR:
118376ac44cSSasha Levin 	case VIRTIO_PCI_GUEST_FEATURES:
119376ac44cSSasha Levin 		break;
120376ac44cSSasha Levin 	case VIRTIO_PCI_QUEUE_PFN: {
121376ac44cSSasha Levin 		struct virt_queue *queue;
122376ac44cSSasha Levin 		void *p;
123376ac44cSSasha Levin 
124376ac44cSSasha Levin 		queue			= &rng_device.vqs[rng_device.queue_selector];
125376ac44cSSasha Levin 		queue->pfn		= ioport__read32(data);
126376ac44cSSasha Levin 		p				= guest_flat_to_host(kvm, queue->pfn << 12);
127376ac44cSSasha Levin 
128376ac44cSSasha Levin 		vring_init(&queue->vring, VIRTIO_RNG_QUEUE_SIZE, p, 4096);
129376ac44cSSasha Levin 
130376ac44cSSasha Levin 		rng_device.jobs[rng_device.queue_selector] =
131*c9ad1e3cSPekka Enberg 			thread_pool__add_job(kvm, virtio_rng_do_io, queue);
132376ac44cSSasha Levin 
133376ac44cSSasha Levin 		break;
134376ac44cSSasha Levin 	}
135376ac44cSSasha Levin 	case VIRTIO_PCI_QUEUE_SEL:
136376ac44cSSasha Levin 		rng_device.queue_selector	= ioport__read16(data);
137376ac44cSSasha Levin 		break;
138376ac44cSSasha Levin 	case VIRTIO_PCI_QUEUE_NOTIFY: {
139376ac44cSSasha Levin 		uint16_t queue_index;
140376ac44cSSasha Levin 		queue_index		= ioport__read16(data);
141*c9ad1e3cSPekka Enberg 		thread_pool__do_job(rng_device.jobs[queue_index]);
142376ac44cSSasha Levin 		break;
143376ac44cSSasha Levin 	}
144376ac44cSSasha Levin 	case VIRTIO_PCI_STATUS:
145376ac44cSSasha Levin 		rng_device.status		= ioport__read8(data);
146376ac44cSSasha Levin 		break;
147376ac44cSSasha Levin 	case VIRTIO_MSI_CONFIG_VECTOR:
148376ac44cSSasha Levin 		rng_device.config_vector	= VIRTIO_MSI_NO_VECTOR;
149376ac44cSSasha Levin 		break;
150376ac44cSSasha Levin 	default:
151376ac44cSSasha Levin 		ret		= false;
152376ac44cSSasha Levin 	};
153376ac44cSSasha Levin 
154376ac44cSSasha Levin 	return ret;
155376ac44cSSasha Levin }
156376ac44cSSasha Levin 
157376ac44cSSasha Levin static struct ioport_operations virtio_rng_io_ops = {
158376ac44cSSasha Levin 	.io_in		= virtio_rng_pci_io_in,
159376ac44cSSasha Levin 	.io_out		= virtio_rng_pci_io_out,
160376ac44cSSasha Levin };
161376ac44cSSasha Levin 
162376ac44cSSasha Levin static struct pci_device_header virtio_rng_pci_device = {
163376ac44cSSasha Levin 	.vendor_id			= PCI_VENDOR_ID_REDHAT_QUMRANET,
164376ac44cSSasha Levin 	.device_id			= PCI_DEVICE_ID_VIRTIO_RNG,
165376ac44cSSasha Levin 	.header_type		= PCI_HEADER_TYPE_NORMAL,
166376ac44cSSasha Levin 	.revision_id		= 0,
167376ac44cSSasha Levin 	.class				= 0x010000,
168376ac44cSSasha Levin 	.subsys_vendor_id	= PCI_SUBSYSTEM_VENDOR_ID_REDHAT_QUMRANET,
169376ac44cSSasha Levin 	.subsys_id			= PCI_SUBSYSTEM_ID_VIRTIO_RNG,
170376ac44cSSasha Levin 	.bar[0]				= IOPORT_VIRTIO_RNG | PCI_BASE_ADDRESS_SPACE_IO,
171376ac44cSSasha Levin 	.irq_pin			= VIRTIO_RNG_PIN,
172376ac44cSSasha Levin 	.irq_line			= VIRTIO_RNG_IRQ,
173376ac44cSSasha Levin };
174376ac44cSSasha Levin 
175376ac44cSSasha Levin void virtio_rng__init(struct kvm *kvm)
176376ac44cSSasha Levin {
177376ac44cSSasha Levin 	rng_device.fd_rng = open("/dev/urandom", O_RDONLY);
178376ac44cSSasha Levin 	if (rng_device.fd_rng < 0)
179376ac44cSSasha Levin 		die("Failed initializing RNG");
180376ac44cSSasha Levin 
181376ac44cSSasha Levin 	pci__register(&virtio_rng_pci_device, PCI_VIRTIO_RNG_DEVNUM);
182376ac44cSSasha Levin 
183376ac44cSSasha Levin 	ioport__register(IOPORT_VIRTIO_RNG, &virtio_rng_io_ops, IOPORT_VIRTIO_RNG_SIZE);
184376ac44cSSasha Levin }
185