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"
40 errno_t
virtio_virtq_setup(virtio_dev_t
*vdev
, uint16_t num
, uint16_t size
)
42 virtq_t
*q
= &vdev
->queues
[num
];
43 virtio_pci_common_cfg_t
*cfg
= vdev
->common_cfg
;
45 /* Program the queue of our interest */
46 pio_write_16(&cfg
->queue_select
, num
);
48 /* Trim the size of the queue as needed */
49 size
= min(pio_read_16(&cfg
->queue_size
), size
);
50 pio_write_16(&cfg
->queue_size
, size
);
51 ddf_msg(LVL_NOTE
, "Virtq %u: %u buffers", num
, (unsigned) size
);
53 size_t avail_offset
= 0;
54 size_t used_offset
= 0;
57 * Compute the size of the needed DMA memory and also the offsets of
58 * the individual components
60 size_t mem_size
= sizeof(virtq_desc_t
[size
]);
61 mem_size
= ALIGN_UP(mem_size
, _Alignof(virtq_avail_t
));
62 avail_offset
= mem_size
;
63 mem_size
+= sizeof(virtq_avail_t
) + sizeof(ioport16_t
[size
]) +
65 mem_size
= ALIGN_UP(mem_size
, _Alignof(virtq_used_t
));
66 used_offset
= mem_size
;
67 mem_size
+= sizeof(virtq_used_t
) + sizeof(virtq_used_elem_t
[size
]) +
71 * Allocate DMA memory for the virtqueues
73 q
->virt
= AS_AREA_ANY
;
74 errno_t rc
= dmamem_map_anonymous(mem_size
, DMAMEM_4GiB
,
75 AS_AREA_READ
| AS_AREA_WRITE
, 0, &q
->phys
, &q
->virt
);
84 q
->avail
= q
->virt
+ avail_offset
;
85 q
->used
= q
->virt
+ used_offset
;
87 memset(q
->virt
, 0, q
->size
);
90 * Initialize the descriptor table
92 for (unsigned i
= 0; i
< size
; i
++) {
99 * Write the configured addresses to device's common config
101 pio_write_64(&cfg
->queue_desc
, q
->phys
);
102 pio_write_64(&cfg
->queue_avail
, q
->phys
+ avail_offset
);
103 pio_write_64(&cfg
->queue_used
, q
->phys
+ used_offset
);
105 ddf_msg(LVL_NOTE
, "DMA memory for virtq %d: virt=%p, phys=%p, size=%zu",
106 num
, q
->virt
, (void *) q
->phys
, q
->size
);
108 /* Determine virtq's notification address */
109 q
->notify
= vdev
->notify_base
+
110 pio_read_16(&cfg
->queue_notif_off
) * vdev
->notify_off_multiplier
;
112 ddf_msg(LVL_NOTE
, "notification register: %p", q
->notify
);
117 void virtio_virtq_teardown(virtio_dev_t
*vdev
, uint16_t num
)
119 virtq_t
*q
= &vdev
->queues
[num
];
121 dmamem_unmap_anonymous(q
->virt
);
125 * Perform device initialization as described in section 3.1.1 of the
126 * specification, steps 1 - 6.
128 errno_t
virtio_device_setup_start(virtio_dev_t
*vdev
, uint32_t features
)
130 virtio_pci_common_cfg_t
*cfg
= vdev
->common_cfg
;
132 /* 1. Reset the device */
133 uint8_t status
= VIRTIO_DEV_STATUS_RESET
;
134 pio_write_8(&cfg
->device_status
, status
);
136 /* 2. Acknowledge we found the device */
137 status
|= VIRTIO_DEV_STATUS_ACKNOWLEDGE
;
138 pio_write_8(&cfg
->device_status
, status
);
140 /* 3. We know how to drive the device */
141 status
|= VIRTIO_DEV_STATUS_DRIVER
;
142 pio_write_8(&cfg
->device_status
, status
);
144 /* 4. Read the offered feature flags */
145 pio_write_32(&cfg
->device_feature_select
, VIRTIO_FEATURES_0_31
);
146 uint32_t device_features
= pio_read_32(&cfg
->device_feature
);
148 ddf_msg(LVL_NOTE
, "offered features %x", device_features
);
149 features
&= device_features
;
154 /* 4. Write the accepted feature flags */
155 pio_write_32(&cfg
->driver_feature_select
, VIRTIO_FEATURES_0_31
);
156 pio_write_32(&cfg
->driver_feature
, features
);
158 ddf_msg(LVL_NOTE
, "accepted features %x", features
);
160 /* 5. Set FEATURES_OK */
161 status
|= VIRTIO_DEV_STATUS_FEATURES_OK
;
162 pio_write_8(&cfg
->device_status
, status
);
164 /* 6. Test if the device supports our feature subset */
165 status
= pio_read_8(&cfg
->device_status
);
166 if (!(status
& VIRTIO_DEV_STATUS_FEATURES_OK
))
173 * Perform device initialization as described in section 3.1.1 of the
174 * specification, step 8 (go live).
176 void virtio_device_setup_finalize(virtio_dev_t
*vdev
)
178 virtio_pci_common_cfg_t
*cfg
= vdev
->common_cfg
;
181 uint8_t status
= pio_read_8(&cfg
->device_status
);
182 pio_write_8(&cfg
->device_status
, status
| VIRTIO_DEV_STATUS_DRIVER_OK
);
185 void virtio_device_setup_fail(virtio_dev_t
*vdev
)
187 virtio_pci_common_cfg_t
*cfg
= vdev
->common_cfg
;
189 uint8_t status
= pio_read_8(&cfg
->device_status
);
190 pio_write_8(&cfg
->device_status
, status
| VIRTIO_DEV_STATUS_FAILED
);