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