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"
34 #include <ddf/driver.h>
36 #include <pci_dev_iface.h>
38 static bool check_bar(virtio_dev_t
*vdev
, uint8_t bar
, uint32_t offset
,
41 /* We must ignore the capability if bar is greater than 5 */
42 if (bar
>= PCI_BAR_COUNT
)
45 /* This is not a mapped BAR */
46 if (!vdev
->bar
[bar
].mapped
)
49 uintptr_t start
= (uintptr_t) vdev
->bar
[bar
].mapped_base
;
50 if (start
+ offset
< start
)
52 if (start
+ offset
> start
+ vdev
->bar
[bar
].mapped_size
)
54 if (start
+ offset
+ length
< start
+ offset
)
56 if (start
+ offset
+ length
> start
+ vdev
->bar
[bar
].mapped_size
)
62 static void virtio_pci_common_cfg(virtio_dev_t
*vdev
, uint8_t bar
,
63 uint32_t offset
, uint32_t length
)
68 if (!check_bar(vdev
, bar
, offset
, length
))
71 vdev
->common_cfg
= vdev
->bar
[bar
].mapped_base
+ offset
;
73 ddf_msg(LVL_NOTE
, "common_cfg=%p", vdev
->common_cfg
);
76 static void virtio_pci_notify_cfg(virtio_dev_t
*vdev
, uint8_t bar
,
77 uint32_t offset
, uint32_t length
, uint32_t multiplier
)
79 if (vdev
->notify_base
)
82 if (!check_bar(vdev
, bar
, offset
, length
))
85 vdev
->notify_base
= vdev
->bar
[bar
].mapped_base
+ offset
;
86 vdev
->notify_off_multiplier
= multiplier
;
88 ddf_msg(LVL_NOTE
, "notify_base=%p, off_multiplier=%u",
89 vdev
->notify_base
, vdev
->notify_off_multiplier
);
92 static void virtio_pci_isr_cfg(virtio_dev_t
*vdev
, uint8_t bar
, uint32_t offset
,
98 if (!check_bar(vdev
, bar
, offset
, length
))
101 vdev
->isr
= vdev
->bar
[bar
].mapped_base
+ offset
;
103 ddf_msg(LVL_NOTE
, "isr=%p", vdev
->isr
);
106 static void virtio_pci_device_cfg(virtio_dev_t
*vdev
, uint8_t bar
,
107 uint32_t offset
, uint32_t length
)
109 if (vdev
->device_cfg
)
112 if (!check_bar(vdev
, bar
, offset
, length
))
115 vdev
->device_cfg
= vdev
->bar
[bar
].mapped_base
+ offset
;
117 ddf_msg(LVL_NOTE
, "device_cfg=%p", vdev
->device_cfg
);
120 static errno_t
enable_resources(async_sess_t
*pci_sess
, virtio_dev_t
*vdev
)
122 pio_window_t pio_window
;
123 errno_t rc
= pio_window_get(pci_sess
, &pio_window
);
127 hw_resource_list_t hw_res
;
128 rc
= hw_res_get_resource_list(pci_sess
, &hw_res
);
133 * Enable resources and reconstruct the mapping between BAR and resource
134 * indices. We are going to need this later when the VIRTIO PCI
135 * capabilities refer to specific BARs.
137 * XXX: The mapping should probably be provided by the PCI driver
140 for (unsigned i
= 0, j
= 0; i
< PCI_BAR_COUNT
&& j
< hw_res
.count
;
142 /* Detect and skip unused BARs */
144 rc
= pci_config_space_read_32(pci_sess
,
145 PCI_BAR0
+ i
* sizeof(uint32_t), &bar
);
151 hw_resource_t
*res
= &hw_res
.resources
[j
];
152 rc
= pio_enable_resource(&pio_window
, res
,
153 &vdev
->bar
[i
].mapped_base
, &vdev
->bar
[i
].mapped_size
);
155 vdev
->bar
[i
].mapped
= true;
162 static errno_t
disable_resources(virtio_dev_t
*vdev
)
164 for (unsigned i
= 0; i
< PCI_BAR_COUNT
; i
++) {
165 if (vdev
->bar
[i
].mapped
) {
166 errno_t rc
= pio_disable(vdev
->bar
[i
].mapped_base
,
167 vdev
->bar
[i
].mapped_size
);
170 vdev
->bar
[i
].mapped
= false;
177 errno_t
virtio_pci_dev_initialize(ddf_dev_t
*dev
, virtio_dev_t
*vdev
)
179 memset(vdev
, 0, sizeof(virtio_dev_t
));
181 async_sess_t
*pci_sess
= ddf_dev_parent_sess_get(dev
);
185 errno_t rc
= enable_resources(pci_sess
, vdev
);
190 * Find the VIRTIO PCI Capabilities
194 for (rc
= pci_config_space_cap_first(pci_sess
, &c
, &cap_vndr
);
196 rc
= pci_config_space_cap_next(pci_sess
, &c
, &cap_vndr
)) {
197 if (cap_vndr
!= PCI_CAP_VENDORSPECID
)
201 rc
= pci_config_space_read_8(pci_sess
,
202 VIRTIO_PCI_CAP_CAP_LEN(c
), &cap_len
);
206 if (cap_len
< VIRTIO_PCI_CAP_END(0)) {
212 rc
= pci_config_space_read_8(pci_sess
,
213 VIRTIO_PCI_CAP_CFG_TYPE(c
), &cfg_type
);
218 rc
= pci_config_space_read_8(pci_sess
, VIRTIO_PCI_CAP_BAR(c
),
224 rc
= pci_config_space_read_32(pci_sess
,
225 VIRTIO_PCI_CAP_OFFSET(c
), &offset
);
230 rc
= pci_config_space_read_32(pci_sess
,
231 VIRTIO_PCI_CAP_LENGTH(c
), &length
);
237 case VIRTIO_PCI_CAP_COMMON_CFG
:
238 virtio_pci_common_cfg(vdev
, bar
, offset
, length
);
240 case VIRTIO_PCI_CAP_NOTIFY_CFG
:
241 if (cap_len
< VIRTIO_PCI_CAP_END(sizeof(uint32_t))) {
245 rc
= pci_config_space_read_32(pci_sess
,
246 VIRTIO_PCI_CAP_END(c
), &multiplier
);
249 virtio_pci_notify_cfg(vdev
, bar
, offset
, length
,
252 case VIRTIO_PCI_CAP_ISR_CFG
:
253 virtio_pci_isr_cfg(vdev
, bar
, offset
, length
);
255 case VIRTIO_PCI_CAP_DEVICE_CFG
:
256 virtio_pci_device_cfg(vdev
, bar
, offset
, length
);
258 case VIRTIO_PCI_CAP_PCI_CFG
:
265 /* Check that the configuration is complete */
266 if (!vdev
->common_cfg
|| !vdev
->notify_base
|| !vdev
->isr
||
275 (void) disable_resources(vdev
);
279 errno_t
virtio_pci_dev_cleanup(virtio_dev_t
*vdev
)
283 i
< pio_read_le16(&vdev
->common_cfg
->num_queues
); i
++)
284 virtio_virtq_teardown(vdev
, i
);
287 return disable_resources(vdev
);