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