2 * Copyright (c) 2018 Jakub Jermar
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
9 * - Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * - Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * - The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 /** @file VIRTIO support
32 #include "virtio-pci.h"
39 #include <libarch/barrier.h>
41 void virtio_virtq_desc_set(virtio_dev_t
*vdev
, uint16_t num
, uint16_t descno
,
42 uint64_t addr
, uint32_t len
, uint16_t flags
, uint16_t next
)
44 virtq_desc_t
*d
= &vdev
->queues
[num
].desc
[descno
];
45 pio_write_le64(&d
->addr
, addr
);
46 pio_write_le32(&d
->len
, len
);
47 pio_write_le16(&d
->flags
, flags
);
48 pio_write_le16(&d
->next
, next
);
51 uint16_t virtio_virtq_desc_get_next(virtio_dev_t
*vdev
, uint16_t num
,
54 virtq_desc_t
*d
= &vdev
->queues
[num
].desc
[descno
];
55 if (!(pio_read_le16(&d
->flags
) & VIRTQ_DESC_F_NEXT
))
56 return (uint16_t) -1U;
57 return pio_read_le16(&d
->next
);
60 void virtio_virtq_produce_available(virtio_dev_t
*vdev
, uint16_t num
,
63 virtq_t
*q
= &vdev
->queues
[num
];
65 fibril_mutex_lock(&q
->lock
);
66 uint16_t idx
= pio_read_le16(&q
->avail
->idx
);
67 pio_write_le16(&q
->avail
->ring
[idx
% q
->queue_size
], descno
);
69 pio_write_le16(&q
->avail
->idx
, idx
+ 1);
71 pio_write_le16(q
->notify
, num
);
72 fibril_mutex_unlock(&q
->lock
);
75 bool virtio_virtq_consume_used(virtio_dev_t
*vdev
, uint16_t num
,
76 uint16_t *descno
, uint32_t *len
)
78 virtq_t
*q
= &vdev
->queues
[num
];
80 fibril_mutex_lock(&q
->lock
);
81 uint16_t last_idx
= q
->used_last_idx
% q
->queue_size
;
82 if (last_idx
== (pio_read_le16(&q
->used
->idx
) % q
->queue_size
)) {
83 fibril_mutex_unlock(&q
->lock
);
87 *descno
= (uint16_t) pio_read_le32(&q
->used
->ring
[last_idx
].id
);
88 *len
= pio_read_le32(&q
->used
->ring
[last_idx
].len
);
91 fibril_mutex_unlock(&q
->lock
);
96 errno_t
virtio_virtq_setup(virtio_dev_t
*vdev
, uint16_t num
, uint16_t size
)
98 virtq_t
*q
= &vdev
->queues
[num
];
99 virtio_pci_common_cfg_t
*cfg
= vdev
->common_cfg
;
101 /* Program the queue of our interest */
102 pio_write_le16(&cfg
->queue_select
, num
);
104 /* Trim the size of the queue as needed */
105 if (size
> pio_read_16(&cfg
->queue_size
)) {
106 ddf_msg(LVL_ERROR
, "Virtq %u: not enough descriptors", num
);
109 pio_write_le16(&cfg
->queue_size
, size
);
110 ddf_msg(LVL_NOTE
, "Virtq %u: %u descriptors", num
, (unsigned) size
);
112 size_t avail_offset
= 0;
113 size_t used_offset
= 0;
116 * Compute the size of the needed DMA memory and also the offsets of
117 * the individual components
119 size_t mem_size
= sizeof(virtq_desc_t
[size
]);
120 mem_size
= ALIGN_UP(mem_size
, _Alignof(virtq_avail_t
));
121 avail_offset
= mem_size
;
122 mem_size
+= sizeof(virtq_avail_t
) + sizeof(ioport16_t
[size
]) +
124 mem_size
= ALIGN_UP(mem_size
, _Alignof(virtq_used_t
));
125 used_offset
= mem_size
;
126 mem_size
+= sizeof(virtq_used_t
) + sizeof(virtq_used_elem_t
[size
]) +
130 * Allocate DMA memory for the virtqueues
132 q
->virt
= AS_AREA_ANY
;
133 errno_t rc
= dmamem_map_anonymous(mem_size
, 0,
134 AS_AREA_READ
| AS_AREA_WRITE
, 0, &q
->phys
, &q
->virt
);
140 fibril_mutex_initialize(&q
->lock
);
143 q
->queue_size
= size
;
145 q
->avail
= q
->virt
+ avail_offset
;
146 q
->used
= q
->virt
+ used_offset
;
147 q
->used_last_idx
= 0;
149 memset(q
->virt
, 0, q
->size
);
152 * Write the configured addresses to device's common config
154 pio_write_le64(&cfg
->queue_desc
, q
->phys
);
155 pio_write_le64(&cfg
->queue_avail
, q
->phys
+ avail_offset
);
156 pio_write_le64(&cfg
->queue_used
, q
->phys
+ used_offset
);
158 ddf_msg(LVL_NOTE
, "DMA memory for virtq %d: virt=%p, phys=%p, size=%zu",
159 num
, q
->virt
, (void *) q
->phys
, q
->size
);
161 /* Determine virtq's notification address */
162 q
->notify
= vdev
->notify_base
+
163 pio_read_le16(&cfg
->queue_notif_off
) * vdev
->notify_off_multiplier
;
165 ddf_msg(LVL_NOTE
, "notification register: %p", q
->notify
);
167 /* Enable the queue */
168 pio_write_le16(&cfg
->queue_enable
, 1);
169 ddf_msg(LVL_NOTE
, "virtq %d set", num
);
174 void virtio_virtq_teardown(virtio_dev_t
*vdev
, uint16_t num
)
176 virtio_pci_common_cfg_t
*cfg
= vdev
->common_cfg
;
178 /* Disable the queue */
179 pio_write_le16(&cfg
->queue_enable
, 0);
181 virtq_t
*q
= &vdev
->queues
[num
];
183 dmamem_unmap_anonymous(q
->virt
);
187 * Perform device initialization as described in section 3.1.1 of the
188 * specification, steps 1 - 6.
190 errno_t
virtio_device_setup_start(virtio_dev_t
*vdev
, uint32_t features
)
192 virtio_pci_common_cfg_t
*cfg
= vdev
->common_cfg
;
194 /* 1. Reset the device */
195 uint8_t status
= VIRTIO_DEV_STATUS_RESET
;
196 pio_write_8(&cfg
->device_status
, status
);
198 /* 2. Acknowledge we found the device */
199 status
|= VIRTIO_DEV_STATUS_ACKNOWLEDGE
;
200 pio_write_8(&cfg
->device_status
, status
);
202 /* 3. We know how to drive the device */
203 status
|= VIRTIO_DEV_STATUS_DRIVER
;
204 pio_write_8(&cfg
->device_status
, status
);
206 /* 4. Read the offered feature flags */
207 pio_write_le32(&cfg
->device_feature_select
, VIRTIO_FEATURES_0_31
);
208 uint32_t device_features
= pio_read_le32(&cfg
->device_feature
);
210 ddf_msg(LVL_NOTE
, "offered features %x", device_features
);
212 if (features
!= (features
& device_features
))
214 features
&= device_features
;
216 /* 4. Write the accepted feature flags */
217 pio_write_le32(&cfg
->driver_feature_select
, VIRTIO_FEATURES_0_31
);
218 pio_write_le32(&cfg
->driver_feature
, features
);
220 ddf_msg(LVL_NOTE
, "accepted features %x", features
);
222 /* 5. Set FEATURES_OK */
223 status
|= VIRTIO_DEV_STATUS_FEATURES_OK
;
224 pio_write_8(&cfg
->device_status
, status
);
226 /* 6. Test if the device supports our feature subset */
227 status
= pio_read_8(&cfg
->device_status
);
228 if (!(status
& VIRTIO_DEV_STATUS_FEATURES_OK
))
235 * Perform device initialization as described in section 3.1.1 of the
236 * specification, step 8 (go live).
238 void virtio_device_setup_finalize(virtio_dev_t
*vdev
)
240 virtio_pci_common_cfg_t
*cfg
= vdev
->common_cfg
;
243 uint8_t status
= pio_read_8(&cfg
->device_status
);
244 pio_write_8(&cfg
->device_status
, status
| VIRTIO_DEV_STATUS_DRIVER_OK
);
247 void virtio_device_setup_fail(virtio_dev_t
*vdev
)
249 virtio_pci_common_cfg_t
*cfg
= vdev
->common_cfg
;
251 uint8_t status
= pio_read_8(&cfg
->device_status
);
252 pio_write_8(&cfg
->device_status
, status
| VIRTIO_DEV_STATUS_FAILED
);