xref: /minix3/minix/lib/libvirtio/virtio.c (revision dc2c582f364d955cff3a86092716702b099d2db2)
1433d6423SLionel Sambuc /*
2433d6423SLionel Sambuc  * Generic virtio library for MINIX 3
3433d6423SLionel Sambuc  *
4433d6423SLionel Sambuc  * Copyright (c) 2013, A. Welzel, <arne.welzel@gmail.com>
5433d6423SLionel Sambuc  *
6433d6423SLionel Sambuc  * This software is released under the BSD license. See the LICENSE file
7433d6423SLionel Sambuc  * included in the main directory of this source distribution for the
8433d6423SLionel Sambuc  * license terms and conditions.
9433d6423SLionel Sambuc  */
10433d6423SLionel Sambuc 
11433d6423SLionel Sambuc #define _SYSTEM 1
12433d6423SLionel Sambuc 
13433d6423SLionel Sambuc #include <assert.h>
14433d6423SLionel Sambuc #include <errno.h>				/* for OK... */
15433d6423SLionel Sambuc #include <string.h>				/* memset() */
16433d6423SLionel Sambuc #include <stdlib.h>				/* malloc() */
17433d6423SLionel Sambuc 
18433d6423SLionel Sambuc #include <machine/pci.h>			/* PCI_ILR, PCI_BAR... */
19433d6423SLionel Sambuc #include <machine/vmparam.h>			/* PAGE_SIZE */
20433d6423SLionel Sambuc 
21433d6423SLionel Sambuc #include <minix/syslib.h>			/* umap, vumap, alloc_..*/
22433d6423SLionel Sambuc #include <minix/sysutil.h>			/* panic(), at least */
23433d6423SLionel Sambuc #include <minix/virtio.h>			/* virtio system include */
24433d6423SLionel Sambuc 
25433d6423SLionel Sambuc #include "virtio_ring.h"			/* virtio types / helper */
26433d6423SLionel Sambuc 
27433d6423SLionel Sambuc /*
28433d6423SLionel Sambuc  * About indirect descriptors:
29433d6423SLionel Sambuc  *
30433d6423SLionel Sambuc  * For each possible thread, a single indirect descriptor table is allocated.
31433d6423SLionel Sambuc  * If using direct descriptors would lead to the situation that another thread
32433d6423SLionel Sambuc  * might not be able to add another descriptor to the ring, indirect descriptors
33433d6423SLionel Sambuc  * are used.
34433d6423SLionel Sambuc  *
35433d6423SLionel Sambuc  * Indirect descriptors are pre-allocated. Each alloc_contig() call involves a
36433d6423SLionel Sambuc  * kernel call which is critical for performance.
37433d6423SLionel Sambuc  *
38433d6423SLionel Sambuc  * The size of indirect descriptor tables is chosen based on MAPVEC_NR. A driver
39433d6423SLionel Sambuc  * using this library should never add more than
40433d6423SLionel Sambuc  *
41433d6423SLionel Sambuc  *    MAPVEC_NR + MAPVEC_NR / 2
42433d6423SLionel Sambuc  *
43433d6423SLionel Sambuc  * descriptors to a queue as this represent the maximum size of an indirect
44433d6423SLionel Sambuc  * descriptor table.
45433d6423SLionel Sambuc  */
46433d6423SLionel Sambuc 
47433d6423SLionel Sambuc struct indirect_desc_table {
48433d6423SLionel Sambuc 	int in_use;
49433d6423SLionel Sambuc 	struct vring_desc *descs;
50433d6423SLionel Sambuc 	phys_bytes paddr;
51433d6423SLionel Sambuc 	size_t len;
52433d6423SLionel Sambuc };
53433d6423SLionel Sambuc 
54433d6423SLionel Sambuc struct virtio_queue {
55433d6423SLionel Sambuc 
56433d6423SLionel Sambuc 	void *vaddr;				/* virtual addr of ring */
57433d6423SLionel Sambuc 	phys_bytes paddr;			/* physical addr of ring */
58433d6423SLionel Sambuc 	u32_t page;				/* physical guest page */
59433d6423SLionel Sambuc 
60433d6423SLionel Sambuc 	u16_t num;				/* number of descriptors */
61433d6423SLionel Sambuc 	u32_t ring_size;			/* size of ring in bytes */
62433d6423SLionel Sambuc 	struct vring vring;
63433d6423SLionel Sambuc 
64433d6423SLionel Sambuc 	u16_t free_num;				/* free descriptors */
65433d6423SLionel Sambuc 	u16_t free_head;			/* next free descriptor */
66433d6423SLionel Sambuc 	u16_t free_tail;			/* last free descriptor */
67433d6423SLionel Sambuc 	u16_t last_used;			/* we checked in used */
68433d6423SLionel Sambuc 
69433d6423SLionel Sambuc 	void **data;				/* points to pointers */
70433d6423SLionel Sambuc };
71433d6423SLionel Sambuc 
72433d6423SLionel Sambuc struct virtio_device {
73433d6423SLionel Sambuc 
74433d6423SLionel Sambuc 	const char *name;			/* for debugging */
75433d6423SLionel Sambuc 
76433d6423SLionel Sambuc 	u16_t  port;				/* io port */
77433d6423SLionel Sambuc 
78433d6423SLionel Sambuc 	struct virtio_feature *features;	/* host / guest features */
79433d6423SLionel Sambuc 	u8_t num_features;			/* max 32 */
80433d6423SLionel Sambuc 
81433d6423SLionel Sambuc 	struct virtio_queue *queues;		/* our queues */
82433d6423SLionel Sambuc 	u16_t num_queues;
83433d6423SLionel Sambuc 
84433d6423SLionel Sambuc 	int irq;				/* interrupt line */
85433d6423SLionel Sambuc 	int irq_hook;				/* hook id */
86433d6423SLionel Sambuc 	int msi;				/* is MSI enabled? */
87433d6423SLionel Sambuc 
88433d6423SLionel Sambuc 	int threads;				/* max number of threads */
89433d6423SLionel Sambuc 
90433d6423SLionel Sambuc 	struct indirect_desc_table *indirect;	/* indirect descriptor tables */
91433d6423SLionel Sambuc 	int num_indirect;
92433d6423SLionel Sambuc };
93433d6423SLionel Sambuc 
94433d6423SLionel Sambuc static int is_matching_device(u16_t expected_sdid, u16_t vid, u16_t sdid);
95433d6423SLionel Sambuc static int init_device(int devind, struct virtio_device *dev);
96433d6423SLionel Sambuc static int init_phys_queues(struct virtio_device *dev);
97433d6423SLionel Sambuc static int exchange_features(struct virtio_device *dev);
98433d6423SLionel Sambuc static int alloc_phys_queue(struct virtio_queue *q);
99433d6423SLionel Sambuc static void free_phys_queue(struct virtio_queue *q);
100433d6423SLionel Sambuc static void init_phys_queue(struct virtio_queue *q);
101433d6423SLionel Sambuc static int init_indirect_desc_table(struct indirect_desc_table *desc);
102433d6423SLionel Sambuc static int init_indirect_desc_tables(struct virtio_device *dev);
103433d6423SLionel Sambuc static void virtio_irq_register(struct virtio_device *dev);
104433d6423SLionel Sambuc static void virtio_irq_unregister(struct virtio_device *dev);
105433d6423SLionel Sambuc static int wants_kick(struct virtio_queue *q);
106433d6423SLionel Sambuc static void kick_queue(struct virtio_device *dev, int qidx);
107433d6423SLionel Sambuc 
108433d6423SLionel Sambuc struct virtio_device *
virtio_setup_device(u16_t subdevid,const char * name,struct virtio_feature * features,int num_features,int threads,int skip)109433d6423SLionel Sambuc virtio_setup_device(u16_t subdevid, const char *name,
110433d6423SLionel Sambuc 		struct virtio_feature *features, int num_features,
111433d6423SLionel Sambuc 		int threads, int skip)
112433d6423SLionel Sambuc {
113433d6423SLionel Sambuc 	int r, devind;
114433d6423SLionel Sambuc 	u16_t vid, did, sdid;
115433d6423SLionel Sambuc 	struct virtio_device *ret;
116433d6423SLionel Sambuc 
117433d6423SLionel Sambuc 	/* bogus values? */
118433d6423SLionel Sambuc 	if (skip < 0 || name == NULL || num_features < 0 || threads <= 0)
119433d6423SLionel Sambuc 		return NULL;
120433d6423SLionel Sambuc 
121433d6423SLionel Sambuc 	pci_init();
122433d6423SLionel Sambuc 
123433d6423SLionel Sambuc 	r = pci_first_dev(&devind, &vid, &did);
124433d6423SLionel Sambuc 
125433d6423SLionel Sambuc 	while (r > 0) {
126433d6423SLionel Sambuc 		sdid = pci_attr_r16(devind, PCI_SUBDID);
127433d6423SLionel Sambuc 		if (is_matching_device(subdevid, vid, sdid)) {
128433d6423SLionel Sambuc 
129433d6423SLionel Sambuc 			/* this is the device we are looking for */
130433d6423SLionel Sambuc 			if (skip == 0)
131433d6423SLionel Sambuc 				break;
132433d6423SLionel Sambuc 
133433d6423SLionel Sambuc 			skip--;
134433d6423SLionel Sambuc 		}
135433d6423SLionel Sambuc 
136433d6423SLionel Sambuc 		r = pci_next_dev(&devind, &vid, &did);
137433d6423SLionel Sambuc 	}
138433d6423SLionel Sambuc 
139433d6423SLionel Sambuc 	/* pci_[first|next_dev()] return 0 if no device was found */
140433d6423SLionel Sambuc 	if (r == 0 || skip > 0)
141433d6423SLionel Sambuc 		return NULL;
142433d6423SLionel Sambuc 
143433d6423SLionel Sambuc 	/* allocate and set known info about the device */
144433d6423SLionel Sambuc 	ret = malloc(sizeof(*ret));
145433d6423SLionel Sambuc 
146433d6423SLionel Sambuc 	if (ret == NULL)
147433d6423SLionel Sambuc 		return NULL;
148433d6423SLionel Sambuc 
149433d6423SLionel Sambuc 	/* Prepare virtio_device intance */
150433d6423SLionel Sambuc 	memset(ret, 0, sizeof(*ret));
151433d6423SLionel Sambuc 	ret->name = name;
152433d6423SLionel Sambuc 	ret->features = features;
153433d6423SLionel Sambuc 	ret->num_features = num_features;
154433d6423SLionel Sambuc 	ret->threads = threads;
155433d6423SLionel Sambuc 	/* see comment in the beginning of this file */
156433d6423SLionel Sambuc 	ret->num_indirect = threads;
157433d6423SLionel Sambuc 
158433d6423SLionel Sambuc 	if (init_device(devind, ret) != OK) {
159433d6423SLionel Sambuc 		printf("%s: Could not initialize device\n", ret->name);
160433d6423SLionel Sambuc 		goto err;
161433d6423SLionel Sambuc 	}
162433d6423SLionel Sambuc 
163433d6423SLionel Sambuc 	/* Ack the device */
164433d6423SLionel Sambuc 	virtio_write8(ret, VIRTIO_DEV_STATUS_OFF, VIRTIO_STATUS_ACK);
165433d6423SLionel Sambuc 
166433d6423SLionel Sambuc 	if (exchange_features(ret) != OK) {
167433d6423SLionel Sambuc 		printf("%s: Could not exchange features\n", ret->name);
168433d6423SLionel Sambuc 		goto err;
169433d6423SLionel Sambuc 	}
170433d6423SLionel Sambuc 
171433d6423SLionel Sambuc 	if (init_indirect_desc_tables(ret) != OK) {
172433d6423SLionel Sambuc 		printf("%s: Could not initialize indirect tables\n", ret->name);
173433d6423SLionel Sambuc 		goto err;
174433d6423SLionel Sambuc 	}
175433d6423SLionel Sambuc 
176433d6423SLionel Sambuc 	/* We know how to drive the device... */
177433d6423SLionel Sambuc 	virtio_write8(ret, VIRTIO_DEV_STATUS_OFF, VIRTIO_STATUS_DRV);
178433d6423SLionel Sambuc 
179433d6423SLionel Sambuc 	return ret;
180433d6423SLionel Sambuc 
181433d6423SLionel Sambuc /* Error path */
182433d6423SLionel Sambuc err:
183433d6423SLionel Sambuc 	free(ret);
184433d6423SLionel Sambuc 	return NULL;
185433d6423SLionel Sambuc }
186433d6423SLionel Sambuc 
187433d6423SLionel Sambuc static int
init_device(int devind,struct virtio_device * dev)188433d6423SLionel Sambuc init_device(int devind, struct virtio_device *dev)
189433d6423SLionel Sambuc {
190433d6423SLionel Sambuc 	u32_t base, size;
191433d6423SLionel Sambuc 	int iof, r;
192433d6423SLionel Sambuc 
193433d6423SLionel Sambuc 	pci_reserve(devind);
194433d6423SLionel Sambuc 
195433d6423SLionel Sambuc 	if ((r = pci_get_bar(devind, PCI_BAR, &base, &size, &iof)) != OK) {
196433d6423SLionel Sambuc 		printf("%s: Could not get BAR (%d)", dev->name, r);
197433d6423SLionel Sambuc 		return r;
198433d6423SLionel Sambuc 	}
199433d6423SLionel Sambuc 
200433d6423SLionel Sambuc 	if (!iof) {
201433d6423SLionel Sambuc 		printf("%s: PCI not IO space?", dev->name);
202433d6423SLionel Sambuc 		return EINVAL;
203433d6423SLionel Sambuc 	}
204433d6423SLionel Sambuc 
205433d6423SLionel Sambuc 	if (base & 0xFFFF0000) {
206433d6423SLionel Sambuc 		printf("%s: IO port weird (%08x)", dev->name, base);
207433d6423SLionel Sambuc 		return EINVAL;
208433d6423SLionel Sambuc 	}
209433d6423SLionel Sambuc 
210433d6423SLionel Sambuc 	/* store the I/O port */
211433d6423SLionel Sambuc 	dev->port = base;
212433d6423SLionel Sambuc 
213433d6423SLionel Sambuc 	/* Reset the device */
214433d6423SLionel Sambuc 	virtio_write8(dev, VIRTIO_DEV_STATUS_OFF, 0);
215433d6423SLionel Sambuc 
216433d6423SLionel Sambuc 	/* Read IRQ line */
217433d6423SLionel Sambuc 	dev->irq = pci_attr_r8(devind, PCI_ILR);
218433d6423SLionel Sambuc 
219433d6423SLionel Sambuc 	return OK;
220433d6423SLionel Sambuc }
221433d6423SLionel Sambuc 
222433d6423SLionel Sambuc static int
exchange_features(struct virtio_device * dev)223433d6423SLionel Sambuc exchange_features(struct virtio_device *dev)
224433d6423SLionel Sambuc {
225433d6423SLionel Sambuc 	u32_t guest_features = 0, host_features = 0;
226433d6423SLionel Sambuc 	struct virtio_feature *f;
227433d6423SLionel Sambuc 
228433d6423SLionel Sambuc 	host_features = virtio_read32(dev, VIRTIO_HOST_F_OFF);
229433d6423SLionel Sambuc 
230433d6423SLionel Sambuc 	for (int i = 0; i < dev->num_features; i++) {
231433d6423SLionel Sambuc 		f = &dev->features[i];
232433d6423SLionel Sambuc 
233433d6423SLionel Sambuc 		/* prepare the features the driver supports */
234433d6423SLionel Sambuc 		guest_features |= (f->guest_support << f->bit);
235433d6423SLionel Sambuc 
236433d6423SLionel Sambuc 		/* just load the host feature int the struct */
237433d6423SLionel Sambuc 		f->host_support =  ((host_features >> f->bit) & 1);
238433d6423SLionel Sambuc 	}
239433d6423SLionel Sambuc 
240433d6423SLionel Sambuc 	/* let the device know about our features */
241433d6423SLionel Sambuc 	virtio_write32(dev, VIRTIO_GUEST_F_OFF, guest_features);
242433d6423SLionel Sambuc 
243433d6423SLionel Sambuc 	return OK;
244433d6423SLionel Sambuc }
245433d6423SLionel Sambuc 
246433d6423SLionel Sambuc int
virtio_alloc_queues(struct virtio_device * dev,int num_queues)247433d6423SLionel Sambuc virtio_alloc_queues(struct virtio_device *dev, int num_queues)
248433d6423SLionel Sambuc {
249433d6423SLionel Sambuc 	int r = OK;
250433d6423SLionel Sambuc 
251433d6423SLionel Sambuc 	assert(dev != NULL);
252433d6423SLionel Sambuc 
253433d6423SLionel Sambuc 	/* Assume there's no device with more than 256 queues */
254433d6423SLionel Sambuc 	if (num_queues < 0 || num_queues > 256)
255433d6423SLionel Sambuc 		return EINVAL;
256433d6423SLionel Sambuc 
257433d6423SLionel Sambuc 	dev->num_queues = num_queues;
258433d6423SLionel Sambuc 	/* allocate queue memory */
259433d6423SLionel Sambuc 	dev->queues = malloc(num_queues * sizeof(dev->queues[0]));
260433d6423SLionel Sambuc 
261433d6423SLionel Sambuc 	if (dev->queues == NULL)
262433d6423SLionel Sambuc 		return ENOMEM;
263433d6423SLionel Sambuc 
264433d6423SLionel Sambuc 	memset(dev->queues, 0, num_queues * sizeof(dev->queues[0]));
265433d6423SLionel Sambuc 
266*dc2c582fSDavid van Moolenbroek 	if ((r = init_phys_queues(dev)) != OK) {
267433d6423SLionel Sambuc 		printf("%s: Could not initialize queues (%d)\n", dev->name, r);
268433d6423SLionel Sambuc 		free(dev->queues);
269433d6423SLionel Sambuc 		dev->queues = NULL;
270433d6423SLionel Sambuc 	}
271433d6423SLionel Sambuc 
272433d6423SLionel Sambuc 	return r;
273433d6423SLionel Sambuc }
274433d6423SLionel Sambuc 
275433d6423SLionel Sambuc static int
init_phys_queues(struct virtio_device * dev)276433d6423SLionel Sambuc init_phys_queues(struct virtio_device *dev)
277433d6423SLionel Sambuc {
278433d6423SLionel Sambuc 	/* Initialize all queues */
279433d6423SLionel Sambuc 	int i, j, r;
280433d6423SLionel Sambuc 	struct virtio_queue *q;
281433d6423SLionel Sambuc 
282433d6423SLionel Sambuc 	for (i = 0; i < dev->num_queues; i++) {
283433d6423SLionel Sambuc 		q = &dev->queues[i];
284433d6423SLionel Sambuc 		/* select the queue */
285433d6423SLionel Sambuc 		virtio_write16(dev, VIRTIO_QSEL_OFF, i);
286433d6423SLionel Sambuc 		q->num = virtio_read16(dev, VIRTIO_QSIZE_OFF);
287433d6423SLionel Sambuc 
288433d6423SLionel Sambuc 		if (q->num & (q->num - 1)) {
289433d6423SLionel Sambuc 			printf("%s: Queue %d num=%d not ^2", dev->name, i,
290433d6423SLionel Sambuc 							     q->num);
291433d6423SLionel Sambuc 			r = EINVAL;
292433d6423SLionel Sambuc 			goto free_phys_queues;
293433d6423SLionel Sambuc 		}
294433d6423SLionel Sambuc 
295433d6423SLionel Sambuc 		if ((r = alloc_phys_queue(q)) != OK)
296433d6423SLionel Sambuc 			goto free_phys_queues;
297433d6423SLionel Sambuc 
298433d6423SLionel Sambuc 		init_phys_queue(q);
299433d6423SLionel Sambuc 
300433d6423SLionel Sambuc 		/* Let the host know about the guest physical page */
301433d6423SLionel Sambuc 		virtio_write32(dev, VIRTIO_QADDR_OFF, q->page);
302433d6423SLionel Sambuc 	}
303433d6423SLionel Sambuc 
304433d6423SLionel Sambuc 	return OK;
305433d6423SLionel Sambuc 
306433d6423SLionel Sambuc /* Error path */
307433d6423SLionel Sambuc free_phys_queues:
308433d6423SLionel Sambuc 	for (j = 0; j < i; j++)
309433d6423SLionel Sambuc 		free_phys_queue(&dev->queues[i]);
310433d6423SLionel Sambuc 
311433d6423SLionel Sambuc 	return r;
312433d6423SLionel Sambuc }
313433d6423SLionel Sambuc 
314433d6423SLionel Sambuc static int
alloc_phys_queue(struct virtio_queue * q)315433d6423SLionel Sambuc alloc_phys_queue(struct virtio_queue *q)
316433d6423SLionel Sambuc {
317433d6423SLionel Sambuc 	assert(q != NULL);
318433d6423SLionel Sambuc 
319433d6423SLionel Sambuc 	/* How much memory do we need? */
320433d6423SLionel Sambuc 	q->ring_size = vring_size(q->num, PAGE_SIZE);
321433d6423SLionel Sambuc 
322433d6423SLionel Sambuc 	q->vaddr = alloc_contig(q->ring_size, AC_ALIGN4K, &q->paddr);
323433d6423SLionel Sambuc 
324433d6423SLionel Sambuc 	if (q->vaddr == NULL)
325433d6423SLionel Sambuc 		return ENOMEM;
326433d6423SLionel Sambuc 
327433d6423SLionel Sambuc 	q->data = alloc_contig(sizeof(q->data[0]) * q->num, AC_ALIGN4K, NULL);
328433d6423SLionel Sambuc 
329433d6423SLionel Sambuc 	if (q->data == NULL) {
330433d6423SLionel Sambuc 		free_contig(q->vaddr, q->ring_size);
331433d6423SLionel Sambuc 		q->vaddr = NULL;
332433d6423SLionel Sambuc 		q->paddr = 0;
333433d6423SLionel Sambuc 		return ENOMEM;
334433d6423SLionel Sambuc 	}
335433d6423SLionel Sambuc 
336433d6423SLionel Sambuc 	return OK;
337433d6423SLionel Sambuc }
338433d6423SLionel Sambuc 
339433d6423SLionel Sambuc void
virtio_device_ready(struct virtio_device * dev)340433d6423SLionel Sambuc virtio_device_ready(struct virtio_device *dev)
341433d6423SLionel Sambuc {
342433d6423SLionel Sambuc 	assert(dev != NULL);
343433d6423SLionel Sambuc 
344433d6423SLionel Sambuc 	/* Register IRQ line */
345433d6423SLionel Sambuc 	virtio_irq_register(dev);
346433d6423SLionel Sambuc 
347433d6423SLionel Sambuc 	/* Driver is ready to go! */
348433d6423SLionel Sambuc 	virtio_write8(dev, VIRTIO_DEV_STATUS_OFF, VIRTIO_STATUS_DRV_OK);
349433d6423SLionel Sambuc }
350433d6423SLionel Sambuc 
351433d6423SLionel Sambuc void
virtio_free_queues(struct virtio_device * dev)352433d6423SLionel Sambuc virtio_free_queues(struct virtio_device *dev)
353433d6423SLionel Sambuc {
354433d6423SLionel Sambuc 	int i;
355433d6423SLionel Sambuc 	assert(dev != NULL);
356433d6423SLionel Sambuc 	assert(dev->queues != NULL);
357433d6423SLionel Sambuc 	assert(dev->num_queues > 0);
358433d6423SLionel Sambuc 
359433d6423SLionel Sambuc 	for (i = 0; i < dev->num_queues; i++)
360433d6423SLionel Sambuc 		free_phys_queue(&dev->queues[i]);
361433d6423SLionel Sambuc 
362433d6423SLionel Sambuc 	dev->num_queues = 0;
363433d6423SLionel Sambuc 	dev->queues = NULL;
364433d6423SLionel Sambuc }
365433d6423SLionel Sambuc 
366433d6423SLionel Sambuc static void
free_phys_queue(struct virtio_queue * q)367433d6423SLionel Sambuc free_phys_queue(struct virtio_queue *q)
368433d6423SLionel Sambuc {
369433d6423SLionel Sambuc 	assert(q != NULL);
370433d6423SLionel Sambuc 	assert(q->vaddr != NULL);
371433d6423SLionel Sambuc 
372433d6423SLionel Sambuc 	free_contig(q->vaddr, q->ring_size);
373433d6423SLionel Sambuc 	q->vaddr = NULL;
374433d6423SLionel Sambuc 	q->paddr = 0;
375433d6423SLionel Sambuc 	q->num = 0;
376433d6423SLionel Sambuc 	free_contig(q->data, sizeof(q->data[0]));
377433d6423SLionel Sambuc 	q->data = NULL;
378433d6423SLionel Sambuc }
379433d6423SLionel Sambuc 
380433d6423SLionel Sambuc static void
init_phys_queue(struct virtio_queue * q)381433d6423SLionel Sambuc init_phys_queue(struct virtio_queue *q)
382433d6423SLionel Sambuc {
383433d6423SLionel Sambuc 	memset(q->vaddr, 0, q->ring_size);
384433d6423SLionel Sambuc 	memset(q->data, 0, sizeof(q->data[0]) * q->num);
385433d6423SLionel Sambuc 
386433d6423SLionel Sambuc 	/* physical page in guest */
387433d6423SLionel Sambuc 	q->page = q->paddr / PAGE_SIZE;
388433d6423SLionel Sambuc 
389433d6423SLionel Sambuc 	/* Set pointers in q->vring according to size */
390433d6423SLionel Sambuc 	vring_init(&q->vring, q->num, q->vaddr, PAGE_SIZE);
391433d6423SLionel Sambuc 
392433d6423SLionel Sambuc 	/* Everything's free at this point */
393433d6423SLionel Sambuc 	for (int i = 0; i < q->num; i++) {
394433d6423SLionel Sambuc 		q->vring.desc[i].flags = VRING_DESC_F_NEXT;
395433d6423SLionel Sambuc 		q->vring.desc[i].next = (i + 1) & (q->num - 1);
396433d6423SLionel Sambuc 	}
397433d6423SLionel Sambuc 
398433d6423SLionel Sambuc 	q->free_num = q->num;
399433d6423SLionel Sambuc 	q->free_head = 0;
400433d6423SLionel Sambuc 	q->free_tail = q->num - 1;
401433d6423SLionel Sambuc 	q->last_used = 0;
402433d6423SLionel Sambuc 
403433d6423SLionel Sambuc 	return;
404433d6423SLionel Sambuc }
405433d6423SLionel Sambuc 
406433d6423SLionel Sambuc void
virtio_free_device(struct virtio_device * dev)407433d6423SLionel Sambuc virtio_free_device(struct virtio_device *dev)
408433d6423SLionel Sambuc {
409433d6423SLionel Sambuc 	int i;
410433d6423SLionel Sambuc 	struct indirect_desc_table *desc;
411433d6423SLionel Sambuc 
412433d6423SLionel Sambuc 	assert(dev != NULL);
413433d6423SLionel Sambuc 
414433d6423SLionel Sambuc 	assert(dev->num_indirect > 0);
415433d6423SLionel Sambuc 
416433d6423SLionel Sambuc 	for (i = 0; i < dev->num_indirect; i++) {
417433d6423SLionel Sambuc 		desc = &dev->indirect[i];
418433d6423SLionel Sambuc 		free_contig(desc->descs, desc->len);
419433d6423SLionel Sambuc 	}
420433d6423SLionel Sambuc 
421433d6423SLionel Sambuc 	dev->num_indirect = 0;
422433d6423SLionel Sambuc 
423433d6423SLionel Sambuc 	assert(dev->indirect != NULL);
424433d6423SLionel Sambuc 	free(dev->indirect);
425433d6423SLionel Sambuc 	dev->indirect = NULL;
426433d6423SLionel Sambuc 
427433d6423SLionel Sambuc 	free(dev);
428433d6423SLionel Sambuc }
429433d6423SLionel Sambuc 
430433d6423SLionel Sambuc static int
init_indirect_desc_table(struct indirect_desc_table * desc)431433d6423SLionel Sambuc init_indirect_desc_table(struct indirect_desc_table *desc)
432433d6423SLionel Sambuc {
433433d6423SLionel Sambuc 	desc->in_use = 0;
434433d6423SLionel Sambuc 	desc->len = (MAPVEC_NR + MAPVEC_NR / 2) * sizeof(struct vring_desc);
435433d6423SLionel Sambuc 
436433d6423SLionel Sambuc 	desc->descs = alloc_contig(desc->len, AC_ALIGN4K, &desc->paddr);
437433d6423SLionel Sambuc 	memset(desc->descs, 0, desc->len);
438433d6423SLionel Sambuc 
439433d6423SLionel Sambuc 	if (desc->descs == NULL)
440433d6423SLionel Sambuc 		return ENOMEM;
441433d6423SLionel Sambuc 
442433d6423SLionel Sambuc 	return OK;
443433d6423SLionel Sambuc }
444433d6423SLionel Sambuc 
445433d6423SLionel Sambuc static int
init_indirect_desc_tables(struct virtio_device * dev)446433d6423SLionel Sambuc init_indirect_desc_tables(struct virtio_device *dev)
447433d6423SLionel Sambuc {
448433d6423SLionel Sambuc 	int i, j, r;
449433d6423SLionel Sambuc 	struct indirect_desc_table *desc;
450433d6423SLionel Sambuc 
451433d6423SLionel Sambuc 	dev->indirect = malloc(dev->num_indirect * sizeof(dev->indirect[0]));
452433d6423SLionel Sambuc 
453433d6423SLionel Sambuc 	if (dev->indirect == NULL) {
454433d6423SLionel Sambuc 		printf("%s: Could not allocate indirect tables\n", dev->name);
455433d6423SLionel Sambuc 		return ENOMEM;
456433d6423SLionel Sambuc 	}
457433d6423SLionel Sambuc 
458433d6423SLionel Sambuc 	memset(dev->indirect, 0, dev->num_indirect* sizeof(dev->indirect[0]));
459433d6423SLionel Sambuc 
460433d6423SLionel Sambuc 	for (i = 0; i < dev->num_indirect; i++) {
461433d6423SLionel Sambuc 		desc = &dev->indirect[i];
462433d6423SLionel Sambuc 		if ((r = init_indirect_desc_table(desc)) != OK) {
463433d6423SLionel Sambuc 
464433d6423SLionel Sambuc 			/* error path */
465433d6423SLionel Sambuc 			for (j = 0; j < i; j++) {
466433d6423SLionel Sambuc 				desc = &dev->indirect[j];
467433d6423SLionel Sambuc 				free_contig(desc->descs, desc->len);
468433d6423SLionel Sambuc 			}
469433d6423SLionel Sambuc 
470433d6423SLionel Sambuc 			free(dev->indirect);
471433d6423SLionel Sambuc 
472433d6423SLionel Sambuc 			return r;
473433d6423SLionel Sambuc 		}
474433d6423SLionel Sambuc 	}
475433d6423SLionel Sambuc 
476433d6423SLionel Sambuc 	return OK;
477433d6423SLionel Sambuc }
478433d6423SLionel Sambuc 
479433d6423SLionel Sambuc static void
clear_indirect_table(struct virtio_device * dev,struct vring_desc * vd)480433d6423SLionel Sambuc clear_indirect_table(struct virtio_device *dev, struct vring_desc *vd)
481433d6423SLionel Sambuc {
482433d6423SLionel Sambuc 	int i;
483433d6423SLionel Sambuc 	struct indirect_desc_table *desc;
484433d6423SLionel Sambuc 
485433d6423SLionel Sambuc 	assert(vd->len > 0);
486433d6423SLionel Sambuc 	assert(vd->flags & VRING_DESC_F_INDIRECT);
487433d6423SLionel Sambuc 	vd->flags = vd->flags & ~VRING_DESC_F_INDIRECT;
488433d6423SLionel Sambuc 	vd->len = 0;;
489433d6423SLionel Sambuc 
490433d6423SLionel Sambuc 	for (i = 0; i < dev->num_indirect; i++) {
491433d6423SLionel Sambuc 		desc = &dev->indirect[i];
492433d6423SLionel Sambuc 
493433d6423SLionel Sambuc 		if (desc->paddr == vd->addr) {
494433d6423SLionel Sambuc 			assert(desc->in_use);
495433d6423SLionel Sambuc 			desc->in_use = 0;
496433d6423SLionel Sambuc 			break;
497433d6423SLionel Sambuc 		}
498433d6423SLionel Sambuc 	}
499433d6423SLionel Sambuc 
500433d6423SLionel Sambuc 	if (i >= dev->num_indirect)
501433d6423SLionel Sambuc 		panic("Could not clear indirect descriptor table ");
502433d6423SLionel Sambuc }
503433d6423SLionel Sambuc 
504433d6423SLionel Sambuc 
5050a6a1f1dSLionel Sambuc inline static void
use_vring_desc(struct vring_desc * vd,struct vumap_phys * vp)506433d6423SLionel Sambuc use_vring_desc(struct vring_desc *vd, struct vumap_phys *vp)
507433d6423SLionel Sambuc {
508433d6423SLionel Sambuc 	vd->addr = vp->vp_addr & ~1UL;
509433d6423SLionel Sambuc 	vd->len = vp->vp_size;
510433d6423SLionel Sambuc 	vd->flags = VRING_DESC_F_NEXT;
511433d6423SLionel Sambuc 
512433d6423SLionel Sambuc 	if (vp->vp_addr & 1)
513433d6423SLionel Sambuc 		vd->flags |= VRING_DESC_F_WRITE;
514433d6423SLionel Sambuc }
515433d6423SLionel Sambuc 
516433d6423SLionel Sambuc static void
set_indirect_descriptors(struct virtio_device * dev,struct virtio_queue * q,struct vumap_phys * bufs,size_t num)517433d6423SLionel Sambuc set_indirect_descriptors(struct virtio_device *dev, struct virtio_queue *q,
518433d6423SLionel Sambuc 	struct vumap_phys *bufs, size_t num)
519433d6423SLionel Sambuc {
520433d6423SLionel Sambuc 	/* Indirect descriptor tables are simply filled from left to right */
521433d6423SLionel Sambuc 	int i;
522433d6423SLionel Sambuc 	struct indirect_desc_table *desc;
523433d6423SLionel Sambuc 	struct vring *vring = &q->vring;
5240a6a1f1dSLionel Sambuc 	struct vring_desc *vd, *ivd = NULL;
5250a6a1f1dSLionel Sambuc 
5260a6a1f1dSLionel Sambuc 	if (0 == num)
5270a6a1f1dSLionel Sambuc 		return;
528433d6423SLionel Sambuc 
529433d6423SLionel Sambuc 	/* Find the first unused indirect descriptor table */
530433d6423SLionel Sambuc 	for (i = 0; i < dev->num_indirect; i++) {
531433d6423SLionel Sambuc 		desc = &dev->indirect[i];
532433d6423SLionel Sambuc 
533433d6423SLionel Sambuc 		/* If an unused indirect descriptor table was found,
534433d6423SLionel Sambuc 		 * mark it as being used and exit the loop.
535433d6423SLionel Sambuc 		 */
536433d6423SLionel Sambuc 		if (!desc->in_use) {
537433d6423SLionel Sambuc 			desc->in_use = 1;
538433d6423SLionel Sambuc 			break;
539433d6423SLionel Sambuc 		}
540433d6423SLionel Sambuc 	}
541433d6423SLionel Sambuc 
542433d6423SLionel Sambuc 	/* Sanity check */
543433d6423SLionel Sambuc 	if (i >= dev->num_indirect)
544433d6423SLionel Sambuc 		panic("No indirect descriptor tables left");
545433d6423SLionel Sambuc 
546433d6423SLionel Sambuc 	/* For indirect descriptor tables, only a single descriptor from
547433d6423SLionel Sambuc 	 * the main ring is used.
548433d6423SLionel Sambuc 	 */
549433d6423SLionel Sambuc 	vd = &vring->desc[q->free_head];
550433d6423SLionel Sambuc 	vd->flags = VRING_DESC_F_INDIRECT;
551433d6423SLionel Sambuc 	vd->addr = desc->paddr;
552433d6423SLionel Sambuc 	vd->len = num * sizeof(desc->descs[0]);
553433d6423SLionel Sambuc 
554433d6423SLionel Sambuc 	/* Initialize the descriptors in the indirect descriptor table */
55565f76edbSDavid van Moolenbroek 	for (i = 0; i < (int)num; i++) {
556433d6423SLionel Sambuc 		ivd = &desc->descs[i];
557433d6423SLionel Sambuc 
558433d6423SLionel Sambuc 		use_vring_desc(ivd, &bufs[i]);
559433d6423SLionel Sambuc 		ivd->next = i + 1;
560433d6423SLionel Sambuc 	}
561433d6423SLionel Sambuc 
562433d6423SLionel Sambuc 	/* Unset the next bit of the last descriptor */
5630a6a1f1dSLionel Sambuc 	if (NULL != ivd)
564433d6423SLionel Sambuc 		ivd->flags = ivd->flags & ~VRING_DESC_F_NEXT;
565433d6423SLionel Sambuc 
566433d6423SLionel Sambuc 	/* Update queue, only a single descriptor was used */
567433d6423SLionel Sambuc 	q->free_num -= 1;
568433d6423SLionel Sambuc 	q->free_head = vd->next;
569433d6423SLionel Sambuc }
570433d6423SLionel Sambuc 
571433d6423SLionel Sambuc static void
set_direct_descriptors(struct virtio_queue * q,struct vumap_phys * bufs,size_t num)572433d6423SLionel Sambuc set_direct_descriptors(struct virtio_queue *q, struct vumap_phys *bufs,
573433d6423SLionel Sambuc 	size_t num)
574433d6423SLionel Sambuc {
575433d6423SLionel Sambuc 	u16_t i;
576433d6423SLionel Sambuc 	size_t count;
577433d6423SLionel Sambuc 	struct vring *vring = &q->vring;
578433d6423SLionel Sambuc 	struct vring_desc *vd;
579433d6423SLionel Sambuc 
5800a6a1f1dSLionel Sambuc 	if (0 == num)
5810a6a1f1dSLionel Sambuc 		return;
5820a6a1f1dSLionel Sambuc 
583433d6423SLionel Sambuc 	for (i = q->free_head, count = 0; count < num; count++) {
584433d6423SLionel Sambuc 
585433d6423SLionel Sambuc 		/* The next free descriptor */
586433d6423SLionel Sambuc 		vd = &vring->desc[i];
587433d6423SLionel Sambuc 
588433d6423SLionel Sambuc 		/* The descriptor is linked in the free list, so
589433d6423SLionel Sambuc 		 * it always has the next bit set.
590433d6423SLionel Sambuc 		 */
591433d6423SLionel Sambuc 		assert(vd->flags & VRING_DESC_F_NEXT);
592433d6423SLionel Sambuc 
593433d6423SLionel Sambuc 		use_vring_desc(vd, &bufs[count]);
594433d6423SLionel Sambuc 		i = vd->next;
595433d6423SLionel Sambuc 	}
596433d6423SLionel Sambuc 
597433d6423SLionel Sambuc 	/* Unset the next bit of the last descriptor */
598433d6423SLionel Sambuc 	vd->flags = vd->flags & ~VRING_DESC_F_NEXT;
599433d6423SLionel Sambuc 
600433d6423SLionel Sambuc 	/* Update queue */
601433d6423SLionel Sambuc 	q->free_num -= num;
602433d6423SLionel Sambuc 	q->free_head = i;
603433d6423SLionel Sambuc }
604433d6423SLionel Sambuc 
605433d6423SLionel Sambuc int
virtio_to_queue(struct virtio_device * dev,int qidx,struct vumap_phys * bufs,size_t num,void * data)606433d6423SLionel Sambuc virtio_to_queue(struct virtio_device *dev, int qidx, struct vumap_phys *bufs,
607433d6423SLionel Sambuc 	size_t num, void *data)
608433d6423SLionel Sambuc {
609433d6423SLionel Sambuc 	u16_t free_first;
610433d6423SLionel Sambuc 	int left;
611433d6423SLionel Sambuc 	struct virtio_queue *q = &dev->queues[qidx];
612433d6423SLionel Sambuc 	struct vring *vring = &q->vring;
613433d6423SLionel Sambuc 
614433d6423SLionel Sambuc 	assert(0 <= qidx && qidx <= dev->num_queues);
615433d6423SLionel Sambuc 
616433d6423SLionel Sambuc 	if (!data)
617433d6423SLionel Sambuc 		panic("%s: NULL data received queue %d", dev->name, qidx);
618433d6423SLionel Sambuc 
619433d6423SLionel Sambuc 	free_first = q->free_head;
620433d6423SLionel Sambuc 
621433d6423SLionel Sambuc 	left = (int)q->free_num - (int)num;
622433d6423SLionel Sambuc 
623433d6423SLionel Sambuc 	if (left < dev->threads)
624433d6423SLionel Sambuc 		set_indirect_descriptors(dev, q, bufs, num);
625433d6423SLionel Sambuc 	else
626433d6423SLionel Sambuc 		set_direct_descriptors(q, bufs, num);
627433d6423SLionel Sambuc 
628433d6423SLionel Sambuc 	/* Next index for host is old free_head */
629433d6423SLionel Sambuc 	vring->avail->ring[vring->avail->idx % q->num] = free_first;
630433d6423SLionel Sambuc 
631433d6423SLionel Sambuc 	/* Provided by the caller to identify this slot */
632433d6423SLionel Sambuc 	q->data[free_first] = data;
633433d6423SLionel Sambuc 
634433d6423SLionel Sambuc 	/* Make sure the host sees the new descriptors */
635433d6423SLionel Sambuc 	__insn_barrier();
636433d6423SLionel Sambuc 
637433d6423SLionel Sambuc 	/* advance last idx */
638433d6423SLionel Sambuc 	vring->avail->idx += 1;
639433d6423SLionel Sambuc 
640433d6423SLionel Sambuc 	/* Make sure the host sees the avail->idx */
641433d6423SLionel Sambuc 	__insn_barrier();
642433d6423SLionel Sambuc 
643433d6423SLionel Sambuc 	/* kick it! */
644433d6423SLionel Sambuc 	kick_queue(dev, qidx);
645433d6423SLionel Sambuc 	return 0;
646433d6423SLionel Sambuc }
647433d6423SLionel Sambuc 
648433d6423SLionel Sambuc int
virtio_from_queue(struct virtio_device * dev,int qidx,void ** data,size_t * len)649d1db724fSDavid van Moolenbroek virtio_from_queue(struct virtio_device *dev, int qidx, void **data,
650d1db724fSDavid van Moolenbroek 	size_t *len)
651433d6423SLionel Sambuc {
652433d6423SLionel Sambuc 	struct virtio_queue *q;
653433d6423SLionel Sambuc 	struct vring *vring;
654433d6423SLionel Sambuc 	struct vring_used_elem *uel;
655433d6423SLionel Sambuc 	struct vring_desc *vd;
656433d6423SLionel Sambuc 	int count = 0;
657433d6423SLionel Sambuc 	u16_t idx;
658433d6423SLionel Sambuc 	u16_t used_idx;
659433d6423SLionel Sambuc 
660433d6423SLionel Sambuc 	assert(0 <= qidx && qidx < dev->num_queues);
661433d6423SLionel Sambuc 
662433d6423SLionel Sambuc 	q = &dev->queues[qidx];
663433d6423SLionel Sambuc 	vring = &q->vring;
664433d6423SLionel Sambuc 
665433d6423SLionel Sambuc 	/* Make sure we see changes done by the host */
666433d6423SLionel Sambuc 	__insn_barrier();
667433d6423SLionel Sambuc 
668433d6423SLionel Sambuc 	/* The index from the host */
669433d6423SLionel Sambuc 	used_idx = vring->used->idx % q->num;
670433d6423SLionel Sambuc 
671433d6423SLionel Sambuc 	/* We already saw this one, nothing to do here */
672433d6423SLionel Sambuc 	if (q->last_used == used_idx)
673433d6423SLionel Sambuc 		return -1;
674433d6423SLionel Sambuc 
675433d6423SLionel Sambuc 	/* Get the vring_used element */
676433d6423SLionel Sambuc 	uel = &q->vring.used->ring[q->last_used];
677433d6423SLionel Sambuc 
678433d6423SLionel Sambuc 	/* Update the last used element */
679433d6423SLionel Sambuc 	q->last_used = (q->last_used + 1) % q->num;
680433d6423SLionel Sambuc 
681433d6423SLionel Sambuc 	/* index of the used element */
682433d6423SLionel Sambuc 	idx = uel->id % q->num;
683433d6423SLionel Sambuc 
684433d6423SLionel Sambuc 	assert(q->data[idx] != NULL);
685433d6423SLionel Sambuc 
686433d6423SLionel Sambuc 	/* Get the descriptor */
687433d6423SLionel Sambuc 	vd = &vring->desc[idx];
688433d6423SLionel Sambuc 
689433d6423SLionel Sambuc 	/* Unconditionally set the tail->next to the first used one */
690433d6423SLionel Sambuc 	assert(vring->desc[q->free_tail].flags & VRING_DESC_F_NEXT);
691433d6423SLionel Sambuc 	vring->desc[q->free_tail].next = idx;
692433d6423SLionel Sambuc 
693433d6423SLionel Sambuc 	/* Find the last index, eventually there has to be one
694433d6423SLionel Sambuc 	 * without a the next flag.
695433d6423SLionel Sambuc 	 *
696433d6423SLionel Sambuc 	 * FIXME: Protect from endless loop
697433d6423SLionel Sambuc 	 */
698433d6423SLionel Sambuc 	while (vd->flags & VRING_DESC_F_NEXT) {
699433d6423SLionel Sambuc 
700433d6423SLionel Sambuc 		if (vd->flags & VRING_DESC_F_INDIRECT)
701433d6423SLionel Sambuc 			clear_indirect_table(dev, vd);
702433d6423SLionel Sambuc 
703433d6423SLionel Sambuc 		idx = vd->next;
704433d6423SLionel Sambuc 		vd = &vring->desc[idx];
705433d6423SLionel Sambuc 		count++;
706433d6423SLionel Sambuc 	}
707433d6423SLionel Sambuc 
708433d6423SLionel Sambuc 	/* Didn't count the last one */
709433d6423SLionel Sambuc 	count++;
710433d6423SLionel Sambuc 
711433d6423SLionel Sambuc 	if (vd->flags & VRING_DESC_F_INDIRECT)
712433d6423SLionel Sambuc 		clear_indirect_table(dev, vd);
713433d6423SLionel Sambuc 
714433d6423SLionel Sambuc 	/* idx points to the tail now, update the queue */
715433d6423SLionel Sambuc 	q->free_tail = idx;
716433d6423SLionel Sambuc 	assert(!(vd->flags & VRING_DESC_F_NEXT));
717433d6423SLionel Sambuc 
718433d6423SLionel Sambuc 	/* We can always connect the tail with the head */
719433d6423SLionel Sambuc 	vring->desc[q->free_tail].next = q->free_head;
720433d6423SLionel Sambuc 	vring->desc[q->free_tail].flags = VRING_DESC_F_NEXT;
721433d6423SLionel Sambuc 
722433d6423SLionel Sambuc 	q->free_num += count;
723433d6423SLionel Sambuc 
724433d6423SLionel Sambuc 	assert(q->free_num <= q->num);
725433d6423SLionel Sambuc 
726433d6423SLionel Sambuc 	*data = q->data[uel->id];
727433d6423SLionel Sambuc 	q->data[uel->id] = NULL;
728433d6423SLionel Sambuc 
729d1db724fSDavid van Moolenbroek 	if (len != NULL)
730d1db724fSDavid van Moolenbroek 		*len = uel->len;
731d1db724fSDavid van Moolenbroek 
732433d6423SLionel Sambuc 	return 0;
733433d6423SLionel Sambuc }
734433d6423SLionel Sambuc 
735433d6423SLionel Sambuc int
virtio_had_irq(struct virtio_device * dev)736433d6423SLionel Sambuc virtio_had_irq(struct virtio_device *dev)
737433d6423SLionel Sambuc {
738433d6423SLionel Sambuc 	return virtio_read8(dev, VIRTIO_ISR_STATUS_OFF) & 1;
739433d6423SLionel Sambuc }
740433d6423SLionel Sambuc 
741433d6423SLionel Sambuc void
virtio_reset_device(struct virtio_device * dev)742433d6423SLionel Sambuc virtio_reset_device(struct virtio_device *dev)
743433d6423SLionel Sambuc {
744433d6423SLionel Sambuc 	virtio_irq_unregister(dev);
745433d6423SLionel Sambuc 	virtio_write8(dev, VIRTIO_DEV_STATUS_OFF, 0);
746433d6423SLionel Sambuc }
747433d6423SLionel Sambuc 
748433d6423SLionel Sambuc 
749433d6423SLionel Sambuc void
virtio_irq_enable(struct virtio_device * dev)750433d6423SLionel Sambuc virtio_irq_enable(struct virtio_device *dev)
751433d6423SLionel Sambuc {
752433d6423SLionel Sambuc 	int r;
753*dc2c582fSDavid van Moolenbroek 	if ((r = sys_irqenable(&dev->irq_hook)) != OK)
754433d6423SLionel Sambuc 		panic("%s Unable to enable IRQ %d", dev->name, r);
755433d6423SLionel Sambuc }
756433d6423SLionel Sambuc 
757433d6423SLionel Sambuc void
virtio_irq_disable(struct virtio_device * dev)758433d6423SLionel Sambuc virtio_irq_disable(struct virtio_device *dev)
759433d6423SLionel Sambuc {
760433d6423SLionel Sambuc 	int r;
761*dc2c582fSDavid van Moolenbroek 	if ((r = sys_irqdisable(&dev->irq_hook)) != OK)
762433d6423SLionel Sambuc 		panic("%s: Unable to disable IRQ %d", dev->name, r);
763433d6423SLionel Sambuc }
764433d6423SLionel Sambuc 
765433d6423SLionel Sambuc static int
wants_kick(struct virtio_queue * q)766433d6423SLionel Sambuc wants_kick(struct virtio_queue *q)
767433d6423SLionel Sambuc {
768433d6423SLionel Sambuc 	assert(q != NULL);
769433d6423SLionel Sambuc 	return !(q->vring.used->flags & VRING_USED_F_NO_NOTIFY);
770433d6423SLionel Sambuc }
771433d6423SLionel Sambuc 
772433d6423SLionel Sambuc static void
kick_queue(struct virtio_device * dev,int qidx)773433d6423SLionel Sambuc kick_queue(struct virtio_device *dev, int qidx)
774433d6423SLionel Sambuc {
775433d6423SLionel Sambuc 	assert(0 <= qidx && qidx < dev->num_queues);
776433d6423SLionel Sambuc 
777433d6423SLionel Sambuc 	if (wants_kick(&dev->queues[qidx]))
778433d6423SLionel Sambuc 		virtio_write16(dev, VIRTIO_QNOTFIY_OFF, qidx);
779433d6423SLionel Sambuc 
780433d6423SLionel Sambuc 	return;
781433d6423SLionel Sambuc }
782433d6423SLionel Sambuc 
783433d6423SLionel Sambuc static int
is_matching_device(u16_t expected_sdid,u16_t vid,u16_t sdid)784433d6423SLionel Sambuc is_matching_device(u16_t expected_sdid, u16_t vid, u16_t sdid)
785433d6423SLionel Sambuc {
786433d6423SLionel Sambuc 	return vid == VIRTIO_VENDOR_ID && sdid == expected_sdid;
787433d6423SLionel Sambuc }
788433d6423SLionel Sambuc 
789433d6423SLionel Sambuc static void
virtio_irq_register(struct virtio_device * dev)790433d6423SLionel Sambuc virtio_irq_register(struct virtio_device *dev)
791433d6423SLionel Sambuc {
792433d6423SLionel Sambuc 	int r;
793*dc2c582fSDavid van Moolenbroek 	if ((r = sys_irqsetpolicy(dev->irq, 0, &dev->irq_hook)) != OK)
794433d6423SLionel Sambuc 		panic("%s: Unable to register IRQ %d", dev->name, r);
795433d6423SLionel Sambuc }
796433d6423SLionel Sambuc 
797433d6423SLionel Sambuc static void
virtio_irq_unregister(struct virtio_device * dev)798433d6423SLionel Sambuc virtio_irq_unregister(struct virtio_device *dev)
799433d6423SLionel Sambuc {
800433d6423SLionel Sambuc 	int r;
801*dc2c582fSDavid van Moolenbroek 	if ((r = sys_irqrmpolicy(&dev->irq_hook)) != OK)
802433d6423SLionel Sambuc 		panic("%s: Unable to unregister IRQ %d", dev->name, r);
803433d6423SLionel Sambuc }
804433d6423SLionel Sambuc 
805433d6423SLionel Sambuc static int
_supports(struct virtio_device * dev,int bit,int host)806433d6423SLionel Sambuc _supports(struct virtio_device *dev, int bit, int host)
807433d6423SLionel Sambuc {
808433d6423SLionel Sambuc 	for (int i = 0; i < dev->num_features; i++) {
809433d6423SLionel Sambuc 		struct virtio_feature *f = &dev->features[i];
810433d6423SLionel Sambuc 
811433d6423SLionel Sambuc 		if (f->bit == bit)
812433d6423SLionel Sambuc 			return host ? f->host_support : f->guest_support;
813433d6423SLionel Sambuc 	}
814433d6423SLionel Sambuc 
815433d6423SLionel Sambuc 	panic("%s: Feature not found bit=%d", dev->name, bit);
816433d6423SLionel Sambuc }
817433d6423SLionel Sambuc 
818433d6423SLionel Sambuc int
virtio_host_supports(struct virtio_device * dev,int bit)819433d6423SLionel Sambuc virtio_host_supports(struct virtio_device *dev, int bit)
820433d6423SLionel Sambuc {
821433d6423SLionel Sambuc 	return _supports(dev, bit, 1);
822433d6423SLionel Sambuc }
823433d6423SLionel Sambuc 
824433d6423SLionel Sambuc int
virtio_guest_supports(struct virtio_device * dev,int bit)825433d6423SLionel Sambuc virtio_guest_supports(struct virtio_device *dev, int bit)
826433d6423SLionel Sambuc {
827433d6423SLionel Sambuc 	return _supports(dev, bit, 0);
828433d6423SLionel Sambuc }
829433d6423SLionel Sambuc 
830433d6423SLionel Sambuc 
831433d6423SLionel Sambuc /* Just some wrappers around sys_read */
832433d6423SLionel Sambuc #define VIRTIO_READ_XX(xx, suff)					\
833433d6423SLionel Sambuc u##xx##_t								\
834433d6423SLionel Sambuc virtio_read##xx(struct virtio_device *dev, i32_t off)			\
835433d6423SLionel Sambuc {									\
836433d6423SLionel Sambuc 	int r;								\
837433d6423SLionel Sambuc 	u32_t ret;							\
838433d6423SLionel Sambuc 	if ((r = sys_in##suff(dev->port + off, &ret)) != OK)		\
839433d6423SLionel Sambuc 		panic("%s: Read failed %d %d r=%d", dev->name,		\
840433d6423SLionel Sambuc 						    dev->port,		\
841433d6423SLionel Sambuc 						    off,		\
842433d6423SLionel Sambuc 						    r);			\
843433d6423SLionel Sambuc 									\
844433d6423SLionel Sambuc 	return ret;							\
845433d6423SLionel Sambuc }
846433d6423SLionel Sambuc 
847433d6423SLionel Sambuc VIRTIO_READ_XX(32, l)
848433d6423SLionel Sambuc VIRTIO_READ_XX(16, w)
849433d6423SLionel Sambuc VIRTIO_READ_XX(8, b)
850433d6423SLionel Sambuc 
851433d6423SLionel Sambuc /* Just some wrappers around sys_write */
852433d6423SLionel Sambuc #define VIRTIO_WRITE_XX(xx, suff)					\
853433d6423SLionel Sambuc void									\
854433d6423SLionel Sambuc virtio_write##xx(struct virtio_device *dev, i32_t off, u##xx##_t val)	\
855433d6423SLionel Sambuc {									\
856433d6423SLionel Sambuc 	int r;								\
857433d6423SLionel Sambuc 	if ((r = sys_out##suff(dev->port + off, val)) != OK)		\
858433d6423SLionel Sambuc 		panic("%s: Write failed %d %d r=%d", dev->name,		\
859433d6423SLionel Sambuc 						     dev->port,		\
860433d6423SLionel Sambuc 						     off,		\
861433d6423SLionel Sambuc 						     r);		\
862433d6423SLionel Sambuc }
863433d6423SLionel Sambuc 
864433d6423SLionel Sambuc VIRTIO_WRITE_XX(32, l)
865433d6423SLionel Sambuc VIRTIO_WRITE_XX(16, w)
866433d6423SLionel Sambuc VIRTIO_WRITE_XX(8, b)
867433d6423SLionel Sambuc 
868433d6423SLionel Sambuc /* Just some wrappers around sys_read */
869433d6423SLionel Sambuc #define VIRTIO_SREAD_XX(xx, suff)					\
870433d6423SLionel Sambuc u##xx##_t								\
871433d6423SLionel Sambuc virtio_sread##xx(struct virtio_device *dev, i32_t off)			\
872433d6423SLionel Sambuc {									\
873433d6423SLionel Sambuc 	int r;								\
874433d6423SLionel Sambuc 	u32_t ret;							\
875433d6423SLionel Sambuc 	off += VIRTIO_DEV_SPECIFIC_OFF; 				\
876433d6423SLionel Sambuc 									\
877433d6423SLionel Sambuc 	if (dev->msi)							\
878433d6423SLionel Sambuc 		off += VIRTIO_MSI_ADD_OFF;				\
879433d6423SLionel Sambuc 									\
880433d6423SLionel Sambuc 	if ((r = sys_in##suff(dev->port + off, &ret)) != OK)		\
881433d6423SLionel Sambuc 		panic("%s: Read failed %d %d r=%d", dev->name,		\
882433d6423SLionel Sambuc 						    dev->port,		\
883433d6423SLionel Sambuc 						    off,		\
884433d6423SLionel Sambuc 						    r);			\
885433d6423SLionel Sambuc 									\
886433d6423SLionel Sambuc 	return ret;							\
887433d6423SLionel Sambuc }
888433d6423SLionel Sambuc 
889433d6423SLionel Sambuc VIRTIO_SREAD_XX(32, l)
890433d6423SLionel Sambuc VIRTIO_SREAD_XX(16, w)
891433d6423SLionel Sambuc VIRTIO_SREAD_XX(8, b)
892433d6423SLionel Sambuc 
893433d6423SLionel Sambuc /* Just some wrappers around sys_write */
894433d6423SLionel Sambuc #define VIRTIO_SWRITE_XX(xx, suff)					\
895433d6423SLionel Sambuc void									\
896433d6423SLionel Sambuc virtio_swrite##xx(struct virtio_device *dev, i32_t off, u##xx##_t val)	\
897433d6423SLionel Sambuc {									\
898433d6423SLionel Sambuc 	int r;								\
899433d6423SLionel Sambuc 	off += VIRTIO_DEV_SPECIFIC_OFF; 				\
900433d6423SLionel Sambuc 									\
901433d6423SLionel Sambuc 	if (dev->msi)							\
902433d6423SLionel Sambuc 		off += VIRTIO_MSI_ADD_OFF;				\
903433d6423SLionel Sambuc 									\
904433d6423SLionel Sambuc 	if ((r = sys_out##suff(dev->port + off, val)) != OK)		\
905433d6423SLionel Sambuc 		panic("%s: Write failed %d %d r=%d", dev->name,		\
906433d6423SLionel Sambuc 						     dev->port,		\
907433d6423SLionel Sambuc 						     off,		\
908433d6423SLionel Sambuc 						     r);		\
909433d6423SLionel Sambuc }
910433d6423SLionel Sambuc 
911433d6423SLionel Sambuc VIRTIO_SWRITE_XX(32, l)
912433d6423SLionel Sambuc VIRTIO_SWRITE_XX(16, w)
913433d6423SLionel Sambuc VIRTIO_SWRITE_XX(8, b)
914