Skip to content

Commit fdc05d4

Browse files
committed
drivers: virtio: add VirtIO MMIO transport driver
Add virtio-mmio driver Signed-off-by: TOKITA Hiroshi <tokita.hiroshi@gmail.com>
1 parent 9e0e04d commit fdc05d4

File tree

4 files changed

+363
-0
lines changed

4 files changed

+363
-0
lines changed

drivers/virtio/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,4 @@ zephyr_library()
55

66
zephyr_library_sources_ifdef(CONFIG_VIRTIO virtqueue.c virtio_common.c)
77
zephyr_library_sources_ifdef(CONFIG_VIRTIO_PCI virtio_pci.c)
8+
zephyr_library_sources_ifdef(CONFIG_VIRTIO_MMIO virtio_mmio.c)

drivers/virtio/Kconfig

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,13 @@ config VIRTIO_PCI
1515
help
1616
Enable options for VIRTIO over PCI
1717

18+
config VIRTIO_MMIO
19+
bool "support for VIRTIO over MMIO"
20+
default y
21+
depends on DT_HAS_VIRTIO_MMIO_ENABLED
22+
help
23+
Enable options for VIRTIO over MMIO
24+
1825
endif # VIRTIO
1926

2027
module = VIRTIO

drivers/virtio/virtio_mmio.c

