1 /* 2 * This file contains NDIS driver VirtIO callbacks 3 * 4 * Copyright (c) 2008-2017 Red Hat, Inc. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met : 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. 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 * 3. Neither the names of the copyright holders nor the names of their contributors 15 * may be used to endorse or promote products derived from this software 16 * without specific prior written permission. 17 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND 18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 * ARE DISCLAIMED.IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE 21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27 * SUCH DAMAGE. 28 */ 29 #include "ndis56common.h" 30 31 ///////////////////////////////////////////////////////////////////////////////////// 32 // 33 // ReadVirtIODeviceRegister\WriteVirtIODeviceRegister 34 // NDIS specific implementation of the IO and memory space read\write 35 // 36 // The lower 64k of memory is never mapped so we can use the same routines 37 // for both port I/O and memory access and use the address alone to decide 38 // which space to use. 39 ///////////////////////////////////////////////////////////////////////////////////// 40 41 #define PORT_MASK 0xFFFF 42 43 static u32 ReadVirtIODeviceRegister(ULONG_PTR ulRegister) 44 { 45 ULONG ulValue; 46 47 if (ulRegister & ~PORT_MASK) { 48 NdisReadRegisterUlong(ulRegister, &ulValue); 49 } else { 50 NdisRawReadPortUlong(ulRegister, &ulValue); 51 } 52 53 DPrintf(6, ("[%s]R[%x]=%x", __FUNCTION__, (ULONG)ulRegister, ulValue)); 54 return ulValue; 55 } 56 57 static void WriteVirtIODeviceRegister(ULONG_PTR ulRegister, u32 ulValue) 58 { 59 DPrintf(6, ("[%s]R[%x]=%x", __FUNCTION__, (ULONG)ulRegister, ulValue)); 60 61 if (ulRegister & ~PORT_MASK) { 62 NdisWriteRegisterUlong((PULONG)ulRegister, ulValue); 63 } else { 64 NdisRawWritePortUlong(ulRegister, ulValue); 65 } 66 } 67 68 static u8 ReadVirtIODeviceByte(ULONG_PTR ulRegister) 69 { 70 u8 bValue; 71 72 if (ulRegister & ~PORT_MASK) { 73 NdisReadRegisterUchar(ulRegister, &bValue); 74 } else { 75 NdisRawReadPortUchar(ulRegister, &bValue); 76 } 77 78 DPrintf(6, ("[%s]R[%x]=%x", __FUNCTION__, (ULONG)ulRegister, bValue)); 79 return bValue; 80 } 81 82 static void WriteVirtIODeviceByte(ULONG_PTR ulRegister, u8 bValue) 83 { 84 DPrintf(6, ("[%s]R[%x]=%x", __FUNCTION__, (ULONG)ulRegister, bValue)); 85 86 if (ulRegister & ~PORT_MASK) { 87 NdisWriteRegisterUchar((PUCHAR)ulRegister, bValue); 88 } else { 89 NdisRawWritePortUchar(ulRegister, bValue); 90 } 91 } 92 93 static u16 ReadVirtIODeviceWord(ULONG_PTR ulRegister) 94 { 95 u16 wValue; 96 97 if (ulRegister & ~PORT_MASK) { 98 NdisReadRegisterUshort(ulRegister, &wValue); 99 } else { 100 NdisRawReadPortUshort(ulRegister, &wValue); 101 } 102 103 DPrintf(6, ("[%s]R[%x]=%x\n", __FUNCTION__, (ULONG)ulRegister, wValue)); 104 return wValue; 105 } 106 107 static void WriteVirtIODeviceWord(ULONG_PTR ulRegister, u16 wValue) 108 { 109 #if 1 110 if (ulRegister & ~PORT_MASK) { 111 NdisWriteRegisterUshort((PUSHORT)ulRegister, wValue); 112 } else { 113 NdisRawWritePortUshort(ulRegister, wValue); 114 } 115 #else 116 // test only to cause long TX waiting queue of NDIS packets 117 // to recognize it and request for reset via Hang handler 118 static int nCounterToFail = 0; 119 static const int StartFail = 200, StopFail = 600; 120 BOOLEAN bFail = FALSE; 121 DPrintf(6, ("%s> R[%x] = %x\n", __FUNCTION__, (ULONG)ulRegister, wValue)); 122 if ((ulRegister & 0x1F) == 0x10) 123 { 124 nCounterToFail++; 125 bFail = nCounterToFail >= StartFail && nCounterToFail < StopFail; 126 } 127 if (!bFail) NdisRawWritePortUshort(ulRegister, wValue); 128 else 129 { 130 DPrintf(0, ("%s> FAILING R[%x] = %x\n", __FUNCTION__, (ULONG)ulRegister, wValue)); 131 } 132 #endif 133 } 134 135 static void *mem_alloc_contiguous_pages(void *context, size_t size) 136 { 137 PARANDIS_ADAPTER *pContext = (PARANDIS_ADAPTER *)context; 138 PVOID retVal = NULL; 139 ULONG i; 140 141 /* find the first unused memory range of the requested size */ 142 for (i = 0; i < MAX_NUM_OF_QUEUES; i++) { 143 if (pContext->SharedMemoryRanges[i].pBase != NULL && 144 pContext->SharedMemoryRanges[i].bUsed == FALSE && 145 pContext->SharedMemoryRanges[i].uLength == (ULONG)size) { 146 retVal = pContext->SharedMemoryRanges[i].pBase; 147 pContext->SharedMemoryRanges[i].bUsed = TRUE; 148 break; 149 } 150 } 151 152 if (!retVal) { 153 /* find the first null memory range descriptor and allocate */ 154 for (i = 0; i < MAX_NUM_OF_QUEUES; i++) { 155 if (pContext->SharedMemoryRanges[i].pBase == NULL) { 156 break; 157 } 158 } 159 if (i < MAX_NUM_OF_QUEUES) { 160 NdisMAllocateSharedMemory( 161 pContext->MiniportHandle, 162 (ULONG)size, 163 TRUE /* Cached */, 164 &pContext->SharedMemoryRanges[i].pBase, 165 &pContext->SharedMemoryRanges[i].BasePA); 166 retVal = pContext->SharedMemoryRanges[i].pBase; 167 if (retVal) { 168 NdisZeroMemory(retVal, size); 169 pContext->SharedMemoryRanges[i].uLength = (ULONG)size; 170 pContext->SharedMemoryRanges[i].bUsed = TRUE; 171 } 172 } 173 } 174 175 if (retVal) { 176 DPrintf(6, ("[%s] returning %p, size %x\n", __FUNCTION__, retVal, (ULONG)size)); 177 } else { 178 DPrintf(0, ("[%s] failed to allocate size %x\n", __FUNCTION__, (ULONG)size)); 179 } 180 return retVal; 181 } 182 183 static void mem_free_contiguous_pages(void *context, void *virt) 184 { 185 PARANDIS_ADAPTER *pContext = (PARANDIS_ADAPTER *)context; 186 ULONG i; 187 188 for (i = 0; i < MAX_NUM_OF_QUEUES; i++) { 189 if (pContext->SharedMemoryRanges[i].pBase == virt) { 190 pContext->SharedMemoryRanges[i].bUsed = FALSE; 191 break; 192 } 193 } 194 195 if (i < MAX_NUM_OF_QUEUES) { 196 DPrintf(6, ("[%s] freed %p at index %d\n", __FUNCTION__, virt, i)); 197 } else { 198 DPrintf(0, ("[%s] failed to free %p\n", __FUNCTION__, virt)); 199 } 200 } 201 202 static ULONGLONG mem_get_physical_address(void *context, void *virt) 203 { 204 PARANDIS_ADAPTER *pContext = (PARANDIS_ADAPTER *)context; 205 ULONG_PTR uAddr = (ULONG_PTR)virt; 206 ULONG i; 207 208 for (i = 0; i < MAX_NUM_OF_QUEUES; i++) { 209 ULONG_PTR uBase = (ULONG_PTR)pContext->SharedMemoryRanges[i].pBase; 210 if (uAddr >= uBase && uAddr < (uBase + pContext->SharedMemoryRanges[i].uLength)) { 211 ULONGLONG retVal = pContext->SharedMemoryRanges[i].BasePA.QuadPart + (uAddr - uBase); 212 213 DPrintf(6, ("[%s] translated %p to %I64X\n", __FUNCTION__, virt, retVal)); 214 return retVal; 215 } 216 } 217 218 DPrintf(0, ("[%s] failed to translate %p\n", __FUNCTION__, virt)); 219 return 0; 220 } 221 222 static void *mem_alloc_nonpaged_block(void *context, size_t size) 223 { 224 PVOID retVal; 225 226 if (NdisAllocateMemoryWithTag( 227 &retVal, 228 (UINT)size, 229 PARANDIS_MEMORY_TAG) != NDIS_STATUS_SUCCESS) { 230 retVal = NULL; 231 } 232 233 if (retVal) { 234 NdisZeroMemory(retVal, size); 235 DPrintf(6, ("[%s] returning %p, len %x\n", __FUNCTION__, retVal, (ULONG)size)); 236 } else { 237 DPrintf(0, ("[%s] failed to allocate size %x\n", __FUNCTION__, (ULONG)size)); 238 } 239 return retVal; 240 } 241 242 static void mem_free_nonpaged_block(void *context, void *addr) 243 { 244 UNREFERENCED_PARAMETER(context); 245 246 NdisFreeMemory(addr, 0, 0); 247 DPrintf(6, ("[%s] freed %p\n", __FUNCTION__, addr)); 248 } 249 250 static int PCIReadConfig(PPARANDIS_ADAPTER pContext, 251 int where, 252 void *buffer, 253 size_t length) 254 { 255 ULONG read; 256 257 read = NdisReadPciSlotInformation( 258 pContext->MiniportHandle, 259 0 /* SlotNumber */, 260 where, 261 buffer, 262 (ULONG)length); 263 264 if (read == length) { 265 DPrintf(6, ("[%s] read %d bytes at %d\n", __FUNCTION__, read, where)); 266 return 0; 267 } else { 268 DPrintf(0, ("[%s] failed to read %d bytes at %d\n", __FUNCTION__, read, where)); 269 return -1; 270 } 271 } 272 273 static int pci_read_config_byte(void *context, int where, u8 *bVal) 274 { 275 return PCIReadConfig((PPARANDIS_ADAPTER)context, where, bVal, sizeof(*bVal)); 276 } 277 278 static int pci_read_config_word(void *context, int where, u16 *wVal) 279 { 280 return PCIReadConfig((PPARANDIS_ADAPTER)context, where, wVal, sizeof(*wVal)); 281 } 282 283 static int pci_read_config_dword(void *context, int where, u32 *dwVal) 284 { 285 return PCIReadConfig((PPARANDIS_ADAPTER)context, where, dwVal, sizeof(*dwVal)); 286 } 287 288 static size_t pci_get_resource_len(void *context, int bar) 289 { 290 PARANDIS_ADAPTER *pContext = (PARANDIS_ADAPTER *)context; 291 292 if (bar < PCI_TYPE0_ADDRESSES) { 293 return pContext->AdapterResources.PciBars[bar].uLength; 294 } 295 296 DPrintf(0, ("[%s] queried invalid BAR %d\n", __FUNCTION__, bar)); 297 return 0; 298 } 299 300 static void *pci_map_address_range(void *context, int bar, size_t offset, size_t maxlen) 301 { 302 PARANDIS_ADAPTER *pContext = (PARANDIS_ADAPTER *)context; 303 304 if (bar < PCI_TYPE0_ADDRESSES) { 305 tBusResource *pRes = &pContext->AdapterResources.PciBars[bar]; 306 if (pRes->pBase == NULL) { 307 /* BAR not mapped yet */ 308 if (pRes->bPortSpace) { 309 if (NDIS_STATUS_SUCCESS == NdisMRegisterIoPortRange( 310 &pRes->pBase, 311 pContext->MiniportHandle, 312 pRes->BasePA.LowPart, 313 pRes->uLength)) { 314 DPrintf(6, ("[%s] mapped port BAR at %x\n", __FUNCTION__, pRes->BasePA.LowPart)); 315 } else { 316 pRes->pBase = NULL; 317 DPrintf(0, ("[%s] failed to map port BAR at %x\n", __FUNCTION__, pRes->BasePA.LowPart)); 318 } 319 } else { 320 if (NDIS_STATUS_SUCCESS == NdisMMapIoSpace( 321 &pRes->pBase, 322 pContext->MiniportHandle, 323 pRes->BasePA, 324 pRes->uLength)) { 325 DPrintf(6, ("[%s] mapped memory BAR at %I64x\n", __FUNCTION__, pRes->BasePA.QuadPart)); 326 } else { 327 pRes->pBase = NULL; 328 DPrintf(0, ("[%s] failed to map memory BAR at %I64x\n", __FUNCTION__, pRes->BasePA.QuadPart)); 329 } 330 } 331 } 332 if (pRes->pBase != NULL && offset < pRes->uLength) { 333 if (pRes->bPortSpace) { 334 /* use physical address for port I/O */ 335 return (PUCHAR)(ULONG_PTR)pRes->BasePA.LowPart + offset; 336 } else { 337 /* use virtual address for memory I/O */ 338 return (PUCHAR)pRes->pBase + offset; 339 } 340 } else { 341 DPrintf(0, ("[%s] failed to get map BAR %d, offset %x\n", __FUNCTION__, bar, offset)); 342 } 343 } else { 344 DPrintf(0, ("[%s] queried invalid BAR %d\n", __FUNCTION__, bar)); 345 } 346 347 return NULL; 348 } 349 350 static u16 vdev_get_msix_vector(void *context, int queue) 351 { 352 PARANDIS_ADAPTER *pContext = (PARANDIS_ADAPTER *)context; 353 u16 vector = VIRTIO_MSI_NO_VECTOR; 354 355 /* we don't run on MSI support so this will never be true */ 356 if (pContext->bUsingMSIX && queue >= 0) { 357 vector = (u16)pContext->AdapterResources.Vector; 358 } 359 360 return vector; 361 } 362 363 static void vdev_sleep(void *context, unsigned int msecs) 364 { 365 UNREFERENCED_PARAMETER(context); 366 367 NdisMSleep(1000 * msecs); 368 } 369 370 VirtIOSystemOps ParaNdisSystemOps = { 371 /* .vdev_read_byte = */ ReadVirtIODeviceByte, 372 /* .vdev_read_word = */ ReadVirtIODeviceWord, 373 /* .vdev_read_dword = */ ReadVirtIODeviceRegister, 374 /* .vdev_write_byte = */ WriteVirtIODeviceByte, 375 /* .vdev_write_word = */ WriteVirtIODeviceWord, 376 /* .vdev_write_dword = */ WriteVirtIODeviceRegister, 377 /* .mem_alloc_contiguous_pages = */ mem_alloc_contiguous_pages, 378 /* .mem_free_contiguous_pages = */ mem_free_contiguous_pages, 379 /* .mem_get_physical_address = */ mem_get_physical_address, 380 /* .mem_alloc_nonpaged_block = */ mem_alloc_nonpaged_block, 381 /* .mem_free_nonpaged_block = */ mem_free_nonpaged_block, 382 /* .pci_read_config_byte = */ pci_read_config_byte, 383 /* .pci_read_config_word = */ pci_read_config_word, 384 /* .pci_read_config_dword = */ pci_read_config_dword, 385 /* .pci_get_resource_len = */ pci_get_resource_len, 386 /* .pci_map_address_range = */ pci_map_address_range, 387 /* .vdev_get_msix_vector = */ vdev_get_msix_vector, 388 /*.vdev_sleep = */ vdev_sleep, 389 }; 390