1*823fdb19SAdam Słaboń /*
2*823fdb19SAdam Słaboń * Virtio PCI driver - legacy (virtio 0.9) device support
3*823fdb19SAdam Słaboń *
4*823fdb19SAdam Słaboń * Copyright IBM Corp. 2007
5*823fdb19SAdam Słaboń *
6*823fdb19SAdam Słaboń * Authors:
7*823fdb19SAdam Słaboń * Anthony Liguori <aliguori@us.ibm.com>
8*823fdb19SAdam Słaboń * Windows porting - Yan Vugenfirer <yvugenfi@redhat.com>
9*823fdb19SAdam Słaboń *
10*823fdb19SAdam Słaboń * Redistribution and use in source and binary forms, with or without
11*823fdb19SAdam Słaboń * modification, are permitted provided that the following conditions
12*823fdb19SAdam Słaboń * are met :
13*823fdb19SAdam Słaboń * 1. Redistributions of source code must retain the above copyright
14*823fdb19SAdam Słaboń * notice, this list of conditions and the following disclaimer.
15*823fdb19SAdam Słaboń * 2. Redistributions in binary form must reproduce the above copyright
16*823fdb19SAdam Słaboń * notice, this list of conditions and the following disclaimer in the
17*823fdb19SAdam Słaboń * documentation and / or other materials provided with the distribution.
18*823fdb19SAdam Słaboń * 3. Neither the names of the copyright holders nor the names of their contributors
19*823fdb19SAdam Słaboń * may be used to endorse or promote products derived from this software
20*823fdb19SAdam Słaboń * without specific prior written permission.
21*823fdb19SAdam Słaboń * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND
22*823fdb19SAdam Słaboń * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23*823fdb19SAdam Słaboń * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24*823fdb19SAdam Słaboń * ARE DISCLAIMED.IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE
25*823fdb19SAdam Słaboń * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26*823fdb19SAdam Słaboń * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27*823fdb19SAdam Słaboń * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28*823fdb19SAdam Słaboń * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29*823fdb19SAdam Słaboń * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30*823fdb19SAdam Słaboń * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31*823fdb19SAdam Słaboń * SUCH DAMAGE.
32*823fdb19SAdam Słaboń */
33*823fdb19SAdam Słaboń #include "osdep.h"
34*823fdb19SAdam Słaboń #include "virtio_pci.h"
35*823fdb19SAdam Słaboń #include "VirtIO.h"
36*823fdb19SAdam Słaboń #include "kdebugprint.h"
37*823fdb19SAdam Słaboń #include "virtio_ring.h"
38*823fdb19SAdam Słaboń #include "virtio_pci_common.h"
39*823fdb19SAdam Słaboń #include "windows/virtio_ring_allocation.h"
40*823fdb19SAdam Słaboń
41*823fdb19SAdam Słaboń #ifdef WPP_EVENT_TRACING
42*823fdb19SAdam Słaboń #include "VirtIOPCILegacy.tmh"
43*823fdb19SAdam Słaboń #endif
44*823fdb19SAdam Słaboń
45*823fdb19SAdam Słaboń /////////////////////////////////////////////////////////////////////////////////////
46*823fdb19SAdam Słaboń //
47*823fdb19SAdam Słaboń // vio_legacy_dump_registers - Dump HW registers of the device
48*823fdb19SAdam Słaboń //
49*823fdb19SAdam Słaboń /////////////////////////////////////////////////////////////////////////////////////
vio_legacy_dump_registers(VirtIODevice * vdev)50*823fdb19SAdam Słaboń void vio_legacy_dump_registers(VirtIODevice *vdev)
51*823fdb19SAdam Słaboń {
52*823fdb19SAdam Słaboń DPrintf(5, "%s\n", __FUNCTION__);
53*823fdb19SAdam Słaboń
54*823fdb19SAdam Słaboń DPrintf(0, "[VIRTIO_PCI_HOST_FEATURES] = %x\n", ioread32(vdev, vdev->addr + VIRTIO_PCI_HOST_FEATURES));
55*823fdb19SAdam Słaboń DPrintf(0, "[VIRTIO_PCI_GUEST_FEATURES] = %x\n", ioread32(vdev, vdev->addr + VIRTIO_PCI_GUEST_FEATURES));
56*823fdb19SAdam Słaboń DPrintf(0, "[VIRTIO_PCI_QUEUE_PFN] = %x\n", ioread32(vdev, vdev->addr + VIRTIO_PCI_QUEUE_PFN));
57*823fdb19SAdam Słaboń DPrintf(0, "[VIRTIO_PCI_QUEUE_NUM] = %x\n", ioread32(vdev, vdev->addr + VIRTIO_PCI_QUEUE_NUM));
58*823fdb19SAdam Słaboń DPrintf(0, "[VIRTIO_PCI_QUEUE_SEL] = %x\n", ioread32(vdev, vdev->addr + VIRTIO_PCI_QUEUE_SEL));
59*823fdb19SAdam Słaboń DPrintf(0, "[VIRTIO_PCI_QUEUE_NOTIFY] = %x\n", ioread32(vdev, vdev->addr + VIRTIO_PCI_QUEUE_NOTIFY));
60*823fdb19SAdam Słaboń DPrintf(0, "[VIRTIO_PCI_STATUS] = %x\n", ioread32(vdev, vdev->addr + VIRTIO_PCI_STATUS));
61*823fdb19SAdam Słaboń DPrintf(0, "[VIRTIO_PCI_ISR] = %x\n", ioread32(vdev, vdev->addr + VIRTIO_PCI_ISR));
62*823fdb19SAdam Słaboń }
63*823fdb19SAdam Słaboń
vio_legacy_get_config(VirtIODevice * vdev,unsigned offset,void * buf,unsigned len)64*823fdb19SAdam Słaboń static void vio_legacy_get_config(VirtIODevice * vdev,
65*823fdb19SAdam Słaboń unsigned offset,
66*823fdb19SAdam Słaboń void *buf,
67*823fdb19SAdam Słaboń unsigned len)
68*823fdb19SAdam Słaboń {
69*823fdb19SAdam Słaboń ULONG_PTR ioaddr = vdev->addr + VIRTIO_PCI_CONFIG(vdev->msix_used) + offset;
70*823fdb19SAdam Słaboń u8 *ptr = buf;
71*823fdb19SAdam Słaboń unsigned i;
72*823fdb19SAdam Słaboń
73*823fdb19SAdam Słaboń DPrintf(5, "%s\n", __FUNCTION__);
74*823fdb19SAdam Słaboń
75*823fdb19SAdam Słaboń for (i = 0; i < len; i++) {
76*823fdb19SAdam Słaboń ptr[i] = ioread8(vdev, ioaddr + i);
77*823fdb19SAdam Słaboń }
78*823fdb19SAdam Słaboń }
79*823fdb19SAdam Słaboń
vio_legacy_set_config(VirtIODevice * vdev,unsigned offset,const void * buf,unsigned len)80*823fdb19SAdam Słaboń static void vio_legacy_set_config(VirtIODevice *vdev,
81*823fdb19SAdam Słaboń unsigned offset,
82*823fdb19SAdam Słaboń const void *buf,
83*823fdb19SAdam Słaboń unsigned len)
84*823fdb19SAdam Słaboń {
85*823fdb19SAdam Słaboń ULONG_PTR ioaddr = vdev->addr + VIRTIO_PCI_CONFIG(vdev->msix_used) + offset;
86*823fdb19SAdam Słaboń const u8 *ptr = buf;
87*823fdb19SAdam Słaboń unsigned i;
88*823fdb19SAdam Słaboń
89*823fdb19SAdam Słaboń DPrintf(5, "%s\n", __FUNCTION__);
90*823fdb19SAdam Słaboń
91*823fdb19SAdam Słaboń for (i = 0; i < len; i++) {
92*823fdb19SAdam Słaboń iowrite8(vdev, ptr[i], ioaddr + i);
93*823fdb19SAdam Słaboń }
94*823fdb19SAdam Słaboń }
95*823fdb19SAdam Słaboń
vio_legacy_get_status(VirtIODevice * vdev)96*823fdb19SAdam Słaboń static u8 vio_legacy_get_status(VirtIODevice *vdev)
97*823fdb19SAdam Słaboń {
98*823fdb19SAdam Słaboń DPrintf(6, "%s\n", __FUNCTION__);
99*823fdb19SAdam Słaboń return ioread8(vdev, vdev->addr + VIRTIO_PCI_STATUS);
100*823fdb19SAdam Słaboń }
101*823fdb19SAdam Słaboń
vio_legacy_set_status(VirtIODevice * vdev,u8 status)102*823fdb19SAdam Słaboń static void vio_legacy_set_status(VirtIODevice *vdev, u8 status)
103*823fdb19SAdam Słaboń {
104*823fdb19SAdam Słaboń DPrintf(6, "%s>>> %x\n", __FUNCTION__, status);
105*823fdb19SAdam Słaboń iowrite8(vdev, status, vdev->addr + VIRTIO_PCI_STATUS);
106*823fdb19SAdam Słaboń }
107*823fdb19SAdam Słaboń
vio_legacy_reset(VirtIODevice * vdev)108*823fdb19SAdam Słaboń static void vio_legacy_reset(VirtIODevice *vdev)
109*823fdb19SAdam Słaboń {
110*823fdb19SAdam Słaboń /* 0 status means a reset. */
111*823fdb19SAdam Słaboń iowrite8(vdev, 0, vdev->addr + VIRTIO_PCI_STATUS);
112*823fdb19SAdam Słaboń }
113*823fdb19SAdam Słaboń
vio_legacy_get_features(VirtIODevice * vdev)114*823fdb19SAdam Słaboń static u64 vio_legacy_get_features(VirtIODevice *vdev)
115*823fdb19SAdam Słaboń {
116*823fdb19SAdam Słaboń return ioread32(vdev, vdev->addr + VIRTIO_PCI_HOST_FEATURES);
117*823fdb19SAdam Słaboń }
118*823fdb19SAdam Słaboń
vio_legacy_set_features(VirtIODevice * vdev,u64 features)119*823fdb19SAdam Słaboń static NTSTATUS vio_legacy_set_features(VirtIODevice *vdev, u64 features)
120*823fdb19SAdam Słaboń {
121*823fdb19SAdam Słaboń /* Give virtio_ring a chance to accept features. */
122*823fdb19SAdam Słaboń vring_transport_features(vdev, &features);
123*823fdb19SAdam Słaboń
124*823fdb19SAdam Słaboń /* Make sure we don't have any features > 32 bits! */
125*823fdb19SAdam Słaboń ASSERT((u32)features == features);
126*823fdb19SAdam Słaboń iowrite32(vdev, (u32)features, vdev->addr + VIRTIO_PCI_GUEST_FEATURES);
127*823fdb19SAdam Słaboń
128*823fdb19SAdam Słaboń return STATUS_SUCCESS;
129*823fdb19SAdam Słaboń }
130*823fdb19SAdam Słaboń
vio_legacy_set_config_vector(VirtIODevice * vdev,u16 vector)131*823fdb19SAdam Słaboń static u16 vio_legacy_set_config_vector(VirtIODevice *vdev, u16 vector)
132*823fdb19SAdam Słaboń {
133*823fdb19SAdam Słaboń /* Setup the vector used for configuration events */
134*823fdb19SAdam Słaboń iowrite16(vdev, vector, vdev->addr + VIRTIO_MSI_CONFIG_VECTOR);
135*823fdb19SAdam Słaboń /* Verify we had enough resources to assign the vector */
136*823fdb19SAdam Słaboń /* Will also flush the write out to device */
137*823fdb19SAdam Słaboń return ioread16(vdev, vdev->addr + VIRTIO_MSI_CONFIG_VECTOR);
138*823fdb19SAdam Słaboń }
139*823fdb19SAdam Słaboń
vio_legacy_set_queue_vector(struct virtqueue * vq,u16 vector)140*823fdb19SAdam Słaboń static u16 vio_legacy_set_queue_vector(struct virtqueue *vq, u16 vector)
141*823fdb19SAdam Słaboń {
142*823fdb19SAdam Słaboń VirtIODevice *vdev = vq->vdev;
143*823fdb19SAdam Słaboń
144*823fdb19SAdam Słaboń iowrite16(vdev, (u16)vq->index, vdev->addr + VIRTIO_PCI_QUEUE_SEL);
145*823fdb19SAdam Słaboń iowrite16(vdev, vector, vdev->addr + VIRTIO_MSI_QUEUE_VECTOR);
146*823fdb19SAdam Słaboń return ioread16(vdev, vdev->addr + VIRTIO_MSI_QUEUE_VECTOR);
147*823fdb19SAdam Słaboń }
148*823fdb19SAdam Słaboń
vio_legacy_query_vq_alloc(VirtIODevice * vdev,unsigned index,unsigned short * pNumEntries,unsigned long * pRingSize,unsigned long * pHeapSize)149*823fdb19SAdam Słaboń static NTSTATUS vio_legacy_query_vq_alloc(VirtIODevice *vdev,
150*823fdb19SAdam Słaboń unsigned index,
151*823fdb19SAdam Słaboń unsigned short *pNumEntries,
152*823fdb19SAdam Słaboń unsigned long *pRingSize,
153*823fdb19SAdam Słaboń unsigned long *pHeapSize)
154*823fdb19SAdam Słaboń {
155*823fdb19SAdam Słaboń unsigned long ring_size, data_size;
156*823fdb19SAdam Słaboń u16 num;
157*823fdb19SAdam Słaboń
158*823fdb19SAdam Słaboń /* Select the queue we're interested in */
159*823fdb19SAdam Słaboń iowrite16(vdev, (u16)index, vdev->addr + VIRTIO_PCI_QUEUE_SEL);
160*823fdb19SAdam Słaboń
161*823fdb19SAdam Słaboń /* Check if queue is either not available or already active. */
162*823fdb19SAdam Słaboń num = ioread16(vdev, vdev->addr + VIRTIO_PCI_QUEUE_NUM);
163*823fdb19SAdam Słaboń if (!num || ioread32(vdev, vdev->addr + VIRTIO_PCI_QUEUE_PFN)) {
164*823fdb19SAdam Słaboń return STATUS_NOT_FOUND;
165*823fdb19SAdam Słaboń }
166*823fdb19SAdam Słaboń
167*823fdb19SAdam Słaboń ring_size = ROUND_TO_PAGES(vring_size(num, VIRTIO_PCI_VRING_ALIGN, false));
168*823fdb19SAdam Słaboń data_size = ROUND_TO_PAGES(vring_control_block_size(num, false));
169*823fdb19SAdam Słaboń
170*823fdb19SAdam Słaboń *pNumEntries = num;
171*823fdb19SAdam Słaboń *pRingSize = ring_size + data_size;
172*823fdb19SAdam Słaboń *pHeapSize = 0;
173*823fdb19SAdam Słaboń
174*823fdb19SAdam Słaboń return STATUS_SUCCESS;
175*823fdb19SAdam Słaboń }
176*823fdb19SAdam Słaboń
vio_legacy_setup_vq(struct virtqueue ** queue,VirtIODevice * vdev,VirtIOQueueInfo * info,unsigned index,u16 msix_vec)177*823fdb19SAdam Słaboń static NTSTATUS vio_legacy_setup_vq(struct virtqueue **queue,
178*823fdb19SAdam Słaboń VirtIODevice *vdev,
179*823fdb19SAdam Słaboń VirtIOQueueInfo *info,
180*823fdb19SAdam Słaboń unsigned index,
181*823fdb19SAdam Słaboń u16 msix_vec)
182*823fdb19SAdam Słaboń {
183*823fdb19SAdam Słaboń struct virtqueue *vq;
184*823fdb19SAdam Słaboń unsigned long ring_size, heap_size;
185*823fdb19SAdam Słaboń NTSTATUS status;
186*823fdb19SAdam Słaboń
187*823fdb19SAdam Słaboń /* Select the queue and query allocation parameters */
188*823fdb19SAdam Słaboń status = vio_legacy_query_vq_alloc(vdev, index, &info->num, &ring_size, &heap_size);
189*823fdb19SAdam Słaboń if (!NT_SUCCESS(status)) {
190*823fdb19SAdam Słaboń return status;
191*823fdb19SAdam Słaboń }
192*823fdb19SAdam Słaboń
193*823fdb19SAdam Słaboń info->queue = mem_alloc_contiguous_pages(vdev, ring_size);
194*823fdb19SAdam Słaboń if (info->queue == NULL) {
195*823fdb19SAdam Słaboń return STATUS_INSUFFICIENT_RESOURCES;
196*823fdb19SAdam Słaboń }
197*823fdb19SAdam Słaboń
198*823fdb19SAdam Słaboń /* activate the queue */
199*823fdb19SAdam Słaboń iowrite32(vdev, (u32)(mem_get_physical_address(vdev, info->queue) >> VIRTIO_PCI_QUEUE_ADDR_SHIFT),
200*823fdb19SAdam Słaboń vdev->addr + VIRTIO_PCI_QUEUE_PFN);
201*823fdb19SAdam Słaboń
202*823fdb19SAdam Słaboń /* create the vring */
203*823fdb19SAdam Słaboń vq = vring_new_virtqueue_split(index, info->num,
204*823fdb19SAdam Słaboń VIRTIO_PCI_VRING_ALIGN, vdev,
205*823fdb19SAdam Słaboń info->queue, vp_notify,
206*823fdb19SAdam Słaboń (u8 *)info->queue + ROUND_TO_PAGES(vring_size(info->num, VIRTIO_PCI_VRING_ALIGN, false)));
207*823fdb19SAdam Słaboń if (!vq) {
208*823fdb19SAdam Słaboń status = STATUS_INSUFFICIENT_RESOURCES;
209*823fdb19SAdam Słaboń goto err_activate_queue;
210*823fdb19SAdam Słaboń }
211*823fdb19SAdam Słaboń
212*823fdb19SAdam Słaboń vq->notification_addr = (void *)(vdev->addr + VIRTIO_PCI_QUEUE_NOTIFY);
213*823fdb19SAdam Słaboń
214*823fdb19SAdam Słaboń if (msix_vec != VIRTIO_MSI_NO_VECTOR) {
215*823fdb19SAdam Słaboń msix_vec = vdev->device->set_queue_vector(vq, msix_vec);
216*823fdb19SAdam Słaboń if (msix_vec == VIRTIO_MSI_NO_VECTOR) {
217*823fdb19SAdam Słaboń status = STATUS_DEVICE_BUSY;
218*823fdb19SAdam Słaboń goto err_assign;
219*823fdb19SAdam Słaboń }
220*823fdb19SAdam Słaboń }
221*823fdb19SAdam Słaboń
222*823fdb19SAdam Słaboń *queue = vq;
223*823fdb19SAdam Słaboń return STATUS_SUCCESS;
224*823fdb19SAdam Słaboń
225*823fdb19SAdam Słaboń err_assign:
226*823fdb19SAdam Słaboń err_activate_queue:
227*823fdb19SAdam Słaboń iowrite32(vdev, 0, vdev->addr + VIRTIO_PCI_QUEUE_PFN);
228*823fdb19SAdam Słaboń mem_free_contiguous_pages(vdev, info->queue);
229*823fdb19SAdam Słaboń return status;
230*823fdb19SAdam Słaboń }
231*823fdb19SAdam Słaboń
vio_legacy_del_vq(VirtIOQueueInfo * info)232*823fdb19SAdam Słaboń static void vio_legacy_del_vq(VirtIOQueueInfo *info)
233*823fdb19SAdam Słaboń {
234*823fdb19SAdam Słaboń struct virtqueue *vq = info->vq;
235*823fdb19SAdam Słaboń VirtIODevice *vdev = vq->vdev;
236*823fdb19SAdam Słaboń
237*823fdb19SAdam Słaboń iowrite16(vdev, (u16)vq->index, vdev->addr + VIRTIO_PCI_QUEUE_SEL);
238*823fdb19SAdam Słaboń
239*823fdb19SAdam Słaboń if (vdev->msix_used) {
240*823fdb19SAdam Słaboń iowrite16(vdev, VIRTIO_MSI_NO_VECTOR,
241*823fdb19SAdam Słaboń vdev->addr + VIRTIO_MSI_QUEUE_VECTOR);
242*823fdb19SAdam Słaboń /* Flush the write out to device */
243*823fdb19SAdam Słaboń ioread8(vdev, vdev->addr + VIRTIO_PCI_ISR);
244*823fdb19SAdam Słaboń }
245*823fdb19SAdam Słaboń
246*823fdb19SAdam Słaboń /* Select and deactivate the queue */
247*823fdb19SAdam Słaboń iowrite32(vdev, 0, vdev->addr + VIRTIO_PCI_QUEUE_PFN);
248*823fdb19SAdam Słaboń
249*823fdb19SAdam Słaboń mem_free_contiguous_pages(vdev, info->queue);
250*823fdb19SAdam Słaboń }
251*823fdb19SAdam Słaboń
252*823fdb19SAdam Słaboń static const struct virtio_device_ops virtio_pci_device_ops = {
253*823fdb19SAdam Słaboń /* .get_config = */ vio_legacy_get_config,
254*823fdb19SAdam Słaboń /* .set_config = */ vio_legacy_set_config,
255*823fdb19SAdam Słaboń /* .get_config_generation = */ NULL,
256*823fdb19SAdam Słaboń /* .get_status = */ vio_legacy_get_status,
257*823fdb19SAdam Słaboń /* .set_status = */ vio_legacy_set_status,
258*823fdb19SAdam Słaboń /* .reset = */ vio_legacy_reset,
259*823fdb19SAdam Słaboń /* .get_features = */ vio_legacy_get_features,
260*823fdb19SAdam Słaboń /* .set_features = */ vio_legacy_set_features,
261*823fdb19SAdam Słaboń /* .set_config_vector = */ vio_legacy_set_config_vector,
262*823fdb19SAdam Słaboń /* .set_queue_vector = */ vio_legacy_set_queue_vector,
263*823fdb19SAdam Słaboń /* .query_queue_alloc = */ vio_legacy_query_vq_alloc,
264*823fdb19SAdam Słaboń /* .setup_queue = */ vio_legacy_setup_vq,
265*823fdb19SAdam Słaboń /* .delete_queue = */ vio_legacy_del_vq,
266*823fdb19SAdam Słaboń };
267*823fdb19SAdam Słaboń
268*823fdb19SAdam Słaboń /* Legacy device initialization */
vio_legacy_initialize(VirtIODevice * vdev)269*823fdb19SAdam Słaboń NTSTATUS vio_legacy_initialize(VirtIODevice *vdev)
270*823fdb19SAdam Słaboń {
271*823fdb19SAdam Słaboń size_t length = pci_get_resource_len(vdev, 0);
272*823fdb19SAdam Słaboń vdev->addr = (ULONG_PTR)pci_map_address_range(vdev, 0, 0, length);
273*823fdb19SAdam Słaboń
274*823fdb19SAdam Słaboń if (!vdev->addr) {
275*823fdb19SAdam Słaboń return STATUS_INSUFFICIENT_RESOURCES;
276*823fdb19SAdam Słaboń }
277*823fdb19SAdam Słaboń
278*823fdb19SAdam Słaboń vdev->isr = (u8 *)vdev->addr + VIRTIO_PCI_ISR;
279*823fdb19SAdam Słaboń
280*823fdb19SAdam Słaboń vdev->device = &virtio_pci_device_ops;
281*823fdb19SAdam Słaboń
282*823fdb19SAdam Słaboń return STATUS_SUCCESS;
283*823fdb19SAdam Słaboń }
284