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