xref: /kvmtool/virtio/rng.c (revision 80ac1d059f2bdf60ea34c303bb918ce3e8dc877f)
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"
13376ac44cSSasha Levin 
14376ac44cSSasha Levin #include <linux/virtio_ring.h>
15376ac44cSSasha Levin #include <linux/virtio_rng.h>
16376ac44cSSasha Levin 
17*80ac1d05SSasha Levin #include <linux/list.h>
18376ac44cSSasha Levin #include <fcntl.h>
19376ac44cSSasha Levin #include <sys/types.h>
20376ac44cSSasha Levin #include <sys/stat.h>
21376ac44cSSasha Levin #include <pthread.h>
22376ac44cSSasha Levin 
23376ac44cSSasha Levin #define NUM_VIRT_QUEUES				1
24376ac44cSSasha Levin #define VIRTIO_RNG_QUEUE_SIZE			128
25376ac44cSSasha Levin 
26*80ac1d05SSasha Levin struct rng_dev_job {
27*80ac1d05SSasha Levin 	struct virt_queue		*vq;
28*80ac1d05SSasha Levin 	struct rng_dev			*rdev;
29*80ac1d05SSasha Levin 	void				*job_id;
302449f6e3SSasha Levin };
312449f6e3SSasha Levin 
3280ffe4d1SSasha Levin struct rng_dev {
33*80ac1d05SSasha Levin 	struct pci_device_header pci_hdr;
34*80ac1d05SSasha Levin 	struct list_head	list;
35*80ac1d05SSasha Levin 
36*80ac1d05SSasha Levin 	u16			base_addr;
373fdf659dSSasha Levin 	u8			status;
38d2963622SAsias He 	u8			isr;
393fdf659dSSasha Levin 	u16			config_vector;
4080ffe4d1SSasha Levin 	int			fd;
41376ac44cSSasha Levin 
42376ac44cSSasha Levin 	/* virtio queue */
433fdf659dSSasha Levin 	u16			queue_selector;
44376ac44cSSasha Levin 	struct virt_queue	vqs[NUM_VIRT_QUEUES];
45*80ac1d05SSasha Levin 	struct rng_dev_job	jobs[NUM_VIRT_QUEUES];
46376ac44cSSasha Levin };
47376ac44cSSasha Levin 
48*80ac1d05SSasha Levin static LIST_HEAD(rdevs);
49376ac44cSSasha Levin 
503d62dea6SSasha Levin static bool virtio_rng_pci_io_in(struct ioport *ioport, struct kvm *kvm, u16 port, void *data, int size, u32 count)
51376ac44cSSasha Levin {
52376ac44cSSasha Levin 	unsigned long offset;
53376ac44cSSasha Levin 	bool ret = true;
54*80ac1d05SSasha Levin 	struct rng_dev *rdev;
55376ac44cSSasha Levin 
56*80ac1d05SSasha Levin 	rdev = ioport->priv;
57*80ac1d05SSasha Levin 	offset = port - rdev->base_addr;
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:
67*80ac1d05SSasha 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:
73*80ac1d05SSasha Levin 		ioport__write8(data, rdev->status);
74376ac44cSSasha Levin 		break;
75376ac44cSSasha Levin 	case VIRTIO_PCI_ISR:
76*80ac1d05SSasha Levin 		ioport__write8(data, rdev->isr);
77*80ac1d05SSasha Levin 		kvm__irq_line(kvm, rdev->pci_hdr.irq_line, VIRTIO_IRQ_LOW);
78*80ac1d05SSasha Levin 		rdev->isr = VIRTIO_IRQ_LOW;
79376ac44cSSasha Levin 		break;
80376ac44cSSasha Levin 	case VIRTIO_MSI_CONFIG_VECTOR:
81*80ac1d05SSasha 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 
91*80ac1d05SSasha Levin static bool virtio_rng_do_io_request(struct kvm *kvm, struct rng_dev *rdev, 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);
98*80ac1d05SSasha 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 {
107*80ac1d05SSasha Levin 	struct rng_dev_job *job = param;
108*80ac1d05SSasha Levin 	struct virt_queue *vq = job->vq;
109*80ac1d05SSasha Levin 	struct rng_dev *rdev = job->rdev;
110376ac44cSSasha Levin 
111376ac44cSSasha Levin 	while (virt_queue__available(vq)) {
112*80ac1d05SSasha Levin 		virtio_rng_do_io_request(kvm, rdev, vq);
113*80ac1d05SSasha Levin 		virt_queue__trigger_irq(vq, rdev->pci_hdr.irq_line, &rdev->isr, kvm);
114376ac44cSSasha Levin 	}
115376ac44cSSasha Levin }
116376ac44cSSasha Levin 
1173d62dea6SSasha Levin static bool virtio_rng_pci_io_out(struct ioport *ioport, struct kvm *kvm, u16 port, void *data, int size, u32 count)
118376ac44cSSasha Levin {
119376ac44cSSasha Levin 	unsigned long offset;
120376ac44cSSasha Levin 	bool ret = true;
121*80ac1d05SSasha Levin 	struct rng_dev *rdev;
122376ac44cSSasha Levin 
123*80ac1d05SSasha Levin 	rdev = ioport->priv;
124*80ac1d05SSasha Levin 	offset = port - rdev->base_addr;
125376ac44cSSasha Levin 
126376ac44cSSasha Levin 	switch (offset) {
127376ac44cSSasha Levin 	case VIRTIO_MSI_QUEUE_VECTOR:
128376ac44cSSasha Levin 	case VIRTIO_PCI_GUEST_FEATURES:
129376ac44cSSasha Levin 		break;
130376ac44cSSasha Levin 	case VIRTIO_PCI_QUEUE_PFN: {
131376ac44cSSasha Levin 		struct virt_queue *queue;
132*80ac1d05SSasha Levin 		struct rng_dev_job *job;
133376ac44cSSasha Levin 		void *p;
134376ac44cSSasha Levin 
135*80ac1d05SSasha Levin 		queue			= &rdev->vqs[rdev->queue_selector];
136376ac44cSSasha Levin 		queue->pfn		= ioport__read32(data);
137aaf0b445SSasha Levin 		p			= guest_pfn_to_host(kvm, queue->pfn);
138376ac44cSSasha Levin 
139*80ac1d05SSasha Levin 		job = &rdev->jobs[rdev->queue_selector];
140*80ac1d05SSasha Levin 
141b8f43678SSasha Levin 		vring_init(&queue->vring, VIRTIO_RNG_QUEUE_SIZE, p, VIRTIO_PCI_VRING_ALIGN);
142376ac44cSSasha Levin 
143*80ac1d05SSasha Levin 		*job			= (struct rng_dev_job) {
144*80ac1d05SSasha Levin 			.vq			= queue,
145*80ac1d05SSasha Levin 			.rdev			= rdev,
146*80ac1d05SSasha Levin 		};
147*80ac1d05SSasha Levin 
148*80ac1d05SSasha Levin 		job->job_id = thread_pool__add_job(kvm, virtio_rng_do_io, job);
149376ac44cSSasha Levin 
150376ac44cSSasha Levin 		break;
151376ac44cSSasha Levin 	}
152376ac44cSSasha Levin 	case VIRTIO_PCI_QUEUE_SEL:
153*80ac1d05SSasha Levin 		rdev->queue_selector	= ioport__read16(data);
154376ac44cSSasha Levin 		break;
155376ac44cSSasha Levin 	case VIRTIO_PCI_QUEUE_NOTIFY: {
1563fdf659dSSasha Levin 		u16 queue_index;
157376ac44cSSasha Levin 		queue_index		= ioport__read16(data);
158*80ac1d05SSasha Levin 		thread_pool__do_job(rdev->jobs[queue_index].job_id);
159376ac44cSSasha Levin 		break;
160376ac44cSSasha Levin 	}
161376ac44cSSasha Levin 	case VIRTIO_PCI_STATUS:
162*80ac1d05SSasha Levin 		rdev->status		= ioport__read8(data);
163376ac44cSSasha Levin 		break;
164376ac44cSSasha Levin 	case VIRTIO_MSI_CONFIG_VECTOR:
165*80ac1d05SSasha Levin 		rdev->config_vector	= VIRTIO_MSI_NO_VECTOR;
166376ac44cSSasha Levin 		break;
167376ac44cSSasha Levin 	default:
168376ac44cSSasha Levin 		ret			= false;
169407475bfSPekka Enberg 		break;
170376ac44cSSasha Levin 	};
171376ac44cSSasha Levin 
172376ac44cSSasha Levin 	return ret;
173376ac44cSSasha Levin }
174376ac44cSSasha Levin 
175376ac44cSSasha Levin static struct ioport_operations virtio_rng_io_ops = {
176376ac44cSSasha Levin 	.io_in				= virtio_rng_pci_io_in,
177376ac44cSSasha Levin 	.io_out				= virtio_rng_pci_io_out,
178376ac44cSSasha Levin };
179376ac44cSSasha Levin 
180376ac44cSSasha Levin void virtio_rng__init(struct kvm *kvm)
181376ac44cSSasha Levin {
1822449f6e3SSasha Levin 	u8 pin, line, dev;
183*80ac1d05SSasha Levin 	u16 rdev_base_addr;
184*80ac1d05SSasha Levin 	struct rng_dev *rdev;
1852449f6e3SSasha Levin 
186*80ac1d05SSasha Levin 	rdev = malloc(sizeof(*rdev));
187*80ac1d05SSasha Levin 	if (rdev == NULL)
188*80ac1d05SSasha Levin 		return;
189*80ac1d05SSasha Levin 
190*80ac1d05SSasha Levin 	rdev_base_addr = ioport__register(IOPORT_EMPTY, &virtio_rng_io_ops, IOPORT_SIZE, rdev);
191*80ac1d05SSasha Levin 
192*80ac1d05SSasha Levin 	rdev->pci_hdr = (struct pci_device_header) {
193*80ac1d05SSasha Levin 		.vendor_id		= PCI_VENDOR_ID_REDHAT_QUMRANET,
194*80ac1d05SSasha Levin 		.device_id		= PCI_DEVICE_ID_VIRTIO_RNG,
195*80ac1d05SSasha Levin 		.header_type		= PCI_HEADER_TYPE_NORMAL,
196*80ac1d05SSasha Levin 		.revision_id		= 0,
197*80ac1d05SSasha Levin 		.class			= 0x010000,
198*80ac1d05SSasha Levin 		.subsys_vendor_id	= PCI_SUBSYSTEM_VENDOR_ID_REDHAT_QUMRANET,
199*80ac1d05SSasha Levin 		.subsys_id		= VIRTIO_ID_RNG,
200*80ac1d05SSasha Levin 		.bar[0]			= rdev_base_addr | PCI_BASE_ADDRESS_SPACE_IO,
201*80ac1d05SSasha Levin 	};
202*80ac1d05SSasha Levin 
203*80ac1d05SSasha Levin 	rdev->base_addr = rdev_base_addr;
204*80ac1d05SSasha Levin 	rdev->fd = open("/dev/urandom", O_RDONLY);
205*80ac1d05SSasha Levin 	if (rdev->fd < 0)
206376ac44cSSasha Levin 		die("Failed initializing RNG");
207376ac44cSSasha Levin 
2080a7ab0c6SSasha Levin 	if (irq__register_device(VIRTIO_ID_RNG, &dev, &pin, &line) < 0)
2092449f6e3SSasha Levin 		return;
2102449f6e3SSasha Levin 
211*80ac1d05SSasha Levin 	rdev->pci_hdr.irq_pin	= pin;
212*80ac1d05SSasha Levin 	rdev->pci_hdr.irq_line	= line;
213*80ac1d05SSasha Levin 	pci__register(&rdev->pci_hdr, dev);
214376ac44cSSasha Levin 
215*80ac1d05SSasha Levin 	list_add_tail(&rdev->list, &rdevs);
216*80ac1d05SSasha Levin }
217*80ac1d05SSasha Levin 
218*80ac1d05SSasha Levin void virtio_rng__delete_all(struct kvm *kvm)
219*80ac1d05SSasha Levin {
220*80ac1d05SSasha Levin 	while (!list_empty(&rdevs)) {
221*80ac1d05SSasha Levin 		struct rng_dev *rdev;
222*80ac1d05SSasha Levin 
223*80ac1d05SSasha Levin 		rdev = list_first_entry(&rdevs, struct rng_dev, list);
224*80ac1d05SSasha Levin 		list_del(&rdev->list);
225*80ac1d05SSasha Levin 		free(rdev);
226*80ac1d05SSasha Levin 	}
227376ac44cSSasha Levin }
228