[NETKVM] Import NetKVM network adapter driver by Red Hat
[reactos.git] / drivers / network / dd / netkvm / Common / ParaNdis-VirtIO.c
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 };