2 * libqos virtio MMIO driver
4 * Copyright (c) 2014 Marc MarĂ
6 * This work is licensed under the terms of the GNU GPL, version 2 or later.
7 * See the COPYING file in the top-level directory.
10 #include "qemu/osdep.h"
12 #include "qemu/module.h"
14 #include "virtio-mmio.h"
17 #include "standard-headers/linux/virtio_ring.h"
19 static uint8_t qvirtio_mmio_config_readb(QVirtioDevice
*d
, uint64_t off
)
21 QVirtioMMIODevice
*dev
= container_of(d
, QVirtioMMIODevice
, vdev
);
22 return qtest_readb(dev
->qts
, dev
->addr
+ QVIRTIO_MMIO_DEVICE_SPECIFIC
+ off
);
25 static uint16_t qvirtio_mmio_config_readw(QVirtioDevice
*d
, uint64_t off
)
27 QVirtioMMIODevice
*dev
= container_of(d
, QVirtioMMIODevice
, vdev
);
28 return qtest_readw(dev
->qts
, dev
->addr
+ QVIRTIO_MMIO_DEVICE_SPECIFIC
+ off
);
31 static uint32_t qvirtio_mmio_config_readl(QVirtioDevice
*d
, uint64_t off
)
33 QVirtioMMIODevice
*dev
= container_of(d
, QVirtioMMIODevice
, vdev
);
34 return qtest_readl(dev
->qts
, dev
->addr
+ QVIRTIO_MMIO_DEVICE_SPECIFIC
+ off
);
37 static uint64_t qvirtio_mmio_config_readq(QVirtioDevice
*d
, uint64_t off
)
39 QVirtioMMIODevice
*dev
= container_of(d
, QVirtioMMIODevice
, vdev
);
40 return qtest_readq(dev
->qts
, dev
->addr
+ QVIRTIO_MMIO_DEVICE_SPECIFIC
+ off
);
43 static uint64_t qvirtio_mmio_get_features(QVirtioDevice
*d
)
45 QVirtioMMIODevice
*dev
= container_of(d
, QVirtioMMIODevice
, vdev
);
49 qtest_writel(dev
->qts
, dev
->addr
+ QVIRTIO_MMIO_HOST_FEATURES_SEL
, 0);
50 lo
= qtest_readl(dev
->qts
, dev
->addr
+ QVIRTIO_MMIO_HOST_FEATURES
);
52 if (dev
->version
>= 2) {
53 qtest_writel(dev
->qts
, dev
->addr
+ QVIRTIO_MMIO_HOST_FEATURES_SEL
, 1);
54 hi
= qtest_readl(dev
->qts
, dev
->addr
+ QVIRTIO_MMIO_HOST_FEATURES
);
57 return (hi
<< 32) | lo
;
60 static void qvirtio_mmio_set_features(QVirtioDevice
*d
, uint64_t features
)
62 QVirtioMMIODevice
*dev
= container_of(d
, QVirtioMMIODevice
, vdev
);
63 dev
->features
= features
;
64 qtest_writel(dev
->qts
, dev
->addr
+ QVIRTIO_MMIO_GUEST_FEATURES_SEL
, 0);
65 qtest_writel(dev
->qts
, dev
->addr
+ QVIRTIO_MMIO_GUEST_FEATURES
, features
);
67 if (dev
->version
>= 2) {
68 qtest_writel(dev
->qts
, dev
->addr
+ QVIRTIO_MMIO_GUEST_FEATURES_SEL
, 1);
69 qtest_writel(dev
->qts
, dev
->addr
+ QVIRTIO_MMIO_GUEST_FEATURES
,
74 static uint64_t qvirtio_mmio_get_guest_features(QVirtioDevice
*d
)
76 QVirtioMMIODevice
*dev
= container_of(d
, QVirtioMMIODevice
, vdev
);
80 static uint8_t qvirtio_mmio_get_status(QVirtioDevice
*d
)
82 QVirtioMMIODevice
*dev
= container_of(d
, QVirtioMMIODevice
, vdev
);
83 return (uint8_t)qtest_readl(dev
->qts
, dev
->addr
+ QVIRTIO_MMIO_DEVICE_STATUS
);
86 static void qvirtio_mmio_set_status(QVirtioDevice
*d
, uint8_t status
)
88 QVirtioMMIODevice
*dev
= container_of(d
, QVirtioMMIODevice
, vdev
);
89 qtest_writel(dev
->qts
, dev
->addr
+ QVIRTIO_MMIO_DEVICE_STATUS
, (uint32_t)status
);
92 static bool qvirtio_mmio_get_queue_isr_status(QVirtioDevice
*d
, QVirtQueue
*vq
)
94 QVirtioMMIODevice
*dev
= container_of(d
, QVirtioMMIODevice
, vdev
);
97 isr
= qtest_readl(dev
->qts
, dev
->addr
+ QVIRTIO_MMIO_INTERRUPT_STATUS
) & 1;
99 qtest_writel(dev
->qts
, dev
->addr
+ QVIRTIO_MMIO_INTERRUPT_ACK
, 1);
106 static bool qvirtio_mmio_get_config_isr_status(QVirtioDevice
*d
)
108 QVirtioMMIODevice
*dev
= container_of(d
, QVirtioMMIODevice
, vdev
);
111 isr
= qtest_readl(dev
->qts
, dev
->addr
+ QVIRTIO_MMIO_INTERRUPT_STATUS
) & 2;
113 qtest_writel(dev
->qts
, dev
->addr
+ QVIRTIO_MMIO_INTERRUPT_ACK
, 2);
120 static void qvirtio_mmio_wait_config_isr_status(QVirtioDevice
*d
,
123 QVirtioMMIODevice
*dev
= container_of(d
, QVirtioMMIODevice
, vdev
);
124 gint64 start_time
= g_get_monotonic_time();
127 g_assert(g_get_monotonic_time() - start_time
<= timeout_us
);
128 qtest_clock_step(dev
->qts
, 100);
129 } while (!qvirtio_mmio_get_config_isr_status(d
));
132 static void qvirtio_mmio_queue_select(QVirtioDevice
*d
, uint16_t index
)
134 QVirtioMMIODevice
*dev
= container_of(d
, QVirtioMMIODevice
, vdev
);
135 qtest_writel(dev
->qts
, dev
->addr
+ QVIRTIO_MMIO_QUEUE_SEL
, (uint32_t)index
);
137 g_assert_cmphex(qtest_readl(dev
->qts
, dev
->addr
+ QVIRTIO_MMIO_QUEUE_PFN
), ==, 0);
140 static uint16_t qvirtio_mmio_get_queue_size(QVirtioDevice
*d
)
142 QVirtioMMIODevice
*dev
= container_of(d
, QVirtioMMIODevice
, vdev
);
143 return (uint16_t)qtest_readl(dev
->qts
, dev
->addr
+ QVIRTIO_MMIO_QUEUE_NUM_MAX
);
146 static void qvirtio_mmio_set_queue_address(QVirtioDevice
*d
, QVirtQueue
*vq
)
148 QVirtioMMIODevice
*dev
= container_of(d
, QVirtioMMIODevice
, vdev
);
149 uint64_t pfn
= vq
->desc
/ dev
->page_size
;
151 qtest_writel(dev
->qts
, dev
->addr
+ QVIRTIO_MMIO_QUEUE_PFN
, pfn
);
154 static QVirtQueue
*qvirtio_mmio_virtqueue_setup(QVirtioDevice
*d
,
155 QGuestAllocator
*alloc
, uint16_t index
)
157 QVirtioMMIODevice
*dev
= container_of(d
, QVirtioMMIODevice
, vdev
);
161 vq
= g_malloc0(sizeof(*vq
));
163 qvirtio_mmio_queue_select(d
, index
);
164 qtest_writel(dev
->qts
, dev
->addr
+ QVIRTIO_MMIO_QUEUE_ALIGN
, dev
->page_size
);
167 vq
->size
= qvirtio_mmio_get_queue_size(d
);
169 vq
->num_free
= vq
->size
;
170 vq
->align
= dev
->page_size
;
171 vq
->indirect
= dev
->features
& (1ull << VIRTIO_RING_F_INDIRECT_DESC
);
172 vq
->event
= dev
->features
& (1ull << VIRTIO_RING_F_EVENT_IDX
);
174 qtest_writel(dev
->qts
, dev
->addr
+ QVIRTIO_MMIO_QUEUE_NUM
, vq
->size
);
176 /* Check different than 0 */
177 g_assert_cmpint(vq
->size
, !=, 0);
179 /* Check power of 2 */
180 g_assert_cmpint(vq
->size
& (vq
->size
- 1), ==, 0);
182 addr
= guest_alloc(alloc
, qvring_size(vq
->size
, dev
->page_size
));
183 qvring_init(dev
->qts
, alloc
, vq
, addr
);
184 qvirtio_mmio_set_queue_address(d
, vq
);
189 static void qvirtio_mmio_virtqueue_cleanup(QVirtQueue
*vq
,
190 QGuestAllocator
*alloc
)
192 guest_free(alloc
, vq
->desc
);
196 static void qvirtio_mmio_virtqueue_kick(QVirtioDevice
*d
, QVirtQueue
*vq
)
198 QVirtioMMIODevice
*dev
= container_of(d
, QVirtioMMIODevice
, vdev
);
199 qtest_writel(dev
->qts
, dev
->addr
+ QVIRTIO_MMIO_QUEUE_NOTIFY
, vq
->index
);
202 const QVirtioBus qvirtio_mmio
= {
203 .config_readb
= qvirtio_mmio_config_readb
,
204 .config_readw
= qvirtio_mmio_config_readw
,
205 .config_readl
= qvirtio_mmio_config_readl
,
206 .config_readq
= qvirtio_mmio_config_readq
,
207 .get_features
= qvirtio_mmio_get_features
,
208 .set_features
= qvirtio_mmio_set_features
,
209 .get_guest_features
= qvirtio_mmio_get_guest_features
,
210 .get_status
= qvirtio_mmio_get_status
,
211 .set_status
= qvirtio_mmio_set_status
,
212 .get_queue_isr_status
= qvirtio_mmio_get_queue_isr_status
,
213 .wait_config_isr_status
= qvirtio_mmio_wait_config_isr_status
,
214 .queue_select
= qvirtio_mmio_queue_select
,
215 .get_queue_size
= qvirtio_mmio_get_queue_size
,
216 .set_queue_address
= qvirtio_mmio_set_queue_address
,
217 .virtqueue_setup
= qvirtio_mmio_virtqueue_setup
,
218 .virtqueue_cleanup
= qvirtio_mmio_virtqueue_cleanup
,
219 .virtqueue_kick
= qvirtio_mmio_virtqueue_kick
,
222 static void *qvirtio_mmio_get_driver(void *obj
, const char *interface
)
224 QVirtioMMIODevice
*virtio_mmio
= obj
;
225 if (!g_strcmp0(interface
, "virtio-bus")) {
226 return &virtio_mmio
->vdev
;
228 fprintf(stderr
, "%s not present in virtio-mmio\n", interface
);
229 g_assert_not_reached();
232 static void qvirtio_mmio_start_hw(QOSGraphObject
*obj
)
234 QVirtioMMIODevice
*dev
= (QVirtioMMIODevice
*) obj
;
235 qvirtio_start_device(&dev
->vdev
);
238 void qvirtio_mmio_init_device(QVirtioMMIODevice
*dev
, QTestState
*qts
,
239 uint64_t addr
, uint32_t page_size
)
242 magic
= qtest_readl(qts
, addr
+ QVIRTIO_MMIO_MAGIC_VALUE
);
243 g_assert(magic
== ('v' | 'i' << 8 | 'r' << 16 | 't' << 24));
245 dev
->version
= qtest_readl(qts
, addr
+ QVIRTIO_MMIO_VERSION
);
246 g_assert(dev
->version
== 1 || dev
->version
== 2);
250 dev
->page_size
= page_size
;
251 dev
->vdev
.device_type
= qtest_readl(qts
, addr
+ QVIRTIO_MMIO_DEVICE_ID
);
252 dev
->vdev
.bus
= &qvirtio_mmio
;
254 qtest_writel(qts
, addr
+ QVIRTIO_MMIO_GUEST_PAGE_SIZE
, page_size
);
256 dev
->obj
.get_driver
= qvirtio_mmio_get_driver
;
257 dev
->obj
.start_hw
= qvirtio_mmio_start_hw
;
260 static void virtio_mmio_register_nodes(void)
262 qos_node_create_driver("virtio-mmio", NULL
);
263 qos_node_produces("virtio-mmio", "virtio-bus");
266 libqos_init(virtio_mmio_register_nodes
);