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_set_desc(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 void virtio_virtq_produce_available(virtio_dev_t
*vdev
, uint16_t num
,
54 virtq_t
*q
= &vdev
->queues
[num
];
56 uint16_t idx
= pio_read_le16(&q
->avail
->idx
);
57 pio_write_le16(&q
->avail
->ring
[idx
], descno
);
59 pio_write_le16(&q
->avail
->idx
, (idx
+ 1) % q
->queue_size
);
61 pio_write_le16(q
->notify
, num
);
64 errno_t
virtio_virtq_setup(virtio_dev_t
*vdev
, uint16_t num
, uint16_t size
)
66 virtq_t
*q
= &vdev
->queues
[num
];
67 virtio_pci_common_cfg_t
*cfg
= vdev
->common_cfg
;
69 /* Program the queue of our interest */
70 pio_write_le16(&cfg
->queue_select
, num
);
72 /* Trim the size of the queue as needed */
73 if (size
> pio_read_16(&cfg
->queue_size
)) {
74 ddf_msg(LVL_ERROR
, "Virtq %u: not enough descriptors", num
);
77 pio_write_le16(&cfg
->queue_size
, size
);
78 ddf_msg(LVL_NOTE
, "Virtq %u: %u descriptors", num
, (unsigned) size
);
80 size_t avail_offset
= 0;
81 size_t used_offset
= 0;
84 * Compute the size of the needed DMA memory and also the offsets of
85 * the individual components
87 size_t mem_size
= sizeof(virtq_desc_t
[size
]);
88 mem_size
= ALIGN_UP(mem_size
, _Alignof(virtq_avail_t
));
89 avail_offset
= mem_size
;
90 mem_size
+= sizeof(virtq_avail_t
) + sizeof(ioport16_t
[size
]) +
92 mem_size
= ALIGN_UP(mem_size
, _Alignof(virtq_used_t
));
93 used_offset
= mem_size
;
94 mem_size
+= sizeof(virtq_used_t
) + sizeof(virtq_used_elem_t
[size
]) +
98 * Allocate DMA memory for the virtqueues
100 q
->virt
= AS_AREA_ANY
;
101 errno_t rc
= dmamem_map_anonymous(mem_size
, 0,
102 AS_AREA_READ
| AS_AREA_WRITE
, 0, &q
->phys
, &q
->virt
);
109 q
->queue_size
= size
;
111 q
->avail
= q
->virt
+ avail_offset
;
112 q
->used
= q
->virt
+ used_offset
;
114 memset(q
->virt
, 0, q
->size
);
117 * Initialize the descriptor table
119 for (unsigned i
= 0; i
< size
; i
++) {
122 q
->desc
[i
].flags
= 0;
126 * Write the configured addresses to device's common config
128 pio_write_le64(&cfg
->queue_desc
, q
->phys
);
129 pio_write_le64(&cfg
->queue_avail
, q
->phys
+ avail_offset
);
130 pio_write_le64(&cfg
->queue_used
, q
->phys
+ used_offset
);
132 ddf_msg(LVL_NOTE
, "DMA memory for virtq %d: virt=%p, phys=%p, size=%zu",
133 num
, q
->virt
, (void *) q
->phys
, q
->size
);
135 /* Determine virtq's notification address */
136 q
->notify
= vdev
->notify_base
+
137 pio_read_le16(&cfg
->queue_notif_off
) * vdev
->notify_off_multiplier
;
139 ddf_msg(LVL_NOTE
, "notification register: %p", q
->notify
);
144 void virtio_virtq_teardown(virtio_dev_t
*vdev
, uint16_t num
)
146 virtq_t
*q
= &vdev
->queues
[num
];
148 dmamem_unmap_anonymous(q
->virt
);
152 * Perform device initialization as described in section 3.1.1 of the
153 * specification, steps 1 - 6.
155 errno_t
virtio_device_setup_start(virtio_dev_t
*vdev
, uint32_t features
)
157 virtio_pci_common_cfg_t
*cfg
= vdev
->common_cfg
;
159 /* 1. Reset the device */
160 uint8_t status
= VIRTIO_DEV_STATUS_RESET
;
161 pio_write_8(&cfg
->device_status
, status
);
163 /* 2. Acknowledge we found the device */
164 status
|= VIRTIO_DEV_STATUS_ACKNOWLEDGE
;
165 pio_write_8(&cfg
->device_status
, status
);
167 /* 3. We know how to drive the device */
168 status
|= VIRTIO_DEV_STATUS_DRIVER
;
169 pio_write_8(&cfg
->device_status
, status
);
171 /* 4. Read the offered feature flags */
172 pio_write_le32(&cfg
->device_feature_select
, VIRTIO_FEATURES_0_31
);
173 uint32_t device_features
= pio_read_le32(&cfg
->device_feature
);
175 ddf_msg(LVL_NOTE
, "offered features %x", device_features
);
176 features
&= device_features
;
181 /* 4. Write the accepted feature flags */
182 pio_write_le32(&cfg
->driver_feature_select
, VIRTIO_FEATURES_0_31
);
183 pio_write_le32(&cfg
->driver_feature
, features
);
185 ddf_msg(LVL_NOTE
, "accepted features %x", features
);
187 /* 5. Set FEATURES_OK */
188 status
|= VIRTIO_DEV_STATUS_FEATURES_OK
;
189 pio_write_8(&cfg
->device_status
, status
);
191 /* 6. Test if the device supports our feature subset */
192 status
= pio_read_8(&cfg
->device_status
);
193 if (!(status
& VIRTIO_DEV_STATUS_FEATURES_OK
))
200 * Perform device initialization as described in section 3.1.1 of the
201 * specification, step 8 (go live).
203 void virtio_device_setup_finalize(virtio_dev_t
*vdev
)
205 virtio_pci_common_cfg_t
*cfg
= vdev
->common_cfg
;
208 uint8_t status
= pio_read_8(&cfg
->device_status
);
209 pio_write_8(&cfg
->device_status
, status
| VIRTIO_DEV_STATUS_DRIVER_OK
);
212 void virtio_device_setup_fail(virtio_dev_t
*vdev
)
214 virtio_pci_common_cfg_t
*cfg
= vdev
->common_cfg
;
216 uint8_t status
= pio_read_8(&cfg
->device_status
);
217 pio_write_8(&cfg
->device_status
, status
| VIRTIO_DEV_STATUS_FAILED
);