1 /*
2 * Generic virtio library for MINIX 3
3 *
4 * Copyright (c) 2013, A. Welzel, <arne.welzel@gmail.com>
5 *
6 * This software is released under the BSD license. See the LICENSE file
7 * included in the main directory of this source distribution for the
8 * license terms and conditions.
9 */
10
11 #define _SYSTEM 1
12
13 #include <assert.h>
14 #include <errno.h> /* for OK... */
15 #include <string.h> /* memset() */
16 #include <stdlib.h> /* malloc() */
17
18 #include <machine/pci.h> /* PCI_ILR, PCI_BAR... */
19 #include <machine/vmparam.h> /* PAGE_SIZE */
20
21 #include <minix/syslib.h> /* umap, vumap, alloc_..*/
22 #include <minix/sysutil.h> /* panic(), at least */
23 #include <minix/virtio.h> /* virtio system include */
24
25 #include "virtio_ring.h" /* virtio types / helper */
26
27 /*
28 * About indirect descriptors:
29 *
30 * For each possible thread, a single indirect descriptor table is allocated.
31 * If using direct descriptors would lead to the situation that another thread
32 * might not be able to add another descriptor to the ring, indirect descriptors
33 * are used.
34 *
35 * Indirect descriptors are pre-allocated. Each alloc_contig() call involves a
36 * kernel call which is critical for performance.
37 *
38 * The size of indirect descriptor tables is chosen based on MAPVEC_NR. A driver
39 * using this library should never add more than
40 *
41 * MAPVEC_NR + MAPVEC_NR / 2
42 *
43 * descriptors to a queue as this represent the maximum size of an indirect
44 * descriptor table.
45 */
46
47 struct indirect_desc_table {
48 int in_use;
49 struct vring_desc *descs;
50 phys_bytes paddr;
51 size_t len;
52 };
53
54 struct virtio_queue {
55
56 void *vaddr; /* virtual addr of ring */
57 phys_bytes paddr; /* physical addr of ring */
58 u32_t page; /* physical guest page */
59
60 u16_t num; /* number of descriptors */
61 u32_t ring_size; /* size of ring in bytes */
62 struct vring vring;
63
64 u16_t free_num; /* free descriptors */
65 u16_t free_head; /* next free descriptor */
66 u16_t free_tail; /* last free descriptor */
67 u16_t last_used; /* we checked in used */
68
69 void **data; /* points to pointers */
70 };
71
72 struct virtio_device {
73
74 const char *name; /* for debugging */
75
76 u16_t port; /* io port */
77
78 struct virtio_feature *features; /* host / guest features */
79 u8_t num_features; /* max 32 */
80
81 struct virtio_queue *queues; /* our queues */
82 u16_t num_queues;
83
84 int irq; /* interrupt line */
85 int irq_hook; /* hook id */
86 int msi; /* is MSI enabled? */
87
88 int threads; /* max number of threads */
89
90 struct indirect_desc_table *indirect; /* indirect descriptor tables */
91 int num_indirect;
92 };
93
94 static int is_matching_device(u16_t expected_sdid, u16_t vid, u16_t sdid);
95 static int init_device(int devind, struct virtio_device *dev);
96 static int init_phys_queues(struct virtio_device *dev);
97 static int exchange_features(struct virtio_device *dev);
98 static int alloc_phys_queue(struct virtio_queue *q);
99 static void free_phys_queue(struct virtio_queue *q);
100 static void init_phys_queue(struct virtio_queue *q);
101 static int init_indirect_desc_table(struct indirect_desc_table *desc);
102 static int init_indirect_desc_tables(struct virtio_device *dev);
103 static void virtio_irq_register(struct virtio_device *dev);
104 static void virtio_irq_unregister(struct virtio_device *dev);
105 static int wants_kick(struct virtio_queue *q);
106 static void kick_queue(struct virtio_device *dev, int qidx);
107
108 struct virtio_device *
virtio_setup_device(u16_t subdevid,const char * name,struct virtio_feature * features,int num_features,int threads,int skip)109 virtio_setup_device(u16_t subdevid, const char *name,
110 struct virtio_feature *features, int num_features,
111 int threads, int skip)
112 {
113 int r, devind;
114 u16_t vid, did, sdid;
115 struct virtio_device *ret;
116
117 /* bogus values? */
118 if (skip < 0 || name == NULL || num_features < 0 || threads <= 0)
119 return NULL;
120
121 pci_init();
122
123 r = pci_first_dev(&devind, &vid, &did);
124
125 while (r > 0) {
126 sdid = pci_attr_r16(devind, PCI_SUBDID);
127 if (is_matching_device(subdevid, vid, sdid)) {
128
129 /* this is the device we are looking for */
130 if (skip == 0)
131 break;
132
133 skip--;
134 }
135
136 r = pci_next_dev(&devind, &vid, &did);
137 }
138
139 /* pci_[first|next_dev()] return 0 if no device was found */
140 if (r == 0 || skip > 0)
141 return NULL;
142
143 /* allocate and set known info about the device */
144 ret = malloc(sizeof(*ret));
145
146 if (ret == NULL)
147 return NULL;
148
149 /* Prepare virtio_device intance */
150 memset(ret, 0, sizeof(*ret));
151 ret->name = name;
152 ret->features = features;
153 ret->num_features = num_features;
154 ret->threads = threads;
155 /* see comment in the beginning of this file */
156 ret->num_indirect = threads;
157
158 if (init_device(devind, ret) != OK) {
159 printf("%s: Could not initialize device\n", ret->name);
160 goto err;
161 }
162
163 /* Ack the device */
164 virtio_write8(ret, VIRTIO_DEV_STATUS_OFF, VIRTIO_STATUS_ACK);
165
166 if (exchange_features(ret) != OK) {
167 printf("%s: Could not exchange features\n", ret->name);
168 goto err;
169 }
170
171 if (init_indirect_desc_tables(ret) != OK) {
172 printf("%s: Could not initialize indirect tables\n", ret->name);
173 goto err;
174 }
175
176 /* We know how to drive the device... */
177 virtio_write8(ret, VIRTIO_DEV_STATUS_OFF, VIRTIO_STATUS_DRV);
178
179 return ret;
180
181 /* Error path */
182 err:
183 free(ret);
184 return NULL;
185 }
186
187 static int
init_device(int devind,struct virtio_device * dev)188 init_device(int devind, struct virtio_device *dev)
189 {
190 u32_t base, size;
191 int iof, r;
192
193 pci_reserve(devind);
194
195 if ((r = pci_get_bar(devind, PCI_BAR, &base, &size, &iof)) != OK) {
196 printf("%s: Could not get BAR (%d)", dev->name, r);
197 return r;
198 }
199
200 if (!iof) {
201 printf("%s: PCI not IO space?", dev->name);
202 return EINVAL;
203 }
204
205 if (base & 0xFFFF0000) {
206 printf("%s: IO port weird (%08x)", dev->name, base);
207 return EINVAL;
208 }
209
210 /* store the I/O port */
211 dev->port = base;
212
213 /* Reset the device */
214 virtio_write8(dev, VIRTIO_DEV_STATUS_OFF, 0);
215
216 /* Read IRQ line */
217 dev->irq = pci_attr_r8(devind, PCI_ILR);
218
219 return OK;
220 }
221
222 static int
exchange_features(struct virtio_device * dev)223 exchange_features(struct virtio_device *dev)
224 {
225 u32_t guest_features = 0, host_features = 0;
226 struct virtio_feature *f;
227
228 host_features = virtio_read32(dev, VIRTIO_HOST_F_OFF);
229
230 for (int i = 0; i < dev->num_features; i++) {
231 f = &dev->features[i];
232
233 /* prepare the features the driver supports */
234 guest_features |= (f->guest_support << f->bit);
235
236 /* just load the host feature int the struct */
237 f->host_support = ((host_features >> f->bit) & 1);
238 }
239
240 /* let the device know about our features */
241 virtio_write32(dev, VIRTIO_GUEST_F_OFF, guest_features);
242
243 return OK;
244 }
245
246 int
virtio_alloc_queues(struct virtio_device * dev,int num_queues)247 virtio_alloc_queues(struct virtio_device *dev, int num_queues)
248 {
249 int r = OK;
250
251 assert(dev != NULL);
252
253 /* Assume there's no device with more than 256 queues */
254 if (num_queues < 0 || num_queues > 256)
255 return EINVAL;
256
257 dev->num_queues = num_queues;
258 /* allocate queue memory */
259 dev->queues = malloc(num_queues * sizeof(dev->queues[0]));
260
261 if (dev->queues == NULL)
262 return ENOMEM;
263
264 memset(dev->queues, 0, num_queues * sizeof(dev->queues[0]));
265
266 if ((r = init_phys_queues(dev)) != OK) {
267 printf("%s: Could not initialize queues (%d)\n", dev->name, r);
268 free(dev->queues);
269 dev->queues = NULL;
270 }
271
272 return r;
273 }
274
275 static int
init_phys_queues(struct virtio_device * dev)276 init_phys_queues(struct virtio_device *dev)
277 {
278 /* Initialize all queues */
279 int i, j, r;
280 struct virtio_queue *q;
281
282 for (i = 0; i < dev->num_queues; i++) {
283 q = &dev->queues[i];
284 /* select the queue */
285 virtio_write16(dev, VIRTIO_QSEL_OFF, i);
286 q->num = virtio_read16(dev, VIRTIO_QSIZE_OFF);
287
288 if (q->num & (q->num - 1)) {
289 printf("%s: Queue %d num=%d not ^2", dev->name, i,
290 q->num);
291 r = EINVAL;
292 goto free_phys_queues;
293 }
294
295 if ((r = alloc_phys_queue(q)) != OK)
296 goto free_phys_queues;
297
298 init_phys_queue(q);
299
300 /* Let the host know about the guest physical page */
301 virtio_write32(dev, VIRTIO_QADDR_OFF, q->page);
302 }
303
304 return OK;
305
306 /* Error path */
307 free_phys_queues:
308 for (j = 0; j < i; j++)
309 free_phys_queue(&dev->queues[i]);
310
311 return r;
312 }
313
314 static int
alloc_phys_queue(struct virtio_queue * q)315 alloc_phys_queue(struct virtio_queue *q)
316 {
317 assert(q != NULL);
318
319 /* How much memory do we need? */
320 q->ring_size = vring_size(q->num, PAGE_SIZE);
321
322 q->vaddr = alloc_contig(q->ring_size, AC_ALIGN4K, &q->paddr);
323
324 if (q->vaddr == NULL)
325 return ENOMEM;
326
327 q->data = alloc_contig(sizeof(q->data[0]) * q->num, AC_ALIGN4K, NULL);
328
329 if (q->data == NULL) {
330 free_contig(q->vaddr, q->ring_size);
331 q->vaddr = NULL;
332 q->paddr = 0;
333 return ENOMEM;
334 }
335
336 return OK;
337 }
338
339 void
virtio_device_ready(struct virtio_device * dev)340 virtio_device_ready(struct virtio_device *dev)
341 {
342 assert(dev != NULL);
343
344 /* Register IRQ line */
345 virtio_irq_register(dev);
346
347 /* Driver is ready to go! */
348 virtio_write8(dev, VIRTIO_DEV_STATUS_OFF, VIRTIO_STATUS_DRV_OK);
349 }
350
351 void
virtio_free_queues(struct virtio_device * dev)352 virtio_free_queues(struct virtio_device *dev)
353 {
354 int i;
355 assert(dev != NULL);
356 assert(dev->queues != NULL);
357 assert(dev->num_queues > 0);
358
359 for (i = 0; i < dev->num_queues; i++)
360 free_phys_queue(&dev->queues[i]);
361
362 dev->num_queues = 0;
363 dev->queues = NULL;
364 }
365
366 static void
free_phys_queue(struct virtio_queue * q)367 free_phys_queue(struct virtio_queue *q)
368 {
369 assert(q != NULL);
370 assert(q->vaddr != NULL);
371
372 free_contig(q->vaddr, q->ring_size);
373 q->vaddr = NULL;
374 q->paddr = 0;
375 q->num = 0;
376 free_contig(q->data, sizeof(q->data[0]));
377 q->data = NULL;
378 }
379
380 static void
init_phys_queue(struct virtio_queue * q)381 init_phys_queue(struct virtio_queue *q)
382 {
383 memset(q->vaddr, 0, q->ring_size);
384 memset(q->data, 0, sizeof(q->data[0]) * q->num);
385
386 /* physical page in guest */
387 q->page = q->paddr / PAGE_SIZE;
388
389 /* Set pointers in q->vring according to size */
390 vring_init(&q->vring, q->num, q->vaddr, PAGE_SIZE);
391
392 /* Everything's free at this point */
393 for (int i = 0; i < q->num; i++) {
394 q->vring.desc[i].flags = VRING_DESC_F_NEXT;
395 q->vring.desc[i].next = (i + 1) & (q->num - 1);
396 }
397
398 q->free_num = q->num;
399 q->free_head = 0;
400 q->free_tail = q->num - 1;
401 q->last_used = 0;
402
403 return;
404 }
405
406 void
virtio_free_device(struct virtio_device * dev)407 virtio_free_device(struct virtio_device *dev)
408 {
409 int i;
410 struct indirect_desc_table *desc;
411
412 assert(dev != NULL);
413
414 assert(dev->num_indirect > 0);
415
416 for (i = 0; i < dev->num_indirect; i++) {
417 desc = &dev->indirect[i];
418 free_contig(desc->descs, desc->len);
419 }
420
421 dev->num_indirect = 0;
422
423 assert(dev->indirect != NULL);
424 free(dev->indirect);
425 dev->indirect = NULL;
426
427 free(dev);
428 }
429
430 static int
init_indirect_desc_table(struct indirect_desc_table * desc)431 init_indirect_desc_table(struct indirect_desc_table *desc)
432 {
433 desc->in_use = 0;
434 desc->len = (MAPVEC_NR + MAPVEC_NR / 2) * sizeof(struct vring_desc);
435
436 desc->descs = alloc_contig(desc->len, AC_ALIGN4K, &desc->paddr);
437 memset(desc->descs, 0, desc->len);
438
439 if (desc->descs == NULL)
440 return ENOMEM;
441
442 return OK;
443 }
444
445 static int
init_indirect_desc_tables(struct virtio_device * dev)446 init_indirect_desc_tables(struct virtio_device *dev)
447 {
448 int i, j, r;
449 struct indirect_desc_table *desc;
450
451 dev->indirect = malloc(dev->num_indirect * sizeof(dev->indirect[0]));
452
453 if (dev->indirect == NULL) {
454 printf("%s: Could not allocate indirect tables\n", dev->name);
455 return ENOMEM;
456 }
457
458 memset(dev->indirect, 0, dev->num_indirect* sizeof(dev->indirect[0]));
459
460 for (i = 0; i < dev->num_indirect; i++) {
461 desc = &dev->indirect[i];
462 if ((r = init_indirect_desc_table(desc)) != OK) {
463
464 /* error path */
465 for (j = 0; j < i; j++) {
466 desc = &dev->indirect[j];
467 free_contig(desc->descs, desc->len);
468 }
469
470 free(dev->indirect);
471
472 return r;
473 }
474 }
475
476 return OK;
477 }
478
479 static void
clear_indirect_table(struct virtio_device * dev,struct vring_desc * vd)480 clear_indirect_table(struct virtio_device *dev, struct vring_desc *vd)
481 {
482 int i;
483 struct indirect_desc_table *desc;
484
485 assert(vd->len > 0);
486 assert(vd->flags & VRING_DESC_F_INDIRECT);
487 vd->flags = vd->flags & ~VRING_DESC_F_INDIRECT;
488 vd->len = 0;;
489
490 for (i = 0; i < dev->num_indirect; i++) {
491 desc = &dev->indirect[i];
492
493 if (desc->paddr == vd->addr) {
494 assert(desc->in_use);
495 desc->in_use = 0;
496 break;
497 }
498 }
499
500 if (i >= dev->num_indirect)
501 panic("Could not clear indirect descriptor table ");
502 }
503
504
505 inline static void
use_vring_desc(struct vring_desc * vd,struct vumap_phys * vp)506 use_vring_desc(struct vring_desc *vd, struct vumap_phys *vp)
507 {
508 vd->addr = vp->vp_addr & ~1UL;
509 vd->len = vp->vp_size;
510 vd->flags = VRING_DESC_F_NEXT;
511
512 if (vp->vp_addr & 1)
513 vd->flags |= VRING_DESC_F_WRITE;
514 }
515
516 static void
set_indirect_descriptors(struct virtio_device * dev,struct virtio_queue * q,struct vumap_phys * bufs,size_t num)517 set_indirect_descriptors(struct virtio_device *dev, struct virtio_queue *q,
518 struct vumap_phys *bufs, size_t num)
519 {
520 /* Indirect descriptor tables are simply filled from left to right */
521 int i;
522 struct indirect_desc_table *desc;
523 struct vring *vring = &q->vring;
524 struct vring_desc *vd, *ivd = NULL;
525
526 if (0 == num)
527 return;
528
529 /* Find the first unused indirect descriptor table */
530 for (i = 0; i < dev->num_indirect; i++) {
531 desc = &dev->indirect[i];
532
533 /* If an unused indirect descriptor table was found,
534 * mark it as being used and exit the loop.
535 */
536 if (!desc->in_use) {
537 desc->in_use = 1;
538 break;
539 }
540 }
541
542 /* Sanity check */
543 if (i >= dev->num_indirect)
544 panic("No indirect descriptor tables left");
545
546 /* For indirect descriptor tables, only a single descriptor from
547 * the main ring is used.
548 */
549 vd = &vring->desc[q->free_head];
550 vd->flags = VRING_DESC_F_INDIRECT;
551 vd->addr = desc->paddr;
552 vd->len = num * sizeof(desc->descs[0]);
553
554 /* Initialize the descriptors in the indirect descriptor table */
555 for (i = 0; i < (int)num; i++) {
556 ivd = &desc->descs[i];
557
558 use_vring_desc(ivd, &bufs[i]);
559 ivd->next = i + 1;
560 }
561
562 /* Unset the next bit of the last descriptor */
563 if (NULL != ivd)
564 ivd->flags = ivd->flags & ~VRING_DESC_F_NEXT;
565
566 /* Update queue, only a single descriptor was used */
567 q->free_num -= 1;
568 q->free_head = vd->next;
569 }
570
571 static void
set_direct_descriptors(struct virtio_queue * q,struct vumap_phys * bufs,size_t num)572 set_direct_descriptors(struct virtio_queue *q, struct vumap_phys *bufs,
573 size_t num)
574 {
575 u16_t i;
576 size_t count;
577 struct vring *vring = &q->vring;
578 struct vring_desc *vd;
579
580 if (0 == num)
581 return;
582
583 for (i = q->free_head, count = 0; count < num; count++) {
584
585 /* The next free descriptor */
586 vd = &vring->desc[i];
587
588 /* The descriptor is linked in the free list, so
589 * it always has the next bit set.
590 */
591 assert(vd->flags & VRING_DESC_F_NEXT);
592
593 use_vring_desc(vd, &bufs[count]);
594 i = vd->next;
595 }
596
597 /* Unset the next bit of the last descriptor */
598 vd->flags = vd->flags & ~VRING_DESC_F_NEXT;
599
600 /* Update queue */
601 q->free_num -= num;
602 q->free_head = i;
603 }
604
605 int
virtio_to_queue(struct virtio_device * dev,int qidx,struct vumap_phys * bufs,size_t num,void * data)606 virtio_to_queue(struct virtio_device *dev, int qidx, struct vumap_phys *bufs,
607 size_t num, void *data)
608 {
609 u16_t free_first;
610 int left;
611 struct virtio_queue *q = &dev->queues[qidx];
612 struct vring *vring = &q->vring;
613
614 assert(0 <= qidx && qidx <= dev->num_queues);
615
616 if (!data)
617 panic("%s: NULL data received queue %d", dev->name, qidx);
618
619 free_first = q->free_head;
620
621 left = (int)q->free_num - (int)num;
622
623 if (left < dev->threads)
624 set_indirect_descriptors(dev, q, bufs, num);
625 else
626 set_direct_descriptors(q, bufs, num);
627
628 /* Next index for host is old free_head */
629 vring->avail->ring[vring->avail->idx % q->num] = free_first;
630
631 /* Provided by the caller to identify this slot */
632 q->data[free_first] = data;
633
634 /* Make sure the host sees the new descriptors */
635 __insn_barrier();
636
637 /* advance last idx */
638 vring->avail->idx += 1;
639
640 /* Make sure the host sees the avail->idx */
641 __insn_barrier();
642
643 /* kick it! */
644 kick_queue(dev, qidx);
645 return 0;
646 }
647
648 int
virtio_from_queue(struct virtio_device * dev,int qidx,void ** data,size_t * len)649 virtio_from_queue(struct virtio_device *dev, int qidx, void **data,
650 size_t *len)
651 {
652 struct virtio_queue *q;
653 struct vring *vring;
654 struct vring_used_elem *uel;
655 struct vring_desc *vd;
656 int count = 0;
657 u16_t idx;
658 u16_t used_idx;
659
660 assert(0 <= qidx && qidx < dev->num_queues);
661
662 q = &dev->queues[qidx];
663 vring = &q->vring;
664
665 /* Make sure we see changes done by the host */
666 __insn_barrier();
667
668 /* The index from the host */
669 used_idx = vring->used->idx % q->num;
670
671 /* We already saw this one, nothing to do here */
672 if (q->last_used == used_idx)
673 return -1;
674
675 /* Get the vring_used element */
676 uel = &q->vring.used->ring[q->last_used];
677
678 /* Update the last used element */
679 q->last_used = (q->last_used + 1) % q->num;
680
681 /* index of the used element */
682 idx = uel->id % q->num;
683
684 assert(q->data[idx] != NULL);
685
686 /* Get the descriptor */
687 vd = &vring->desc[idx];
688
689 /* Unconditionally set the tail->next to the first used one */
690 assert(vring->desc[q->free_tail].flags & VRING_DESC_F_NEXT);
691 vring->desc[q->free_tail].next = idx;
692
693 /* Find the last index, eventually there has to be one
694 * without a the next flag.
695 *
696 * FIXME: Protect from endless loop
697 */
698 while (vd->flags & VRING_DESC_F_NEXT) {
699
700 if (vd->flags & VRING_DESC_F_INDIRECT)
701 clear_indirect_table(dev, vd);
702
703 idx = vd->next;
704 vd = &vring->desc[idx];
705 count++;
706 }
707
708 /* Didn't count the last one */
709 count++;
710
711 if (vd->flags & VRING_DESC_F_INDIRECT)
712 clear_indirect_table(dev, vd);
713
714 /* idx points to the tail now, update the queue */
715 q->free_tail = idx;
716 assert(!(vd->flags & VRING_DESC_F_NEXT));
717
718 /* We can always connect the tail with the head */
719 vring->desc[q->free_tail].next = q->free_head;
720 vring->desc[q->free_tail].flags = VRING_DESC_F_NEXT;
721
722 q->free_num += count;
723
724 assert(q->free_num <= q->num);
725
726 *data = q->data[uel->id];
727 q->data[uel->id] = NULL;
728
729 if (len != NULL)
730 *len = uel->len;
731
732 return 0;
733 }
734
735 int
virtio_had_irq(struct virtio_device * dev)736 virtio_had_irq(struct virtio_device *dev)
737 {
738 return virtio_read8(dev, VIRTIO_ISR_STATUS_OFF) & 1;
739 }
740
741 void
virtio_reset_device(struct virtio_device * dev)742 virtio_reset_device(struct virtio_device *dev)
743 {
744 virtio_irq_unregister(dev);
745 virtio_write8(dev, VIRTIO_DEV_STATUS_OFF, 0);
746 }
747
748
749 void
virtio_irq_enable(struct virtio_device * dev)750 virtio_irq_enable(struct virtio_device *dev)
751 {
752 int r;
753 if ((r = sys_irqenable(&dev->irq_hook)) != OK)
754 panic("%s Unable to enable IRQ %d", dev->name, r);
755 }
756
757 void
virtio_irq_disable(struct virtio_device * dev)758 virtio_irq_disable(struct virtio_device *dev)
759 {
760 int r;
761 if ((r = sys_irqdisable(&dev->irq_hook)) != OK)
762 panic("%s: Unable to disable IRQ %d", dev->name, r);
763 }
764
765 static int
wants_kick(struct virtio_queue * q)766 wants_kick(struct virtio_queue *q)
767 {
768 assert(q != NULL);
769 return !(q->vring.used->flags & VRING_USED_F_NO_NOTIFY);
770 }
771
772 static void
kick_queue(struct virtio_device * dev,int qidx)773 kick_queue(struct virtio_device *dev, int qidx)
774 {
775 assert(0 <= qidx && qidx < dev->num_queues);
776
777 if (wants_kick(&dev->queues[qidx]))
778 virtio_write16(dev, VIRTIO_QNOTFIY_OFF, qidx);
779
780 return;
781 }
782
783 static int
is_matching_device(u16_t expected_sdid,u16_t vid,u16_t sdid)784 is_matching_device(u16_t expected_sdid, u16_t vid, u16_t sdid)
785 {
786 return vid == VIRTIO_VENDOR_ID && sdid == expected_sdid;
787 }
788
789 static void
virtio_irq_register(struct virtio_device * dev)790 virtio_irq_register(struct virtio_device *dev)
791 {
792 int r;
793 if ((r = sys_irqsetpolicy(dev->irq, 0, &dev->irq_hook)) != OK)
794 panic("%s: Unable to register IRQ %d", dev->name, r);
795 }
796
797 static void
virtio_irq_unregister(struct virtio_device * dev)798 virtio_irq_unregister(struct virtio_device *dev)
799 {
800 int r;
801 if ((r = sys_irqrmpolicy(&dev->irq_hook)) != OK)
802 panic("%s: Unable to unregister IRQ %d", dev->name, r);
803 }
804
805 static int
_supports(struct virtio_device * dev,int bit,int host)806 _supports(struct virtio_device *dev, int bit, int host)
807 {
808 for (int i = 0; i < dev->num_features; i++) {
809 struct virtio_feature *f = &dev->features[i];
810
811 if (f->bit == bit)
812 return host ? f->host_support : f->guest_support;
813 }
814
815 panic("%s: Feature not found bit=%d", dev->name, bit);
816 }
817
818 int
virtio_host_supports(struct virtio_device * dev,int bit)819 virtio_host_supports(struct virtio_device *dev, int bit)
820 {
821 return _supports(dev, bit, 1);
822 }
823
824 int
virtio_guest_supports(struct virtio_device * dev,int bit)825 virtio_guest_supports(struct virtio_device *dev, int bit)
826 {
827 return _supports(dev, bit, 0);
828 }
829
830
831 /* Just some wrappers around sys_read */
832 #define VIRTIO_READ_XX(xx, suff) \
833 u##xx##_t \
834 virtio_read##xx(struct virtio_device *dev, i32_t off) \
835 { \
836 int r; \
837 u32_t ret; \
838 if ((r = sys_in##suff(dev->port + off, &ret)) != OK) \
839 panic("%s: Read failed %d %d r=%d", dev->name, \
840 dev->port, \
841 off, \
842 r); \
843 \
844 return ret; \
845 }
846
847 VIRTIO_READ_XX(32, l)
848 VIRTIO_READ_XX(16, w)
849 VIRTIO_READ_XX(8, b)
850
851 /* Just some wrappers around sys_write */
852 #define VIRTIO_WRITE_XX(xx, suff) \
853 void \
854 virtio_write##xx(struct virtio_device *dev, i32_t off, u##xx##_t val) \
855 { \
856 int r; \
857 if ((r = sys_out##suff(dev->port + off, val)) != OK) \
858 panic("%s: Write failed %d %d r=%d", dev->name, \
859 dev->port, \
860 off, \
861 r); \
862 }
863
864 VIRTIO_WRITE_XX(32, l)
865 VIRTIO_WRITE_XX(16, w)
866 VIRTIO_WRITE_XX(8, b)
867
868 /* Just some wrappers around sys_read */
869 #define VIRTIO_SREAD_XX(xx, suff) \
870 u##xx##_t \
871 virtio_sread##xx(struct virtio_device *dev, i32_t off) \
872 { \
873 int r; \
874 u32_t ret; \
875 off += VIRTIO_DEV_SPECIFIC_OFF; \
876 \
877 if (dev->msi) \
878 off += VIRTIO_MSI_ADD_OFF; \
879 \
880 if ((r = sys_in##suff(dev->port + off, &ret)) != OK) \
881 panic("%s: Read failed %d %d r=%d", dev->name, \
882 dev->port, \
883 off, \
884 r); \
885 \
886 return ret; \
887 }
888
889 VIRTIO_SREAD_XX(32, l)
890 VIRTIO_SREAD_XX(16, w)
891 VIRTIO_SREAD_XX(8, b)
892
893 /* Just some wrappers around sys_write */
894 #define VIRTIO_SWRITE_XX(xx, suff) \
895 void \
896 virtio_swrite##xx(struct virtio_device *dev, i32_t off, u##xx##_t val) \
897 { \
898 int r; \
899 off += VIRTIO_DEV_SPECIFIC_OFF; \
900 \
901 if (dev->msi) \
902 off += VIRTIO_MSI_ADD_OFF; \
903 \
904 if ((r = sys_out##suff(dev->port + off, val)) != OK) \
905 panic("%s: Write failed %d %d r=%d", dev->name, \
906 dev->port, \
907 off, \
908 r); \
909 }
910
911 VIRTIO_SWRITE_XX(32, l)
912 VIRTIO_SWRITE_XX(16, w)
913 VIRTIO_SWRITE_XX(8, b)
914