6dc9943cf60721727c9d6f2593d4d7d35db26862
[mirror_edk2.git] / MdeModulePkg / Universal / Network / SnpDxe / Callback.c
1 /** @file\r
2   This file contains two sets of callback routines for undi3.0 and undi3.1.\r
3   the callback routines for Undi3.1 have an extra parameter UniqueId which\r
4   stores the interface context for the NIC that snp is trying to talk.\r
5 \r
6 Copyright (c) 2006, Intel Corporation\r
7 All rights reserved. This program and the accompanying materials\r
8 are licensed and made available under the terms and conditions of the BSD License\r
9 which accompanies this distribution.  The full text of the license may be found at\r
10 http://opensource.org/licenses/bsd-license.php\r
11 \r
12 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
13 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
14 \r
15 **/\r
16 \r
17 #include "Snp.h"\r
18 \r
19 //\r
20 // Global variables\r
21 // these 2 global variables are used only for 3.0 undi. we could not place\r
22 // them in the snp structure because we will not know which snp structure\r
23 // in the callback context!\r
24 //\r
25 BOOLEAN              mInitializeLock = TRUE;\r
26 EFI_LOCK             mLock;\r
27 \r
28 //\r
29 // End Global variables\r
30 //\r
31 extern EFI_PCI_IO_PROTOCOL  *mPciIoFncs;\r
32 \r
33 VOID\r
34 snp_undi32_callback_v2p_30 (\r
35   IN UINT64     CpuAddr,\r
36   IN OUT UINT64 DeviceAddrPtr\r
37   )\r
38 /*++\r
39 \r
40 Routine Description:\r
41  This is a callback routine supplied to UNDI at undi_start time.\r
42  UNDI call this routine with a virtual or CPU address that SNP provided\r
43  to convert it to a physical or device address. Since EFI uses the identical\r
44  mapping, this routine returns the physical address same as the virtual address\r
45  for most of the addresses. an address above 4GB cannot generally be used as a\r
46  device address, it needs to be mapped to a lower physical address. This routine\r
47  does not call the map routine itself, but it assumes that the mapping was done\r
48  at the time of providing the address to UNDI. This routine just looks up the\r
49  address in a map table (which is the v2p structure chain)\r
50 \r
51 Arguments:\r
52  CpuAddr - virtual address of a buffer\r
53  DeviceAddrPtr - pointer to the physical address\r
54 \r
55 Returns:\r
56  void - The DeviceAddrPtr will contain 0 in case of any error\r
57 \r
58 --*/\r
59 {\r
60   struct s_v2p  *v2p;\r
61   //\r
62   // Do nothing if virtual address is zero or physical pointer is NULL.\r
63   // No need to map if the virtual address is within 4GB limit since\r
64   // EFI uses identical mapping\r
65   //\r
66   if ((CpuAddr == 0) || (DeviceAddrPtr == 0)) {\r
67     DEBUG ((EFI_D_ERROR, "\nv2p: Null virtual address or physical pointer.\n"));\r
68     return ;\r
69   }\r
70 \r
71   if (CpuAddr < FOUR_GIGABYTES) {\r
72     *(UINT64 *) (UINTN) DeviceAddrPtr = CpuAddr;\r
73     return ;\r
74   }\r
75   //\r
76   // SNP creates a vaddr tp paddr mapping at the time of calling undi with any\r
77   // big address, this callback routine just looks up in the v2p list and\r
78   // returns the physical address for any given virtual address.\r
79   //\r
80   if (find_v2p (&v2p, (VOID *) (UINTN) CpuAddr) != EFI_SUCCESS) {\r
81     *(UINT64 *) (UINTN) DeviceAddrPtr = CpuAddr;\r
82   } else {\r
83     *(UINT64 *) (UINTN) DeviceAddrPtr = v2p->paddr;\r
84   }\r
85 }\r
86 \r
87 VOID\r
88 snp_undi32_callback_block_30 (\r
89   IN UINT32 Enable\r
90   )\r
91 /*++\r
92 \r
93 Routine Description:\r
94  This is a callback routine supplied to UNDI at undi_start time.\r
95  UNDI call this routine when it wants to have exclusive access to a critical\r
96  section of the code/data\r
97 \r
98 Arguments:\r
99  Enable - non-zero indicates acquire\r
100           zero indicates release\r
101 \r
102 Returns:\r
103  void\r
104 --*/\r
105 {\r
106   //\r
107   // tcpip was calling snp at tpl_notify and if we acquire a lock that was\r
108   // created at a lower level (TPL_CALLBACK) it gives an assert!\r
109   //\r
110   if (mInitializeLock) {\r
111     EfiInitializeLock (&mLock, TPL_NOTIFY);\r
112     mInitializeLock = FALSE;\r
113   }\r
114 \r
115   if (Enable != 0) {\r
116     EfiAcquireLock (&mLock);\r
117   } else {\r
118     EfiReleaseLock (&mLock);\r
119   }\r
120 }\r
121 \r
122 VOID\r
123 snp_undi32_callback_delay_30 (\r
124   IN UINT64 MicroSeconds\r
125   )\r
126 /*++\r
127 \r
128 Routine Description:\r
129  This is a callback routine supplied to UNDI at undi_start time.\r
130  UNDI call this routine with the number of micro seconds when it wants to\r
131  pause.\r
132 \r
133 Arguments:\r
134  MicroSeconds - number of micro seconds to pause, ususlly multiple of 10\r
135 \r
136 Returns:\r
137  void\r
138 --*/\r
139 {\r
140   if (MicroSeconds != 0) {\r
141     gBS->Stall ((UINTN) MicroSeconds);\r
142   }\r
143 }\r
144 \r
145 VOID\r
146 snp_undi32_callback_memio_30 (\r
147   IN UINT8      ReadOrWrite,\r
148   IN UINT8      NumBytes,\r
149   IN UINT64     Address,\r
150   IN OUT UINT64 BufferAddr\r
151   )\r
152 /*++\r
153 \r
154 Routine Description:\r
155  This is a callback routine supplied to UNDI at undi_start time.\r
156  This is the IO routine for UNDI. This is not currently being used by UNDI3.0\r
157  because Undi3.0 uses io/mem offsets relative to the beginning of the device\r
158  io/mem address and so it needs to use the PCI_IO_FUNCTION that abstracts the\r
159  start of the device's io/mem addresses. Since SNP cannot retrive the context\r
160  of the undi3.0 interface it cannot use the PCI_IO_FUNCTION that specific for\r
161  that NIC and uses one global IO functions structure, this does not work.\r
162  This however works fine for EFI1.0 Undis because they use absolute addresses\r
163  for io/mem access.\r
164 \r
165 Arguments:\r
166   ReadOrWrite - indicates read or write, IO or Memory\r
167   NumBytes    - number of bytes to read or write\r
168   Address     - IO or memory address to read from or write to\r
169   BufferAddr  - memory location to read into or that contains the bytes\r
170                 to write\r
171 \r
172 Returns:\r
173 \r
174 --*/\r
175 {\r
176   EFI_PCI_IO_PROTOCOL_WIDTH Width;\r
177 \r
178   switch (NumBytes) {\r
179   case 2:\r
180     Width = (EFI_PCI_IO_PROTOCOL_WIDTH) 1;\r
181     break;\r
182 \r
183   case 4:\r
184     Width = (EFI_PCI_IO_PROTOCOL_WIDTH) 2;\r
185     break;\r
186 \r
187   case 8:\r
188     Width = (EFI_PCI_IO_PROTOCOL_WIDTH) 3;\r
189     break;\r
190 \r
191   default:\r
192     Width = (EFI_PCI_IO_PROTOCOL_WIDTH) 0;\r
193   }\r
194 \r
195   switch (ReadOrWrite) {\r
196   case PXE_IO_READ:\r
197     mPciIoFncs->Io.Read (\r
198                     mPciIoFncs,\r
199                     Width,\r
200                     1,    // BAR 1, IO base address\r
201                     Address,\r
202                     1,    // count\r
203                     (VOID *) (UINTN) BufferAddr\r
204                     );\r
205     break;\r
206 \r
207   case PXE_IO_WRITE:\r
208     mPciIoFncs->Io.Write (\r
209                     mPciIoFncs,\r
210                     Width,\r
211                     1,    // BAR 1, IO base address\r
212                     Address,\r
213                     1,    // count\r
214                     (VOID *) (UINTN) BufferAddr\r
215                     );\r
216     break;\r
217 \r
218   case PXE_MEM_READ:\r
219     mPciIoFncs->Mem.Read (\r
220                       mPciIoFncs,\r
221                       Width,\r
222                       0,  // BAR 0, Memory base address\r
223                       Address,\r
224                       1,  // count\r
225                       (VOID *) (UINTN) BufferAddr\r
226                       );\r
227     break;\r
228 \r
229   case PXE_MEM_WRITE:\r
230     mPciIoFncs->Mem.Write (\r
231                       mPciIoFncs,\r
232                       Width,\r
233                       0,  // BAR 0, Memory base address\r
234                       Address,\r
235                       1,  // count\r
236                       (VOID *) (UINTN) BufferAddr\r
237                       );\r
238     break;\r
239   }\r
240 \r
241   return ;\r
242 }\r
243 //\r
244 // New callbacks for 3.1:\r
245 // there won't be a virtual2physical callback for UNDI 3.1 because undi3.1 uses\r
246 // the MemMap call to map the required address by itself!\r
247 //\r
248 VOID\r
249 snp_undi32_callback_block (\r
250   IN UINT64 UniqueId,\r
251   IN UINT32 Enable\r
252   )\r
253 /*++\r
254 \r
255 Routine Description:\r
256  This is a callback routine supplied to UNDI3.1 at undi_start time.\r
257  UNDI call this routine when it wants to have exclusive access to a critical\r
258  section of the code/data\r
259 \r
260 Arguments:\r
261  UniqueId - This was supplied to UNDI at Undi_Start, SNP uses this to store\r
262             Undi interface context (Undi does not read or write this variable)\r
263  Enable   - non-zero indicates acquire\r
264             zero indicates release\r
265 \r
266 Returns:\r
267  void\r
268 \r
269 --*/\r
270 {\r
271   SNP_DRIVER  *snp;\r
272 \r
273   snp = (SNP_DRIVER *) (UINTN) UniqueId;\r
274   //\r
275   // tcpip was calling snp at tpl_notify and when we acquire a lock that was\r
276   // created at a lower level (TPL_CALLBACK) it gives an assert!\r
277   //\r
278   if (Enable != 0) {\r
279     EfiAcquireLock (&snp->lock);\r
280   } else {\r
281     EfiReleaseLock (&snp->lock);\r
282   }\r
283 }\r
284 \r
285 VOID\r
286 snp_undi32_callback_delay (\r
287   IN UINT64 UniqueId,\r
288   IN UINT64 MicroSeconds\r
289   )\r
290 /*++\r
291 \r
292 Routine Description:\r
293  This is a callback routine supplied to UNDI at undi_start time.\r
294  UNDI call this routine with the number of micro seconds when it wants to\r
295  pause.\r
296 \r
297 Arguments:\r
298  MicroSeconds - number of micro seconds to pause, ususlly multiple of 10\r
299 \r
300 Returns:\r
301  void\r
302 --*/\r
303 {\r
304   if (MicroSeconds != 0) {\r
305     gBS->Stall ((UINTN) MicroSeconds);\r
306   }\r
307 }\r
308 \r
309 /*\r
310  *  IO routine for UNDI start CPB.\r
311  */\r
312 VOID\r
313 snp_undi32_callback_memio (\r
314   UINT64 UniqueId,\r
315   UINT8  ReadOrWrite,\r
316   UINT8  NumBytes,\r
317   UINT64 Address,\r
318   UINT64 BufferAddr\r
319   )\r
320 /*++\r
321 \r
322 Routine Description:\r
323  This is a callback routine supplied to UNDI at undi_start time.\r
324  This is the IO routine for UNDI3.1.\r
325 \r
326 Arguments:\r
327   ReadOrWrite - indicates read or write, IO or Memory\r
328   NumBytes    - number of bytes to read or write\r
329   Address     - IO or memory address to read from or write to\r
330   BufferAddr  - memory location to read into or that contains the bytes\r
331                 to write\r
332 \r
333 Returns:\r
334 \r
335 --*/\r
336 {\r
337   SNP_DRIVER                *snp;\r
338   EFI_PCI_IO_PROTOCOL_WIDTH Width;\r
339 \r
340   snp   = (SNP_DRIVER *) (UINTN) UniqueId;\r
341 \r
342   Width = (EFI_PCI_IO_PROTOCOL_WIDTH) 0;\r
343   switch (NumBytes) {\r
344   case 2:\r
345     Width = (EFI_PCI_IO_PROTOCOL_WIDTH) 1;\r
346     break;\r
347 \r
348   case 4:\r
349     Width = (EFI_PCI_IO_PROTOCOL_WIDTH) 2;\r
350     break;\r
351 \r
352   case 8:\r
353     Width = (EFI_PCI_IO_PROTOCOL_WIDTH) 3;\r
354     break;\r
355   }\r
356 \r
357   switch (ReadOrWrite) {\r
358   case PXE_IO_READ:\r
359     snp->IoFncs->Io.Read (\r
360                       snp->IoFncs,\r
361                       Width,\r
362                       snp->IoBarIndex,      // BAR 1 (for 32bit regs), IO base address\r
363                       Address,\r
364                       1,                    // count\r
365                       (VOID *) (UINTN) BufferAddr\r
366                       );\r
367     break;\r
368 \r
369   case PXE_IO_WRITE:\r
370     snp->IoFncs->Io.Write (\r
371                       snp->IoFncs,\r
372                       Width,\r
373                       snp->IoBarIndex,      // BAR 1 (for 32bit regs), IO base address\r
374                       Address,\r
375                       1,                    // count\r
376                       (VOID *) (UINTN) BufferAddr\r
377                       );\r
378     break;\r
379 \r
380   case PXE_MEM_READ:\r
381     snp->IoFncs->Mem.Read (\r
382                       snp->IoFncs,\r
383                       Width,\r
384                       snp->MemoryBarIndex,  // BAR 0, Memory base address\r
385                       Address,\r
386                       1,                    // count\r
387                       (VOID *) (UINTN) BufferAddr\r
388                       );\r
389     break;\r
390 \r
391   case PXE_MEM_WRITE:\r
392     snp->IoFncs->Mem.Write (\r
393                       snp->IoFncs,\r
394                       Width,\r
395                       snp->MemoryBarIndex,  // BAR 0, Memory base address\r
396                       Address,\r
397                       1,                    // count\r
398                       (VOID *) (UINTN) BufferAddr\r
399                       );\r
400     break;\r
401   }\r
402 \r
403   return ;\r
404 }\r
405 \r
406 VOID\r
407 snp_undi32_callback_map (\r
408   IN UINT64     UniqueId,\r
409   IN UINT64     CpuAddr,\r
410   IN UINT32     NumBytes,\r
411   IN UINT32     Direction,\r
412   IN OUT UINT64 DeviceAddrPtr\r
413   )\r
414 /*++\r
415 \r
416 Routine Description:\r
417   This is a callback routine supplied to UNDI at undi_start time.\r
418   UNDI call this routine when it has to map a CPU address to a device\r
419   address.\r
420 \r
421 Arguments:\r
422   UniqueId      - This was supplied to UNDI at Undi_Start, SNP uses this to store\r
423                   Undi interface context (Undi does not read or write this variable)\r
424   CpuAddr       - Virtual address to be mapped!\r
425   NumBytes      - size of memory to be mapped\r
426   Direction     - direction of data flow for this memory's usage:\r
427                   cpu->device, device->cpu or both ways\r
428   DeviceAddrPtr - pointer to return the mapped device address\r
429 \r
430 Returns:\r
431   None\r
432 \r
433 --*/\r
434 {\r
435   EFI_PHYSICAL_ADDRESS          *DevAddrPtr;\r
436   EFI_PCI_IO_PROTOCOL_OPERATION DirectionFlag;\r
437   UINTN                         BuffSize;\r
438   SNP_DRIVER                    *snp;\r
439   UINTN                         Index;\r
440   EFI_STATUS                    Status;\r
441 \r
442   BuffSize    = (UINTN) NumBytes;\r
443   snp         = (SNP_DRIVER *) (UINTN) UniqueId;\r
444   DevAddrPtr  = (EFI_PHYSICAL_ADDRESS *) (UINTN) DeviceAddrPtr;\r
445 \r
446   if (CpuAddr == 0) {\r
447     *DevAddrPtr = 0;\r
448     return ;\r
449   }\r
450 \r
451   switch (Direction) {\r
452   case TO_AND_FROM_DEVICE:\r
453     DirectionFlag = EfiPciIoOperationBusMasterCommonBuffer;\r
454     break;\r
455 \r
456   case FROM_DEVICE:\r
457     DirectionFlag = EfiPciIoOperationBusMasterWrite;\r
458     break;\r
459 \r
460   case TO_DEVICE:\r
461     DirectionFlag = EfiPciIoOperationBusMasterRead;\r
462     break;\r
463 \r
464   default:\r
465     *DevAddrPtr = 0;\r
466     //\r
467     // any non zero indicates error!\r
468     //\r
469     return ;\r
470   }\r
471   //\r
472   // find an unused map_list entry\r
473   //\r
474   for (Index = 0; Index < MAX_MAP_LENGTH; Index++) {\r
475     if (snp->map_list[Index].virt == 0) {\r
476       break;\r
477     }\r
478   }\r
479 \r
480   if (Index >= MAX_MAP_LENGTH) {\r
481     DEBUG ((EFI_D_INFO, "SNP maplist is FULL\n"));\r
482     *DevAddrPtr = 0;\r
483     return ;\r
484   }\r
485 \r
486   snp->map_list[Index].virt = (EFI_PHYSICAL_ADDRESS) CpuAddr;\r
487 \r
488   Status = snp->IoFncs->Map (\r
489                           snp->IoFncs,\r
490                           DirectionFlag,\r
491                           (VOID *) (UINTN) CpuAddr,\r
492                           &BuffSize,\r
493                           DevAddrPtr,\r
494                           &(snp->map_list[Index].map_cookie)\r
495                           );\r
496   if (Status != EFI_SUCCESS) {\r
497     *DevAddrPtr               = 0;\r
498     snp->map_list[Index].virt = 0;\r
499   }\r
500 \r
501   return ;\r
502 }\r
503 \r
504 VOID\r
505 snp_undi32_callback_unmap (\r
506   IN UINT64 UniqueId,\r
507   IN UINT64 CpuAddr,\r
508   IN UINT32 NumBytes,\r
509   IN UINT32 Direction,\r
510   IN UINT64 DeviceAddr\r
511   )\r
512 /*++\r
513 \r
514 Routine Description:\r
515  This is a callback routine supplied to UNDI at undi_start time.\r
516  UNDI call this routine when it wants to unmap an address that was previously\r
517  mapped using map callback\r
518 \r
519 Arguments:\r
520  UniqueId - This was supplied to UNDI at Undi_Start, SNP uses this to store\r
521             Undi interface context (Undi does not read or write this variable)\r
522  CpuAddr  - Virtual address that was mapped!\r
523  NumBytes - size of memory mapped\r
524  Direction- direction of data flow for this memory's usage:\r
525             cpu->device, device->cpu or both ways\r
526  DeviceAddr - the mapped device address\r
527 \r
528 Returns:\r
529 \r
530 --*/\r
531 {\r
532   SNP_DRIVER  *snp;\r
533   UINT16      Index;\r
534 \r
535   snp = (SNP_DRIVER *) (UINTN) UniqueId;\r
536 \r
537   for (Index = 0; Index < MAX_MAP_LENGTH; Index++) {\r
538     if (snp->map_list[Index].virt == CpuAddr) {\r
539       break;\r
540     }\r
541   }\r
542 \r
543   if (Index >= MAX_MAP_LENGTH)\r
544   {\r
545     DEBUG ((EFI_D_ERROR, "SNP could not find a mapping, failed to unmap.\n"));\r
546     return ;\r
547   }\r
548 \r
549   snp->IoFncs->Unmap (snp->IoFncs, snp->map_list[Index].map_cookie);\r
550   snp->map_list[Index].virt       = 0;\r
551   snp->map_list[Index].map_cookie = NULL;\r
552   return ;\r
553 }\r
554 \r
555 VOID\r
556 snp_undi32_callback_sync (\r
557   UINT64 UniqueId,\r
558   UINT64 CpuAddr,\r
559   UINT32 NumBytes,\r
560   UINT32 Direction,\r
561   UINT64 DeviceAddr\r
562   )\r
563 /*++\r
564 \r
565 Routine Description:\r
566  This is a callback routine supplied to UNDI at undi_start time.\r
567  UNDI call this routine when it wants synchronize the virtual buffer contents\r
568  with the mapped buffer contents. The virtual and mapped buffers need not\r
569  correspond to the same physical memory (especially if the virtual address is\r
570  > 4GB). Depending on the direction for which the buffer is mapped, undi will\r
571  need to synchronize their contents whenever it writes to/reads from the buffer\r
572  using either the cpu address or the device address.\r
573 \r
574  EFI does not provide a sync call, since virt=physical, we sould just do\r
575  the synchronization ourself here!\r
576 \r
577 Arguments:\r
578  UniqueId - This was supplied to UNDI at Undi_Start, SNP uses this to store\r
579             Undi interface context (Undi does not read or write this variable)\r
580  CpuAddr  - Virtual address that was mapped!\r
581  NumBytes - size of memory mapped\r
582  Direction- direction of data flow for this memory's usage:\r
583             cpu->device, device->cpu or both ways\r
584  DeviceAddr - the mapped device address\r
585 \r
586 Returns:\r
587 \r
588 --*/\r
589 {\r
590   if ((CpuAddr == 0) || (DeviceAddr == 0) || (NumBytes == 0)) {\r
591     return ;\r
592 \r
593   }\r
594 \r
595   switch (Direction) {\r
596   case FROM_DEVICE:\r
597     CopyMem ((UINT8 *) (UINTN) CpuAddr, (UINT8 *) (UINTN) DeviceAddr, NumBytes);\r
598     break;\r
599 \r
600   case TO_DEVICE:\r
601     CopyMem ((UINT8 *) (UINTN) DeviceAddr, (UINT8 *) (UINTN) CpuAddr, NumBytes);\r
602     break;\r
603   }\r
604 \r
605   return ;\r
606 }\r