Lines changed: 347 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,347 @@
1+
/*
2+
* Copyright (c) 2025 TOKITA Hiroshi
3+
*
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
#include <zephyr/device.h>
8+
#include <zephyr/kernel/mm.h>
9+
#include <zephyr/logging/log.h>
10+
#include <zephyr/spinlock.h>
11+
#include <zephyr/sys/barrier.h>
12+
#include <zephyr/sys/byteorder.h>
13+
#include <zephyr/virtio/virtio.h>
14+
#include <zephyr/virtio/virtqueue.h>
15+
#include "virtio_common.h"
16+
17+
#define DT_DRV_COMPAT virtio_mmio
18+
19+
LOG_MODULE_REGISTER(virtio_mmio, CONFIG_VIRTIO_LOG_LEVEL);
20+
21+
/*
22+
* Based on Virtual I/O Device (VIRTIO) Version 1.3 specification:
23+
* https://docs.oasis-open.org/virtio/virtio/v1.3/csd01/virtio-v1.3-csd01.pdf
24+
*/
25+
26+
#define VIRTIO_MMIO_MAGIC_VALUE 0x000
27+
#define VIRTIO_MMIO_VERSION 0x004
28+
#define VIRTIO_MMIO_DEVICE_ID 0x008
29+
#define VIRTIO_MMIO_VENDOR_ID 0x00c
30+
#define VIRTIO_MMIO_DEVICE_FEATURES 0x010
31+
#define VIRTIO_MMIO_DEVICE_FEATURES_SEL 0x014
32+
#define VIRTIO_MMIO_DRIVER_FEATURES 0x020
33+
#define VIRTIO_MMIO_DRIVER_FEATURES_SEL 0x024
34+
#define VIRTIO_MMIO_QUEUE_SEL 0x030
35+
#define VIRTIO_MMIO_QUEUE_SIZE_MAX 0x034
36+
#define VIRTIO_MMIO_QUEUE_SIZE 0x038
37+
#define VIRTIO_MMIO_QUEUE_READY 0x044
38+
#define VIRTIO_MMIO_QUEUE_NOTIFY 0x050
39+
#define VIRTIO_MMIO_INTERRUPT_STATUS 0x060
40+
#define VIRTIO_MMIO_INTERRUPT_ACK 0x064
41+
#define VIRTIO_MMIO_STATUS 0x070
42+
#define VIRTIO_MMIO_QUEUE_DESC_LOW 0x080
43+
#define VIRTIO_MMIO_QUEUE_DESC_HIGH 0x084
44+
#define VIRTIO_MMIO_QUEUE_DRIVER_LOW 0x090
45+
#define VIRTIO_MMIO_QUEUE_DRIVER_HIGH 0x094
46+
#define VIRTIO_MMIO_QUEUE_DEVICE_LOW 0x0a0
47+
#define VIRTIO_MMIO_QUEUE_DEVICE_HIGH 0x0a4
48+
#define VIRTIO_MMIO_QUEUE_SHM_SEL 0x0ac
49+
#define VIRTIO_MMIO_QUEUE_SHM_LEN_LOW 0x0b0
50+
#define VIRTIO_MMIO_QUEUE_SHM_LEN_HIGH 0x0b4
51+
#define VIRTIO_MMIO_QUEUE_SHM_BASE_LOW 0x0b8
52+
#define VIRTIO_MMIO_QUEUE_SHM_BASE_HIGH 0x0bc
53+
#define VIRTIO_MMIO_QUEUE_RESET 0x0c0
54+
#define VIRTIO_MMIO_QUEUE_CONFIG_GENERATION 0x0fc
55+
#define VIRTIO_MMIO_CONFIG 0x100
56+
57+
#define VIRTIO_MMIO_MAGIC 0x74726976
58+
#define VIRTIO_MMIO_SUPPORTED_VERSION 2
59+
#define VIRTIO_MMIO_INVALID_DEVICE_ID 0
60+
61+
#define DEV_CFG(dev) ((const struct virtio_mmio_config *)(dev)->config)
62+
#define DEV_DATA(dev) ((struct virtio_mmio_data *)(dev)->data)
63+
64+
struct virtio_mmio_data {
65+
DEVICE_MMIO_NAMED_RAM(reg_base);
66+
67+
struct virtq *virtqueues;
68+
uint16_t virtqueue_count;
69+
70+
struct k_spinlock isr_lock;
71+
struct k_spinlock notify_lock;
72+
};
73+
74+
struct virtio_mmio_config {
75+
DEVICE_MMIO_NAMED_ROM(reg_base);
76+
};
77+
78+
static inline uint32_t virtio_mmio_read32(const struct device *dev, uint32_t offset)
79+
{
80+
const mem_addr_t reg = DEVICE_MMIO_NAMED_GET(dev, reg_base) + offset;
81+
uint32_t val;
82+
83+
barrier_dmem_fence_full();
84+
val = sys_read32(reg);
85+
return sys_le32_to_cpu(val);
86+
}
87+
88+
static inline void virtio_mmio_write32(const struct device *dev, uint32_t offset, uint32_t val)
89+
{
90+
const mem_addr_t reg = DEVICE_MMIO_NAMED_GET(dev, reg_base) + offset;
91+
92+
sys_write32(sys_cpu_to_le32(val), reg);
93+
barrier_dmem_fence_full();
94+
}
95+
96+
static void virtio_mmio_isr(const struct device *dev)
97+
{
98+
struct virtio_mmio_data *data = dev->data;
99+
k_spinlock_key_t key = k_spin_lock(&data->isr_lock);
100+
const uint32_t isr_status = virtio_mmio_read32(dev, VIRTIO_MMIO_INTERRUPT_STATUS);
101+
102+
virtio_mmio_write32(dev, VIRTIO_MMIO_INTERRUPT_ACK, isr_status);
103+
virtio_isr(dev, isr_status, data->virtqueue_count);
104+
105+
k_spin_unlock(&data->isr_lock, key);
106+
}
107+
108+
static void virtio_mmio_reset(const struct device *dev)
109+
{
110+
virtio_mmio_write32(dev, VIRTIO_MMIO_STATUS, 0);
111+
}
112+
113+
struct virtq *virtio_mmio_get_virtqueue(const struct device *dev, uint16_t queue_idx)
114+
{
115+
struct virtio_mmio_data *data = dev->data;
116+
117+
return queue_idx < data->virtqueue_count ? &data->virtqueues[queue_idx] : NULL;
118+
}
119+
120+
static void virtio_mmio_notify_queue(const struct device *dev, uint16_t queue_idx)
121+
{
122+
struct virtio_mmio_data *data = dev->data;
123+
124+
k_spinlock_key_t key = k_spin_lock(&data->notify_lock);
125+
126+
virtio_mmio_write32(dev, VIRTIO_MMIO_QUEUE_NOTIFY, 0);
127+
128+
k_spin_unlock(&data->notify_lock, key);
129+
}
130+
131+
static void *virtio_mmio_get_device_specific_config(const struct device *dev)
132+
{
133+
const mem_addr_t reg = DEVICE_MMIO_NAMED_GET(dev, reg_base) + VIRTIO_MMIO_CONFIG;
134+
135+
barrier_dmem_fence_full();
136+
137+
return (void *)reg;
138+
}
139+
140+
static bool virtio_mmio_read_device_feature_bit(const struct device *dev, int bit)
141+
{
142+
virtio_mmio_write32(dev, VIRTIO_MMIO_DEVICE_FEATURES_SEL, bit / 32);
143+
const uint32_t val = virtio_mmio_read32(dev, VIRTIO_MMIO_DEVICE_FEATURES);
144+
145+
return !!(val & BIT(bit % 32));
146+
}
147+
148+
static void virtio_mmio_write_driver_feature_bit(const struct device *dev, int bit, bool value)
149+
{
150+
const uint32_t mask = sys_cpu_to_le32(BIT(bit % 32));
151+
152+
virtio_mmio_write32(dev, VIRTIO_MMIO_DRIVER_FEATURES_SEL, bit / 32);
153+
154+
const uint32_t regval = virtio_mmio_read32(dev, VIRTIO_MMIO_DRIVER_FEATURES);
155+
156+
if (value) {
157+
virtio_mmio_write32(dev, VIRTIO_MMIO_DRIVER_FEATURES, regval | mask);
158+
} else {
159+
virtio_mmio_write32(dev, VIRTIO_MMIO_DRIVER_FEATURES, regval & ~mask);
160+
}
161+
}
162+
163+
static bool virtio_mmio_read_status_bit(const struct device *dev, int bit)
164+
{
165+
const uint32_t val = virtio_mmio_read32(dev, VIRTIO_MMIO_STATUS);
166+
167+
return !!(val & BIT(bit));
168+
}
169+
170+
static void virtio_mmio_write_status_bit(const struct device *dev, int bit)
171+
{
172+
const uint32_t mask = sys_cpu_to_le32(BIT(bit));
173+
const uint32_t val = virtio_mmio_read32(dev, VIRTIO_MMIO_STATUS);
174+
175+
virtio_mmio_write32(dev, VIRTIO_MMIO_STATUS, val | mask);
176+
}
177+
178+
static int virtio_mmio_write_driver_feature_bit_range_check(const struct device *dev, int bit,
179+
bool value)
180+
{
181+
if (!IN_RANGE(bit, DEV_TYPE_FEAT_RANGE_0_BEGIN, DEV_TYPE_FEAT_RANGE_0_END) ||
182+
!IN_RANGE(bit, DEV_TYPE_FEAT_RANGE_1_BEGIN, DEV_TYPE_FEAT_RANGE_1_END)) {
183+
return -EINVAL;
184+
}
185+
186+
virtio_mmio_write_driver_feature_bit(dev, bit, value);
187+
188+
return 0;
189+
}
190+
191+
static int virtio_mmio_commit_feature_bits(const struct device *dev)
192+
{
193+
virtio_mmio_write_status_bit(dev, DEVICE_STATUS_FEATURES_OK);
194+
if (!virtio_mmio_read_status_bit(dev, DEVICE_STATUS_FEATURES_OK)) {
195+
return -EINVAL;
196+
}
197+
198+
return 0;
199+
}
200+
201+
static int virtio_mmio_set_virtqueue(const struct device *dev, uint16_t virtqueue_n,
202+
struct virtq *virtqueue)
203+
{
204+
virtio_mmio_write32(dev, VIRTIO_MMIO_QUEUE_SEL, virtqueue_n);
205+
206+
uint16_t max_queue_size = virtio_mmio_read32(dev, VIRTIO_MMIO_QUEUE_SIZE_MAX);
207+
208+
if (max_queue_size < virtqueue->num) {
209+
LOG_ERR("%s doesn't support queue %d bigger than %d, tried to set "
210+
"one with size %d",
211+
dev->name, virtqueue_n, max_queue_size, virtqueue->num);
212+
return -EINVAL;
213+
}
214+
215+
virtio_mmio_write32(dev, VIRTIO_MMIO_QUEUE_SIZE, virtqueue->num);
216+
virtio_mmio_write32(dev, VIRTIO_MMIO_QUEUE_DESC_LOW,
217+
k_mem_phys_addr(virtqueue->desc) & UINT32_MAX);
218+
virtio_mmio_write32(dev, VIRTIO_MMIO_QUEUE_DESC_HIGH,
219+
k_mem_phys_addr(virtqueue->desc) >> 32);
220+
virtio_mmio_write32(dev, VIRTIO_MMIO_QUEUE_DRIVER_LOW,
221+
k_mem_phys_addr(virtqueue->avail) & UINT32_MAX);
222+
virtio_mmio_write32(dev, VIRTIO_MMIO_QUEUE_DRIVER_HIGH,
223+
k_mem_phys_addr(virtqueue->avail) >> 32);
224+
virtio_mmio_write32(dev, VIRTIO_MMIO_QUEUE_DEVICE_LOW,
225+
k_mem_phys_addr(virtqueue->used) & UINT32_MAX);
226+
virtio_mmio_write32(dev, VIRTIO_MMIO_QUEUE_DEVICE_HIGH,
227+
k_mem_phys_addr(virtqueue->used) >> 32);
228+
229+
virtio_mmio_write32(dev, VIRTIO_MMIO_QUEUE_READY, 1);
230+
231+
return 0;
232+
}
233+
234+
static int virtio_mmio_set_virtqueues(const struct device *dev, uint16_t queue_count,
235+
virtio_enumerate_queues cb, void *opaque)
236+
{
237+
struct virtio_mmio_data *data = dev->data;
238+
239+
data->virtqueues = k_malloc(queue_count * sizeof(struct virtq));
240+
if (!data->virtqueues) {
241+
LOG_ERR("failed to allocate virtqueue array");
242+
return -ENOMEM;
243+
}
244+
data->virtqueue_count = queue_count;
245+
246+
for (int i = 0; i < queue_count; i++) {
247+
virtio_mmio_write32(dev, VIRTIO_MMIO_QUEUE_SEL, i);
248+
249+
const uint16_t queue_size =
250+
cb(i, virtio_mmio_read32(dev, VIRTIO_MMIO_QUEUE_SIZE_MAX), opaque);
251+
252+
int ret = virtq_create(&data->virtqueues[i], queue_size);
253+
254+
if (ret != 0) {
255+
for (int j = 0; j < i; i++) {
256+
virtq_free(&data->virtqueues[j]);
257+
}
258+
return ret;
259+
}
260+
ret = virtio_mmio_set_virtqueue(dev, i, &data->virtqueues[i]);
261+
if (ret != 0) {
262+
for (int j = 0; j < i; i++) {
263+
virtq_free(&data->virtqueues[j]);
264+
}
265+
return ret;
266+
}
267+
}
268+
269+
return 0;
270+
}
271+
272+
static int virtio_mmio_init_virtqueues(const struct device *dev, uint16_t num_queues,
273+
virtio_enumerate_queues cb, void *opaque)
274+
{
275+
return virtio_mmio_set_virtqueues(dev, num_queues, cb, opaque);
276+
}
277+
278+
static void virtio_mmio_finalize_init(const struct device *dev)
279+
{
280+
virtio_mmio_write_status_bit(dev, DEVICE_STATUS_DRIVER_OK);
281+
}
282+
283+
static const struct virtio_driver_api virtio_mmio_driver_api = {
284+
.get_virtqueue = virtio_mmio_get_virtqueue,
285+
.notify_virtqueue = virtio_mmio_notify_queue,
286+
.get_device_specific_config = virtio_mmio_get_device_specific_config,
287+
.read_device_feature_bit = virtio_mmio_read_device_feature_bit,
288+
.write_driver_feature_bit = virtio_mmio_write_driver_feature_bit_range_check,
289+
.commit_feature_bits = virtio_mmio_commit_feature_bits,
290+
.init_virtqueues = virtio_mmio_init_virtqueues,
291+
.finalize_init = virtio_mmio_finalize_init,
292+
};
293+
294+
static int virtio_mmio_init_common(const struct device *dev)
295+
{
296+
DEVICE_MMIO_NAMED_MAP(dev, reg_base, K_MEM_CACHE_NONE);
297+
298+
const uint32_t magic = virtio_mmio_read32(dev, VIRTIO_MMIO_MAGIC_VALUE);
299+
300+
if (magic != VIRTIO_MMIO_MAGIC) {
301+
LOG_ERR("Invalid magic value %x", magic);
302+
return -EINVAL;
303+
}
304+
305+
const uint32_t version = virtio_mmio_read32(dev, VIRTIO_MMIO_VERSION);
306+
307+
if (version != VIRTIO_MMIO_SUPPORTED_VERSION) {
308+
LOG_ERR("Invalid version %x", version);
309+
return -EINVAL;
310+
}
311+
312+
const uint32_t dev_id = virtio_mmio_read32(dev, VIRTIO_MMIO_DEVICE_ID);
313+
314+
if (dev_id == VIRTIO_MMIO_INVALID_DEVICE_ID) {
315+
LOG_ERR("Invalid device id %x", dev_id);
316+
return -EINVAL;
317+
}
318+
319+
LOG_DBG("VENDOR_ID = %x", virtio_mmio_read32(dev, VIRTIO_MMIO_VENDOR_ID));
320+
321+
virtio_mmio_reset(dev);
322+
323+
virtio_mmio_write_status_bit(dev, DEVICE_STATUS_ACKNOWLEDGE);
324+
virtio_mmio_write_status_bit(dev, DEVICE_STATUS_DRIVER);
325+
326+
virtio_mmio_write_driver_feature_bit(dev, VIRTIO_F_VERSION_1, true);
327+
328+
return 0;
329+
};
330+
331+
#define VIRTIO_MMIO_DEFINE(inst) \
332+
static struct virtio_mmio_data virtio_mmio_data##inst; \
333+
static struct virtio_mmio_config virtio_mmio_config##inst = { \
334+
DEVICE_MMIO_NAMED_ROM_INIT(reg_base, DT_DRV_INST(inst)), \
335+
}; \
336+
static int virtio_mmio_init##inst(const struct device *dev) \
337+
{ \
338+
IRQ_CONNECT(DT_INST_IRQN(inst), DT_INST_IRQ(inst, priority), virtio_mmio_isr, \
339+
DEVICE_DT_INST_GET(inst), 0); \
340+
int ret = virtio_mmio_init_common(dev); \
341+
irq_enable(DT_INST_IRQN(inst)); \
342+
return ret; \
343+
} \
344+
DEVICE_DT_INST_DEFINE(inst, virtio_mmio_init##inst, NULL, &virtio_mmio_data##inst, \
345+
&virtio_mmio_config##inst, POST_KERNEL, 0, &virtio_mmio_driver_api);
346+
347+
DT_INST_FOREACH_STATUS_OKAY(VIRTIO_MMIO_DEFINE)

dts/bindings/virtio/virtio,mmio.yaml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
# Copyright (c) 2025 TOKITA Hiroshi
2+
# SPDX-License-Identifier: Apache-2.0
3+
4+
description: VIRTIO over MMIO
5+
6+
compatible: "virtio,mmio"
7+
8+
include: [base.yaml]

0 commit comments

Comments
 (0)