NetworkPkg: Move Network library and drivers from MdeModulePkg to NetworkPkg
[mirror_edk2.git] / NetworkPkg / Library / DxeNetLib / DxeNetLib.c
1 /** @file\r
2   Network library.\r
3 \r
4 Copyright (c) 2005 - 2018, Intel Corporation. All rights reserved.<BR>\r
5 (C) Copyright 2015 Hewlett Packard Enterprise Development LP<BR>\r
6 SPDX-License-Identifier: BSD-2-Clause-Patent\r
7 **/\r
8 \r
9 #include <Uefi.h>\r
10 \r
11 #include <IndustryStandard/SmBios.h>\r
12 \r
13 #include <Protocol/DriverBinding.h>\r
14 #include <Protocol/ServiceBinding.h>\r
15 #include <Protocol/SimpleNetwork.h>\r
16 #include <Protocol/AdapterInformation.h>\r
17 #include <Protocol/ManagedNetwork.h>\r
18 #include <Protocol/Ip4Config2.h>\r
19 #include <Protocol/ComponentName.h>\r
20 #include <Protocol/ComponentName2.h>\r
21 \r
22 #include <Guid/SmBios.h>\r
23 \r
24 #include <Library/NetLib.h>\r
25 #include <Library/BaseLib.h>\r
26 #include <Library/DebugLib.h>\r
27 #include <Library/BaseMemoryLib.h>\r
28 #include <Library/UefiBootServicesTableLib.h>\r
29 #include <Library/UefiRuntimeServicesTableLib.h>\r
30 #include <Library/MemoryAllocationLib.h>\r
31 #include <Library/DevicePathLib.h>\r
32 #include <Library/PrintLib.h>\r
33 #include <Library/UefiLib.h>\r
34 \r
35 #define NIC_ITEM_CONFIG_SIZE   (sizeof (NIC_IP4_CONFIG_INFO) + sizeof (EFI_IP4_ROUTE_TABLE) * MAX_IP4_CONFIG_IN_VARIABLE)\r
36 #define DEFAULT_ZERO_START     ((UINTN) ~0)\r
37 \r
38 //\r
39 // All the supported IP4 maskes in host byte order.\r
40 //\r
41 GLOBAL_REMOVE_IF_UNREFERENCED IP4_ADDR  gIp4AllMasks[IP4_MASK_NUM] = {\r
42   0x00000000,\r
43   0x80000000,\r
44   0xC0000000,\r
45   0xE0000000,\r
46   0xF0000000,\r
47   0xF8000000,\r
48   0xFC000000,\r
49   0xFE000000,\r
50 \r
51   0xFF000000,\r
52   0xFF800000,\r
53   0xFFC00000,\r
54   0xFFE00000,\r
55   0xFFF00000,\r
56   0xFFF80000,\r
57   0xFFFC0000,\r
58   0xFFFE0000,\r
59 \r
60   0xFFFF0000,\r
61   0xFFFF8000,\r
62   0xFFFFC000,\r
63   0xFFFFE000,\r
64   0xFFFFF000,\r
65   0xFFFFF800,\r
66   0xFFFFFC00,\r
67   0xFFFFFE00,\r
68 \r
69   0xFFFFFF00,\r
70   0xFFFFFF80,\r
71   0xFFFFFFC0,\r
72   0xFFFFFFE0,\r
73   0xFFFFFFF0,\r
74   0xFFFFFFF8,\r
75   0xFFFFFFFC,\r
76   0xFFFFFFFE,\r
77   0xFFFFFFFF,\r
78 };\r
79 \r
80 GLOBAL_REMOVE_IF_UNREFERENCED EFI_IPv4_ADDRESS  mZeroIp4Addr = {{0, 0, 0, 0}};\r
81 \r
82 //\r
83 // Any error level digitally larger than mNetDebugLevelMax\r
84 // will be silently discarded.\r
85 //\r
86 GLOBAL_REMOVE_IF_UNREFERENCED UINTN  mNetDebugLevelMax = NETDEBUG_LEVEL_ERROR;\r
87 GLOBAL_REMOVE_IF_UNREFERENCED UINT32 mSyslogPacketSeq  = 0xDEADBEEF;\r
88 \r
89 //\r
90 // You can change mSyslogDstMac mSyslogDstIp and mSyslogSrcIp\r
91 // here to direct the syslog packets to the syslog deamon. The\r
92 // default is broadcast to both the ethernet and IP.\r
93 //\r
94 GLOBAL_REMOVE_IF_UNREFERENCED UINT8  mSyslogDstMac[NET_ETHER_ADDR_LEN] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};\r
95 GLOBAL_REMOVE_IF_UNREFERENCED UINT32 mSyslogDstIp                      = 0xffffffff;\r
96 GLOBAL_REMOVE_IF_UNREFERENCED UINT32 mSyslogSrcIp                      = 0;\r
97 \r
98 GLOBAL_REMOVE_IF_UNREFERENCED CHAR8 *mMonthName[] = {\r
99   "Jan",\r
100   "Feb",\r
101   "Mar",\r
102   "Apr",\r
103   "May",\r
104   "Jun",\r
105   "Jul",\r
106   "Aug",\r
107   "Sep",\r
108   "Oct",\r
109   "Nov",\r
110   "Dec"\r
111 };\r
112 \r
113 //\r
114 // VLAN device path node template\r
115 //\r
116 GLOBAL_REMOVE_IF_UNREFERENCED VLAN_DEVICE_PATH mNetVlanDevicePathTemplate = {\r
117   {\r
118     MESSAGING_DEVICE_PATH,\r
119     MSG_VLAN_DP,\r
120     {\r
121       (UINT8) (sizeof (VLAN_DEVICE_PATH)),\r
122       (UINT8) ((sizeof (VLAN_DEVICE_PATH)) >> 8)\r
123     }\r
124   },\r
125   0\r
126 };\r
127 \r
128 /**\r
129   Locate the handles that support SNP, then open one of them\r
130   to send the syslog packets. The caller isn't required to close\r
131   the SNP after use because the SNP is opened by HandleProtocol.\r
132 \r
133   @return The point to SNP if one is properly openned. Otherwise NULL\r
134 \r
135 **/\r
136 EFI_SIMPLE_NETWORK_PROTOCOL *\r
137 SyslogLocateSnp (\r
138   VOID\r
139   )\r
140 {\r
141   EFI_SIMPLE_NETWORK_PROTOCOL *Snp;\r
142   EFI_STATUS                  Status;\r
143   EFI_HANDLE                  *Handles;\r
144   UINTN                       HandleCount;\r
145   UINTN                       Index;\r
146 \r
147   //\r
148   // Locate the handles which has SNP installed.\r
149   //\r
150   Handles = NULL;\r
151   Status  = gBS->LocateHandleBuffer (\r
152                    ByProtocol,\r
153                    &gEfiSimpleNetworkProtocolGuid,\r
154                    NULL,\r
155                    &HandleCount,\r
156                    &Handles\r
157                    );\r
158 \r
159   if (EFI_ERROR (Status) || (HandleCount == 0)) {\r
160     return NULL;\r
161   }\r
162 \r
163   //\r
164   // Try to open one of the ethernet SNP protocol to send packet\r
165   //\r
166   Snp = NULL;\r
167 \r
168   for (Index = 0; Index < HandleCount; Index++) {\r
169     Status = gBS->HandleProtocol (\r
170                     Handles[Index],\r
171                     &gEfiSimpleNetworkProtocolGuid,\r
172                     (VOID **) &Snp\r
173                     );\r
174 \r
175     if ((Status == EFI_SUCCESS) && (Snp != NULL) &&\r
176         (Snp->Mode->IfType == NET_IFTYPE_ETHERNET) &&\r
177         (Snp->Mode->MaxPacketSize >= NET_SYSLOG_PACKET_LEN)) {\r
178 \r
179       break;\r
180     }\r
181 \r
182     Snp = NULL;\r
183   }\r
184 \r
185   FreePool (Handles);\r
186   return Snp;\r
187 }\r
188 \r
189 /**\r
190   Transmit a syslog packet synchronously through SNP. The Packet\r
191   already has the ethernet header prepended. This function should\r
192   fill in the source MAC because it will try to locate a SNP each\r
193   time it is called to avoid the problem if SNP is unloaded.\r
194   This code snip is copied from MNP.\r
195   If Packet is NULL, then ASSERT().\r
196 \r
197   @param[in] Packet          The Syslog packet\r
198   @param[in] Length          The length of the packet\r
199 \r
200   @retval EFI_DEVICE_ERROR   Failed to locate a usable SNP protocol\r
201   @retval EFI_TIMEOUT        Timeout happened to send the packet.\r
202   @retval EFI_SUCCESS        Packet is sent.\r
203 \r
204 **/\r
205 EFI_STATUS\r
206 SyslogSendPacket (\r
207   IN CHAR8                    *Packet,\r
208   IN UINT32                   Length\r
209   )\r
210 {\r
211   EFI_SIMPLE_NETWORK_PROTOCOL *Snp;\r
212   ETHER_HEAD                  *Ether;\r
213   EFI_STATUS                  Status;\r
214   EFI_EVENT                   TimeoutEvent;\r
215   UINT8                       *TxBuf;\r
216 \r
217   ASSERT (Packet != NULL);\r
218 \r
219   Snp = SyslogLocateSnp ();\r
220 \r
221   if (Snp == NULL) {\r
222     return EFI_DEVICE_ERROR;\r
223   }\r
224 \r
225   Ether = (ETHER_HEAD *) Packet;\r
226   CopyMem (Ether->SrcMac, Snp->Mode->CurrentAddress.Addr, NET_ETHER_ADDR_LEN);\r
227 \r
228   //\r
229   // Start the timeout event.\r
230   //\r
231   Status = gBS->CreateEvent (\r
232                   EVT_TIMER,\r
233                   TPL_NOTIFY,\r
234                   NULL,\r
235                   NULL,\r
236                   &TimeoutEvent\r
237                   );\r
238 \r
239   if (EFI_ERROR (Status)) {\r
240     return Status;\r
241   }\r
242 \r
243   Status = gBS->SetTimer (TimeoutEvent, TimerRelative, NET_SYSLOG_TX_TIMEOUT);\r
244 \r
245   if (EFI_ERROR (Status)) {\r
246     goto ON_EXIT;\r
247   }\r
248 \r
249   for (;;) {\r
250     //\r
251     // Transmit the packet through SNP.\r
252     //\r
253     Status = Snp->Transmit (Snp, 0, Length, Packet, NULL, NULL, NULL);\r
254 \r
255     if ((Status != EFI_SUCCESS) && (Status != EFI_NOT_READY)) {\r
256       Status = EFI_DEVICE_ERROR;\r
257       break;\r
258     }\r
259 \r
260     //\r
261     // If Status is EFI_SUCCESS, the packet is put in the transmit queue.\r
262     // if Status is EFI_NOT_READY, the transmit engine of the network\r
263     // interface is busy. Both need to sync SNP.\r
264     //\r
265     TxBuf = NULL;\r
266 \r
267     do {\r
268       //\r
269       // Get the recycled transmit buffer status.\r
270       //\r
271       Snp->GetStatus (Snp, NULL, (VOID **) &TxBuf);\r
272 \r
273       if (!EFI_ERROR (gBS->CheckEvent (TimeoutEvent))) {\r
274         Status = EFI_TIMEOUT;\r
275         break;\r
276       }\r
277 \r
278     } while (TxBuf == NULL);\r
279 \r
280     if ((Status == EFI_SUCCESS) || (Status == EFI_TIMEOUT)) {\r
281       break;\r
282     }\r
283 \r
284     //\r
285     // Status is EFI_NOT_READY. Restart the timer event and\r
286     // call Snp->Transmit again.\r
287     //\r
288     gBS->SetTimer (TimeoutEvent, TimerRelative, NET_SYSLOG_TX_TIMEOUT);\r
289   }\r
290 \r
291   gBS->SetTimer (TimeoutEvent, TimerCancel, 0);\r
292 \r
293 ON_EXIT:\r
294   gBS->CloseEvent (TimeoutEvent);\r
295   return Status;\r
296 }\r
297 \r
298 /**\r
299   Build a syslog packet, including the Ethernet/Ip/Udp headers\r
300   and user's message.\r
301 \r
302   @param[in]  Level     Syslog severity level\r
303   @param[in]  Module    The module that generates the log\r
304   @param[in]  File      The file that contains the current log\r
305   @param[in]  Line      The line of code in the File that contains the current log\r
306   @param[in]  Message   The log message\r
307   @param[in]  BufLen    The lenght of the Buf\r
308   @param[out] Buf       The buffer to put the packet data\r
309 \r
310   @return The length of the syslog packet built, 0 represents no packet is built.\r
311 \r
312 **/\r
313 UINT32\r
314 SyslogBuildPacket (\r
315   IN  UINT32                Level,\r
316   IN  UINT8                 *Module,\r
317   IN  UINT8                 *File,\r
318   IN  UINT32                Line,\r
319   IN  UINT8                 *Message,\r
320   IN  UINT32                BufLen,\r
321   OUT CHAR8                 *Buf\r
322   )\r
323 {\r
324   EFI_STATUS                Status;\r
325   ETHER_HEAD                *Ether;\r
326   IP4_HEAD                  *Ip4;\r
327   EFI_UDP_HEADER            *Udp4;\r
328   EFI_TIME                  Time;\r
329   UINT32                    Pri;\r
330   UINT32                    Len;\r
331 \r
332   //\r
333   // Fill in the Ethernet header. Leave alone the source MAC.\r
334   // SyslogSendPacket will fill in the address for us.\r
335   //\r
336   Ether = (ETHER_HEAD *) Buf;\r
337   CopyMem (Ether->DstMac, mSyslogDstMac, NET_ETHER_ADDR_LEN);\r
338   ZeroMem (Ether->SrcMac, NET_ETHER_ADDR_LEN);\r
339 \r
340   Ether->EtherType = HTONS (0x0800);    // IPv4 protocol\r
341 \r
342   Buf             += sizeof (ETHER_HEAD);\r
343   BufLen          -= sizeof (ETHER_HEAD);\r
344 \r
345   //\r
346   // Fill in the IP header\r
347   //\r
348   Ip4              = (IP4_HEAD *) Buf;\r
349   Ip4->HeadLen     = 5;\r
350   Ip4->Ver         = 4;\r
351   Ip4->Tos         = 0;\r
352   Ip4->TotalLen    = 0;\r
353   Ip4->Id          = (UINT16) mSyslogPacketSeq;\r
354   Ip4->Fragment    = 0;\r
355   Ip4->Ttl         = 16;\r
356   Ip4->Protocol    = 0x11;\r
357   Ip4->Checksum    = 0;\r
358   Ip4->Src         = mSyslogSrcIp;\r
359   Ip4->Dst         = mSyslogDstIp;\r
360 \r
361   Buf             += sizeof (IP4_HEAD);\r
362   BufLen          -= sizeof (IP4_HEAD);\r
363 \r
364   //\r
365   // Fill in the UDP header, Udp checksum is optional. Leave it zero.\r
366   //\r
367   Udp4             = (EFI_UDP_HEADER *) Buf;\r
368   Udp4->SrcPort    = HTONS (514);\r
369   Udp4->DstPort    = HTONS (514);\r
370   Udp4->Length     = 0;\r
371   Udp4->Checksum   = 0;\r
372 \r
373   Buf             += sizeof (EFI_UDP_HEADER);\r
374   BufLen          -= sizeof (EFI_UDP_HEADER);\r
375 \r
376   //\r
377   // Build the syslog message body with <PRI> Timestamp  machine module Message\r
378   //\r
379   Pri = ((NET_SYSLOG_FACILITY & 31) << 3) | (Level & 7);\r
380   Status = gRT->GetTime (&Time, NULL);\r
381   if (EFI_ERROR (Status)) {\r
382     return 0;\r
383   }\r
384 \r
385   //\r
386   // Use %a to format the ASCII strings, %s to format UNICODE strings\r
387   //\r
388   Len  = 0;\r
389   Len += (UINT32) AsciiSPrint (\r
390                     Buf,\r
391                     BufLen,\r
392                     "<%d> %a %d %d:%d:%d ",\r
393                     Pri,\r
394                     mMonthName [Time.Month-1],\r
395                     Time.Day,\r
396                     Time.Hour,\r
397                     Time.Minute,\r
398                     Time.Second\r
399                     );\r
400 \r
401   Len += (UINT32) AsciiSPrint (\r
402                     Buf + Len,\r
403                     BufLen - Len,\r
404                     "Tiano %a: %a (Line: %d File: %a)",\r
405                     Module,\r
406                     Message,\r
407                     Line,\r
408                     File\r
409                     );\r
410   Len ++;\r
411 \r
412   //\r
413   // OK, patch the IP length/checksum and UDP length fields.\r
414   //\r
415   Len           += sizeof (EFI_UDP_HEADER);\r
416   Udp4->Length   = HTONS ((UINT16) Len);\r
417 \r
418   Len           += sizeof (IP4_HEAD);\r
419   Ip4->TotalLen  = HTONS ((UINT16) Len);\r
420   Ip4->Checksum  = (UINT16) (~NetblockChecksum ((UINT8 *) Ip4, sizeof (IP4_HEAD)));\r
421 \r
422   return Len + sizeof (ETHER_HEAD);\r
423 }\r
424 \r
425 /**\r
426   Allocate a buffer, then format the message to it. This is a\r
427   help function for the NET_DEBUG_XXX macros. The PrintArg of\r
428   these macros treats the variable length print parameters as a\r
429   single parameter, and pass it to the NetDebugASPrint. For\r
430   example, NET_DEBUG_TRACE ("Tcp", ("State transit to %a\n", Name))\r
431   if extracted to:\r
432 \r
433          NetDebugOutput (\r
434            NETDEBUG_LEVEL_TRACE,\r
435            "Tcp",\r
436            __FILE__,\r
437            __LINE__,\r
438            NetDebugASPrint ("State transit to %a\n", Name)\r
439          )\r
440 \r
441   If Format is NULL, then ASSERT().\r
442 \r
443   @param Format  The ASCII format string.\r
444   @param ...     The variable length parameter whose format is determined\r
445                  by the Format string.\r
446 \r
447   @return        The buffer containing the formatted message,\r
448                  or NULL if failed to allocate memory.\r
449 \r
450 **/\r
451 CHAR8 *\r
452 EFIAPI\r
453 NetDebugASPrint (\r
454   IN CHAR8                  *Format,\r
455   ...\r
456   )\r
457 {\r
458   VA_LIST                   Marker;\r
459   CHAR8                     *Buf;\r
460 \r
461   ASSERT (Format != NULL);\r
462 \r
463   Buf = (CHAR8 *) AllocatePool (NET_DEBUG_MSG_LEN);\r
464 \r
465   if (Buf == NULL) {\r
466     return NULL;\r
467   }\r
468 \r
469   VA_START (Marker, Format);\r
470   AsciiVSPrint (Buf, NET_DEBUG_MSG_LEN, Format, Marker);\r
471   VA_END (Marker);\r
472 \r
473   return Buf;\r
474 }\r
475 \r
476 /**\r
477   Builds an UDP4 syslog packet and send it using SNP.\r
478 \r
479   This function will locate a instance of SNP then send the message through it.\r
480   Because it isn't open the SNP BY_DRIVER, apply caution when using it.\r
481 \r
482   @param Level    The severity level of the message.\r
483   @param Module   The Moudle that generates the log.\r
484   @param File     The file that contains the log.\r
485   @param Line     The exact line that contains the log.\r
486   @param Message  The user message to log.\r
487 \r
488   @retval EFI_INVALID_PARAMETER Any input parameter is invalid.\r
489   @retval EFI_OUT_OF_RESOURCES  Failed to allocate memory for the packet.\r
490   @retval EFI_DEVICE_ERROR      Device error occurs.\r
491   @retval EFI_SUCCESS           The log is discard because that it is more verbose\r
492                                 than the mNetDebugLevelMax. Or, it has been sent out.\r
493 **/\r
494 EFI_STATUS\r
495 EFIAPI\r
496 NetDebugOutput (\r
497   IN UINT32                    Level,\r
498   IN UINT8                     *Module,\r
499   IN UINT8                     *File,\r
500   IN UINT32                    Line,\r
501   IN UINT8                     *Message\r
502   )\r
503 {\r
504   CHAR8                        *Packet;\r
505   UINT32                       Len;\r
506   EFI_STATUS                   Status;\r
507 \r
508   //\r
509   // Check whether the message should be sent out\r
510   //\r
511   if (Message == NULL || File == NULL || Module == NULL) {\r
512     return EFI_INVALID_PARAMETER;\r
513   }\r
514 \r
515   if (Level > mNetDebugLevelMax) {\r
516     Status = EFI_SUCCESS;\r
517     goto ON_EXIT;\r
518   }\r
519 \r
520   //\r
521   // Allocate a maxium of 1024 bytes, the caller should ensure\r
522   // that the message plus the ethernet/ip/udp header is shorter\r
523   // than this\r
524   //\r
525   Packet = (CHAR8 *) AllocatePool (NET_SYSLOG_PACKET_LEN);\r
526 \r
527   if (Packet == NULL) {\r
528     Status = EFI_OUT_OF_RESOURCES;\r
529     goto ON_EXIT;\r
530   }\r
531 \r
532   //\r
533   // Build the message: Ethernet header + IP header + Udp Header + user data\r
534   //\r
535   Len = SyslogBuildPacket (\r
536           Level,\r
537           Module,\r
538           File,\r
539           Line,\r
540           Message,\r
541           NET_SYSLOG_PACKET_LEN,\r
542           Packet\r
543           );\r
544   if (Len == 0) {\r
545     Status = EFI_DEVICE_ERROR;\r
546   } else {\r
547     mSyslogPacketSeq++;\r
548     Status = SyslogSendPacket (Packet, Len);\r
549   }\r
550 \r
551   FreePool (Packet);\r
552 \r
553 ON_EXIT:\r
554   FreePool (Message);\r
555   return Status;\r
556 }\r
557 /**\r
558   Return the length of the mask.\r
559 \r
560   Return the length of the mask, the correct value is from 0 to 32.\r
561   If the mask is invalid, return the invalid length 33, which is IP4_MASK_NUM.\r
562   NetMask is in the host byte order.\r
563 \r
564   @param[in]  NetMask              The netmask to get the length from.\r
565 \r
566   @return The length of the netmask, IP4_MASK_NUM if the mask is invalid.\r
567 \r
568 **/\r
569 INTN\r
570 EFIAPI\r
571 NetGetMaskLength (\r
572   IN IP4_ADDR               NetMask\r
573   )\r
574 {\r
575   INTN                      Index;\r
576 \r
577   for (Index = 0; Index <= IP4_MASK_MAX; Index++) {\r
578     if (NetMask == gIp4AllMasks[Index]) {\r
579       break;\r
580     }\r
581   }\r
582 \r
583   return Index;\r
584 }\r
585 \r
586 \r
587 \r
588 /**\r
589   Return the class of the IP address, such as class A, B, C.\r
590   Addr is in host byte order.\r
591 \r
592   [ATTENTION]\r
593   Classful addressing (IP class A/B/C) has been deprecated according to RFC4632.\r
594   Caller of this function could only check the returned value against\r
595   IP4_ADDR_CLASSD (multicast) or IP4_ADDR_CLASSE (reserved) now.\r
596 \r
597   The address of class A  starts with 0.\r
598   If the address belong to class A, return IP4_ADDR_CLASSA.\r
599   The address of class B  starts with 10.\r
600   If the address belong to class B, return IP4_ADDR_CLASSB.\r
601   The address of class C  starts with 110.\r
602   If the address belong to class C, return IP4_ADDR_CLASSC.\r
603   The address of class D  starts with 1110.\r
604   If the address belong to class D, return IP4_ADDR_CLASSD.\r
605   The address of class E  starts with 1111.\r
606   If the address belong to class E, return IP4_ADDR_CLASSE.\r
607 \r
608 \r
609   @param[in]   Addr                  The address to get the class from.\r
610 \r
611   @return IP address class, such as IP4_ADDR_CLASSA.\r
612 \r
613 **/\r
614 INTN\r
615 EFIAPI\r
616 NetGetIpClass (\r
617   IN IP4_ADDR               Addr\r
618   )\r
619 {\r
620   UINT8                     ByteOne;\r
621 \r
622   ByteOne = (UINT8) (Addr >> 24);\r
623 \r
624   if ((ByteOne & 0x80) == 0) {\r
625     return IP4_ADDR_CLASSA;\r
626 \r
627   } else if ((ByteOne & 0xC0) == 0x80) {\r
628     return IP4_ADDR_CLASSB;\r
629 \r
630   } else if ((ByteOne & 0xE0) == 0xC0) {\r
631     return IP4_ADDR_CLASSC;\r
632 \r
633   } else if ((ByteOne & 0xF0) == 0xE0) {\r
634     return IP4_ADDR_CLASSD;\r
635 \r
636   } else {\r
637     return IP4_ADDR_CLASSE;\r
638 \r
639   }\r
640 }\r
641 \r
642 \r
643 /**\r
644   Check whether the IP is a valid unicast address according to\r
645   the netmask.\r
646 \r
647   ASSERT if NetMask is zero.\r
648 \r
649   If all bits of the host address of IP are 0 or 1, IP is also not a valid unicast address,\r
650   except when the originator is one of the endpoints of a point-to-point link with a 31-bit\r
651   mask (RFC3021), or a 32bit NetMask (all 0xFF) is used for special network environment (e.g.\r
652   PPP link).\r
653 \r
654   @param[in]  Ip                    The IP to check against.\r
655   @param[in]  NetMask               The mask of the IP.\r
656 \r
657   @return TRUE if IP is a valid unicast address on the network, otherwise FALSE.\r
658 \r
659 **/\r
660 BOOLEAN\r
661 EFIAPI\r
662 NetIp4IsUnicast (\r
663   IN IP4_ADDR               Ip,\r
664   IN IP4_ADDR               NetMask\r
665   )\r
666 {\r
667   INTN   MaskLength;\r
668 \r
669   ASSERT (NetMask != 0);\r
670 \r
671   if (Ip == 0 || IP4_IS_LOCAL_BROADCAST (Ip)) {\r
672     return FALSE;\r
673   }\r
674 \r
675   MaskLength = NetGetMaskLength (NetMask);\r
676   ASSERT ((MaskLength >= 0) && (MaskLength <= IP4_MASK_NUM));\r
677   if (MaskLength < 31) {\r
678     if (((Ip &~NetMask) == ~NetMask) || ((Ip &~NetMask) == 0)) {\r
679       return FALSE;\r
680     }\r
681   }\r
682 \r
683   return TRUE;\r
684 }\r
685 \r
686 /**\r
687   Check whether the incoming IPv6 address is a valid unicast address.\r
688 \r
689   ASSERT if Ip6 is NULL.\r
690 \r
691   If the address is a multicast address has binary 0xFF at the start, it is not\r
692   a valid unicast address. If the address is unspecified ::, it is not a valid\r
693   unicast address to be assigned to any node. If the address is loopback address\r
694   ::1, it is also not a valid unicast address to be assigned to any physical\r
695   interface.\r
696 \r
697   @param[in]  Ip6                   The IPv6 address to check against.\r
698 \r
699   @return TRUE if Ip6 is a valid unicast address on the network, otherwise FALSE.\r
700 \r
701 **/\r
702 BOOLEAN\r
703 EFIAPI\r
704 NetIp6IsValidUnicast (\r
705   IN EFI_IPv6_ADDRESS       *Ip6\r
706   )\r
707 {\r
708   UINT8 Byte;\r
709   UINT8 Index;\r
710 \r
711   ASSERT (Ip6 != NULL);\r
712 \r
713   if (Ip6->Addr[0] == 0xFF) {\r
714     return FALSE;\r
715   }\r
716 \r
717   for (Index = 0; Index < 15; Index++) {\r
718     if (Ip6->Addr[Index] != 0) {\r
719       return TRUE;\r
720     }\r
721   }\r
722 \r
723   Byte = Ip6->Addr[Index];\r
724 \r
725   if (Byte == 0x0 || Byte == 0x1) {\r
726     return FALSE;\r
727   }\r
728 \r
729   return TRUE;\r
730 }\r
731 \r
732 /**\r
733   Check whether the incoming Ipv6 address is the unspecified address or not.\r
734 \r
735   ASSERT if Ip6 is NULL.\r
736 \r
737   @param[in] Ip6   - Ip6 address, in network order.\r
738 \r
739   @retval TRUE     - Yes, unspecified\r
740   @retval FALSE    - No\r
741 \r
742 **/\r
743 BOOLEAN\r
744 EFIAPI\r
745 NetIp6IsUnspecifiedAddr (\r
746   IN EFI_IPv6_ADDRESS       *Ip6\r
747   )\r
748 {\r
749   UINT8 Index;\r
750 \r
751   ASSERT (Ip6 != NULL);\r
752 \r
753   for (Index = 0; Index < 16; Index++) {\r
754     if (Ip6->Addr[Index] != 0) {\r
755       return FALSE;\r
756     }\r
757   }\r
758 \r
759   return TRUE;\r
760 }\r
761 \r
762 /**\r
763   Check whether the incoming Ipv6 address is a link-local address.\r
764 \r
765   ASSERT if Ip6 is NULL.\r
766 \r
767   @param[in] Ip6   - Ip6 address, in network order.\r
768 \r
769   @retval TRUE  - Yes, link-local address\r
770   @retval FALSE - No\r
771 \r
772 **/\r
773 BOOLEAN\r
774 EFIAPI\r
775 NetIp6IsLinkLocalAddr (\r
776   IN EFI_IPv6_ADDRESS *Ip6\r
777   )\r
778 {\r
779   UINT8 Index;\r
780 \r
781   ASSERT (Ip6 != NULL);\r
782 \r
783   if (Ip6->Addr[0] != 0xFE) {\r
784     return FALSE;\r
785   }\r
786 \r
787   if (Ip6->Addr[1] != 0x80) {\r
788     return FALSE;\r
789   }\r
790 \r
791   for (Index = 2; Index < 8; Index++) {\r
792     if (Ip6->Addr[Index] != 0) {\r
793       return FALSE;\r
794     }\r
795   }\r
796 \r
797   return TRUE;\r
798 }\r
799 \r
800 /**\r
801   Check whether the Ipv6 address1 and address2 are on the connected network.\r
802 \r
803   ASSERT if Ip1 or Ip2 is NULL.\r
804   ASSERT if PrefixLength exceeds or equals to IP6_PREFIX_MAX.\r
805 \r
806   @param[in] Ip1          - Ip6 address1, in network order.\r
807   @param[in] Ip2          - Ip6 address2, in network order.\r
808   @param[in] PrefixLength - The prefix length of the checking net.\r
809 \r
810   @retval TRUE            - Yes, connected.\r
811   @retval FALSE           - No.\r
812 \r
813 **/\r
814 BOOLEAN\r
815 EFIAPI\r
816 NetIp6IsNetEqual (\r
817   EFI_IPv6_ADDRESS *Ip1,\r
818   EFI_IPv6_ADDRESS *Ip2,\r
819   UINT8            PrefixLength\r
820   )\r
821 {\r
822   UINT8 Byte;\r
823   UINT8 Bit;\r
824   UINT8 Mask;\r
825 \r
826   ASSERT ((Ip1 != NULL) && (Ip2 != NULL) && (PrefixLength < IP6_PREFIX_MAX));\r
827 \r
828   if (PrefixLength == 0) {\r
829     return TRUE;\r
830   }\r
831 \r
832   Byte = (UINT8) (PrefixLength / 8);\r
833   Bit  = (UINT8) (PrefixLength % 8);\r
834 \r
835   if (CompareMem (Ip1, Ip2, Byte) != 0) {\r
836     return FALSE;\r
837   }\r
838 \r
839   if (Bit > 0) {\r
840     Mask = (UINT8) (0xFF << (8 - Bit));\r
841 \r
842     ASSERT (Byte < 16);\r
843     if (Byte >= 16) {\r
844       return FALSE;\r
845     }\r
846     if ((Ip1->Addr[Byte] & Mask) != (Ip2->Addr[Byte] & Mask)) {\r
847       return FALSE;\r
848     }\r
849   }\r
850 \r
851   return TRUE;\r
852 }\r
853 \r
854 \r
855 /**\r
856   Switches the endianess of an IPv6 address\r
857 \r
858   ASSERT if Ip6 is NULL.\r
859 \r
860   This function swaps the bytes in a 128-bit IPv6 address to switch the value\r
861   from little endian to big endian or vice versa. The byte swapped value is\r
862   returned.\r
863 \r
864   @param  Ip6 Points to an IPv6 address\r
865 \r
866   @return The byte swapped IPv6 address.\r
867 \r
868 **/\r
869 EFI_IPv6_ADDRESS *\r
870 EFIAPI\r
871 Ip6Swap128 (\r
872   EFI_IPv6_ADDRESS *Ip6\r
873   )\r
874 {\r
875   UINT64 High;\r
876   UINT64 Low;\r
877 \r
878   ASSERT (Ip6 != NULL);\r
879 \r
880   CopyMem (&High, Ip6, sizeof (UINT64));\r
881   CopyMem (&Low, &Ip6->Addr[8], sizeof (UINT64));\r
882 \r
883   High = SwapBytes64 (High);\r
884   Low  = SwapBytes64 (Low);\r
885 \r
886   CopyMem (Ip6, &Low, sizeof (UINT64));\r
887   CopyMem (&Ip6->Addr[8], &High, sizeof (UINT64));\r
888 \r
889   return Ip6;\r
890 }\r
891 \r
892 /**\r
893   Initialize a random seed using current time and monotonic count.\r
894 \r
895   Get current time and monotonic count first. Then initialize a random seed\r
896   based on some basic mathematics operation on the hour, day, minute, second,\r
897   nanosecond and year of the current time and the monotonic count value.\r
898 \r
899   @return The random seed initialized with current time.\r
900 \r
901 **/\r
902 UINT32\r
903 EFIAPI\r
904 NetRandomInitSeed (\r
905   VOID\r
906   )\r
907 {\r
908   EFI_TIME                  Time;\r
909   UINT32                    Seed;\r
910   UINT64                    MonotonicCount;\r
911 \r
912   gRT->GetTime (&Time, NULL);\r
913   Seed = (Time.Hour << 24 | Time.Day << 16 | Time.Minute << 8 | Time.Second);\r
914   Seed ^= Time.Nanosecond;\r
915   Seed ^= Time.Year << 7;\r
916 \r
917   gBS->GetNextMonotonicCount (&MonotonicCount);\r
918   Seed += (UINT32) MonotonicCount;\r
919 \r
920   return Seed;\r
921 }\r
922 \r
923 \r
924 /**\r
925   Extract a UINT32 from a byte stream.\r
926 \r
927   ASSERT if Buf is NULL.\r
928 \r
929   Copy a UINT32 from a byte stream, then converts it from Network\r
930   byte order to host byte order. Use this function to avoid alignment error.\r
931 \r
932   @param[in]  Buf                 The buffer to extract the UINT32.\r
933 \r
934   @return The UINT32 extracted.\r
935 \r
936 **/\r
937 UINT32\r
938 EFIAPI\r
939 NetGetUint32 (\r
940   IN UINT8                  *Buf\r
941   )\r
942 {\r
943   UINT32                    Value;\r
944 \r
945   ASSERT (Buf != NULL);\r
946 \r
947   CopyMem (&Value, Buf, sizeof (UINT32));\r
948   return NTOHL (Value);\r
949 }\r
950 \r
951 \r
952 /**\r
953   Put a UINT32 to the byte stream in network byte order.\r
954 \r
955   ASSERT if Buf is NULL.\r
956 \r
957   Converts a UINT32 from host byte order to network byte order. Then copy it to the\r
958   byte stream.\r
959 \r
960   @param[in, out]  Buf          The buffer to put the UINT32.\r
961   @param[in]       Data         The data to be converted and put into the byte stream.\r
962 \r
963 **/\r
964 VOID\r
965 EFIAPI\r
966 NetPutUint32 (\r
967   IN OUT UINT8                 *Buf,\r
968   IN     UINT32                Data\r
969   )\r
970 {\r
971   ASSERT (Buf != NULL);\r
972 \r
973   Data = HTONL (Data);\r
974   CopyMem (Buf, &Data, sizeof (UINT32));\r
975 }\r
976 \r
977 \r
978 /**\r
979   Remove the first node entry on the list, and return the removed node entry.\r
980 \r
981   Removes the first node Entry from a doubly linked list. It is up to the caller of\r
982   this function to release the memory used by the first node if that is required. On\r
983   exit, the removed node is returned.\r
984 \r
985   If Head is NULL, then ASSERT().\r
986   If Head was not initialized, then ASSERT().\r
987   If PcdMaximumLinkedListLength is not zero, and the number of nodes in the\r
988   linked list including the head node is greater than or equal to PcdMaximumLinkedListLength,\r
989   then ASSERT().\r
990 \r
991   @param[in, out]  Head                  The list header.\r
992 \r
993   @return The first node entry that is removed from the list, NULL if the list is empty.\r
994 \r
995 **/\r
996 LIST_ENTRY *\r
997 EFIAPI\r
998 NetListRemoveHead (\r
999   IN OUT LIST_ENTRY            *Head\r
1000   )\r
1001 {\r
1002   LIST_ENTRY            *First;\r
1003 \r
1004   ASSERT (Head != NULL);\r
1005 \r
1006   if (IsListEmpty (Head)) {\r
1007     return NULL;\r
1008   }\r
1009 \r
1010   First                         = Head->ForwardLink;\r
1011   Head->ForwardLink             = First->ForwardLink;\r
1012   First->ForwardLink->BackLink  = Head;\r
1013 \r
1014   DEBUG_CODE (\r
1015     First->ForwardLink  = (LIST_ENTRY *) NULL;\r
1016     First->BackLink     = (LIST_ENTRY *) NULL;\r
1017   );\r
1018 \r
1019   return First;\r
1020 }\r
1021 \r
1022 \r
1023 /**\r
1024   Remove the last node entry on the list and and return the removed node entry.\r
1025 \r
1026   Removes the last node entry from a doubly linked list. It is up to the caller of\r
1027   this function to release the memory used by the first node if that is required. On\r
1028   exit, the removed node is returned.\r
1029 \r
1030   If Head is NULL, then ASSERT().\r
1031   If Head was not initialized, then ASSERT().\r
1032   If PcdMaximumLinkedListLength is not zero, and the number of nodes in the\r
1033   linked list including the head node is greater than or equal to PcdMaximumLinkedListLength,\r
1034   then ASSERT().\r
1035 \r
1036   @param[in, out]  Head                  The list head.\r
1037 \r
1038   @return The last node entry that is removed from the list, NULL if the list is empty.\r
1039 \r
1040 **/\r
1041 LIST_ENTRY *\r
1042 EFIAPI\r
1043 NetListRemoveTail (\r
1044   IN OUT LIST_ENTRY            *Head\r
1045   )\r
1046 {\r
1047   LIST_ENTRY            *Last;\r
1048 \r
1049   ASSERT (Head != NULL);\r
1050 \r
1051   if (IsListEmpty (Head)) {\r
1052     return NULL;\r
1053   }\r
1054 \r
1055   Last                        = Head->BackLink;\r
1056   Head->BackLink              = Last->BackLink;\r
1057   Last->BackLink->ForwardLink = Head;\r
1058 \r
1059   DEBUG_CODE (\r
1060     Last->ForwardLink = (LIST_ENTRY *) NULL;\r
1061     Last->BackLink    = (LIST_ENTRY *) NULL;\r
1062   );\r
1063 \r
1064   return Last;\r
1065 }\r
1066 \r
1067 \r
1068 /**\r
1069   Insert a new node entry after a designated node entry of a doubly linked list.\r
1070 \r
1071   ASSERT if PrevEntry or NewEntry is NULL.\r
1072 \r
1073   Inserts a new node entry donated by NewEntry after the node entry donated by PrevEntry\r
1074   of the doubly linked list.\r
1075 \r
1076   @param[in, out]  PrevEntry             The previous entry to insert after.\r
1077   @param[in, out]  NewEntry              The new entry to insert.\r
1078 \r
1079 **/\r
1080 VOID\r
1081 EFIAPI\r
1082 NetListInsertAfter (\r
1083   IN OUT LIST_ENTRY         *PrevEntry,\r
1084   IN OUT LIST_ENTRY         *NewEntry\r
1085   )\r
1086 {\r
1087   ASSERT (PrevEntry != NULL && NewEntry != NULL);\r
1088 \r
1089   NewEntry->BackLink                = PrevEntry;\r
1090   NewEntry->ForwardLink             = PrevEntry->ForwardLink;\r
1091   PrevEntry->ForwardLink->BackLink  = NewEntry;\r
1092   PrevEntry->ForwardLink            = NewEntry;\r
1093 }\r
1094 \r
1095 \r
1096 /**\r
1097   Insert a new node entry before a designated node entry of a doubly linked list.\r
1098 \r
1099   ASSERT if PostEntry or NewEntry is NULL.\r
1100 \r
1101   Inserts a new node entry donated by NewEntry after the node entry donated by PostEntry\r
1102   of the doubly linked list.\r
1103 \r
1104   @param[in, out]  PostEntry             The entry to insert before.\r
1105   @param[in, out]  NewEntry              The new entry to insert.\r
1106 \r
1107 **/\r
1108 VOID\r
1109 EFIAPI\r
1110 NetListInsertBefore (\r
1111   IN OUT LIST_ENTRY     *PostEntry,\r
1112   IN OUT LIST_ENTRY     *NewEntry\r
1113   )\r
1114 {\r
1115   ASSERT (PostEntry != NULL && NewEntry != NULL);\r
1116 \r
1117   NewEntry->ForwardLink             = PostEntry;\r
1118   NewEntry->BackLink                = PostEntry->BackLink;\r
1119   PostEntry->BackLink->ForwardLink  = NewEntry;\r
1120   PostEntry->BackLink               = NewEntry;\r
1121 }\r
1122 \r
1123 /**\r
1124   Safe destroy nodes in a linked list, and return the length of the list after all possible operations finished.\r
1125 \r
1126   Destroy network child instance list by list traversals is not safe due to graph dependencies between nodes.\r
1127   This function performs a safe traversal to destroy these nodes by checking to see if the node being destroyed\r
1128   has been removed from the list or not.\r
1129   If it has been removed, then restart the traversal from the head.\r
1130   If it hasn't been removed, then continue with the next node directly.\r
1131   This function will end the iterate and return the CallBack's last return value if error happens,\r
1132   or retrun EFI_SUCCESS if 2 complete passes are made with no changes in the number of children in the list.\r
1133 \r
1134   @param[in]    List             The head of the list.\r
1135   @param[in]    CallBack         Pointer to the callback function to destroy one node in the list.\r
1136   @param[in]    Context          Pointer to the callback function's context: corresponds to the\r
1137                                  parameter Context in NET_DESTROY_LINK_LIST_CALLBACK.\r
1138   @param[out]   ListLength       The length of the link list if the function returns successfully.\r
1139 \r
1140   @retval EFI_SUCCESS            Two complete passes are made with no changes in the number of children.\r
1141   @retval EFI_INVALID_PARAMETER  The input parameter is invalid.\r
1142   @retval Others                 Return the CallBack's last return value.\r
1143 \r
1144 **/\r
1145 EFI_STATUS\r
1146 EFIAPI\r
1147 NetDestroyLinkList (\r
1148   IN   LIST_ENTRY                       *List,\r
1149   IN   NET_DESTROY_LINK_LIST_CALLBACK   CallBack,\r
1150   IN   VOID                             *Context,    OPTIONAL\r
1151   OUT  UINTN                            *ListLength  OPTIONAL\r
1152   )\r
1153 {\r
1154   UINTN                         PreviousLength;\r
1155   LIST_ENTRY                    *Entry;\r
1156   LIST_ENTRY                    *Ptr;\r
1157   UINTN                         Length;\r
1158   EFI_STATUS                    Status;\r
1159 \r
1160   if (List == NULL || CallBack == NULL) {\r
1161     return EFI_INVALID_PARAMETER;\r
1162   }\r
1163 \r
1164   Length = 0;\r
1165   do {\r
1166     PreviousLength = Length;\r
1167     Entry = GetFirstNode (List);\r
1168     while (!IsNull (List, Entry)) {\r
1169       Status = CallBack (Entry, Context);\r
1170       if (EFI_ERROR (Status)) {\r
1171         return Status;\r
1172       }\r
1173       //\r
1174       // Walk through the list to see whether the Entry has been removed or not.\r
1175       // If the Entry still exists, just try to destroy the next one.\r
1176       // If not, go back to the start point to iterate the list again.\r
1177       //\r
1178       for (Ptr = List->ForwardLink; Ptr != List; Ptr = Ptr->ForwardLink) {\r
1179         if (Ptr == Entry) {\r
1180           break;\r
1181         }\r
1182       }\r
1183       if (Ptr == Entry) {\r
1184         Entry = GetNextNode (List, Entry);\r
1185       } else {\r
1186         Entry = GetFirstNode (List);\r
1187       }\r
1188     }\r
1189     for (Length = 0, Ptr = List->ForwardLink; Ptr != List; Length++, Ptr = Ptr->ForwardLink);\r
1190   } while (Length != PreviousLength);\r
1191 \r
1192   if (ListLength != NULL) {\r
1193     *ListLength = Length;\r
1194   }\r
1195   return EFI_SUCCESS;\r
1196 }\r
1197 \r
1198 /**\r
1199   This function checks the input Handle to see if it's one of these handles in ChildHandleBuffer.\r
1200 \r
1201   @param[in]  Handle             Handle to be checked.\r
1202   @param[in]  NumberOfChildren   Number of Handles in ChildHandleBuffer.\r
1203   @param[in]  ChildHandleBuffer  An array of child handles to be freed. May be NULL\r
1204                                  if NumberOfChildren is 0.\r
1205 \r
1206   @retval TRUE                   Found the input Handle in ChildHandleBuffer.\r
1207   @retval FALSE                  Can't find the input Handle in ChildHandleBuffer.\r
1208 \r
1209 **/\r
1210 BOOLEAN\r
1211 EFIAPI\r
1212 NetIsInHandleBuffer (\r
1213   IN  EFI_HANDLE          Handle,\r
1214   IN  UINTN               NumberOfChildren,\r
1215   IN  EFI_HANDLE          *ChildHandleBuffer OPTIONAL\r
1216   )\r
1217 {\r
1218   UINTN     Index;\r
1219 \r
1220   if (NumberOfChildren == 0 || ChildHandleBuffer == NULL) {\r
1221     return FALSE;\r
1222   }\r
1223 \r
1224   for (Index = 0; Index < NumberOfChildren; Index++) {\r
1225     if (Handle == ChildHandleBuffer[Index]) {\r
1226       return TRUE;\r
1227     }\r
1228   }\r
1229 \r
1230   return FALSE;\r
1231 }\r
1232 \r
1233 \r
1234 /**\r
1235   Initialize the netmap. Netmap is a reposity to keep the <Key, Value> pairs.\r
1236 \r
1237   Initialize the forward and backward links of two head nodes donated by Map->Used\r
1238   and Map->Recycled of two doubly linked lists.\r
1239   Initializes the count of the <Key, Value> pairs in the netmap to zero.\r
1240 \r
1241   If Map is NULL, then ASSERT().\r
1242   If the address of Map->Used is NULL, then ASSERT().\r
1243   If the address of Map->Recycled is NULl, then ASSERT().\r
1244 \r
1245   @param[in, out]  Map                   The netmap to initialize.\r
1246 \r
1247 **/\r
1248 VOID\r
1249 EFIAPI\r
1250 NetMapInit (\r
1251   IN OUT NET_MAP                *Map\r
1252   )\r
1253 {\r
1254   ASSERT (Map != NULL);\r
1255 \r
1256   InitializeListHead (&Map->Used);\r
1257   InitializeListHead (&Map->Recycled);\r
1258   Map->Count = 0;\r
1259 }\r
1260 \r
1261 \r
1262 /**\r
1263   To clean up the netmap, that is, release allocated memories.\r
1264 \r
1265   Removes all nodes of the Used doubly linked list and free memory of all related netmap items.\r
1266   Removes all nodes of the Recycled doubly linked list and free memory of all related netmap items.\r
1267   The number of the <Key, Value> pairs in the netmap is set to be zero.\r
1268 \r
1269   If Map is NULL, then ASSERT().\r
1270 \r
1271   @param[in, out]  Map                   The netmap to clean up.\r
1272 \r
1273 **/\r
1274 VOID\r
1275 EFIAPI\r
1276 NetMapClean (\r
1277   IN OUT NET_MAP            *Map\r
1278   )\r
1279 {\r
1280   NET_MAP_ITEM              *Item;\r
1281   LIST_ENTRY                *Entry;\r
1282   LIST_ENTRY                *Next;\r
1283 \r
1284   ASSERT (Map != NULL);\r
1285 \r
1286   NET_LIST_FOR_EACH_SAFE (Entry, Next, &Map->Used) {\r
1287     Item = NET_LIST_USER_STRUCT (Entry, NET_MAP_ITEM, Link);\r
1288 \r
1289     RemoveEntryList (&Item->Link);\r
1290     Map->Count--;\r
1291 \r
1292     gBS->FreePool (Item);\r
1293   }\r
1294 \r
1295   ASSERT ((Map->Count == 0) && IsListEmpty (&Map->Used));\r
1296 \r
1297   NET_LIST_FOR_EACH_SAFE (Entry, Next, &Map->Recycled) {\r
1298     Item = NET_LIST_USER_STRUCT (Entry, NET_MAP_ITEM, Link);\r
1299 \r
1300     RemoveEntryList (&Item->Link);\r
1301     gBS->FreePool (Item);\r
1302   }\r
1303 \r
1304   ASSERT (IsListEmpty (&Map->Recycled));\r
1305 }\r
1306 \r
1307 \r
1308 /**\r
1309   Test whether the netmap is empty and return true if it is.\r
1310 \r
1311   If the number of the <Key, Value> pairs in the netmap is zero, return TRUE.\r
1312 \r
1313   If Map is NULL, then ASSERT().\r
1314 \r
1315   @param[in]  Map                   The net map to test.\r
1316 \r
1317   @return TRUE if the netmap is empty, otherwise FALSE.\r
1318 \r
1319 **/\r
1320 BOOLEAN\r
1321 EFIAPI\r
1322 NetMapIsEmpty (\r
1323   IN NET_MAP                *Map\r
1324   )\r
1325 {\r
1326   ASSERT (Map != NULL);\r
1327   return (BOOLEAN) (Map->Count == 0);\r
1328 }\r
1329 \r
1330 \r
1331 /**\r
1332   Return the number of the <Key, Value> pairs in the netmap.\r
1333 \r
1334   If Map is NULL, then ASSERT().\r
1335 \r
1336   @param[in]  Map                   The netmap to get the entry number.\r
1337 \r
1338   @return The entry number in the netmap.\r
1339 \r
1340 **/\r
1341 UINTN\r
1342 EFIAPI\r
1343 NetMapGetCount (\r
1344   IN NET_MAP                *Map\r
1345   )\r
1346 {\r
1347   ASSERT (Map != NULL);\r
1348   return Map->Count;\r
1349 }\r
1350 \r
1351 \r
1352 /**\r
1353   Return one allocated item.\r
1354 \r
1355   If the Recycled doubly linked list of the netmap is empty, it will try to allocate\r
1356   a batch of items if there are enough resources and add corresponding nodes to the begining\r
1357   of the Recycled doubly linked list of the netmap. Otherwise, it will directly remove\r
1358   the fist node entry of the Recycled doubly linked list and return the corresponding item.\r
1359 \r
1360   If Map is NULL, then ASSERT().\r
1361 \r
1362   @param[in, out]  Map          The netmap to allocate item for.\r
1363 \r
1364   @return                       The allocated item. If NULL, the\r
1365                                 allocation failed due to resource limit.\r
1366 \r
1367 **/\r
1368 NET_MAP_ITEM *\r
1369 NetMapAllocItem (\r
1370   IN OUT NET_MAP            *Map\r
1371   )\r
1372 {\r
1373   NET_MAP_ITEM              *Item;\r
1374   LIST_ENTRY                *Head;\r
1375   UINTN                     Index;\r
1376 \r
1377   ASSERT (Map != NULL);\r
1378 \r
1379   Head = &Map->Recycled;\r
1380 \r
1381   if (IsListEmpty (Head)) {\r
1382     for (Index = 0; Index < NET_MAP_INCREAMENT; Index++) {\r
1383       Item = AllocatePool (sizeof (NET_MAP_ITEM));\r
1384 \r
1385       if (Item == NULL) {\r
1386         if (Index == 0) {\r
1387           return NULL;\r
1388         }\r
1389 \r
1390         break;\r
1391       }\r
1392 \r
1393       InsertHeadList (Head, &Item->Link);\r
1394     }\r
1395   }\r
1396 \r
1397   Item = NET_LIST_HEAD (Head, NET_MAP_ITEM, Link);\r
1398   NetListRemoveHead (Head);\r
1399 \r
1400   return Item;\r
1401 }\r
1402 \r
1403 \r
1404 /**\r
1405   Allocate an item to save the <Key, Value> pair to the head of the netmap.\r
1406 \r
1407   Allocate an item to save the <Key, Value> pair and add corresponding node entry\r
1408   to the beginning of the Used doubly linked list. The number of the <Key, Value>\r
1409   pairs in the netmap increase by 1.\r
1410 \r
1411   If Map is NULL, then ASSERT().\r
1412   If Key is NULL, then ASSERT().\r
1413 \r
1414   @param[in, out]  Map                   The netmap to insert into.\r
1415   @param[in]       Key                   The user's key.\r
1416   @param[in]       Value                 The user's value for the key.\r
1417 \r
1418   @retval EFI_OUT_OF_RESOURCES  Failed to allocate the memory for the item.\r
1419   @retval EFI_SUCCESS           The item is inserted to the head.\r
1420 \r
1421 **/\r
1422 EFI_STATUS\r
1423 EFIAPI\r
1424 NetMapInsertHead (\r
1425   IN OUT NET_MAP            *Map,\r
1426   IN VOID                   *Key,\r
1427   IN VOID                   *Value    OPTIONAL\r
1428   )\r
1429 {\r
1430   NET_MAP_ITEM              *Item;\r
1431 \r
1432   ASSERT (Map != NULL && Key != NULL);\r
1433 \r
1434   Item = NetMapAllocItem (Map);\r
1435 \r
1436   if (Item == NULL) {\r
1437     return EFI_OUT_OF_RESOURCES;\r
1438   }\r
1439 \r
1440   Item->Key   = Key;\r
1441   Item->Value = Value;\r
1442   InsertHeadList (&Map->Used, &Item->Link);\r
1443 \r
1444   Map->Count++;\r
1445   return EFI_SUCCESS;\r
1446 }\r
1447 \r
1448 \r
1449 /**\r
1450   Allocate an item to save the <Key, Value> pair to the tail of the netmap.\r
1451 \r
1452   Allocate an item to save the <Key, Value> pair and add corresponding node entry\r
1453   to the tail of the Used doubly linked list. The number of the <Key, Value>\r
1454   pairs in the netmap increase by 1.\r
1455 \r
1456   If Map is NULL, then ASSERT().\r
1457   If Key is NULL, then ASSERT().\r
1458 \r
1459   @param[in, out]  Map                   The netmap to insert into.\r
1460   @param[in]       Key                   The user's key.\r
1461   @param[in]       Value                 The user's value for the key.\r
1462 \r
1463   @retval EFI_OUT_OF_RESOURCES  Failed to allocate the memory for the item.\r
1464   @retval EFI_SUCCESS           The item is inserted to the tail.\r
1465 \r
1466 **/\r
1467 EFI_STATUS\r
1468 EFIAPI\r
1469 NetMapInsertTail (\r
1470   IN OUT NET_MAP            *Map,\r
1471   IN VOID                   *Key,\r
1472   IN VOID                   *Value    OPTIONAL\r
1473   )\r
1474 {\r
1475   NET_MAP_ITEM              *Item;\r
1476 \r
1477   ASSERT (Map != NULL && Key != NULL);\r
1478 \r
1479   Item = NetMapAllocItem (Map);\r
1480 \r
1481   if (Item == NULL) {\r
1482     return EFI_OUT_OF_RESOURCES;\r
1483   }\r
1484 \r
1485   Item->Key   = Key;\r
1486   Item->Value = Value;\r
1487   InsertTailList (&Map->Used, &Item->Link);\r
1488 \r
1489   Map->Count++;\r
1490 \r
1491   return EFI_SUCCESS;\r
1492 }\r
1493 \r
1494 \r
1495 /**\r
1496   Check whether the item is in the Map and return TRUE if it is.\r
1497 \r
1498   If Map is NULL, then ASSERT().\r
1499   If Item is NULL, then ASSERT().\r
1500 \r
1501   @param[in]  Map                   The netmap to search within.\r
1502   @param[in]  Item                  The item to search.\r
1503 \r
1504   @return TRUE if the item is in the netmap, otherwise FALSE.\r
1505 \r
1506 **/\r
1507 BOOLEAN\r
1508 NetItemInMap (\r
1509   IN NET_MAP                *Map,\r
1510   IN NET_MAP_ITEM           *Item\r
1511   )\r
1512 {\r
1513   LIST_ENTRY            *ListEntry;\r
1514 \r
1515   ASSERT (Map != NULL && Item != NULL);\r
1516 \r
1517   NET_LIST_FOR_EACH (ListEntry, &Map->Used) {\r
1518     if (ListEntry == &Item->Link) {\r
1519       return TRUE;\r
1520     }\r
1521   }\r
1522 \r
1523   return FALSE;\r
1524 }\r
1525 \r
1526 \r
1527 /**\r
1528   Find the key in the netmap and returns the point to the item contains the Key.\r
1529 \r
1530   Iterate the Used doubly linked list of the netmap to get every item. Compare the key of every\r
1531   item with the key to search. It returns the point to the item contains the Key if found.\r
1532 \r
1533   If Map is NULL, then ASSERT().\r
1534   If Key is NULL, then ASSERT().\r
1535 \r
1536   @param[in]  Map                   The netmap to search within.\r
1537   @param[in]  Key                   The key to search.\r
1538 \r
1539   @return The point to the item contains the Key, or NULL if Key isn't in the map.\r
1540 \r
1541 **/\r
1542 NET_MAP_ITEM *\r
1543 EFIAPI\r
1544 NetMapFindKey (\r
1545   IN  NET_MAP               *Map,\r
1546   IN  VOID                  *Key\r
1547   )\r
1548 {\r
1549   LIST_ENTRY              *Entry;\r
1550   NET_MAP_ITEM            *Item;\r
1551 \r
1552   ASSERT (Map != NULL && Key != NULL);\r
1553 \r
1554   NET_LIST_FOR_EACH (Entry, &Map->Used) {\r
1555     Item = NET_LIST_USER_STRUCT (Entry, NET_MAP_ITEM, Link);\r
1556 \r
1557     if (Item->Key == Key) {\r
1558       return Item;\r
1559     }\r
1560   }\r
1561 \r
1562   return NULL;\r
1563 }\r
1564 \r
1565 \r
1566 /**\r
1567   Remove the node entry of the item from the netmap and return the key of the removed item.\r
1568 \r
1569   Remove the node entry of the item from the Used doubly linked list of the netmap.\r
1570   The number of the <Key, Value> pairs in the netmap decrease by 1. Then add the node\r
1571   entry of the item to the Recycled doubly linked list of the netmap. If Value is not NULL,\r
1572   Value will point to the value of the item. It returns the key of the removed item.\r
1573 \r
1574   If Map is NULL, then ASSERT().\r
1575   If Item is NULL, then ASSERT().\r
1576   if item in not in the netmap, then ASSERT().\r
1577 \r
1578   @param[in, out]  Map                   The netmap to remove the item from.\r
1579   @param[in, out]  Item                  The item to remove.\r
1580   @param[out]      Value                 The variable to receive the value if not NULL.\r
1581 \r
1582   @return                                The key of the removed item.\r
1583 \r
1584 **/\r
1585 VOID *\r
1586 EFIAPI\r
1587 NetMapRemoveItem (\r
1588   IN  OUT NET_MAP             *Map,\r
1589   IN  OUT NET_MAP_ITEM        *Item,\r
1590   OUT VOID                    **Value           OPTIONAL\r
1591   )\r
1592 {\r
1593   ASSERT ((Map != NULL) && (Item != NULL));\r
1594   ASSERT (NetItemInMap (Map, Item));\r
1595 \r
1596   RemoveEntryList (&Item->Link);\r
1597   Map->Count--;\r
1598   InsertHeadList (&Map->Recycled, &Item->Link);\r
1599 \r
1600   if (Value != NULL) {\r
1601     *Value = Item->Value;\r
1602   }\r
1603 \r
1604   return Item->Key;\r
1605 }\r
1606 \r
1607 \r
1608 /**\r
1609   Remove the first node entry on the netmap and return the key of the removed item.\r
1610 \r
1611   Remove the first node entry from the Used doubly linked list of the netmap.\r
1612   The number of the <Key, Value> pairs in the netmap decrease by 1. Then add the node\r
1613   entry to the Recycled doubly linked list of the netmap. If parameter Value is not NULL,\r
1614   parameter Value will point to the value of the item. It returns the key of the removed item.\r
1615 \r
1616   If Map is NULL, then ASSERT().\r
1617   If the Used doubly linked list is empty, then ASSERT().\r
1618 \r
1619   @param[in, out]  Map                   The netmap to remove the head from.\r
1620   @param[out]      Value                 The variable to receive the value if not NULL.\r
1621 \r
1622   @return                                The key of the item removed.\r
1623 \r
1624 **/\r
1625 VOID *\r
1626 EFIAPI\r
1627 NetMapRemoveHead (\r
1628   IN OUT NET_MAP            *Map,\r
1629   OUT VOID                  **Value         OPTIONAL\r
1630   )\r
1631 {\r
1632   NET_MAP_ITEM  *Item;\r
1633 \r
1634   //\r
1635   // Often, it indicates a programming error to remove\r
1636   // the first entry in an empty list\r
1637   //\r
1638   ASSERT (Map && !IsListEmpty (&Map->Used));\r
1639 \r
1640   Item = NET_LIST_HEAD (&Map->Used, NET_MAP_ITEM, Link);\r
1641   RemoveEntryList (&Item->Link);\r
1642   Map->Count--;\r
1643   InsertHeadList (&Map->Recycled, &Item->Link);\r
1644 \r
1645   if (Value != NULL) {\r
1646     *Value = Item->Value;\r
1647   }\r
1648 \r
1649   return Item->Key;\r
1650 }\r
1651 \r
1652 \r
1653 /**\r
1654   Remove the last node entry on the netmap and return the key of the removed item.\r
1655 \r
1656   Remove the last node entry from the Used doubly linked list of the netmap.\r
1657   The number of the <Key, Value> pairs in the netmap decrease by 1. Then add the node\r
1658   entry to the Recycled doubly linked list of the netmap. If parameter Value is not NULL,\r
1659   parameter Value will point to the value of the item. It returns the key of the removed item.\r
1660 \r
1661   If Map is NULL, then ASSERT().\r
1662   If the Used doubly linked list is empty, then ASSERT().\r
1663 \r
1664   @param[in, out]  Map                   The netmap to remove the tail from.\r
1665   @param[out]      Value                 The variable to receive the value if not NULL.\r
1666 \r
1667   @return                                The key of the item removed.\r
1668 \r
1669 **/\r
1670 VOID *\r
1671 EFIAPI\r
1672 NetMapRemoveTail (\r
1673   IN OUT NET_MAP            *Map,\r
1674   OUT VOID                  **Value       OPTIONAL\r
1675   )\r
1676 {\r
1677   NET_MAP_ITEM              *Item;\r
1678 \r
1679   //\r
1680   // Often, it indicates a programming error to remove\r
1681   // the last entry in an empty list\r
1682   //\r
1683   ASSERT (Map && !IsListEmpty (&Map->Used));\r
1684 \r
1685   Item = NET_LIST_TAIL (&Map->Used, NET_MAP_ITEM, Link);\r
1686   RemoveEntryList (&Item->Link);\r
1687   Map->Count--;\r
1688   InsertHeadList (&Map->Recycled, &Item->Link);\r
1689 \r
1690   if (Value != NULL) {\r
1691     *Value = Item->Value;\r
1692   }\r
1693 \r
1694   return Item->Key;\r
1695 }\r
1696 \r
1697 \r
1698 /**\r
1699   Iterate through the netmap and call CallBack for each item.\r
1700 \r
1701   It will continue the traverse if CallBack returns EFI_SUCCESS, otherwise, break\r
1702   from the loop. It returns the CallBack's last return value. This function is\r
1703   delete safe for the current item.\r
1704 \r
1705   If Map is NULL, then ASSERT().\r
1706   If CallBack is NULL, then ASSERT().\r
1707 \r
1708   @param[in]  Map                   The Map to iterate through.\r
1709   @param[in]  CallBack              The callback function to call for each item.\r
1710   @param[in]  Arg                   The opaque parameter to the callback.\r
1711 \r
1712   @retval EFI_SUCCESS            There is no item in the netmap or CallBack for each item\r
1713                                  return EFI_SUCCESS.\r
1714   @retval Others                 It returns the CallBack's last return value.\r
1715 \r
1716 **/\r
1717 EFI_STATUS\r
1718 EFIAPI\r
1719 NetMapIterate (\r
1720   IN NET_MAP                *Map,\r
1721   IN NET_MAP_CALLBACK       CallBack,\r
1722   IN VOID                   *Arg      OPTIONAL\r
1723   )\r
1724 {\r
1725 \r
1726   LIST_ENTRY            *Entry;\r
1727   LIST_ENTRY            *Next;\r
1728   LIST_ENTRY            *Head;\r
1729   NET_MAP_ITEM          *Item;\r
1730   EFI_STATUS            Result;\r
1731 \r
1732   ASSERT ((Map != NULL) && (CallBack != NULL));\r
1733 \r
1734   Head = &Map->Used;\r
1735 \r
1736   if (IsListEmpty (Head)) {\r
1737     return EFI_SUCCESS;\r
1738   }\r
1739 \r
1740   NET_LIST_FOR_EACH_SAFE (Entry, Next, Head) {\r
1741     Item   = NET_LIST_USER_STRUCT (Entry, NET_MAP_ITEM, Link);\r
1742     Result = CallBack (Map, Item, Arg);\r
1743 \r
1744     if (EFI_ERROR (Result)) {\r
1745       return Result;\r
1746     }\r
1747   }\r
1748 \r
1749   return EFI_SUCCESS;\r
1750 }\r
1751 \r
1752 \r
1753 /**\r
1754   This is the default unload handle for all the network drivers.\r
1755 \r
1756   Disconnect the driver specified by ImageHandle from all the devices in the handle database.\r
1757   Uninstall all the protocols installed in the driver entry point.\r
1758 \r
1759   @param[in]  ImageHandle       The drivers' driver image.\r
1760 \r
1761   @retval EFI_SUCCESS           The image is unloaded.\r
1762   @retval Others                Failed to unload the image.\r
1763 \r
1764 **/\r
1765 EFI_STATUS\r
1766 EFIAPI\r
1767 NetLibDefaultUnload (\r
1768   IN EFI_HANDLE             ImageHandle\r
1769   )\r
1770 {\r
1771   EFI_STATUS                        Status;\r
1772   EFI_HANDLE                        *DeviceHandleBuffer;\r
1773   UINTN                             DeviceHandleCount;\r
1774   UINTN                             Index;\r
1775   UINTN                             Index2;\r
1776   EFI_DRIVER_BINDING_PROTOCOL       *DriverBinding;\r
1777   EFI_COMPONENT_NAME_PROTOCOL       *ComponentName;\r
1778   EFI_COMPONENT_NAME2_PROTOCOL      *ComponentName2;\r
1779 \r
1780   //\r
1781   // Get the list of all the handles in the handle database.\r
1782   // If there is an error getting the list, then the unload\r
1783   // operation fails.\r
1784   //\r
1785   Status = gBS->LocateHandleBuffer (\r
1786                   AllHandles,\r
1787                   NULL,\r
1788                   NULL,\r
1789                   &DeviceHandleCount,\r
1790                   &DeviceHandleBuffer\r
1791                   );\r
1792 \r
1793   if (EFI_ERROR (Status)) {\r
1794     return Status;\r
1795   }\r
1796 \r
1797   for (Index = 0; Index < DeviceHandleCount; Index++) {\r
1798     Status = gBS->HandleProtocol (\r
1799                     DeviceHandleBuffer[Index],\r
1800                     &gEfiDriverBindingProtocolGuid,\r
1801                     (VOID **) &DriverBinding\r
1802                     );\r
1803     if (EFI_ERROR (Status)) {\r
1804       continue;\r
1805     }\r
1806 \r
1807     if (DriverBinding->ImageHandle != ImageHandle) {\r
1808       continue;\r
1809     }\r
1810 \r
1811     //\r
1812     // Disconnect the driver specified by ImageHandle from all\r
1813     // the devices in the handle database.\r
1814     //\r
1815     for (Index2 = 0; Index2 < DeviceHandleCount; Index2++) {\r
1816       Status = gBS->DisconnectController (\r
1817                       DeviceHandleBuffer[Index2],\r
1818                       DriverBinding->DriverBindingHandle,\r
1819                       NULL\r
1820                       );\r
1821     }\r
1822 \r
1823     //\r
1824     // Uninstall all the protocols installed in the driver entry point\r
1825     //\r
1826     gBS->UninstallProtocolInterface (\r
1827           DriverBinding->DriverBindingHandle,\r
1828           &gEfiDriverBindingProtocolGuid,\r
1829           DriverBinding\r
1830           );\r
1831 \r
1832     Status = gBS->HandleProtocol (\r
1833                     DeviceHandleBuffer[Index],\r
1834                     &gEfiComponentNameProtocolGuid,\r
1835                     (VOID **) &ComponentName\r
1836                     );\r
1837     if (!EFI_ERROR (Status)) {\r
1838       gBS->UninstallProtocolInterface (\r
1839              DriverBinding->DriverBindingHandle,\r
1840              &gEfiComponentNameProtocolGuid,\r
1841              ComponentName\r
1842              );\r
1843     }\r
1844 \r
1845     Status = gBS->HandleProtocol (\r
1846                     DeviceHandleBuffer[Index],\r
1847                     &gEfiComponentName2ProtocolGuid,\r
1848                     (VOID **) &ComponentName2\r
1849                     );\r
1850     if (!EFI_ERROR (Status)) {\r
1851       gBS->UninstallProtocolInterface (\r
1852              DriverBinding->DriverBindingHandle,\r
1853              &gEfiComponentName2ProtocolGuid,\r
1854              ComponentName2\r
1855              );\r
1856     }\r
1857   }\r
1858 \r
1859   //\r
1860   // Free the buffer containing the list of handles from the handle database\r
1861   //\r
1862   if (DeviceHandleBuffer != NULL) {\r
1863     gBS->FreePool (DeviceHandleBuffer);\r
1864   }\r
1865 \r
1866   return EFI_SUCCESS;\r
1867 }\r
1868 \r
1869 \r
1870 \r
1871 /**\r
1872   Create a child of the service that is identified by ServiceBindingGuid.\r
1873 \r
1874   Get the ServiceBinding Protocol first, then use it to create a child.\r
1875 \r
1876   If ServiceBindingGuid is NULL, then ASSERT().\r
1877   If ChildHandle is NULL, then ASSERT().\r
1878 \r
1879   @param[in]       Controller            The controller which has the service installed.\r
1880   @param[in]       Image                 The image handle used to open service.\r
1881   @param[in]       ServiceBindingGuid    The service's Guid.\r
1882   @param[in, out]  ChildHandle           The handle to receive the create child.\r
1883 \r
1884   @retval EFI_SUCCESS           The child is successfully created.\r
1885   @retval Others                Failed to create the child.\r
1886 \r
1887 **/\r
1888 EFI_STATUS\r
1889 EFIAPI\r
1890 NetLibCreateServiceChild (\r
1891   IN  EFI_HANDLE            Controller,\r
1892   IN  EFI_HANDLE            Image,\r
1893   IN  EFI_GUID              *ServiceBindingGuid,\r
1894   IN  OUT EFI_HANDLE        *ChildHandle\r
1895   )\r
1896 {\r
1897   EFI_STATUS                    Status;\r
1898   EFI_SERVICE_BINDING_PROTOCOL  *Service;\r
1899 \r
1900 \r
1901   ASSERT ((ServiceBindingGuid != NULL) && (ChildHandle != NULL));\r
1902 \r
1903   //\r
1904   // Get the ServiceBinding Protocol\r
1905   //\r
1906   Status = gBS->OpenProtocol (\r
1907                   Controller,\r
1908                   ServiceBindingGuid,\r
1909                   (VOID **) &Service,\r
1910                   Image,\r
1911                   Controller,\r
1912                   EFI_OPEN_PROTOCOL_GET_PROTOCOL\r
1913                   );\r
1914 \r
1915   if (EFI_ERROR (Status)) {\r
1916     return Status;\r
1917   }\r
1918 \r
1919   //\r
1920   // Create a child\r
1921   //\r
1922   Status = Service->CreateChild (Service, ChildHandle);\r
1923   return Status;\r
1924 }\r
1925 \r
1926 \r
1927 /**\r
1928   Destroy a child of the service that is identified by ServiceBindingGuid.\r
1929 \r
1930   Get the ServiceBinding Protocol first, then use it to destroy a child.\r
1931 \r
1932   If ServiceBindingGuid is NULL, then ASSERT().\r
1933 \r
1934   @param[in]   Controller            The controller which has the service installed.\r
1935   @param[in]   Image                 The image handle used to open service.\r
1936   @param[in]   ServiceBindingGuid    The service's Guid.\r
1937   @param[in]   ChildHandle           The child to destroy.\r
1938 \r
1939   @retval EFI_SUCCESS           The child is successfully destroyed.\r
1940   @retval Others                Failed to destroy the child.\r
1941 \r
1942 **/\r
1943 EFI_STATUS\r
1944 EFIAPI\r
1945 NetLibDestroyServiceChild (\r
1946   IN  EFI_HANDLE            Controller,\r
1947   IN  EFI_HANDLE            Image,\r
1948   IN  EFI_GUID              *ServiceBindingGuid,\r
1949   IN  EFI_HANDLE            ChildHandle\r
1950   )\r
1951 {\r
1952   EFI_STATUS                    Status;\r
1953   EFI_SERVICE_BINDING_PROTOCOL  *Service;\r
1954 \r
1955   ASSERT (ServiceBindingGuid != NULL);\r
1956 \r
1957   //\r
1958   // Get the ServiceBinding Protocol\r
1959   //\r
1960   Status = gBS->OpenProtocol (\r
1961                   Controller,\r
1962                   ServiceBindingGuid,\r
1963                   (VOID **) &Service,\r
1964                   Image,\r
1965                   Controller,\r
1966                   EFI_OPEN_PROTOCOL_GET_PROTOCOL\r
1967                   );\r
1968 \r
1969   if (EFI_ERROR (Status)) {\r
1970     return Status;\r
1971   }\r
1972 \r
1973   //\r
1974   // destroy the child\r
1975   //\r
1976   Status = Service->DestroyChild (Service, ChildHandle);\r
1977   return Status;\r
1978 }\r
1979 \r
1980 /**\r
1981   Get handle with Simple Network Protocol installed on it.\r
1982 \r
1983   There should be MNP Service Binding Protocol installed on the input ServiceHandle.\r
1984   If Simple Network Protocol is already installed on the ServiceHandle, the\r
1985   ServiceHandle will be returned. If SNP is not installed on the ServiceHandle,\r
1986   try to find its parent handle with SNP installed.\r
1987 \r
1988   @param[in]   ServiceHandle    The handle where network service binding protocols are\r
1989                                 installed on.\r
1990   @param[out]  Snp              The pointer to store the address of the SNP instance.\r
1991                                 This is an optional parameter that may be NULL.\r
1992 \r
1993   @return The SNP handle, or NULL if not found.\r
1994 \r
1995 **/\r
1996 EFI_HANDLE\r
1997 EFIAPI\r
1998 NetLibGetSnpHandle (\r
1999   IN   EFI_HANDLE                  ServiceHandle,\r
2000   OUT  EFI_SIMPLE_NETWORK_PROTOCOL **Snp  OPTIONAL\r
2001   )\r
2002 {\r
2003   EFI_STATUS                   Status;\r
2004   EFI_SIMPLE_NETWORK_PROTOCOL  *SnpInstance;\r
2005   EFI_DEVICE_PATH_PROTOCOL     *DevicePath;\r
2006   EFI_HANDLE                   SnpHandle;\r
2007 \r
2008   //\r
2009   // Try to open SNP from ServiceHandle\r
2010   //\r
2011   SnpInstance = NULL;\r
2012   Status = gBS->HandleProtocol (ServiceHandle, &gEfiSimpleNetworkProtocolGuid, (VOID **) &SnpInstance);\r
2013   if (!EFI_ERROR (Status)) {\r
2014     if (Snp != NULL) {\r
2015       *Snp = SnpInstance;\r
2016     }\r
2017     return ServiceHandle;\r
2018   }\r
2019 \r
2020   //\r
2021   // Failed to open SNP, try to get SNP handle by LocateDevicePath()\r
2022   //\r
2023   DevicePath = DevicePathFromHandle (ServiceHandle);\r
2024   if (DevicePath == NULL) {\r
2025     return NULL;\r
2026   }\r
2027 \r
2028   SnpHandle = NULL;\r
2029   Status = gBS->LocateDevicePath (&gEfiSimpleNetworkProtocolGuid, &DevicePath, &SnpHandle);\r
2030   if (EFI_ERROR (Status)) {\r
2031     //\r
2032     // Failed to find SNP handle\r
2033     //\r
2034     return NULL;\r
2035   }\r
2036 \r
2037   Status = gBS->HandleProtocol (SnpHandle, &gEfiSimpleNetworkProtocolGuid, (VOID **) &SnpInstance);\r
2038   if (!EFI_ERROR (Status)) {\r
2039     if (Snp != NULL) {\r
2040       *Snp = SnpInstance;\r
2041     }\r
2042     return SnpHandle;\r
2043   }\r
2044 \r
2045   return NULL;\r
2046 }\r
2047 \r
2048 /**\r
2049   Retrieve VLAN ID of a VLAN device handle.\r
2050 \r
2051   Search VLAN device path node in Device Path of specified ServiceHandle and\r
2052   return its VLAN ID. If no VLAN device path node found, then this ServiceHandle\r
2053   is not a VLAN device handle, and 0 will be returned.\r
2054 \r
2055   @param[in]   ServiceHandle    The handle where network service binding protocols are\r
2056                                 installed on.\r
2057 \r
2058   @return VLAN ID of the device handle, or 0 if not a VLAN device.\r
2059 \r
2060 **/\r
2061 UINT16\r
2062 EFIAPI\r
2063 NetLibGetVlanId (\r
2064   IN EFI_HANDLE             ServiceHandle\r
2065   )\r
2066 {\r
2067   EFI_DEVICE_PATH_PROTOCOL  *DevicePath;\r
2068   EFI_DEVICE_PATH_PROTOCOL  *Node;\r
2069 \r
2070   DevicePath = DevicePathFromHandle (ServiceHandle);\r
2071   if (DevicePath == NULL) {\r
2072     return 0;\r
2073   }\r
2074 \r
2075   Node = DevicePath;\r
2076   while (!IsDevicePathEnd (Node)) {\r
2077     if (Node->Type == MESSAGING_DEVICE_PATH && Node->SubType == MSG_VLAN_DP) {\r
2078       return ((VLAN_DEVICE_PATH *) Node)->VlanId;\r
2079     }\r
2080     Node = NextDevicePathNode (Node);\r
2081   }\r
2082 \r
2083   return 0;\r
2084 }\r
2085 \r
2086 /**\r
2087   Find VLAN device handle with specified VLAN ID.\r
2088 \r
2089   The VLAN child device handle is created by VLAN Config Protocol on ControllerHandle.\r
2090   This function will append VLAN device path node to the parent device path,\r
2091   and then use LocateDevicePath() to find the correct VLAN device handle.\r
2092 \r
2093   @param[in]   ControllerHandle The handle where network service binding protocols are\r
2094                                 installed on.\r
2095   @param[in]   VlanId           The configured VLAN ID for the VLAN device.\r
2096 \r
2097   @return The VLAN device handle, or NULL if not found.\r
2098 \r
2099 **/\r
2100 EFI_HANDLE\r
2101 EFIAPI\r
2102 NetLibGetVlanHandle (\r
2103   IN EFI_HANDLE             ControllerHandle,\r
2104   IN UINT16                 VlanId\r
2105   )\r
2106 {\r
2107   EFI_DEVICE_PATH_PROTOCOL  *ParentDevicePath;\r
2108   EFI_DEVICE_PATH_PROTOCOL  *VlanDevicePath;\r
2109   EFI_DEVICE_PATH_PROTOCOL  *DevicePath;\r
2110   VLAN_DEVICE_PATH          VlanNode;\r
2111   EFI_HANDLE                Handle;\r
2112 \r
2113   ParentDevicePath = DevicePathFromHandle (ControllerHandle);\r
2114   if (ParentDevicePath == NULL) {\r
2115     return NULL;\r
2116   }\r
2117 \r
2118   //\r
2119   // Construct VLAN device path\r
2120   //\r
2121   CopyMem (&VlanNode, &mNetVlanDevicePathTemplate, sizeof (VLAN_DEVICE_PATH));\r
2122   VlanNode.VlanId = VlanId;\r
2123   VlanDevicePath = AppendDevicePathNode (\r
2124                      ParentDevicePath,\r
2125                      (EFI_DEVICE_PATH_PROTOCOL *) &VlanNode\r
2126                      );\r
2127   if (VlanDevicePath == NULL) {\r
2128     return NULL;\r
2129   }\r
2130 \r
2131   //\r
2132   // Find VLAN device handle\r
2133   //\r
2134   Handle = NULL;\r
2135   DevicePath = VlanDevicePath;\r
2136   gBS->LocateDevicePath (\r
2137          &gEfiDevicePathProtocolGuid,\r
2138          &DevicePath,\r
2139          &Handle\r
2140          );\r
2141   if (!IsDevicePathEnd (DevicePath)) {\r
2142     //\r
2143     // Device path is not exactly match\r
2144     //\r
2145     Handle = NULL;\r
2146   }\r
2147 \r
2148   FreePool (VlanDevicePath);\r
2149   return Handle;\r
2150 }\r
2151 \r
2152 /**\r
2153   Get MAC address associated with the network service handle.\r
2154 \r
2155   If MacAddress is NULL, then ASSERT().\r
2156   If AddressSize is NULL, then ASSERT().\r
2157 \r
2158   There should be MNP Service Binding Protocol installed on the input ServiceHandle.\r
2159   If SNP is installed on the ServiceHandle or its parent handle, MAC address will\r
2160   be retrieved from SNP. If no SNP found, try to get SNP mode data use MNP.\r
2161 \r
2162   @param[in]   ServiceHandle    The handle where network service binding protocols are\r
2163                                 installed on.\r
2164   @param[out]  MacAddress       The pointer to store the returned MAC address.\r
2165   @param[out]  AddressSize      The length of returned MAC address.\r
2166 \r
2167   @retval EFI_SUCCESS           MAC address is returned successfully.\r
2168   @retval Others                Failed to get SNP mode data.\r
2169 \r
2170 **/\r
2171 EFI_STATUS\r
2172 EFIAPI\r
2173 NetLibGetMacAddress (\r
2174   IN  EFI_HANDLE            ServiceHandle,\r
2175   OUT EFI_MAC_ADDRESS       *MacAddress,\r
2176   OUT UINTN                 *AddressSize\r
2177   )\r
2178 {\r
2179   EFI_STATUS                   Status;\r
2180   EFI_SIMPLE_NETWORK_PROTOCOL  *Snp;\r
2181   EFI_SIMPLE_NETWORK_MODE      *SnpMode;\r
2182   EFI_SIMPLE_NETWORK_MODE      SnpModeData;\r
2183   EFI_MANAGED_NETWORK_PROTOCOL *Mnp;\r
2184   EFI_SERVICE_BINDING_PROTOCOL *MnpSb;\r
2185   EFI_HANDLE                   *SnpHandle;\r
2186   EFI_HANDLE                   MnpChildHandle;\r
2187 \r
2188   ASSERT (MacAddress != NULL);\r
2189   ASSERT (AddressSize != NULL);\r
2190 \r
2191   //\r
2192   // Try to get SNP handle\r
2193   //\r
2194   Snp = NULL;\r
2195   SnpHandle = NetLibGetSnpHandle (ServiceHandle, &Snp);\r
2196   if (SnpHandle != NULL) {\r
2197     //\r
2198     // SNP found, use it directly\r
2199     //\r
2200     SnpMode = Snp->Mode;\r
2201   } else {\r
2202     //\r
2203     // Failed to get SNP handle, try to get MAC address from MNP\r
2204     //\r
2205     MnpChildHandle = NULL;\r
2206     Status = gBS->HandleProtocol (\r
2207                     ServiceHandle,\r
2208                     &gEfiManagedNetworkServiceBindingProtocolGuid,\r
2209                     (VOID **) &MnpSb\r
2210                     );\r
2211     if (EFI_ERROR (Status)) {\r
2212       return Status;\r
2213     }\r
2214 \r
2215     //\r
2216     // Create a MNP child\r
2217     //\r
2218     Status = MnpSb->CreateChild (MnpSb, &MnpChildHandle);\r
2219     if (EFI_ERROR (Status)) {\r
2220       return Status;\r
2221     }\r
2222 \r
2223     //\r
2224     // Open MNP protocol\r
2225     //\r
2226     Status = gBS->HandleProtocol (\r
2227                     MnpChildHandle,\r
2228                     &gEfiManagedNetworkProtocolGuid,\r
2229                     (VOID **) &Mnp\r
2230                     );\r
2231     if (EFI_ERROR (Status)) {\r
2232       MnpSb->DestroyChild (MnpSb, MnpChildHandle);\r
2233       return Status;\r
2234     }\r
2235 \r
2236     //\r
2237     // Try to get SNP mode from MNP\r
2238     //\r
2239     Status = Mnp->GetModeData (Mnp, NULL, &SnpModeData);\r
2240     if (EFI_ERROR (Status) && (Status != EFI_NOT_STARTED)) {\r
2241       MnpSb->DestroyChild (MnpSb, MnpChildHandle);\r
2242       return Status;\r
2243     }\r
2244     SnpMode = &SnpModeData;\r
2245 \r
2246     //\r
2247     // Destroy the MNP child\r
2248     //\r
2249     MnpSb->DestroyChild (MnpSb, MnpChildHandle);\r
2250   }\r
2251 \r
2252   *AddressSize = SnpMode->HwAddressSize;\r
2253   CopyMem (MacAddress->Addr, SnpMode->CurrentAddress.Addr, SnpMode->HwAddressSize);\r
2254 \r
2255   return EFI_SUCCESS;\r
2256 }\r
2257 \r
2258 /**\r
2259   Convert MAC address of the NIC associated with specified Service Binding Handle\r
2260   to a unicode string. Callers are responsible for freeing the string storage.\r
2261 \r
2262   If MacString is NULL, then ASSERT().\r
2263 \r
2264   Locate simple network protocol associated with the Service Binding Handle and\r
2265   get the mac address from SNP. Then convert the mac address into a unicode\r
2266   string. It takes 2 unicode characters to represent a 1 byte binary buffer.\r
2267   Plus one unicode character for the null-terminator.\r
2268 \r
2269   @param[in]   ServiceHandle         The handle where network service binding protocol is\r
2270                                      installed on.\r
2271   @param[in]   ImageHandle           The image handle used to act as the agent handle to\r
2272                                      get the simple network protocol. This parameter is\r
2273                                      optional and may be NULL.\r
2274   @param[out]  MacString             The pointer to store the address of the string\r
2275                                      representation of  the mac address.\r
2276 \r
2277   @retval EFI_SUCCESS           Convert the mac address a unicode string successfully.\r
2278   @retval EFI_OUT_OF_RESOURCES  There are not enough memory resource.\r
2279   @retval Others                Failed to open the simple network protocol.\r
2280 \r
2281 **/\r
2282 EFI_STATUS\r
2283 EFIAPI\r
2284 NetLibGetMacString (\r
2285   IN  EFI_HANDLE            ServiceHandle,\r
2286   IN  EFI_HANDLE            ImageHandle, OPTIONAL\r
2287   OUT CHAR16                **MacString\r
2288   )\r
2289 {\r
2290   EFI_STATUS                   Status;\r
2291   EFI_MAC_ADDRESS              MacAddress;\r
2292   UINT8                        *HwAddress;\r
2293   UINTN                        HwAddressSize;\r
2294   UINT16                       VlanId;\r
2295   CHAR16                       *String;\r
2296   UINTN                        Index;\r
2297   UINTN                        BufferSize;\r
2298 \r
2299   ASSERT (MacString != NULL);\r
2300 \r
2301   //\r
2302   // Get MAC address of the network device\r
2303   //\r
2304   Status = NetLibGetMacAddress (ServiceHandle, &MacAddress, &HwAddressSize);\r
2305   if (EFI_ERROR (Status)) {\r
2306     return Status;\r
2307   }\r
2308 \r
2309   //\r
2310   // It takes 2 unicode characters to represent a 1 byte binary buffer.\r
2311   // If VLAN is configured, it will need extra 5 characters like "\0005".\r
2312   // Plus one unicode character for the null-terminator.\r
2313   //\r
2314   BufferSize = (2 * HwAddressSize + 5 + 1) * sizeof (CHAR16);\r
2315   String = AllocateZeroPool (BufferSize);\r
2316   if (String == NULL) {\r
2317     return EFI_OUT_OF_RESOURCES;\r
2318   }\r
2319   *MacString = String;\r
2320 \r
2321   //\r
2322   // Convert the MAC address into a unicode string.\r
2323   //\r
2324   HwAddress = &MacAddress.Addr[0];\r
2325   for (Index = 0; Index < HwAddressSize; Index++) {\r
2326     UnicodeValueToStringS (\r
2327       String,\r
2328       BufferSize - ((UINTN)String - (UINTN)*MacString),\r
2329       PREFIX_ZERO | RADIX_HEX,\r
2330       *(HwAddress++),\r
2331       2\r
2332       );\r
2333     String += StrnLenS (String, (BufferSize - ((UINTN)String - (UINTN)*MacString)) / sizeof (CHAR16));\r
2334   }\r
2335 \r
2336   //\r
2337   // Append VLAN ID if any\r
2338   //\r
2339   VlanId = NetLibGetVlanId (ServiceHandle);\r
2340   if (VlanId != 0) {\r
2341     *String++ = L'\\';\r
2342     UnicodeValueToStringS (\r
2343       String,\r
2344       BufferSize - ((UINTN)String - (UINTN)*MacString),\r
2345       PREFIX_ZERO | RADIX_HEX,\r
2346       VlanId,\r
2347       4\r
2348       );\r
2349     String += StrnLenS (String, (BufferSize - ((UINTN)String - (UINTN)*MacString)) / sizeof (CHAR16));\r
2350   }\r
2351 \r
2352   //\r
2353   // Null terminate the Unicode string\r
2354   //\r
2355   *String = L'\0';\r
2356 \r
2357   return EFI_SUCCESS;\r
2358 }\r
2359 \r
2360 /**\r
2361   Detect media status for specified network device.\r
2362 \r
2363   If MediaPresent is NULL, then ASSERT().\r
2364 \r
2365   The underlying UNDI driver may or may not support reporting media status from\r
2366   GET_STATUS command (PXE_STATFLAGS_GET_STATUS_NO_MEDIA_SUPPORTED). This routine\r
2367   will try to invoke Snp->GetStatus() to get the media status: if media already\r
2368   present, it return directly; if media not present, it will stop SNP and then\r
2369   restart SNP to get the latest media status, this give chance to get the correct\r
2370   media status for old UNDI driver which doesn't support reporting media status\r
2371   from GET_STATUS command.\r
2372   Note: there will be two limitations for current algorithm:\r
2373   1) for UNDI with this capability, in case of cable is not attached, there will\r
2374      be an redundant Stop/Start() process;\r
2375   2) for UNDI without this capability, in case that network cable is attached when\r
2376      Snp->Initialize() is invoked while network cable is unattached later,\r
2377      NetLibDetectMedia() will report MediaPresent as TRUE, causing upper layer\r
2378      apps to wait for timeout time.\r
2379 \r
2380   @param[in]   ServiceHandle    The handle where network service binding protocols are\r
2381                                 installed on.\r
2382   @param[out]  MediaPresent     The pointer to store the media status.\r
2383 \r
2384   @retval EFI_SUCCESS           Media detection success.\r
2385   @retval EFI_INVALID_PARAMETER ServiceHandle is not valid network device handle.\r
2386   @retval EFI_UNSUPPORTED       Network device does not support media detection.\r
2387   @retval EFI_DEVICE_ERROR      SNP is in unknown state.\r
2388 \r
2389 **/\r
2390 EFI_STATUS\r
2391 EFIAPI\r
2392 NetLibDetectMedia (\r
2393   IN  EFI_HANDLE            ServiceHandle,\r
2394   OUT BOOLEAN               *MediaPresent\r
2395   )\r
2396 {\r
2397   EFI_STATUS                   Status;\r
2398   EFI_HANDLE                   SnpHandle;\r
2399   EFI_SIMPLE_NETWORK_PROTOCOL  *Snp;\r
2400   UINT32                       InterruptStatus;\r
2401   UINT32                       OldState;\r
2402   EFI_MAC_ADDRESS              *MCastFilter;\r
2403   UINT32                       MCastFilterCount;\r
2404   UINT32                       EnableFilterBits;\r
2405   UINT32                       DisableFilterBits;\r
2406   BOOLEAN                      ResetMCastFilters;\r
2407 \r
2408   ASSERT (MediaPresent != NULL);\r
2409 \r
2410   //\r
2411   // Get SNP handle\r
2412   //\r
2413   Snp = NULL;\r
2414   SnpHandle = NetLibGetSnpHandle (ServiceHandle, &Snp);\r
2415   if (SnpHandle == NULL) {\r
2416     return EFI_INVALID_PARAMETER;\r
2417   }\r
2418 \r
2419   //\r
2420   // Check whether SNP support media detection\r
2421   //\r
2422   if (!Snp->Mode->MediaPresentSupported) {\r
2423     return EFI_UNSUPPORTED;\r
2424   }\r
2425 \r
2426   //\r
2427   // Invoke Snp->GetStatus() to refresh MediaPresent field in SNP mode data\r
2428   //\r
2429   Status = Snp->GetStatus (Snp, &InterruptStatus, NULL);\r
2430   if (EFI_ERROR (Status)) {\r
2431     return Status;\r
2432   }\r
2433 \r
2434   if (Snp->Mode->MediaPresent) {\r
2435     //\r
2436     // Media is present, return directly\r
2437     //\r
2438     *MediaPresent = TRUE;\r
2439     return EFI_SUCCESS;\r
2440   }\r
2441 \r
2442   //\r
2443   // Till now, GetStatus() report no media; while, in case UNDI not support\r
2444   // reporting media status from GetStatus(), this media status may be incorrect.\r
2445   // So, we will stop SNP and then restart it to get the correct media status.\r
2446   //\r
2447   OldState = Snp->Mode->State;\r
2448   if (OldState >= EfiSimpleNetworkMaxState) {\r
2449     return EFI_DEVICE_ERROR;\r
2450   }\r
2451 \r
2452   MCastFilter = NULL;\r
2453 \r
2454   if (OldState == EfiSimpleNetworkInitialized) {\r
2455     //\r
2456     // SNP is already in use, need Shutdown/Stop and then Start/Initialize\r
2457     //\r
2458 \r
2459     //\r
2460     // Backup current SNP receive filter settings\r
2461     //\r
2462     EnableFilterBits  = Snp->Mode->ReceiveFilterSetting;\r
2463     DisableFilterBits = Snp->Mode->ReceiveFilterMask ^ EnableFilterBits;\r
2464 \r
2465     ResetMCastFilters = TRUE;\r
2466     MCastFilterCount  = Snp->Mode->MCastFilterCount;\r
2467     if (MCastFilterCount != 0) {\r
2468       MCastFilter = AllocateCopyPool (\r
2469                       MCastFilterCount * sizeof (EFI_MAC_ADDRESS),\r
2470                       Snp->Mode->MCastFilter\r
2471                       );\r
2472       ASSERT (MCastFilter != NULL);\r
2473       if (MCastFilter == NULL) {\r
2474         Status = EFI_OUT_OF_RESOURCES;\r
2475         goto Exit;\r
2476       }\r
2477 \r
2478       ResetMCastFilters = FALSE;\r
2479     }\r
2480 \r
2481     //\r
2482     // Shutdown/Stop the simple network\r
2483     //\r
2484     Status = Snp->Shutdown (Snp);\r
2485     if (!EFI_ERROR (Status)) {\r
2486       Status = Snp->Stop (Snp);\r
2487     }\r
2488     if (EFI_ERROR (Status)) {\r
2489       goto Exit;\r
2490     }\r
2491 \r
2492     //\r
2493     // Start/Initialize the simple network\r
2494     //\r
2495     Status = Snp->Start (Snp);\r
2496     if (!EFI_ERROR (Status)) {\r
2497       Status = Snp->Initialize (Snp, 0, 0);\r
2498     }\r
2499     if (EFI_ERROR (Status)) {\r
2500       goto Exit;\r
2501     }\r
2502 \r
2503     //\r
2504     // Here we get the correct media status\r
2505     //\r
2506     *MediaPresent = Snp->Mode->MediaPresent;\r
2507 \r
2508     //\r
2509     // Restore SNP receive filter settings\r
2510     //\r
2511     Status = Snp->ReceiveFilters (\r
2512                     Snp,\r
2513                     EnableFilterBits,\r
2514                     DisableFilterBits,\r
2515                     ResetMCastFilters,\r
2516                     MCastFilterCount,\r
2517                     MCastFilter\r
2518                     );\r
2519 \r
2520     if (MCastFilter != NULL) {\r
2521       FreePool (MCastFilter);\r
2522     }\r
2523 \r
2524     return Status;\r
2525   }\r
2526 \r
2527   //\r
2528   // SNP is not in use, it's in state of EfiSimpleNetworkStopped or EfiSimpleNetworkStarted\r
2529   //\r
2530   if (OldState == EfiSimpleNetworkStopped) {\r
2531     //\r
2532     // SNP not start yet, start it\r
2533     //\r
2534     Status = Snp->Start (Snp);\r
2535     if (EFI_ERROR (Status)) {\r
2536       goto Exit;\r
2537     }\r
2538   }\r
2539 \r
2540   //\r
2541   // Initialize the simple network\r
2542   //\r
2543   Status = Snp->Initialize (Snp, 0, 0);\r
2544   if (EFI_ERROR (Status)) {\r
2545     Status = EFI_DEVICE_ERROR;\r
2546     goto Exit;\r
2547   }\r
2548 \r
2549   //\r
2550   // Here we get the correct media status\r
2551   //\r
2552   *MediaPresent = Snp->Mode->MediaPresent;\r
2553 \r
2554   //\r
2555   // Shut down the simple network\r
2556   //\r
2557   Snp->Shutdown (Snp);\r
2558 \r
2559 Exit:\r
2560   if (OldState == EfiSimpleNetworkStopped) {\r
2561     //\r
2562     // Original SNP sate is Stopped, restore to original state\r
2563     //\r
2564     Snp->Stop (Snp);\r
2565   }\r
2566 \r
2567   if (MCastFilter != NULL) {\r
2568     FreePool (MCastFilter);\r
2569   }\r
2570 \r
2571   return Status;\r
2572 }\r
2573 \r
2574 /**\r
2575 \r
2576   Detect media state for a network device. This routine will wait for a period of time at\r
2577   a specified checking interval when a certain network is under connecting until connection\r
2578   process finishs or timeout. If Aip protocol is supported by low layer drivers, three kinds\r
2579   of media states can be detected: EFI_SUCCESS, EFI_NOT_READY and EFI_NO_MEDIA, represents\r
2580   connected state, connecting state and no media state respectively. When function detects\r
2581   the current state is EFI_NOT_READY, it will loop to wait for next time's check until state\r
2582   turns to be EFI_SUCCESS or EFI_NO_MEDIA. If Aip protocol is not supported, function will\r
2583   call NetLibDetectMedia() and return state directly.\r
2584 \r
2585   @param[in]   ServiceHandle    The handle where network service binding protocols are\r
2586                                 installed on.\r
2587   @param[in]   Timeout          The maximum number of 100ns units to wait when network\r
2588                                 is connecting. Zero value means detect once and return\r
2589                                 immediately.\r
2590   @param[out]  MediaState       The pointer to the detected media state.\r
2591 \r
2592   @retval EFI_SUCCESS           Media detection success.\r
2593   @retval EFI_INVALID_PARAMETER ServiceHandle is not a valid network device handle or\r
2594                                 MediaState pointer is NULL.\r
2595   @retval EFI_DEVICE_ERROR      A device error occurred.\r
2596   @retval EFI_TIMEOUT           Network is connecting but timeout.\r
2597 \r
2598 **/\r
2599 EFI_STATUS\r
2600 EFIAPI\r
2601 NetLibDetectMediaWaitTimeout (\r
2602   IN  EFI_HANDLE            ServiceHandle,\r
2603   IN  UINT64                Timeout,\r
2604   OUT EFI_STATUS            *MediaState\r
2605   )\r
2606 {\r
2607   EFI_STATUS                        Status;\r
2608   EFI_HANDLE                        SnpHandle;\r
2609   EFI_SIMPLE_NETWORK_PROTOCOL       *Snp;\r
2610   EFI_ADAPTER_INFORMATION_PROTOCOL  *Aip;\r
2611   EFI_ADAPTER_INFO_MEDIA_STATE      *MediaInfo;\r
2612   BOOLEAN                           MediaPresent;\r
2613   UINTN                             DataSize;\r
2614   EFI_STATUS                        TimerStatus;\r
2615   EFI_EVENT                         Timer;\r
2616   UINT64                            TimeRemained;\r
2617 \r
2618   if (MediaState == NULL) {\r
2619     return EFI_INVALID_PARAMETER;\r
2620   }\r
2621   *MediaState = EFI_SUCCESS;\r
2622   MediaInfo   = NULL;\r
2623 \r
2624   //\r
2625   // Get SNP handle\r
2626   //\r
2627   Snp = NULL;\r
2628   SnpHandle = NetLibGetSnpHandle (ServiceHandle, &Snp);\r
2629   if (SnpHandle == NULL) {\r
2630     return EFI_INVALID_PARAMETER;\r
2631   }\r
2632 \r
2633   Status = gBS->HandleProtocol (\r
2634                   SnpHandle,\r
2635                   &gEfiAdapterInformationProtocolGuid,\r
2636                   (VOID *) &Aip\r
2637                   );\r
2638   if (EFI_ERROR (Status)) {\r
2639 \r
2640     MediaPresent = TRUE;\r
2641     Status = NetLibDetectMedia (ServiceHandle, &MediaPresent);\r
2642     if (!EFI_ERROR (Status)) {\r
2643       if (MediaPresent) {\r
2644         *MediaState = EFI_SUCCESS;\r
2645       } else {\r
2646         *MediaState = EFI_NO_MEDIA;\r
2647       }\r
2648     }\r
2649 \r
2650     //\r
2651     // NetLibDetectMedia doesn't support EFI_NOT_READY status, return now!\r
2652     //\r
2653     return Status;\r
2654   }\r
2655 \r
2656   Status = Aip->GetInformation (\r
2657                   Aip,\r
2658                   &gEfiAdapterInfoMediaStateGuid,\r
2659                   (VOID **) &MediaInfo,\r
2660                   &DataSize\r
2661                   );\r
2662   if (!EFI_ERROR (Status)) {\r
2663 \r
2664     *MediaState = MediaInfo->MediaState;\r
2665     FreePool (MediaInfo);\r
2666     if (*MediaState != EFI_NOT_READY || Timeout < MEDIA_STATE_DETECT_TIME_INTERVAL) {\r
2667 \r
2668       return EFI_SUCCESS;\r
2669     }\r
2670   } else {\r
2671 \r
2672     if (MediaInfo != NULL) {\r
2673       FreePool (MediaInfo);\r
2674     }\r
2675 \r
2676     if (Status == EFI_UNSUPPORTED) {\r
2677 \r
2678       //\r
2679       // If gEfiAdapterInfoMediaStateGuid is not supported, call NetLibDetectMedia to get media state!\r
2680       //\r
2681       MediaPresent = TRUE;\r
2682       Status = NetLibDetectMedia (ServiceHandle, &MediaPresent);\r
2683       if (!EFI_ERROR (Status)) {\r
2684         if (MediaPresent) {\r
2685           *MediaState = EFI_SUCCESS;\r
2686         } else {\r
2687           *MediaState = EFI_NO_MEDIA;\r
2688         }\r
2689       }\r
2690       return Status;\r
2691     }\r
2692 \r
2693     return Status;\r
2694   }\r
2695 \r
2696   //\r
2697   // Loop to check media state\r
2698   //\r
2699 \r
2700   Timer        = NULL;\r
2701   TimeRemained = Timeout;\r
2702   Status = gBS->CreateEvent (EVT_TIMER, TPL_CALLBACK, NULL, NULL, &Timer);\r
2703   if (EFI_ERROR (Status)) {\r
2704     return EFI_DEVICE_ERROR;\r
2705   }\r
2706 \r
2707   do {\r
2708     Status = gBS->SetTimer (\r
2709                     Timer,\r
2710                     TimerRelative,\r
2711                     MEDIA_STATE_DETECT_TIME_INTERVAL\r
2712                     );\r
2713     if (EFI_ERROR (Status)) {\r
2714       gBS->CloseEvent(Timer);\r
2715       return EFI_DEVICE_ERROR;\r
2716     }\r
2717 \r
2718     do {\r
2719       TimerStatus = gBS->CheckEvent (Timer);\r
2720       if (!EFI_ERROR (TimerStatus)) {\r
2721 \r
2722         TimeRemained -= MEDIA_STATE_DETECT_TIME_INTERVAL;\r
2723         Status = Aip->GetInformation (\r
2724                         Aip,\r
2725                         &gEfiAdapterInfoMediaStateGuid,\r
2726                         (VOID **) &MediaInfo,\r
2727                         &DataSize\r
2728                         );\r
2729         if (!EFI_ERROR (Status)) {\r
2730 \r
2731           *MediaState = MediaInfo->MediaState;\r
2732           FreePool (MediaInfo);\r
2733         } else {\r
2734 \r
2735           if (MediaInfo != NULL) {\r
2736             FreePool (MediaInfo);\r
2737           }\r
2738           gBS->CloseEvent(Timer);\r
2739           return Status;\r
2740         }\r
2741       }\r
2742     } while (TimerStatus == EFI_NOT_READY);\r
2743   } while (*MediaState == EFI_NOT_READY && TimeRemained >= MEDIA_STATE_DETECT_TIME_INTERVAL);\r
2744 \r
2745   gBS->CloseEvent(Timer);\r
2746   if (*MediaState == EFI_NOT_READY && TimeRemained < MEDIA_STATE_DETECT_TIME_INTERVAL) {\r
2747     return EFI_TIMEOUT;\r
2748   } else {\r
2749     return EFI_SUCCESS;\r
2750   }\r
2751 }\r
2752 \r
2753 /**\r
2754   Check the default address used by the IPv4 driver is static or dynamic (acquired\r
2755   from DHCP).\r
2756 \r
2757   If the controller handle does not have the EFI_IP4_CONFIG2_PROTOCOL installed, the\r
2758   default address is static. If failed to get the policy from Ip4 Config2 Protocol,\r
2759   the default address is static. Otherwise, get the result from Ip4 Config2 Protocol.\r
2760 \r
2761   @param[in]   Controller     The controller handle which has the EFI_IP4_CONFIG2_PROTOCOL\r
2762                               relative with the default address to judge.\r
2763 \r
2764   @retval TRUE           If the default address is static.\r
2765   @retval FALSE          If the default address is acquired from DHCP.\r
2766 \r
2767 **/\r
2768 BOOLEAN\r
2769 NetLibDefaultAddressIsStatic (\r
2770   IN EFI_HANDLE  Controller\r
2771   )\r
2772 {\r
2773   EFI_STATUS                       Status;\r
2774   EFI_IP4_CONFIG2_PROTOCOL         *Ip4Config2;\r
2775   UINTN                            DataSize;\r
2776   EFI_IP4_CONFIG2_POLICY           Policy;\r
2777   BOOLEAN                          IsStatic;\r
2778 \r
2779   Ip4Config2 = NULL;\r
2780 \r
2781   DataSize = sizeof (EFI_IP4_CONFIG2_POLICY);\r
2782 \r
2783   IsStatic   = TRUE;\r
2784 \r
2785   //\r
2786   // Get Ip4Config2 policy.\r
2787   //\r
2788   Status = gBS->HandleProtocol (Controller, &gEfiIp4Config2ProtocolGuid, (VOID **) &Ip4Config2);\r
2789   if (EFI_ERROR (Status)) {\r
2790     goto ON_EXIT;\r
2791   }\r
2792 \r
2793   Status = Ip4Config2->GetData (Ip4Config2, Ip4Config2DataTypePolicy, &DataSize, &Policy);\r
2794   if (EFI_ERROR (Status)) {\r
2795     goto ON_EXIT;\r
2796   }\r
2797 \r
2798   IsStatic = (BOOLEAN) (Policy == Ip4Config2PolicyStatic);\r
2799 \r
2800 ON_EXIT:\r
2801 \r
2802   return IsStatic;\r
2803 }\r
2804 \r
2805 /**\r
2806   Create an IPv4 device path node.\r
2807 \r
2808   If Node is NULL, then ASSERT().\r
2809 \r
2810   The header type of IPv4 device path node is MESSAGING_DEVICE_PATH.\r
2811   The header subtype of IPv4 device path node is MSG_IPv4_DP.\r
2812   Get other info from parameters to make up the whole IPv4 device path node.\r
2813 \r
2814   @param[in, out]  Node                  Pointer to the IPv4 device path node.\r
2815   @param[in]       Controller            The controller handle.\r
2816   @param[in]       LocalIp               The local IPv4 address.\r
2817   @param[in]       LocalPort             The local port.\r
2818   @param[in]       RemoteIp              The remote IPv4 address.\r
2819   @param[in]       RemotePort            The remote port.\r
2820   @param[in]       Protocol              The protocol type in the IP header.\r
2821   @param[in]       UseDefaultAddress     Whether this instance is using default address or not.\r
2822 \r
2823 **/\r
2824 VOID\r
2825 EFIAPI\r
2826 NetLibCreateIPv4DPathNode (\r
2827   IN OUT IPv4_DEVICE_PATH  *Node,\r
2828   IN EFI_HANDLE            Controller,\r
2829   IN IP4_ADDR              LocalIp,\r
2830   IN UINT16                LocalPort,\r
2831   IN IP4_ADDR              RemoteIp,\r
2832   IN UINT16                RemotePort,\r
2833   IN UINT16                Protocol,\r
2834   IN BOOLEAN               UseDefaultAddress\r
2835   )\r
2836 {\r
2837   ASSERT (Node != NULL);\r
2838 \r
2839   Node->Header.Type    = MESSAGING_DEVICE_PATH;\r
2840   Node->Header.SubType = MSG_IPv4_DP;\r
2841   SetDevicePathNodeLength (&Node->Header, sizeof (IPv4_DEVICE_PATH));\r
2842 \r
2843   CopyMem (&Node->LocalIpAddress, &LocalIp, sizeof (EFI_IPv4_ADDRESS));\r
2844   CopyMem (&Node->RemoteIpAddress, &RemoteIp, sizeof (EFI_IPv4_ADDRESS));\r
2845 \r
2846   Node->LocalPort  = LocalPort;\r
2847   Node->RemotePort = RemotePort;\r
2848 \r
2849   Node->Protocol = Protocol;\r
2850 \r
2851   if (!UseDefaultAddress) {\r
2852     Node->StaticIpAddress = TRUE;\r
2853   } else {\r
2854     Node->StaticIpAddress = NetLibDefaultAddressIsStatic (Controller);\r
2855   }\r
2856 \r
2857   //\r
2858   // Set the Gateway IP address to default value 0:0:0:0.\r
2859   // Set the Subnet mask to default value 255:255:255:0.\r
2860   //\r
2861   ZeroMem (&Node->GatewayIpAddress, sizeof (EFI_IPv4_ADDRESS));\r
2862   SetMem (&Node->SubnetMask, sizeof (EFI_IPv4_ADDRESS), 0xff);\r
2863   Node->SubnetMask.Addr[3] = 0;\r
2864 }\r
2865 \r
2866 /**\r
2867   Create an IPv6 device path node.\r
2868 \r
2869   If Node is NULL, then ASSERT().\r
2870   If LocalIp is NULL, then ASSERT().\r
2871   If RemoteIp is NULL, then ASSERT().\r
2872 \r
2873   The header type of IPv6 device path node is MESSAGING_DEVICE_PATH.\r
2874   The header subtype of IPv6 device path node is MSG_IPv6_DP.\r
2875   Get other info from parameters to make up the whole IPv6 device path node.\r
2876 \r
2877   @param[in, out]  Node                  Pointer to the IPv6 device path node.\r
2878   @param[in]       Controller            The controller handle.\r
2879   @param[in]       LocalIp               The local IPv6 address.\r
2880   @param[in]       LocalPort             The local port.\r
2881   @param[in]       RemoteIp              The remote IPv6 address.\r
2882   @param[in]       RemotePort            The remote port.\r
2883   @param[in]       Protocol              The protocol type in the IP header.\r
2884 \r
2885 **/\r
2886 VOID\r
2887 EFIAPI\r
2888 NetLibCreateIPv6DPathNode (\r
2889   IN OUT IPv6_DEVICE_PATH  *Node,\r
2890   IN EFI_HANDLE            Controller,\r
2891   IN EFI_IPv6_ADDRESS      *LocalIp,\r
2892   IN UINT16                LocalPort,\r
2893   IN EFI_IPv6_ADDRESS      *RemoteIp,\r
2894   IN UINT16                RemotePort,\r
2895   IN UINT16                Protocol\r
2896   )\r
2897 {\r
2898   ASSERT (Node != NULL && LocalIp != NULL && RemoteIp != NULL);\r
2899 \r
2900   Node->Header.Type    = MESSAGING_DEVICE_PATH;\r
2901   Node->Header.SubType = MSG_IPv6_DP;\r
2902   SetDevicePathNodeLength (&Node->Header, sizeof (IPv6_DEVICE_PATH));\r
2903 \r
2904   CopyMem (&Node->LocalIpAddress, LocalIp, sizeof (EFI_IPv6_ADDRESS));\r
2905   CopyMem (&Node->RemoteIpAddress, RemoteIp, sizeof (EFI_IPv6_ADDRESS));\r
2906 \r
2907   Node->LocalPort  = LocalPort;\r
2908   Node->RemotePort = RemotePort;\r
2909 \r
2910   Node->Protocol        = Protocol;\r
2911 \r
2912   //\r
2913   // Set default value to IPAddressOrigin, PrefixLength.\r
2914   // Set the Gateway IP address to unspecified address.\r
2915   //\r
2916   Node->IpAddressOrigin = 0;\r
2917   Node->PrefixLength    = IP6_PREFIX_LENGTH;\r
2918   ZeroMem (&Node->GatewayIpAddress, sizeof (EFI_IPv6_ADDRESS));\r
2919 }\r
2920 \r
2921 /**\r
2922   Find the UNDI/SNP handle from controller and protocol GUID.\r
2923 \r
2924   If ProtocolGuid is NULL, then ASSERT().\r
2925 \r
2926   For example, IP will open a MNP child to transmit/receive\r
2927   packets, when MNP is stopped, IP should also be stopped. IP\r
2928   needs to find its own private data which is related the IP's\r
2929   service binding instance that is install on UNDI/SNP handle.\r
2930   Now, the controller is either a MNP or ARP child handle. But\r
2931   IP opens these handle BY_DRIVER, use that info, we can get the\r
2932   UNDI/SNP handle.\r
2933 \r
2934   @param[in]  Controller            Then protocol handle to check.\r
2935   @param[in]  ProtocolGuid          The protocol that is related with the handle.\r
2936 \r
2937   @return The UNDI/SNP handle or NULL for errors.\r
2938 \r
2939 **/\r
2940 EFI_HANDLE\r
2941 EFIAPI\r
2942 NetLibGetNicHandle (\r
2943   IN EFI_HANDLE             Controller,\r
2944   IN EFI_GUID               *ProtocolGuid\r
2945   )\r
2946 {\r
2947   EFI_OPEN_PROTOCOL_INFORMATION_ENTRY *OpenBuffer;\r
2948   EFI_HANDLE                          Handle;\r
2949   EFI_STATUS                          Status;\r
2950   UINTN                               OpenCount;\r
2951   UINTN                               Index;\r
2952 \r
2953   ASSERT (ProtocolGuid != NULL);\r
2954 \r
2955   Status = gBS->OpenProtocolInformation (\r
2956                   Controller,\r
2957                   ProtocolGuid,\r
2958                   &OpenBuffer,\r
2959                   &OpenCount\r
2960                   );\r
2961 \r
2962   if (EFI_ERROR (Status)) {\r
2963     return NULL;\r
2964   }\r
2965 \r
2966   Handle = NULL;\r
2967 \r
2968   for (Index = 0; Index < OpenCount; Index++) {\r
2969     if ((OpenBuffer[Index].Attributes & EFI_OPEN_PROTOCOL_BY_DRIVER) != 0) {\r
2970       Handle = OpenBuffer[Index].ControllerHandle;\r
2971       break;\r
2972     }\r
2973   }\r
2974 \r
2975   gBS->FreePool (OpenBuffer);\r
2976   return Handle;\r
2977 }\r
2978 \r
2979 /**\r
2980   Convert one Null-terminated ASCII string (decimal dotted) to EFI_IPv4_ADDRESS.\r
2981 \r
2982   @param[in]      String         The pointer to the Ascii string.\r
2983   @param[out]     Ip4Address     The pointer to the converted IPv4 address.\r
2984 \r
2985   @retval EFI_SUCCESS            Convert to IPv4 address successfully.\r
2986   @retval EFI_INVALID_PARAMETER  The string is mal-formated or Ip4Address is NULL.\r
2987 \r
2988 **/\r
2989 EFI_STATUS\r
2990 EFIAPI\r
2991 NetLibAsciiStrToIp4 (\r
2992   IN CONST CHAR8                 *String,\r
2993   OUT      EFI_IPv4_ADDRESS      *Ip4Address\r
2994   )\r
2995 {\r
2996   RETURN_STATUS                  Status;\r
2997   CHAR8                          *EndPointer;\r
2998 \r
2999   Status = AsciiStrToIpv4Address (String, &EndPointer, Ip4Address, NULL);\r
3000   if (RETURN_ERROR (Status) || (*EndPointer != '\0')) {\r
3001     return EFI_INVALID_PARAMETER;\r
3002   } else {\r
3003     return EFI_SUCCESS;\r
3004   }\r
3005 }\r
3006 \r
3007 \r
3008 /**\r
3009   Convert one Null-terminated ASCII string to EFI_IPv6_ADDRESS. The format of the\r
3010   string is defined in RFC 4291 - Text Representation of Addresses.\r
3011 \r
3012   @param[in]      String         The pointer to the Ascii string.\r
3013   @param[out]     Ip6Address     The pointer to the converted IPv6 address.\r
3014 \r
3015   @retval EFI_SUCCESS            Convert to IPv6 address successfully.\r
3016   @retval EFI_INVALID_PARAMETER  The string is mal-formated or Ip6Address is NULL.\r
3017 \r
3018 **/\r
3019 EFI_STATUS\r
3020 EFIAPI\r
3021 NetLibAsciiStrToIp6 (\r
3022   IN CONST CHAR8                 *String,\r
3023   OUT      EFI_IPv6_ADDRESS      *Ip6Address\r
3024   )\r
3025 {\r
3026   RETURN_STATUS                  Status;\r
3027   CHAR8                          *EndPointer;\r
3028 \r
3029   Status = AsciiStrToIpv6Address (String, &EndPointer, Ip6Address, NULL);\r
3030   if (RETURN_ERROR (Status) || (*EndPointer != '\0')) {\r
3031     return EFI_INVALID_PARAMETER;\r
3032   } else {\r
3033     return EFI_SUCCESS;\r
3034   }\r
3035 }\r
3036 \r
3037 \r
3038 /**\r
3039   Convert one Null-terminated Unicode string (decimal dotted) to EFI_IPv4_ADDRESS.\r
3040 \r
3041   @param[in]      String         The pointer to the Ascii string.\r
3042   @param[out]     Ip4Address     The pointer to the converted IPv4 address.\r
3043 \r
3044   @retval EFI_SUCCESS            Convert to IPv4 address successfully.\r
3045   @retval EFI_INVALID_PARAMETER  The string is mal-formated or Ip4Address is NULL.\r
3046 \r
3047 **/\r
3048 EFI_STATUS\r
3049 EFIAPI\r
3050 NetLibStrToIp4 (\r
3051   IN CONST CHAR16                *String,\r
3052   OUT      EFI_IPv4_ADDRESS      *Ip4Address\r
3053   )\r
3054 {\r
3055   RETURN_STATUS                  Status;\r
3056   CHAR16                         *EndPointer;\r
3057 \r
3058   Status = StrToIpv4Address (String, &EndPointer, Ip4Address, NULL);\r
3059   if (RETURN_ERROR (Status) || (*EndPointer != L'\0')) {\r
3060     return EFI_INVALID_PARAMETER;\r
3061   } else {\r
3062     return EFI_SUCCESS;\r
3063   }\r
3064 }\r
3065 \r
3066 \r
3067 /**\r
3068   Convert one Null-terminated Unicode string to EFI_IPv6_ADDRESS.  The format of\r
3069   the string is defined in RFC 4291 - Text Representation of Addresses.\r
3070 \r
3071   @param[in]      String         The pointer to the Ascii string.\r
3072   @param[out]     Ip6Address     The pointer to the converted IPv6 address.\r
3073 \r
3074   @retval EFI_SUCCESS            Convert to IPv6 address successfully.\r
3075   @retval EFI_INVALID_PARAMETER  The string is mal-formated or Ip6Address is NULL.\r
3076 \r
3077 **/\r
3078 EFI_STATUS\r
3079 EFIAPI\r
3080 NetLibStrToIp6 (\r
3081   IN CONST CHAR16                *String,\r
3082   OUT      EFI_IPv6_ADDRESS      *Ip6Address\r
3083   )\r
3084 {\r
3085   RETURN_STATUS                  Status;\r
3086   CHAR16                         *EndPointer;\r
3087 \r
3088   Status = StrToIpv6Address (String, &EndPointer, Ip6Address, NULL);\r
3089   if (RETURN_ERROR (Status) || (*EndPointer != L'\0')) {\r
3090     return EFI_INVALID_PARAMETER;\r
3091   } else {\r
3092     return EFI_SUCCESS;\r
3093   }\r
3094 }\r
3095 \r
3096 /**\r
3097   Convert one Null-terminated Unicode string to EFI_IPv6_ADDRESS and prefix length.\r
3098   The format of the string is defined in RFC 4291 - Text Representation of Addresses\r
3099   Prefixes: ipv6-address/prefix-length.\r
3100 \r
3101   @param[in]      String         The pointer to the Ascii string.\r
3102   @param[out]     Ip6Address     The pointer to the converted IPv6 address.\r
3103   @param[out]     PrefixLength   The pointer to the converted prefix length.\r
3104 \r
3105   @retval EFI_SUCCESS            Convert to IPv6 address successfully.\r
3106   @retval EFI_INVALID_PARAMETER  The string is mal-formated or Ip6Address is NULL.\r
3107 \r
3108 **/\r
3109 EFI_STATUS\r
3110 EFIAPI\r
3111 NetLibStrToIp6andPrefix (\r
3112   IN CONST CHAR16                *String,\r
3113   OUT      EFI_IPv6_ADDRESS      *Ip6Address,\r
3114   OUT      UINT8                 *PrefixLength\r
3115   )\r
3116 {\r
3117   RETURN_STATUS                  Status;\r
3118   CHAR16                         *EndPointer;\r
3119 \r
3120   Status = StrToIpv6Address (String, &EndPointer, Ip6Address, PrefixLength);\r
3121   if (RETURN_ERROR (Status) || (*EndPointer != L'\0')) {\r
3122     return EFI_INVALID_PARAMETER;\r
3123   } else {\r
3124     return EFI_SUCCESS;\r
3125   }\r
3126 }\r
3127 \r
3128 /**\r
3129 \r
3130   Convert one EFI_IPv6_ADDRESS to Null-terminated Unicode string.\r
3131   The text representation of address is defined in RFC 4291.\r
3132 \r
3133   @param[in]       Ip6Address     The pointer to the IPv6 address.\r
3134   @param[out]      String         The buffer to return the converted string.\r
3135   @param[in]       StringSize     The length in bytes of the input String.\r
3136 \r
3137   @retval EFI_SUCCESS             Convert to string successfully.\r
3138   @retval EFI_INVALID_PARAMETER   The input parameter is invalid.\r
3139   @retval EFI_BUFFER_TOO_SMALL    The BufferSize is too small for the result. BufferSize has been\r
3140                                   updated with the size needed to complete the request.\r
3141 **/\r
3142 EFI_STATUS\r
3143 EFIAPI\r
3144 NetLibIp6ToStr (\r
3145   IN         EFI_IPv6_ADDRESS      *Ip6Address,\r
3146   OUT        CHAR16                *String,\r
3147   IN         UINTN                 StringSize\r
3148   )\r
3149 {\r
3150   UINT16     Ip6Addr[8];\r
3151   UINTN      Index;\r
3152   UINTN      LongestZerosStart;\r
3153   UINTN      LongestZerosLength;\r
3154   UINTN      CurrentZerosStart;\r
3155   UINTN      CurrentZerosLength;\r
3156   CHAR16     Buffer[sizeof"ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"];\r
3157   CHAR16     *Ptr;\r
3158 \r
3159   if (Ip6Address == NULL || String == NULL || StringSize == 0) {\r
3160     return EFI_INVALID_PARAMETER;\r
3161   }\r
3162 \r
3163   //\r
3164   // Convert the UINT8 array to an UINT16 array for easy handling.\r
3165   //\r
3166   ZeroMem (Ip6Addr, sizeof (Ip6Addr));\r
3167   for (Index = 0; Index < 16; Index++) {\r
3168     Ip6Addr[Index / 2] |= (Ip6Address->Addr[Index] << ((1 - (Index % 2)) << 3));\r
3169   }\r
3170 \r
3171   //\r
3172   // Find the longest zeros and mark it.\r
3173   //\r
3174   CurrentZerosStart  = DEFAULT_ZERO_START;\r
3175   CurrentZerosLength = 0;\r
3176   LongestZerosStart  = DEFAULT_ZERO_START;\r
3177   LongestZerosLength = 0;\r
3178   for (Index = 0; Index < 8; Index++) {\r
3179     if (Ip6Addr[Index] == 0) {\r
3180       if (CurrentZerosStart == DEFAULT_ZERO_START) {\r
3181         CurrentZerosStart = Index;\r
3182         CurrentZerosLength = 1;\r
3183       } else {\r
3184         CurrentZerosLength++;\r
3185       }\r
3186     } else {\r
3187       if (CurrentZerosStart != DEFAULT_ZERO_START) {\r
3188         if (CurrentZerosLength > 2 && (LongestZerosStart == (DEFAULT_ZERO_START) || CurrentZerosLength > LongestZerosLength)) {\r
3189           LongestZerosStart  = CurrentZerosStart;\r
3190           LongestZerosLength = CurrentZerosLength;\r
3191         }\r
3192         CurrentZerosStart  = DEFAULT_ZERO_START;\r
3193         CurrentZerosLength = 0;\r
3194       }\r
3195     }\r
3196   }\r
3197 \r
3198   if (CurrentZerosStart != DEFAULT_ZERO_START && CurrentZerosLength > 2) {\r
3199     if (LongestZerosStart == DEFAULT_ZERO_START || LongestZerosLength < CurrentZerosLength) {\r
3200       LongestZerosStart  = CurrentZerosStart;\r
3201       LongestZerosLength = CurrentZerosLength;\r
3202     }\r
3203   }\r
3204 \r
3205   Ptr = Buffer;\r
3206   for (Index = 0; Index < 8; Index++) {\r
3207     if (LongestZerosStart != DEFAULT_ZERO_START && Index >= LongestZerosStart && Index < LongestZerosStart + LongestZerosLength) {\r
3208       if (Index == LongestZerosStart) {\r
3209         *Ptr++ = L':';\r
3210       }\r
3211       continue;\r
3212     }\r
3213     if (Index != 0) {\r
3214       *Ptr++ = L':';\r
3215     }\r
3216     Ptr += UnicodeSPrint(Ptr, 10, L"%x", Ip6Addr[Index]);\r
3217   }\r
3218 \r
3219   if (LongestZerosStart != DEFAULT_ZERO_START && LongestZerosStart + LongestZerosLength == 8) {\r
3220     *Ptr++ = L':';\r
3221   }\r
3222   *Ptr = L'\0';\r
3223 \r
3224   if ((UINTN)Ptr - (UINTN)Buffer > StringSize) {\r
3225     return EFI_BUFFER_TOO_SMALL;\r
3226   }\r
3227 \r
3228   StrCpyS (String, StringSize / sizeof (CHAR16), Buffer);\r
3229 \r
3230   return EFI_SUCCESS;\r
3231 }\r
3232 \r
3233 /**\r
3234   This function obtains the system guid from the smbios table.\r
3235 \r
3236   If SystemGuid is NULL, then ASSERT().\r
3237 \r
3238   @param[out]  SystemGuid     The pointer of the retu