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 "libqos/virtio.h"
13 #include "libqos/virtio-mmio.h"
14 #include "libqos/malloc.h"
15 #include "libqos/malloc-generic.h"
17 static uint8_t qvirtio_mmio_config_readb(QVirtioDevice
*d
, uint64_t addr
)
19 QVirtioMMIODevice
*dev
= (QVirtioMMIODevice
*)d
;
20 return readb(dev
->addr
+ addr
);
23 static uint16_t qvirtio_mmio_config_readw(QVirtioDevice
*d
, uint64_t addr
)
25 QVirtioMMIODevice
*dev
= (QVirtioMMIODevice
*)d
;
26 return readw(dev
->addr
+ addr
);
29 static uint32_t qvirtio_mmio_config_readl(QVirtioDevice
*d
, uint64_t addr
)
31 QVirtioMMIODevice
*dev
= (QVirtioMMIODevice
*)d
;
32 return readl(dev
->addr
+ addr
);
35 static uint64_t qvirtio_mmio_config_readq(QVirtioDevice
*d
, uint64_t addr
)
37 QVirtioMMIODevice
*dev
= (QVirtioMMIODevice
*)d
;
38 return readq(dev
->addr
+ addr
);
41 static uint32_t qvirtio_mmio_get_features(QVirtioDevice
*d
)
43 QVirtioMMIODevice
*dev
= (QVirtioMMIODevice
*)d
;
44 writel(dev
->addr
+ QVIRTIO_MMIO_HOST_FEATURES_SEL
, 0);
45 return readl(dev
->addr
+ QVIRTIO_MMIO_HOST_FEATURES
);
48 static void qvirtio_mmio_set_features(QVirtioDevice
*d
, uint32_t features
)
50 QVirtioMMIODevice
*dev
= (QVirtioMMIODevice
*)d
;
51 dev
->features
= features
;
52 writel(dev
->addr
+ QVIRTIO_MMIO_GUEST_FEATURES_SEL
, 0);
53 writel(dev
->addr
+ QVIRTIO_MMIO_GUEST_FEATURES
, features
);
56 static uint32_t qvirtio_mmio_get_guest_features(QVirtioDevice
*d
)
58 QVirtioMMIODevice
*dev
= (QVirtioMMIODevice
*)d
;
62 static uint8_t qvirtio_mmio_get_status(QVirtioDevice
*d
)
64 QVirtioMMIODevice
*dev
= (QVirtioMMIODevice
*)d
;
65 return (uint8_t)readl(dev
->addr
+ QVIRTIO_MMIO_DEVICE_STATUS
);
68 static void qvirtio_mmio_set_status(QVirtioDevice
*d
, uint8_t status
)
70 QVirtioMMIODevice
*dev
= (QVirtioMMIODevice
*)d
;
71 writel(dev
->addr
+ QVIRTIO_MMIO_DEVICE_STATUS
, (uint32_t)status
);
74 static bool qvirtio_mmio_get_queue_isr_status(QVirtioDevice
*d
, QVirtQueue
*vq
)
76 QVirtioMMIODevice
*dev
= (QVirtioMMIODevice
*)d
;
79 isr
= readl(dev
->addr
+ QVIRTIO_MMIO_INTERRUPT_STATUS
) & 1;
81 writel(dev
->addr
+ QVIRTIO_MMIO_INTERRUPT_ACK
, 1);
88 static bool qvirtio_mmio_get_config_isr_status(QVirtioDevice
*d
)
90 QVirtioMMIODevice
*dev
= (QVirtioMMIODevice
*)d
;
93 isr
= readl(dev
->addr
+ QVIRTIO_MMIO_INTERRUPT_STATUS
) & 2;
95 writel(dev
->addr
+ QVIRTIO_MMIO_INTERRUPT_ACK
, 2);
102 static void qvirtio_mmio_queue_select(QVirtioDevice
*d
, uint16_t index
)
104 QVirtioMMIODevice
*dev
= (QVirtioMMIODevice
*)d
;
105 writel(dev
->addr
+ QVIRTIO_MMIO_QUEUE_SEL
, (uint32_t)index
);
107 g_assert_cmphex(readl(dev
->addr
+ QVIRTIO_MMIO_QUEUE_PFN
), ==, 0);
110 static uint16_t qvirtio_mmio_get_queue_size(QVirtioDevice
*d
)
112 QVirtioMMIODevice
*dev
= (QVirtioMMIODevice
*)d
;
113 return (uint16_t)readl(dev
->addr
+ QVIRTIO_MMIO_QUEUE_NUM_MAX
);
116 static void qvirtio_mmio_set_queue_address(QVirtioDevice
*d
, uint32_t pfn
)
118 QVirtioMMIODevice
*dev
= (QVirtioMMIODevice
*)d
;
119 writel(dev
->addr
+ QVIRTIO_MMIO_QUEUE_PFN
, pfn
);
122 static QVirtQueue
*qvirtio_mmio_virtqueue_setup(QVirtioDevice
*d
,
123 QGuestAllocator
*alloc
, uint16_t index
)
125 QVirtioMMIODevice
*dev
= (QVirtioMMIODevice
*)d
;
129 vq
= g_malloc0(sizeof(*vq
));
130 qvirtio_mmio_queue_select(d
, index
);
131 writel(dev
->addr
+ QVIRTIO_MMIO_QUEUE_ALIGN
, dev
->page_size
);
134 vq
->size
= qvirtio_mmio_get_queue_size(d
);
136 vq
->num_free
= vq
->size
;
137 vq
->align
= dev
->page_size
;
138 vq
->indirect
= (dev
->features
& QVIRTIO_F_RING_INDIRECT_DESC
) != 0;
139 vq
->event
= (dev
->features
& QVIRTIO_F_RING_EVENT_IDX
) != 0;
141 writel(dev
->addr
+ QVIRTIO_MMIO_QUEUE_NUM
, vq
->size
);
143 /* Check different than 0 */
144 g_assert_cmpint(vq
->size
, !=, 0);
146 /* Check power of 2 */
147 g_assert_cmpint(vq
->size
& (vq
->size
- 1), ==, 0);
149 addr
= guest_alloc(alloc
, qvring_size(vq
->size
, dev
->page_size
));
150 qvring_init(alloc
, vq
, addr
);
151 qvirtio_mmio_set_queue_address(d
, vq
->desc
/ dev
->page_size
);
156 static void qvirtio_mmio_virtqueue_kick(QVirtioDevice
*d
, QVirtQueue
*vq
)
158 QVirtioMMIODevice
*dev
= (QVirtioMMIODevice
*)d
;
159 writel(dev
->addr
+ QVIRTIO_MMIO_QUEUE_NOTIFY
, vq
->index
);
162 const QVirtioBus qvirtio_mmio
= {
163 .config_readb
= qvirtio_mmio_config_readb
,
164 .config_readw
= qvirtio_mmio_config_readw
,
165 .config_readl
= qvirtio_mmio_config_readl
,
166 .config_readq
= qvirtio_mmio_config_readq
,
167 .get_features
= qvirtio_mmio_get_features
,
168 .set_features
= qvirtio_mmio_set_features
,
169 .get_guest_features
= qvirtio_mmio_get_guest_features
,
170 .get_status
= qvirtio_mmio_get_status
,
171 .set_status
= qvirtio_mmio_set_status
,
172 .get_queue_isr_status
= qvirtio_mmio_get_queue_isr_status
,
173 .get_config_isr_status
= qvirtio_mmio_get_config_isr_status
,
174 .queue_select
= qvirtio_mmio_queue_select
,
175 .get_queue_size
= qvirtio_mmio_get_queue_size
,
176 .set_queue_address
= qvirtio_mmio_set_queue_address
,
177 .virtqueue_setup
= qvirtio_mmio_virtqueue_setup
,
178 .virtqueue_kick
= qvirtio_mmio_virtqueue_kick
,
181 QVirtioMMIODevice
*qvirtio_mmio_init_device(uint64_t addr
, uint32_t page_size
)
183 QVirtioMMIODevice
*dev
;
185 dev
= g_malloc0(sizeof(*dev
));
187 magic
= readl(addr
+ QVIRTIO_MMIO_MAGIC_VALUE
);
188 g_assert(magic
== ('v' | 'i' << 8 | 'r' << 16 | 't' << 24));
191 dev
->page_size
= page_size
;
192 dev
->vdev
.device_type
= readl(addr
+ QVIRTIO_MMIO_DEVICE_ID
);
194 writel(addr
+ QVIRTIO_MMIO_GUEST_PAGE_SIZE
, page_size
);