1*823fdb19SAdam Słaboń /*
2*823fdb19SAdam Słaboń * Virtio PCI driver - modern (virtio 1.0) device support
3*823fdb19SAdam Słaboń *
4*823fdb19SAdam Słaboń * Copyright IBM Corp. 2007
5*823fdb19SAdam Słaboń * Copyright Red Hat, Inc. 2014
6*823fdb19SAdam Słaboń *
7*823fdb19SAdam Słaboń * Authors:
8*823fdb19SAdam Słaboń * Anthony Liguori <aliguori@us.ibm.com>
9*823fdb19SAdam Słaboń * Rusty Russell <rusty@rustcorp.com.au>
10*823fdb19SAdam Słaboń * Michael S. Tsirkin <mst@redhat.com>
11*823fdb19SAdam Słaboń *
12*823fdb19SAdam Słaboń * Redistribution and use in source and binary forms, with or without
13*823fdb19SAdam Słaboń * modification, are permitted provided that the following conditions
14*823fdb19SAdam Słaboń * are met :
15*823fdb19SAdam Słaboń * 1. Redistributions of source code must retain the above copyright
16*823fdb19SAdam Słaboń * notice, this list of conditions and the following disclaimer.
17*823fdb19SAdam Słaboń * 2. Redistributions in binary form must reproduce the above copyright
18*823fdb19SAdam Słaboń * notice, this list of conditions and the following disclaimer in the
19*823fdb19SAdam Słaboń * documentation and / or other materials provided with the distribution.
20*823fdb19SAdam Słaboń * 3. Neither the names of the copyright holders nor the names of their contributors
21*823fdb19SAdam Słaboń * may be used to endorse or promote products derived from this software
22*823fdb19SAdam Słaboń * without specific prior written permission.
23*823fdb19SAdam Słaboń * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND
24*823fdb19SAdam Słaboń * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25*823fdb19SAdam Słaboń * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26*823fdb19SAdam Słaboń * ARE DISCLAIMED.IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE
27*823fdb19SAdam Słaboń * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28*823fdb19SAdam Słaboń * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29*823fdb19SAdam Słaboń * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30*823fdb19SAdam Słaboń * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31*823fdb19SAdam Słaboń * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32*823fdb19SAdam Słaboń * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33*823fdb19SAdam Słaboń * SUCH DAMAGE.
34*823fdb19SAdam Słaboń */
35*823fdb19SAdam Słaboń #include "osdep.h"
36*823fdb19SAdam Słaboń #define VIRTIO_PCI_NO_LEGACY
37*823fdb19SAdam Słaboń #include "virtio_pci.h"
38*823fdb19SAdam Słaboń #include "VirtIO.h"
39*823fdb19SAdam Słaboń #include "kdebugprint.h"
40*823fdb19SAdam Słaboń #include "virtio_ring.h"
41*823fdb19SAdam Słaboń #include "virtio_pci_common.h"
42*823fdb19SAdam Słaboń #include "windows/virtio_ring_allocation.h"
43*823fdb19SAdam Słaboń #include <stddef.h>
44*823fdb19SAdam Słaboń
45*823fdb19SAdam Słaboń #ifdef WPP_EVENT_TRACING
46*823fdb19SAdam Słaboń #include "VirtIOPCIModern.tmh"
47*823fdb19SAdam Słaboń #endif
48*823fdb19SAdam Słaboń
vio_modern_map_capability(VirtIODevice * vdev,int cap_offset,size_t minlen,u32 alignment,u32 start,u32 size,size_t * len)49*823fdb19SAdam Słaboń static void *vio_modern_map_capability(VirtIODevice *vdev, int cap_offset,
50*823fdb19SAdam Słaboń size_t minlen, u32 alignment,
51*823fdb19SAdam Słaboń u32 start, u32 size, size_t *len)
52*823fdb19SAdam Słaboń {
53*823fdb19SAdam Słaboń u8 bar;
54*823fdb19SAdam Słaboń u32 bar_offset, bar_length;
55*823fdb19SAdam Słaboń void *addr;
56*823fdb19SAdam Słaboń
57*823fdb19SAdam Słaboń pci_read_config_byte(vdev, cap_offset + offsetof(struct virtio_pci_cap, bar), &bar);
58*823fdb19SAdam Słaboń pci_read_config_dword(vdev, cap_offset + offsetof(struct virtio_pci_cap, offset), &bar_offset);
59*823fdb19SAdam Słaboń pci_read_config_dword(vdev, cap_offset + offsetof(struct virtio_pci_cap, length), &bar_length);
60*823fdb19SAdam Słaboń
61*823fdb19SAdam Słaboń if (start + minlen > bar_length) {
62*823fdb19SAdam Słaboń DPrintf(0, "bar %i cap is not large enough to map %zu bytes at offset %u\n", bar, minlen, start);
63*823fdb19SAdam Słaboń return NULL;
64*823fdb19SAdam Słaboń }
65*823fdb19SAdam Słaboń
66*823fdb19SAdam Słaboń bar_length -= start;
67*823fdb19SAdam Słaboń bar_offset += start;
68*823fdb19SAdam Słaboń
69*823fdb19SAdam Słaboń if (bar_offset & (alignment - 1)) {
70*823fdb19SAdam Słaboń DPrintf(0, "bar %i offset %u not aligned to %u\n", bar, bar_offset, alignment);
71*823fdb19SAdam Słaboń return NULL;
72*823fdb19SAdam Słaboń }
73*823fdb19SAdam Słaboń
74*823fdb19SAdam Słaboń if (bar_length > size) {
75*823fdb19SAdam Słaboń bar_length = size;
76*823fdb19SAdam Słaboń }
77*823fdb19SAdam Słaboń
78*823fdb19SAdam Słaboń if (len) {
79*823fdb19SAdam Słaboń *len = bar_length;
80*823fdb19SAdam Słaboń }
81*823fdb19SAdam Słaboń
82*823fdb19SAdam Słaboń if (bar_offset + minlen > pci_get_resource_len(vdev, bar)) {
83*823fdb19SAdam Słaboń DPrintf(0, "bar %i is not large enough to map %zu bytes at offset %u\n", bar, minlen, bar_offset);
84*823fdb19SAdam Słaboń return NULL;
85*823fdb19SAdam Słaboń }
86*823fdb19SAdam Słaboń
87*823fdb19SAdam Słaboń addr = pci_map_address_range(vdev, bar, bar_offset, bar_length);
88*823fdb19SAdam Słaboń if (!addr) {
89*823fdb19SAdam Słaboń DPrintf(0, "unable to map %u bytes at bar %i offset %u\n", bar_length, bar, bar_offset);
90*823fdb19SAdam Słaboń }
91*823fdb19SAdam Słaboń return addr;
92*823fdb19SAdam Słaboń }
93*823fdb19SAdam Słaboń
vio_modern_map_simple_capability(VirtIODevice * vdev,int cap_offset,size_t length,u32 alignment)94*823fdb19SAdam Słaboń static void *vio_modern_map_simple_capability(VirtIODevice *vdev, int cap_offset, size_t length, u32 alignment)
95*823fdb19SAdam Słaboń {
96*823fdb19SAdam Słaboń return vio_modern_map_capability(
97*823fdb19SAdam Słaboń vdev,
98*823fdb19SAdam Słaboń cap_offset,
99*823fdb19SAdam Słaboń length, // minlen
100*823fdb19SAdam Słaboń alignment,
101*823fdb19SAdam Słaboń 0, // offset
102*823fdb19SAdam Słaboń (u32)length, // size is equal to minlen
103*823fdb19SAdam Słaboń NULL); // not interested in the full length
104*823fdb19SAdam Słaboń }
105*823fdb19SAdam Słaboń
vio_modern_get_config(VirtIODevice * vdev,unsigned offset,void * buf,unsigned len)106*823fdb19SAdam Słaboń static void vio_modern_get_config(VirtIODevice *vdev, unsigned offset,
107*823fdb19SAdam Słaboń void *buf, unsigned len)
108*823fdb19SAdam Słaboń {
109*823fdb19SAdam Słaboń if (!vdev->config) {
110*823fdb19SAdam Słaboń ASSERT(!"Device has no config to read");
111*823fdb19SAdam Słaboń return;
112*823fdb19SAdam Słaboń }
113*823fdb19SAdam Słaboń if (offset + len > vdev->config_len) {
114*823fdb19SAdam Słaboń ASSERT(!"Can't read beyond the config length");
115*823fdb19SAdam Słaboń return;
116*823fdb19SAdam Słaboń }
117*823fdb19SAdam Słaboń
118*823fdb19SAdam Słaboń switch (len) {
119*823fdb19SAdam Słaboń case 1:
120*823fdb19SAdam Słaboń *(u8 *)buf = ioread8(vdev, vdev->config + offset);
121*823fdb19SAdam Słaboń break;
122*823fdb19SAdam Słaboń case 2:
123*823fdb19SAdam Słaboń *(u16 *)buf = ioread16(vdev, vdev->config + offset);
124*823fdb19SAdam Słaboń break;
125*823fdb19SAdam Słaboń case 4:
126*823fdb19SAdam Słaboń *(u32 *)buf = ioread32(vdev, vdev->config + offset);
127*823fdb19SAdam Słaboń break;
128*823fdb19SAdam Słaboń default:
129*823fdb19SAdam Słaboń ASSERT(!"Only 1, 2, 4 byte config reads are supported");
130*823fdb19SAdam Słaboń }
131*823fdb19SAdam Słaboń }
132*823fdb19SAdam Słaboń
vio_modern_set_config(VirtIODevice * vdev,unsigned offset,const void * buf,unsigned len)133*823fdb19SAdam Słaboń static void vio_modern_set_config(VirtIODevice *vdev, unsigned offset,
134*823fdb19SAdam Słaboń const void *buf, unsigned len)
135*823fdb19SAdam Słaboń {
136*823fdb19SAdam Słaboń if (!vdev->config) {
137*823fdb19SAdam Słaboń ASSERT(!"Device has no config to write");
138*823fdb19SAdam Słaboń return;
139*823fdb19SAdam Słaboń }
140*823fdb19SAdam Słaboń if (offset + len > vdev->config_len) {
141*823fdb19SAdam Słaboń ASSERT(!"Can't write beyond the config length");
142*823fdb19SAdam Słaboń return;
143*823fdb19SAdam Słaboń }
144*823fdb19SAdam Słaboń
145*823fdb19SAdam Słaboń switch (len) {
146*823fdb19SAdam Słaboń case 1:
147*823fdb19SAdam Słaboń iowrite8(vdev, *(u8 *)buf, vdev->config + offset);
148*823fdb19SAdam Słaboń break;
149*823fdb19SAdam Słaboń case 2:
150*823fdb19SAdam Słaboń iowrite16(vdev, *(u16 *)buf, vdev->config + offset);
151*823fdb19SAdam Słaboń break;
152*823fdb19SAdam Słaboń case 4:
153*823fdb19SAdam Słaboń iowrite32(vdev, *(u32 *)buf, vdev->config + offset);
154*823fdb19SAdam Słaboń break;
155*823fdb19SAdam Słaboń default:
156*823fdb19SAdam Słaboń ASSERT(!"Only 1, 2, 4 byte config writes are supported");
157*823fdb19SAdam Słaboń }
158*823fdb19SAdam Słaboń }
159*823fdb19SAdam Słaboń
vio_modern_get_generation(VirtIODevice * vdev)160*823fdb19SAdam Słaboń static u32 vio_modern_get_generation(VirtIODevice *vdev)
161*823fdb19SAdam Słaboń {
162*823fdb19SAdam Słaboń return ioread8(vdev, &vdev->common->config_generation);
163*823fdb19SAdam Słaboń }
164*823fdb19SAdam Słaboń
vio_modern_get_status(VirtIODevice * vdev)165*823fdb19SAdam Słaboń static u8 vio_modern_get_status(VirtIODevice *vdev)
166*823fdb19SAdam Słaboń {
167*823fdb19SAdam Słaboń return ioread8(vdev, &vdev->common->device_status);
168*823fdb19SAdam Słaboń }
169*823fdb19SAdam Słaboń
vio_modern_set_status(VirtIODevice * vdev,u8 status)170*823fdb19SAdam Słaboń static void vio_modern_set_status(VirtIODevice *vdev, u8 status)
171*823fdb19SAdam Słaboń {
172*823fdb19SAdam Słaboń /* We should never be setting status to 0. */
173*823fdb19SAdam Słaboń ASSERT(status != 0);
174*823fdb19SAdam Słaboń iowrite8(vdev, status, &vdev->common->device_status);
175*823fdb19SAdam Słaboń }
176*823fdb19SAdam Słaboń
vio_modern_reset(VirtIODevice * vdev)177*823fdb19SAdam Słaboń static void vio_modern_reset(VirtIODevice *vdev)
178*823fdb19SAdam Słaboń {
179*823fdb19SAdam Słaboń /* 0 status means a reset. */
180*823fdb19SAdam Słaboń iowrite8(vdev, 0, &vdev->common->device_status);
181*823fdb19SAdam Słaboń /* After writing 0 to device_status, the driver MUST wait for a read of
182*823fdb19SAdam Słaboń * device_status to return 0 before reinitializing the device.
183*823fdb19SAdam Słaboń * This will flush out the status write, and flush in device writes,
184*823fdb19SAdam Słaboń * including MSI-X interrupts, if any.
185*823fdb19SAdam Słaboń */
186*823fdb19SAdam Słaboń while (ioread8(vdev, &vdev->common->device_status)) {
187*823fdb19SAdam Słaboń u16 val;
188*823fdb19SAdam Słaboń if (pci_read_config_word(vdev, 0, &val) || val == 0xffff) {
189*823fdb19SAdam Słaboń DPrintf(0, "PCI config space is not readable, probably the device is removed\n", 0);
190*823fdb19SAdam Słaboń break;
191*823fdb19SAdam Słaboń }
192*823fdb19SAdam Słaboń vdev_sleep(vdev, 1);
193*823fdb19SAdam Słaboń }
194*823fdb19SAdam Słaboń }
195*823fdb19SAdam Słaboń
vio_modern_get_features(VirtIODevice * vdev)196*823fdb19SAdam Słaboń static u64 vio_modern_get_features(VirtIODevice *vdev)
197*823fdb19SAdam Słaboń {
198*823fdb19SAdam Słaboń u64 features;
199*823fdb19SAdam Słaboń
200*823fdb19SAdam Słaboń iowrite32(vdev, 0, &vdev->common->device_feature_select);
201*823fdb19SAdam Słaboń features = ioread32(vdev, &vdev->common->device_feature);
202*823fdb19SAdam Słaboń iowrite32(vdev, 1, &vdev->common->device_feature_select);
203*823fdb19SAdam Słaboń features |= ((u64)ioread32(vdev, &vdev->common->device_feature) << 32);
204*823fdb19SAdam Słaboń
205*823fdb19SAdam Słaboń return features;
206*823fdb19SAdam Słaboń }
207*823fdb19SAdam Słaboń
vio_modern_set_features(VirtIODevice * vdev,u64 features)208*823fdb19SAdam Słaboń static NTSTATUS vio_modern_set_features(VirtIODevice *vdev, u64 features)
209*823fdb19SAdam Słaboń {
210*823fdb19SAdam Słaboń /* Give virtio_ring a chance to accept features. */
211*823fdb19SAdam Słaboń vring_transport_features(vdev, &features);
212*823fdb19SAdam Słaboń
213*823fdb19SAdam Słaboń if (!virtio_is_feature_enabled(features, VIRTIO_F_VERSION_1)) {
214*823fdb19SAdam Słaboń DPrintf(0, "virtio: device uses modern interface but does not have VIRTIO_F_VERSION_1\n", 0);
215*823fdb19SAdam Słaboń return STATUS_INVALID_PARAMETER;
216*823fdb19SAdam Słaboń }
217*823fdb19SAdam Słaboń
218*823fdb19SAdam Słaboń iowrite32(vdev, 0, &vdev->common->guest_feature_select);
219*823fdb19SAdam Słaboń iowrite32(vdev, (u32)features, &vdev->common->guest_feature);
220*823fdb19SAdam Słaboń iowrite32(vdev, 1, &vdev->common->guest_feature_select);
221*823fdb19SAdam Słaboń iowrite32(vdev, features >> 32, &vdev->common->guest_feature);
222*823fdb19SAdam Słaboń
223*823fdb19SAdam Słaboń return STATUS_SUCCESS;
224*823fdb19SAdam Słaboń }
225*823fdb19SAdam Słaboń
vio_modern_set_config_vector(VirtIODevice * vdev,u16 vector)226*823fdb19SAdam Słaboń static u16 vio_modern_set_config_vector(VirtIODevice *vdev, u16 vector)
227*823fdb19SAdam Słaboń {
228*823fdb19SAdam Słaboń /* Setup the vector used for configuration events */
229*823fdb19SAdam Słaboń iowrite16(vdev, vector, &vdev->common->msix_config);
230*823fdb19SAdam Słaboń /* Verify we had enough resources to assign the vector */
231*823fdb19SAdam Słaboń /* Will also flush the write out to device */
232*823fdb19SAdam Słaboń return ioread16(vdev, &vdev->common->msix_config);
233*823fdb19SAdam Słaboń }
234*823fdb19SAdam Słaboń
vio_modern_set_queue_vector(struct virtqueue * vq,u16 vector)235*823fdb19SAdam Słaboń static u16 vio_modern_set_queue_vector(struct virtqueue *vq, u16 vector)
236*823fdb19SAdam Słaboń {
237*823fdb19SAdam Słaboń VirtIODevice *vdev = vq->vdev;
238*823fdb19SAdam Słaboń volatile struct virtio_pci_common_cfg *cfg = vdev->common;
239*823fdb19SAdam Słaboń
240*823fdb19SAdam Słaboń iowrite16(vdev, (u16)vq->index, &cfg->queue_select);
241*823fdb19SAdam Słaboń iowrite16(vdev, vector, &cfg->queue_msix_vector);
242*823fdb19SAdam Słaboń return ioread16(vdev, &cfg->queue_msix_vector);
243*823fdb19SAdam Słaboń }
244*823fdb19SAdam Słaboń
vring_pci_size(u16 num,bool packed)245*823fdb19SAdam Słaboń static size_t vring_pci_size(u16 num, bool packed)
246*823fdb19SAdam Słaboń {
247*823fdb19SAdam Słaboń /* We only need a cacheline separation. */
248*823fdb19SAdam Słaboń return (size_t)ROUND_TO_PAGES(vring_size(num, SMP_CACHE_BYTES, packed));
249*823fdb19SAdam Słaboń }
250*823fdb19SAdam Słaboń
vio_modern_query_vq_alloc(VirtIODevice * vdev,unsigned index,unsigned short * pNumEntries,unsigned long * pRingSize,unsigned long * pHeapSize)251*823fdb19SAdam Słaboń static NTSTATUS vio_modern_query_vq_alloc(VirtIODevice *vdev,
252*823fdb19SAdam Słaboń unsigned index,
253*823fdb19SAdam Słaboń unsigned short *pNumEntries,
254*823fdb19SAdam Słaboń unsigned long *pRingSize,
255*823fdb19SAdam Słaboń unsigned long *pHeapSize)
256*823fdb19SAdam Słaboń {
257*823fdb19SAdam Słaboń volatile struct virtio_pci_common_cfg *cfg = vdev->common;
258*823fdb19SAdam Słaboń u16 num;
259*823fdb19SAdam Słaboń
260*823fdb19SAdam Słaboń if (index >= ioread16(vdev, &cfg->num_queues)) {
261*823fdb19SAdam Słaboń return STATUS_NOT_FOUND;
262*823fdb19SAdam Słaboń }
263*823fdb19SAdam Słaboń
264*823fdb19SAdam Słaboń /* Select the queue we're interested in */
265*823fdb19SAdam Słaboń iowrite16(vdev, (u16)index, &cfg->queue_select);
266*823fdb19SAdam Słaboń
267*823fdb19SAdam Słaboń /* Check if queue is either not available or already active. */
268*823fdb19SAdam Słaboń num = ioread16(vdev, &cfg->queue_size);
269*823fdb19SAdam Słaboń /* QEMU has a bug where queues don't revert to inactive on device
270*823fdb19SAdam Słaboń * reset. Skip checking the queue_enable field until it is fixed.
271*823fdb19SAdam Słaboń */
272*823fdb19SAdam Słaboń if (!num /*|| ioread16(vdev, &cfg->queue_enable)*/) {
273*823fdb19SAdam Słaboń return STATUS_NOT_FOUND;
274*823fdb19SAdam Słaboń }
275*823fdb19SAdam Słaboń
276*823fdb19SAdam Słaboń if (num & (num - 1)) {
277*823fdb19SAdam Słaboń DPrintf(0, "%p: bad queue size %u", vdev, num);
278*823fdb19SAdam Słaboń return STATUS_INVALID_PARAMETER;
279*823fdb19SAdam Słaboń }
280*823fdb19SAdam Słaboń
281*823fdb19SAdam Słaboń *pNumEntries = num;
282*823fdb19SAdam Słaboń *pRingSize = (unsigned long)vring_pci_size(num, vdev->packed_ring);
283*823fdb19SAdam Słaboń *pHeapSize = vring_control_block_size(num, vdev->packed_ring);
284*823fdb19SAdam Słaboń
285*823fdb19SAdam Słaboń return STATUS_SUCCESS;
286*823fdb19SAdam Słaboń }
287*823fdb19SAdam Słaboń
vio_modern_setup_vq(struct virtqueue ** queue,VirtIODevice * vdev,VirtIOQueueInfo * info,unsigned index,u16 msix_vec)288*823fdb19SAdam Słaboń static NTSTATUS vio_modern_setup_vq(struct virtqueue **queue,
289*823fdb19SAdam Słaboń VirtIODevice *vdev,
290*823fdb19SAdam Słaboń VirtIOQueueInfo *info,
291*823fdb19SAdam Słaboń unsigned index,
292*823fdb19SAdam Słaboń u16 msix_vec)
293*823fdb19SAdam Słaboń {
294*823fdb19SAdam Słaboń volatile struct virtio_pci_common_cfg *cfg = vdev->common;
295*823fdb19SAdam Słaboń struct virtqueue *vq;
296*823fdb19SAdam Słaboń void *vq_addr;
297*823fdb19SAdam Słaboń u16 off;
298*823fdb19SAdam Słaboń unsigned long ring_size, heap_size;
299*823fdb19SAdam Słaboń NTSTATUS status;
300*823fdb19SAdam Słaboń
301*823fdb19SAdam Słaboń /* select the queue and query allocation parameters */
302*823fdb19SAdam Słaboń status = vio_modern_query_vq_alloc(vdev, index, &info->num, &ring_size, &heap_size);
303*823fdb19SAdam Słaboń if (!NT_SUCCESS(status)) {
304*823fdb19SAdam Słaboń return status;
305*823fdb19SAdam Słaboń }
306*823fdb19SAdam Słaboń
307*823fdb19SAdam Słaboń /* get offset of notification word for this vq */
308*823fdb19SAdam Słaboń off = ioread16(vdev, &cfg->queue_notify_off);
309*823fdb19SAdam Słaboń
310*823fdb19SAdam Słaboń /* try to allocate contiguous pages, scale down on failure */
311*823fdb19SAdam Słaboń while (!(info->queue = mem_alloc_contiguous_pages(vdev, vring_pci_size(info->num, vdev->packed_ring)))) {
312*823fdb19SAdam Słaboń if (info->num > 0) {
313*823fdb19SAdam Słaboń info->num /= 2;
314*823fdb19SAdam Słaboń } else {
315*823fdb19SAdam Słaboń return STATUS_INSUFFICIENT_RESOURCES;
316*823fdb19SAdam Słaboń }
317*823fdb19SAdam Słaboń }
318*823fdb19SAdam Słaboń
319*823fdb19SAdam Słaboń vq_addr = mem_alloc_nonpaged_block(vdev, heap_size);
320*823fdb19SAdam Słaboń if (vq_addr == NULL) {
321*823fdb19SAdam Słaboń return STATUS_INSUFFICIENT_RESOURCES;
322*823fdb19SAdam Słaboń }
323*823fdb19SAdam Słaboń
324*823fdb19SAdam Słaboń /* create the vring */
325*823fdb19SAdam Słaboń if (vdev->packed_ring) {
326*823fdb19SAdam Słaboń vq = vring_new_virtqueue_packed(index, info->num,
327*823fdb19SAdam Słaboń SMP_CACHE_BYTES, vdev,
328*823fdb19SAdam Słaboń info->queue, vp_notify, vq_addr);
329*823fdb19SAdam Słaboń } else {
330*823fdb19SAdam Słaboń vq = vring_new_virtqueue_split(index, info->num,
331*823fdb19SAdam Słaboń SMP_CACHE_BYTES, vdev,
332*823fdb19SAdam Słaboń info->queue, vp_notify, vq_addr);
333*823fdb19SAdam Słaboń }
334*823fdb19SAdam Słaboń
335*823fdb19SAdam Słaboń if (!vq) {
336*823fdb19SAdam Słaboń status = STATUS_INSUFFICIENT_RESOURCES;
337*823fdb19SAdam Słaboń goto err_new_queue;
338*823fdb19SAdam Słaboń }
339*823fdb19SAdam Słaboń
340*823fdb19SAdam Słaboń /* activate the queue */
341*823fdb19SAdam Słaboń iowrite16(vdev, info->num, &cfg->queue_size);
342*823fdb19SAdam Słaboń iowrite64_twopart(vdev, mem_get_physical_address(vdev, info->queue),
343*823fdb19SAdam Słaboń &cfg->queue_desc_lo, &cfg->queue_desc_hi);
344*823fdb19SAdam Słaboń iowrite64_twopart(vdev, mem_get_physical_address(vdev, vq->avail_va),
345*823fdb19SAdam Słaboń &cfg->queue_avail_lo, &cfg->queue_avail_hi);
346*823fdb19SAdam Słaboń iowrite64_twopart(vdev, mem_get_physical_address(vdev, vq->used_va),
347*823fdb19SAdam Słaboń &cfg->queue_used_lo, &cfg->queue_used_hi);
348*823fdb19SAdam Słaboń
349*823fdb19SAdam Słaboń if (vdev->notify_base) {
350*823fdb19SAdam Słaboń /* offset should not wrap */
351*823fdb19SAdam Słaboń if ((u64)off * vdev->notify_offset_multiplier + 2
352*823fdb19SAdam Słaboń > vdev->notify_len) {
353*823fdb19SAdam Słaboń DPrintf(0,
354*823fdb19SAdam Słaboń "%p: bad notification offset %u (x %u) "
355*823fdb19SAdam Słaboń "for queue %u > %zd",
356*823fdb19SAdam Słaboń vdev,
357*823fdb19SAdam Słaboń off, vdev->notify_offset_multiplier,
358*823fdb19SAdam Słaboń index, vdev->notify_len);
359*823fdb19SAdam Słaboń status = STATUS_INVALID_PARAMETER;
360*823fdb19SAdam Słaboń goto err_map_notify;
361*823fdb19SAdam Słaboń }
362*823fdb19SAdam Słaboń vq->notification_addr = (void *)(vdev->notify_base +
363*823fdb19SAdam Słaboń off * vdev->notify_offset_multiplier);
364*823fdb19SAdam Słaboń } else {
365*823fdb19SAdam Słaboń vq->notification_addr = vio_modern_map_capability(vdev,
366*823fdb19SAdam Słaboń vdev->notify_map_cap, 2, 2,
367*823fdb19SAdam Słaboń off * vdev->notify_offset_multiplier, 2,
368*823fdb19SAdam Słaboń NULL);
369*823fdb19SAdam Słaboń }
370*823fdb19SAdam Słaboń
371*823fdb19SAdam Słaboń if (!vq->notification_addr) {
372*823fdb19SAdam Słaboń status = STATUS_INSUFFICIENT_RESOURCES;
373*823fdb19SAdam Słaboń goto err_map_notify;
374*823fdb19SAdam Słaboń }
375*823fdb19SAdam Słaboń
376*823fdb19SAdam Słaboń if (msix_vec != VIRTIO_MSI_NO_VECTOR) {
377*823fdb19SAdam Słaboń msix_vec = vdev->device->set_queue_vector(vq, msix_vec);
378*823fdb19SAdam Słaboń if (msix_vec == VIRTIO_MSI_NO_VECTOR) {
379*823fdb19SAdam Słaboń status = STATUS_DEVICE_BUSY;
380*823fdb19SAdam Słaboń goto err_assign_vector;
381*823fdb19SAdam Słaboń }
382*823fdb19SAdam Słaboń }
383*823fdb19SAdam Słaboń
384*823fdb19SAdam Słaboń /* enable the queue */
385*823fdb19SAdam Słaboń iowrite16(vdev, 1, &vdev->common->queue_enable);
386*823fdb19SAdam Słaboń
387*823fdb19SAdam Słaboń *queue = vq;
388*823fdb19SAdam Słaboń return STATUS_SUCCESS;
389*823fdb19SAdam Słaboń
390*823fdb19SAdam Słaboń err_assign_vector:
391*823fdb19SAdam Słaboń err_map_notify:
392*823fdb19SAdam Słaboń virtqueue_shutdown(vq);
393*823fdb19SAdam Słaboń err_new_queue:
394*823fdb19SAdam Słaboń mem_free_nonpaged_block(vdev, vq_addr);
395*823fdb19SAdam Słaboń mem_free_contiguous_pages(vdev, info->queue);
396*823fdb19SAdam Słaboń return status;
397*823fdb19SAdam Słaboń }
398*823fdb19SAdam Słaboń
vio_modern_del_vq(VirtIOQueueInfo * info)399*823fdb19SAdam Słaboń static void vio_modern_del_vq(VirtIOQueueInfo *info)
400*823fdb19SAdam Słaboń {
401*823fdb19SAdam Słaboń struct virtqueue *vq = info->vq;
402*823fdb19SAdam Słaboń VirtIODevice *vdev = vq->vdev;
403*823fdb19SAdam Słaboń
404*823fdb19SAdam Słaboń iowrite16(vdev, (u16)vq->index, &vdev->common->queue_select);
405*823fdb19SAdam Słaboń
406*823fdb19SAdam Słaboń if (vdev->msix_used) {
407*823fdb19SAdam Słaboń iowrite16(vdev, VIRTIO_MSI_NO_VECTOR, &vdev->common->queue_msix_vector);
408*823fdb19SAdam Słaboń /* Flush the write out to device */
409*823fdb19SAdam Słaboń ioread16(vdev, &vdev->common->queue_msix_vector);
410*823fdb19SAdam Słaboń }
411*823fdb19SAdam Słaboń
412*823fdb19SAdam Słaboń virtqueue_shutdown(vq);
413*823fdb19SAdam Słaboń
414*823fdb19SAdam Słaboń mem_free_nonpaged_block(vdev, vq);
415*823fdb19SAdam Słaboń mem_free_contiguous_pages(vdev, info->queue);
416*823fdb19SAdam Słaboń }
417*823fdb19SAdam Słaboń
418*823fdb19SAdam Słaboń static const struct virtio_device_ops virtio_pci_device_ops = {
419*823fdb19SAdam Słaboń /* .get_config = */ vio_modern_get_config,
420*823fdb19SAdam Słaboń /* .set_config = */ vio_modern_set_config,
421*823fdb19SAdam Słaboń /* .get_config_generation = */ vio_modern_get_generation,
422*823fdb19SAdam Słaboń /* .get_status = */ vio_modern_get_status,
423*823fdb19SAdam Słaboń /* .set_status = */ vio_modern_set_status,
424*823fdb19SAdam Słaboń /* .reset = */ vio_modern_reset,
425*823fdb19SAdam Słaboń /* .get_features = */ vio_modern_get_features,
426*823fdb19SAdam Słaboń /* .set_features = */ vio_modern_set_features,
427*823fdb19SAdam Słaboń /* .set_config_vector = */ vio_modern_set_config_vector,
428*823fdb19SAdam Słaboń /* .set_queue_vector = */ vio_modern_set_queue_vector,
429*823fdb19SAdam Słaboń /* .query_queue_alloc = */ vio_modern_query_vq_alloc,
430*823fdb19SAdam Słaboń /* .setup_queue = */ vio_modern_setup_vq,
431*823fdb19SAdam Słaboń /* .delete_queue = */ vio_modern_del_vq,
432*823fdb19SAdam Słaboń };
433*823fdb19SAdam Słaboń
find_next_pci_vendor_capability(VirtIODevice * vdev,u8 offset)434*823fdb19SAdam Słaboń static u8 find_next_pci_vendor_capability(VirtIODevice *vdev, u8 offset)
435*823fdb19SAdam Słaboń {
436*823fdb19SAdam Słaboń u8 id = 0;
437*823fdb19SAdam Słaboń int iterations = 48;
438*823fdb19SAdam Słaboń
439*823fdb19SAdam Słaboń if (pci_read_config_byte(vdev, offset, &offset) != 0) {
440*823fdb19SAdam Słaboń return 0;
441*823fdb19SAdam Słaboń }
442*823fdb19SAdam Słaboń
443*823fdb19SAdam Słaboń while (iterations-- && offset >= 0x40) {
444*823fdb19SAdam Słaboń offset &= ~3;
445*823fdb19SAdam Słaboń if (pci_read_config_byte(vdev, offset + offsetof(PCI_CAPABILITIES_HEADER,
446*823fdb19SAdam Słaboń CapabilityID), &id) != 0) {
447*823fdb19SAdam Słaboń break;
448*823fdb19SAdam Słaboń }
449*823fdb19SAdam Słaboń if (id == 0xFF) {
450*823fdb19SAdam Słaboń break;
451*823fdb19SAdam Słaboń }
452*823fdb19SAdam Słaboń if (id == PCI_CAPABILITY_ID_VENDOR_SPECIFIC) {
453*823fdb19SAdam Słaboń return offset;
454*823fdb19SAdam Słaboń }
455*823fdb19SAdam Słaboń if (pci_read_config_byte(vdev, offset + offsetof(PCI_CAPABILITIES_HEADER,
456*823fdb19SAdam Słaboń Next), &offset) != 0) {
457*823fdb19SAdam Słaboń break;
458*823fdb19SAdam Słaboń }
459*823fdb19SAdam Słaboń }
460*823fdb19SAdam Słaboń return 0;
461*823fdb19SAdam Słaboń }
462*823fdb19SAdam Słaboń
find_first_pci_vendor_capability(VirtIODevice * vdev)463*823fdb19SAdam Słaboń static u8 find_first_pci_vendor_capability(VirtIODevice *vdev)
464*823fdb19SAdam Słaboń {
465*823fdb19SAdam Słaboń u8 hdr_type, offset;
466*823fdb19SAdam Słaboń u16 status;
467*823fdb19SAdam Słaboń
468*823fdb19SAdam Słaboń if (pci_read_config_byte(vdev, offsetof(PCI_COMMON_HEADER, HeaderType), &hdr_type) != 0) {
469*823fdb19SAdam Słaboń return 0;
470*823fdb19SAdam Słaboń }
471*823fdb19SAdam Słaboń if (pci_read_config_word(vdev, offsetof(PCI_COMMON_HEADER, Status), &status) != 0) {
472*823fdb19SAdam Słaboń return 0;
473*823fdb19SAdam Słaboń }
474*823fdb19SAdam Słaboń if ((status & PCI_STATUS_CAPABILITIES_LIST) == 0) {
475*823fdb19SAdam Słaboń return 0;
476*823fdb19SAdam Słaboń }
477*823fdb19SAdam Słaboń
478*823fdb19SAdam Słaboń switch (hdr_type & ~PCI_MULTIFUNCTION) {
479*823fdb19SAdam Słaboń case PCI_BRIDGE_TYPE:
480*823fdb19SAdam Słaboń offset = offsetof(PCI_COMMON_HEADER, u.type1.CapabilitiesPtr);
481*823fdb19SAdam Słaboń break;
482*823fdb19SAdam Słaboń case PCI_CARDBUS_BRIDGE_TYPE:
483*823fdb19SAdam Słaboń offset = offsetof(PCI_COMMON_HEADER, u.type2.CapabilitiesPtr);
484*823fdb19SAdam Słaboń break;
485*823fdb19SAdam Słaboń default:
486*823fdb19SAdam Słaboń offset = offsetof(PCI_COMMON_HEADER, u.type0.CapabilitiesPtr);
487*823fdb19SAdam Słaboń break;
488*823fdb19SAdam Słaboń }
489*823fdb19SAdam Słaboń
490*823fdb19SAdam Słaboń if (offset != 0) {
491*823fdb19SAdam Słaboń offset = find_next_pci_vendor_capability(vdev, offset);
492*823fdb19SAdam Słaboń }
493*823fdb19SAdam Słaboń return offset;
494*823fdb19SAdam Słaboń }
495*823fdb19SAdam Słaboń
496*823fdb19SAdam Słaboń /* Populate Offsets with virtio vendor capability offsets within the PCI config space */
find_pci_vendor_capabilities(VirtIODevice * vdev,int * Offsets,size_t nOffsets)497*823fdb19SAdam Słaboń static void find_pci_vendor_capabilities(VirtIODevice *vdev, int *Offsets, size_t nOffsets)
498*823fdb19SAdam Słaboń {
499*823fdb19SAdam Słaboń u8 offset = find_first_pci_vendor_capability(vdev);
500*823fdb19SAdam Słaboń while (offset > 0) {
501*823fdb19SAdam Słaboń u8 cfg_type, bar;
502*823fdb19SAdam Słaboń pci_read_config_byte(vdev, offset + offsetof(struct virtio_pci_cap, cfg_type), &cfg_type);
503*823fdb19SAdam Słaboń pci_read_config_byte(vdev, offset + offsetof(struct virtio_pci_cap, bar), &bar);
504*823fdb19SAdam Słaboń
505*823fdb19SAdam Słaboń if (bar < PCI_TYPE0_ADDRESSES &&
506*823fdb19SAdam Słaboń cfg_type < nOffsets &&
507*823fdb19SAdam Słaboń pci_get_resource_len(vdev, bar) > 0) {
508*823fdb19SAdam Słaboń Offsets[cfg_type] = offset;
509*823fdb19SAdam Słaboń }
510*823fdb19SAdam Słaboń
511*823fdb19SAdam Słaboń offset = find_next_pci_vendor_capability(vdev, offset + offsetof(PCI_CAPABILITIES_HEADER, Next));
512*823fdb19SAdam Słaboń }
513*823fdb19SAdam Słaboń }
514*823fdb19SAdam Słaboń
515*823fdb19SAdam Słaboń /* Modern device initialization */
vio_modern_initialize(VirtIODevice * vdev)516*823fdb19SAdam Słaboń NTSTATUS vio_modern_initialize(VirtIODevice *vdev)
517*823fdb19SAdam Słaboń {
518*823fdb19SAdam Słaboń int capabilities[VIRTIO_PCI_CAP_PCI_CFG];
519*823fdb19SAdam Słaboń
520*823fdb19SAdam Słaboń u32 notify_length;
521*823fdb19SAdam Słaboń u32 notify_offset;
522*823fdb19SAdam Słaboń
523*823fdb19SAdam Słaboń RtlZeroMemory(capabilities, sizeof(capabilities));
524*823fdb19SAdam Słaboń find_pci_vendor_capabilities(vdev, capabilities, VIRTIO_PCI_CAP_PCI_CFG);
525*823fdb19SAdam Słaboń
526*823fdb19SAdam Słaboń /* Check for a common config, if not found use legacy mode */
527*823fdb19SAdam Słaboń if (!capabilities[VIRTIO_PCI_CAP_COMMON_CFG]) {
528*823fdb19SAdam Słaboń DPrintf(0, "%s(%p): device not found\n", __FUNCTION__, vdev);
529*823fdb19SAdam Słaboń return STATUS_DEVICE_NOT_CONNECTED;
530*823fdb19SAdam Słaboń }
531*823fdb19SAdam Słaboń
532*823fdb19SAdam Słaboń /* Check isr and notify caps, if not found fail */
533*823fdb19SAdam Słaboń if (!capabilities[VIRTIO_PCI_CAP_ISR_CFG] || !capabilities[VIRTIO_PCI_CAP_NOTIFY_CFG]) {
534*823fdb19SAdam Słaboń DPrintf(0, "%s(%p): missing capabilities %i/%i/%i\n",
535*823fdb19SAdam Słaboń __FUNCTION__, vdev,
536*823fdb19SAdam Słaboń capabilities[VIRTIO_PCI_CAP_COMMON_CFG],
537*823fdb19SAdam Słaboń capabilities[VIRTIO_PCI_CAP_ISR_CFG],
538*823fdb19SAdam Słaboń capabilities[VIRTIO_PCI_CAP_NOTIFY_CFG]);
539*823fdb19SAdam Słaboń return STATUS_INVALID_PARAMETER;
540*823fdb19SAdam Słaboń }
541*823fdb19SAdam Słaboń
542*823fdb19SAdam Słaboń /* Map bars according to the capabilities */
543*823fdb19SAdam Słaboń vdev->common = vio_modern_map_simple_capability(vdev,
544*823fdb19SAdam Słaboń capabilities[VIRTIO_PCI_CAP_COMMON_CFG],
545*823fdb19SAdam Słaboń sizeof(struct virtio_pci_common_cfg), 4);
546*823fdb19SAdam Słaboń if (!vdev->common) {
547*823fdb19SAdam Słaboń return STATUS_INVALID_PARAMETER;
548*823fdb19SAdam Słaboń }
549*823fdb19SAdam Słaboń
550*823fdb19SAdam Słaboń vdev->isr = vio_modern_map_simple_capability(vdev,
551*823fdb19SAdam Słaboń capabilities[VIRTIO_PCI_CAP_ISR_CFG],
552*823fdb19SAdam Słaboń sizeof(u8), 1);
553*823fdb19SAdam Słaboń if (!vdev->isr) {
554*823fdb19SAdam Słaboń return STATUS_INVALID_PARAMETER;
555*823fdb19SAdam Słaboń }
556*823fdb19SAdam Słaboń
557*823fdb19SAdam Słaboń /* Read notify_off_multiplier from config space. */
558*823fdb19SAdam Słaboń pci_read_config_dword(vdev,
559*823fdb19SAdam Słaboń capabilities[VIRTIO_PCI_CAP_NOTIFY_CFG] + offsetof(struct virtio_pci_notify_cap,
560*823fdb19SAdam Słaboń notify_off_multiplier),
561*823fdb19SAdam Słaboń &vdev->notify_offset_multiplier);
562*823fdb19SAdam Słaboń
563*823fdb19SAdam Słaboń /* Read notify length and offset from config space. */
564*823fdb19SAdam Słaboń pci_read_config_dword(vdev,
565*823fdb19SAdam Słaboń capabilities[VIRTIO_PCI_CAP_NOTIFY_CFG] + offsetof(struct virtio_pci_notify_cap,
566*823fdb19SAdam Słaboń cap.length),
567*823fdb19SAdam Słaboń ¬ify_length);
568*823fdb19SAdam Słaboń pci_read_config_dword(vdev,
569*823fdb19SAdam Słaboń capabilities[VIRTIO_PCI_CAP_NOTIFY_CFG] + offsetof(struct virtio_pci_notify_cap,
570*823fdb19SAdam Słaboń cap.offset),
571*823fdb19SAdam Słaboń ¬ify_offset);
572*823fdb19SAdam Słaboń
573*823fdb19SAdam Słaboń /* Map the notify capability if it's small enough.
574*823fdb19SAdam Słaboń * Otherwise, map each VQ individually later.
575*823fdb19SAdam Słaboń */
576*823fdb19SAdam Słaboń if (notify_length + (notify_offset % PAGE_SIZE) <= PAGE_SIZE) {
577*823fdb19SAdam Słaboń vdev->notify_base = vio_modern_map_capability(vdev,
578*823fdb19SAdam Słaboń capabilities[VIRTIO_PCI_CAP_NOTIFY_CFG], 2, 2,
579*823fdb19SAdam Słaboń 0, notify_length,
580*823fdb19SAdam Słaboń &vdev->notify_len);
581*823fdb19SAdam Słaboń if (!vdev->notify_base) {
582*823fdb19SAdam Słaboń return STATUS_INVALID_PARAMETER;
583*823fdb19SAdam Słaboń }
584*823fdb19SAdam Słaboń } else {
585*823fdb19SAdam Słaboń vdev->notify_map_cap = capabilities[VIRTIO_PCI_CAP_NOTIFY_CFG];
586*823fdb19SAdam Słaboń }
587*823fdb19SAdam Słaboń
588*823fdb19SAdam Słaboń /* Map the device config capability, the PAGE_SIZE size is a guess */
589*823fdb19SAdam Słaboń if (capabilities[VIRTIO_PCI_CAP_DEVICE_CFG]) {
590*823fdb19SAdam Słaboń vdev->config = vio_modern_map_capability(vdev,
591*823fdb19SAdam Słaboń capabilities[VIRTIO_PCI_CAP_DEVICE_CFG], 0, 4,
592*823fdb19SAdam Słaboń 0, PAGE_SIZE,
593*823fdb19SAdam Słaboń &vdev->config_len);
594*823fdb19SAdam Słaboń if (!vdev->config) {
595*823fdb19SAdam Słaboń return STATUS_INVALID_PARAMETER;
596*823fdb19SAdam Słaboń }
597*823fdb19SAdam Słaboń }
598*823fdb19SAdam Słaboń
599*823fdb19SAdam Słaboń vdev->device = &virtio_pci_device_ops;
600*823fdb19SAdam Słaboń
601*823fdb19SAdam Słaboń return STATUS_SUCCESS;
602*823fdb19SAdam Słaboń }
603