xref: /qemu/tests/qtest/libqos/virtio.c (revision bf3c63d2010c5ef52f8b988bee2a1486a056795f)
146e0cf76SMarc Marí /*
246e0cf76SMarc Marí  * libqos virtio driver
346e0cf76SMarc Marí  *
446e0cf76SMarc Marí  * Copyright (c) 2014 Marc Marí
546e0cf76SMarc Marí  *
646e0cf76SMarc Marí  * This work is licensed under the terms of the GNU GPL, version 2 or later.
746e0cf76SMarc Marí  * See the COPYING file in the top-level directory.
846e0cf76SMarc Marí  */
946e0cf76SMarc Marí 
1046e0cf76SMarc Marí #include <glib.h>
1146e0cf76SMarc Marí #include "libqtest.h"
1246e0cf76SMarc Marí #include "libqos/virtio.h"
1346e0cf76SMarc Marí 
1446e0cf76SMarc Marí uint8_t qvirtio_config_readb(const QVirtioBus *bus, QVirtioDevice *d,
1546e0cf76SMarc Marí                                                                 void *addr)
1646e0cf76SMarc Marí {
1746e0cf76SMarc Marí     return bus->config_readb(d, addr);
1846e0cf76SMarc Marí }
1946e0cf76SMarc Marí 
2046e0cf76SMarc Marí uint16_t qvirtio_config_readw(const QVirtioBus *bus, QVirtioDevice *d,
2146e0cf76SMarc Marí                                                                 void *addr)
2246e0cf76SMarc Marí {
2346e0cf76SMarc Marí     return bus->config_readw(d, addr);
2446e0cf76SMarc Marí }
2546e0cf76SMarc Marí 
2646e0cf76SMarc Marí uint32_t qvirtio_config_readl(const QVirtioBus *bus, QVirtioDevice *d,
2746e0cf76SMarc Marí                                                                 void *addr)
2846e0cf76SMarc Marí {
2946e0cf76SMarc Marí     return bus->config_readl(d, addr);
3046e0cf76SMarc Marí }
3146e0cf76SMarc Marí 
3246e0cf76SMarc Marí uint64_t qvirtio_config_readq(const QVirtioBus *bus, QVirtioDevice *d,
3346e0cf76SMarc Marí                                                                 void *addr)
3446e0cf76SMarc Marí {
3546e0cf76SMarc Marí     return bus->config_readq(d, addr);
3646e0cf76SMarc Marí }
3746e0cf76SMarc Marí 
38*bf3c63d2SMarc Marí uint32_t qvirtio_get_features(const QVirtioBus *bus, QVirtioDevice *d)
39*bf3c63d2SMarc Marí {
40*bf3c63d2SMarc Marí     return bus->get_features(d);
41*bf3c63d2SMarc Marí }
42*bf3c63d2SMarc Marí 
43*bf3c63d2SMarc Marí void qvirtio_set_features(const QVirtioBus *bus, QVirtioDevice *d,
44*bf3c63d2SMarc Marí                                                             uint32_t features)
45*bf3c63d2SMarc Marí {
46*bf3c63d2SMarc Marí     bus->set_features(d, features);
47*bf3c63d2SMarc Marí }
48*bf3c63d2SMarc Marí 
49*bf3c63d2SMarc Marí QVirtQueue *qvirtqueue_setup(const QVirtioBus *bus, QVirtioDevice *d,
50*bf3c63d2SMarc Marí                                         QGuestAllocator *alloc, uint16_t index)
51*bf3c63d2SMarc Marí {
52*bf3c63d2SMarc Marí     return bus->virtqueue_setup(d, alloc, index);
53*bf3c63d2SMarc Marí }
54*bf3c63d2SMarc Marí 
5546e0cf76SMarc Marí void qvirtio_reset(const QVirtioBus *bus, QVirtioDevice *d)
5646e0cf76SMarc Marí {
5746e0cf76SMarc Marí     bus->set_status(d, QVIRTIO_RESET);
5846e0cf76SMarc Marí     g_assert_cmphex(bus->get_status(d), ==, QVIRTIO_RESET);
5946e0cf76SMarc Marí }
6046e0cf76SMarc Marí 
6146e0cf76SMarc Marí void qvirtio_set_acknowledge(const QVirtioBus *bus, QVirtioDevice *d)
6246e0cf76SMarc Marí {
6346e0cf76SMarc Marí     bus->set_status(d, bus->get_status(d) | QVIRTIO_ACKNOWLEDGE);
6446e0cf76SMarc Marí     g_assert_cmphex(bus->get_status(d), ==, QVIRTIO_ACKNOWLEDGE);
6546e0cf76SMarc Marí }
6646e0cf76SMarc Marí 
6746e0cf76SMarc Marí void qvirtio_set_driver(const QVirtioBus *bus, QVirtioDevice *d)
6846e0cf76SMarc Marí {
6946e0cf76SMarc Marí     bus->set_status(d, bus->get_status(d) | QVIRTIO_DRIVER);
7046e0cf76SMarc Marí     g_assert_cmphex(bus->get_status(d), ==,
7146e0cf76SMarc Marí                                     QVIRTIO_DRIVER | QVIRTIO_ACKNOWLEDGE);
7246e0cf76SMarc Marí }
73*bf3c63d2SMarc Marí 
74*bf3c63d2SMarc Marí void qvirtio_set_driver_ok(const QVirtioBus *bus, QVirtioDevice *d)
75*bf3c63d2SMarc Marí {
76*bf3c63d2SMarc Marí     bus->set_status(d, bus->get_status(d) | QVIRTIO_DRIVER_OK);
77*bf3c63d2SMarc Marí     g_assert_cmphex(bus->get_status(d), ==,
78*bf3c63d2SMarc Marí                 QVIRTIO_DRIVER_OK | QVIRTIO_DRIVER | QVIRTIO_ACKNOWLEDGE);
79*bf3c63d2SMarc Marí }
80*bf3c63d2SMarc Marí 
81*bf3c63d2SMarc Marí bool qvirtio_wait_isr(const QVirtioBus *bus, QVirtioDevice *d, uint8_t mask,
82*bf3c63d2SMarc Marí                                                             uint64_t timeout)
83*bf3c63d2SMarc Marí {
84*bf3c63d2SMarc Marí     do {
85*bf3c63d2SMarc Marí         clock_step(10);
86*bf3c63d2SMarc Marí         if (bus->get_isr_status(d) & mask) {
87*bf3c63d2SMarc Marí             break; /* It has ended */
88*bf3c63d2SMarc Marí         }
89*bf3c63d2SMarc Marí     } while (--timeout);
90*bf3c63d2SMarc Marí 
91*bf3c63d2SMarc Marí     return timeout != 0;
92*bf3c63d2SMarc Marí }
93*bf3c63d2SMarc Marí 
94*bf3c63d2SMarc Marí void qvring_init(const QGuestAllocator *alloc, QVirtQueue *vq, uint64_t addr)
95*bf3c63d2SMarc Marí {
96*bf3c63d2SMarc Marí     int i;
97*bf3c63d2SMarc Marí 
98*bf3c63d2SMarc Marí     vq->desc = addr;
99*bf3c63d2SMarc Marí     vq->avail = vq->desc + vq->size*sizeof(QVRingDesc);
100*bf3c63d2SMarc Marí     vq->used = (uint64_t)((vq->avail + sizeof(uint16_t) * (3 + vq->size)
101*bf3c63d2SMarc Marí         + vq->align - 1) & ~(vq->align - 1));
102*bf3c63d2SMarc Marí 
103*bf3c63d2SMarc Marí     for (i = 0; i < vq->size - 1; i++) {
104*bf3c63d2SMarc Marí         /* vq->desc[i].addr */
105*bf3c63d2SMarc Marí         writew(vq->desc + (16 * i), 0);
106*bf3c63d2SMarc Marí         /* vq->desc[i].next */
107*bf3c63d2SMarc Marí         writew(vq->desc + (16 * i) + 14, i + 1);
108*bf3c63d2SMarc Marí     }
109*bf3c63d2SMarc Marí 
110*bf3c63d2SMarc Marí     /* vq->avail->flags */
111*bf3c63d2SMarc Marí     writew(vq->avail, 0);
112*bf3c63d2SMarc Marí     /* vq->avail->idx */
113*bf3c63d2SMarc Marí     writew(vq->avail + 2, 0);
114*bf3c63d2SMarc Marí 
115*bf3c63d2SMarc Marí     /* vq->used->flags */
116*bf3c63d2SMarc Marí     writew(vq->used, 0);
117*bf3c63d2SMarc Marí }
118*bf3c63d2SMarc Marí 
119*bf3c63d2SMarc Marí uint32_t qvirtqueue_add(QVirtQueue *vq, uint64_t data, uint32_t len, bool write,
120*bf3c63d2SMarc Marí                                                                     bool next)
121*bf3c63d2SMarc Marí {
122*bf3c63d2SMarc Marí     uint16_t flags = 0;
123*bf3c63d2SMarc Marí     vq->num_free--;
124*bf3c63d2SMarc Marí 
125*bf3c63d2SMarc Marí     if (write) {
126*bf3c63d2SMarc Marí         flags |= QVRING_DESC_F_WRITE;
127*bf3c63d2SMarc Marí     }
128*bf3c63d2SMarc Marí 
129*bf3c63d2SMarc Marí     if (next) {
130*bf3c63d2SMarc Marí         flags |= QVRING_DESC_F_NEXT;
131*bf3c63d2SMarc Marí     }
132*bf3c63d2SMarc Marí 
133*bf3c63d2SMarc Marí     /* vq->desc[vq->free_head].addr */
134*bf3c63d2SMarc Marí     writeq(vq->desc + (16 * vq->free_head), data);
135*bf3c63d2SMarc Marí     /* vq->desc[vq->free_head].len */
136*bf3c63d2SMarc Marí     writel(vq->desc + (16 * vq->free_head) + 8, len);
137*bf3c63d2SMarc Marí     /* vq->desc[vq->free_head].flags */
138*bf3c63d2SMarc Marí     writew(vq->desc + (16 * vq->free_head) + 12, flags);
139*bf3c63d2SMarc Marí 
140*bf3c63d2SMarc Marí     return vq->free_head++; /* Return and increase, in this order */
141*bf3c63d2SMarc Marí }
142*bf3c63d2SMarc Marí 
143*bf3c63d2SMarc Marí void qvirtqueue_kick(const QVirtioBus *bus, QVirtioDevice *d, QVirtQueue *vq,
144*bf3c63d2SMarc Marí                                                             uint32_t free_head)
145*bf3c63d2SMarc Marí {
146*bf3c63d2SMarc Marí     /* vq->avail->idx */
147*bf3c63d2SMarc Marí     uint16_t idx = readl(vq->avail + 2);
148*bf3c63d2SMarc Marí 
149*bf3c63d2SMarc Marí     /* vq->avail->ring[idx % vq->size] */
150*bf3c63d2SMarc Marí     writel(vq->avail + 4 + (2 * (idx % vq->size)), free_head);
151*bf3c63d2SMarc Marí     /* vq->avail->idx */
152*bf3c63d2SMarc Marí     writel(vq->avail + 2, idx + 1);
153*bf3c63d2SMarc Marí 
154*bf3c63d2SMarc Marí     bus->virtqueue_kick(d, vq);
155*bf3c63d2SMarc Marí }
156