]> git.proxmox.com Git - mirror_edk2.git/blobdiff - MdeModulePkg/Library/DxeNetLib/DxeNetLib.c
git-svn-id: https://edk2.svn.sourceforge.net/svnroot/edk2/trunk/edk2@10288 6f19259b...
[mirror_edk2.git] / MdeModulePkg / Library / DxeNetLib / DxeNetLib.c
index 485c09f9634eb4b15d22553718e1b8aa586e2094..034031d09f68ecfc0a0ac5bdca23de3de2daead0 100644 (file)
@@ -375,7 +375,7 @@ SyslogBuildPacket (
   //\r
   Pri = ((NET_SYSLOG_FACILITY & 31) << 3) | (Level & 7);\r
   gRT->GetTime (&Time, NULL);\r
-  ASSERT (Time.Month <= 12);\r
+  ASSERT ((Time.Month <= 12) && (Time.Month >= 1));\r
 \r
   //\r
   // Use %a to format the ASCII strings, %s to format UNICODE strings\r
@@ -901,7 +901,7 @@ NetGetUint32 (
   byte stream.\r
 \r
   @param[in, out]  Buf          The buffer to put the UINT32.\r
-  @param[in]      Data          The data to put.\r
+  @param[in]       Data         The data to be converted and put into the byte stream.\r
 \r
 **/\r
 VOID\r
@@ -2145,6 +2145,214 @@ NetLibGetMacString (
   return EFI_SUCCESS;\r
 }\r
 \r
+/**\r
+  Detect media status for specified network device.\r
+\r
+  The underlying UNDI driver may or may not support reporting media status from\r
+  GET_STATUS command (PXE_STATFLAGS_GET_STATUS_NO_MEDIA_SUPPORTED). This routine\r
+  will try to invoke Snp->GetStatus() to get the media status: if media already\r
+  present, it return directly; if media not present, it will stop SNP and then\r
+  restart SNP to get the latest media status, this give chance to get the correct\r
+  media status for old UNDI driver which doesn't support reporting media status\r
+  from GET_STATUS command.\r
+  Note: there will be two limitations for current algorithm:\r
+  1) for UNDI with this capability, in case of cable is not attached, there will\r
+     be an redundant Stop/Start() process;\r
+  2) for UNDI without this capability, in case that network cable is attached when\r
+     Snp->Initialize() is invoked while network cable is unattached later,\r
+     NetLibDetectMedia() will report MediaPresent as TRUE, causing upper layer\r
+     apps to wait for timeout time.\r
+\r
+  @param[in]   ServiceHandle    The handle where network service binding protocols are\r
+                                installed on.\r
+  @param[out]  MediaPresent     The pointer to store the media status.\r
+\r
+  @retval EFI_SUCCESS           Media detection success.\r
+  @retval EFI_INVALID_PARAMETER ServiceHandle is not valid network device handle.\r
+  @retval EFI_UNSUPPORTED       Network device does not support media detection.\r
+  @retval EFI_DEVICE_ERROR      SNP is in unknown state.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+NetLibDetectMedia (\r
+  IN  EFI_HANDLE            ServiceHandle,\r
+  OUT BOOLEAN               *MediaPresent\r
+  )\r
+{\r
+  EFI_STATUS                   Status;\r
+  EFI_HANDLE                   SnpHandle;\r
+  EFI_SIMPLE_NETWORK_PROTOCOL  *Snp;\r
+  UINT32                       InterruptStatus;\r
+  UINT32                       OldState;\r
+  EFI_MAC_ADDRESS              *MCastFilter;\r
+  UINT32                       MCastFilterCount;\r
+  UINT32                       EnableFilterBits;\r
+  UINT32                       DisableFilterBits;\r
+  BOOLEAN                      ResetMCastFilters;\r
+\r
+  ASSERT (MediaPresent != NULL);\r
+\r
+  //\r
+  // Get SNP handle\r
+  //\r
+  Snp = NULL;\r
+  SnpHandle = NetLibGetSnpHandle (ServiceHandle, &Snp);\r
+  if (SnpHandle == NULL) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  //\r
+  // Check whether SNP support media detection\r
+  //\r
+  if (!Snp->Mode->MediaPresentSupported) {\r
+    return EFI_UNSUPPORTED;\r
+  }\r
+\r
+  //\r
+  // Invoke Snp->GetStatus() to refresh MediaPresent field in SNP mode data\r
+  //\r
+  Status = Snp->GetStatus (Snp, &InterruptStatus, NULL);\r
+  if (EFI_ERROR (Status)) {\r
+    return Status;\r
+  }\r
+\r
+  if (Snp->Mode->MediaPresent) {\r
+    //\r
+    // Media is present, return directly\r
+    //\r
+    *MediaPresent = TRUE;\r
+    return EFI_SUCCESS;\r
+  }\r
+\r
+  //\r
+  // Till now, GetStatus() report no media; while, in case UNDI not support\r
+  // reporting media status from GetStatus(), this media status may be incorrect.\r
+  // So, we will stop SNP and then restart it to get the correct media status.\r
+  //\r
+  OldState = Snp->Mode->State;\r
+  if (OldState >= EfiSimpleNetworkMaxState) {\r
+    return EFI_DEVICE_ERROR;\r
+  }\r
+\r
+  MCastFilter = NULL;\r
+\r
+  if (OldState == EfiSimpleNetworkInitialized) {\r
+    //\r
+    // SNP is already in use, need Shutdown/Stop and then Start/Initialize\r
+    //\r
+\r
+    //\r
+    // Backup current SNP receive filter settings\r
+    //\r
+    EnableFilterBits  = Snp->Mode->ReceiveFilterSetting;\r
+    DisableFilterBits = Snp->Mode->ReceiveFilterMask ^ EnableFilterBits;\r
+\r
+    ResetMCastFilters = TRUE;\r
+    MCastFilterCount  = Snp->Mode->MCastFilterCount;\r
+    if (MCastFilterCount != 0) {\r
+      MCastFilter = AllocateCopyPool (\r
+                      MCastFilterCount * sizeof (EFI_MAC_ADDRESS),\r
+                      Snp->Mode->MCastFilter\r
+                      );\r
+      ASSERT (MCastFilter != NULL);\r
+\r
+      ResetMCastFilters = FALSE;\r
+    }\r
+\r
+    //\r
+    // Shutdown/Stop the simple network\r
+    //\r
+    Status = Snp->Shutdown (Snp);\r
+    if (!EFI_ERROR (Status)) {\r
+      Status = Snp->Stop (Snp);\r
+    }\r
+    if (EFI_ERROR (Status)) {\r
+      goto Exit;\r
+    }\r
+\r
+    //\r
+    // Start/Initialize the simple network\r
+    //\r
+    Status = Snp->Start (Snp);\r
+    if (!EFI_ERROR (Status)) {\r
+      Status = Snp->Initialize (Snp, 0, 0);\r
+    }\r
+    if (EFI_ERROR (Status)) {\r
+      goto Exit;\r
+    }\r
+\r
+    //\r
+    // Here we get the correct media status\r
+    //\r
+    *MediaPresent = Snp->Mode->MediaPresent;\r
+\r
+    //\r
+    // Restore SNP receive filter settings\r
+    //\r
+    Status = Snp->ReceiveFilters (\r
+                    Snp,\r
+                    EnableFilterBits,\r
+                    DisableFilterBits,\r
+                    ResetMCastFilters,\r
+                    MCastFilterCount,\r
+                    MCastFilter\r
+                    );\r
+\r
+    if (MCastFilter != NULL) {\r
+      FreePool (MCastFilter);\r
+    }\r
+\r
+    return Status;\r
+  }\r
+\r
+  //\r
+  // SNP is not in use, it's in state of EfiSimpleNetworkStopped or EfiSimpleNetworkStarted\r
+  //\r
+  if (OldState == EfiSimpleNetworkStopped) {\r
+    //\r
+    // SNP not start yet, start it\r
+    //\r
+    Status = Snp->Start (Snp);\r
+    if (EFI_ERROR (Status)) {\r
+      goto Exit;\r
+    }\r
+  }\r
+\r
+  //\r
+  // Initialize the simple network\r
+  //\r
+  Status = Snp->Initialize (Snp, 0, 0);\r
+  if (EFI_ERROR (Status)) {\r
+    Status = EFI_DEVICE_ERROR;\r
+    goto Exit;\r
+  }\r
+\r
+  //\r
+  // Here we get the correct media status\r
+  //\r
+  *MediaPresent = Snp->Mode->MediaPresent;\r
+\r
+  //\r
+  // Shut down the simple network\r
+  //\r
+  Snp->Shutdown (Snp);\r
+\r
+Exit:\r
+  if (OldState == EfiSimpleNetworkStopped) {\r
+    //\r
+    // Original SNP sate is Stopped, restore to original state\r
+    //\r
+    Snp->Stop (Snp);\r
+  }\r
+\r
+  if (MCastFilter != NULL) {\r
+    FreePool (MCastFilter);\r
+  }\r
+\r
+  return Status;\r
+}\r
+\r
 /**\r
   Check the default address used by the IPv4 driver is static or dynamic (acquired\r
   from DHCP).\r
@@ -2410,3 +2618,387 @@ NetLibGetNicHandle (
   gBS->FreePool (OpenBuffer);\r
   return Handle;\r
 }\r
+\r
+/**\r
+  Convert one Null-terminated ASCII string (decimal dotted) to EFI_IPv4_ADDRESS.\r
+\r
+  @param[in]      String         The pointer to the Ascii string.\r
+  @param[out]     Ip4Address     The pointer to the converted IPv4 address.\r
+\r
+  @retval EFI_SUCCESS            Convert to IPv4 address successfully.\r
+  @retval EFI_INVALID_PARAMETER  The string is mal-formated or Ip4Address is NULL.\r
+\r
+**/\r
+EFI_STATUS\r
+NetLibAsciiStrToIp4 (\r
+  IN CONST CHAR8                 *String,\r
+  OUT      EFI_IPv4_ADDRESS      *Ip4Address\r
+  )\r
+{\r
+  UINT8                          Index;\r
+  CHAR8                          *Ip4Str;\r
+  CHAR8                          *TempStr;\r
+  UINTN                          NodeVal;\r
+\r
+  if ((String == NULL) || (Ip4Address == NULL)) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  Ip4Str = (CHAR8 *) String;\r
+\r
+  for (Index = 0; Index < 4; Index++) {\r
+    TempStr = Ip4Str;\r
+\r
+    while ((*Ip4Str != '\0') && (*Ip4Str != '.')) {\r
+      Ip4Str++;\r
+    }\r
+\r
+    //\r
+    // The IPv4 address is X.X.X.X\r
+    //\r
+    if (*Ip4Str == '.') {\r
+      if (Index == 3) {\r
+        return EFI_INVALID_PARAMETER;\r
+      }\r
+    } else {\r
+      if (Index != 3) {\r
+        return EFI_INVALID_PARAMETER;\r
+      }\r
+    }\r
+\r
+    //\r
+    // Convert the string to IPv4 address. AsciiStrDecimalToUintn stops at the\r
+    // first character that is not a valid decimal character, '.' or '\0' here.\r
+    //\r
+    NodeVal = AsciiStrDecimalToUintn (TempStr);\r
+    if (NodeVal > 0xFF) {\r
+      return EFI_INVALID_PARAMETER;\r
+    }\r
+\r
+    Ip4Address->Addr[Index] = (UINT8) NodeVal;\r
+\r
+    Ip4Str++;\r
+  }\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+\r
+/**\r
+  Convert one Null-terminated ASCII string to EFI_IPv6_ADDRESS. The format of the\r
+  string is defined in RFC 4291 - Text Pepresentation of Addresses.\r
+\r
+  @param[in]      String         The pointer to the Ascii string.\r
+  @param[out]     Ip6Address     The pointer to the converted IPv6 address.\r
+\r
+  @retval EFI_SUCCESS            Convert to IPv6 address successfully.\r
+  @retval EFI_INVALID_PARAMETER  The string is mal-formated or Ip6Address is NULL.\r
+\r
+**/\r
+EFI_STATUS\r
+NetLibAsciiStrToIp6 (\r
+  IN CONST CHAR8                 *String,\r
+  OUT      EFI_IPv6_ADDRESS      *Ip6Address\r
+  )\r
+{\r
+  UINT8                          Index;\r
+  CHAR8                          *Ip6Str;\r
+  CHAR8                          *TempStr;\r
+  CHAR8                          *TempStr2;\r
+  UINT8                          NodeCnt;\r
+  UINT8                          TailNodeCnt;\r
+  UINT8                          AllowedCnt;\r
+  UINTN                          NodeVal;\r
+  BOOLEAN                        Short;\r
+  BOOLEAN                        Update;\r
+\r
+  if ((String == NULL) || (Ip6Address == NULL)) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  Ip6Str     = (CHAR8 *) String;\r
+  AllowedCnt = 6;\r
+\r
+  //\r
+  // An IPv6 address leading with : looks strange.\r
+  //\r
+  if (*Ip6Str == ':') {\r
+    if (*(Ip6Str + 1) != ':') {\r
+      return EFI_INVALID_PARAMETER;\r
+    } else {\r
+      AllowedCnt = 7;\r
+    }\r
+  }\r
+\r
+  ZeroMem (Ip6Address, sizeof (EFI_IPv6_ADDRESS));\r
+\r
+  NodeCnt     = 0;\r
+  TailNodeCnt = 0;\r
+  Short       = FALSE;\r
+  Update      = FALSE;\r
+\r
+  for (Index = 0; Index < 15; Index = (UINT8) (Index + 2)) {\r
+    TempStr = Ip6Str;\r
+\r
+    while ((*Ip6Str != '\0') && (*Ip6Str != ':')) {\r
+      Ip6Str++;\r
+    }\r
+\r
+    if ((*Ip6Str == '\0') && (Index != 14)) {\r
+      return EFI_INVALID_PARAMETER;\r
+    }\r
+\r
+    if (*Ip6Str == ':') {\r
+      if (*(Ip6Str + 1) == ':') {\r
+        if ((*(Ip6Str + 2) == '0') || (NodeCnt > 6)) {\r
+          //\r
+          // ::0 looks strange. report error to user.\r
+          //\r
+          return EFI_INVALID_PARAMETER;\r
+        }\r
+\r
+        //\r
+        // Skip the abbreviation part of IPv6 address.\r
+        //\r
+        TempStr2 = Ip6Str + 2;\r
+        while ((*TempStr2 != '\0')) {\r
+          if (*TempStr2 == ':') {\r
+            if (*(TempStr2 + 1) == ':') {\r
+              //\r
+              // :: can only appear once in IPv6 address.\r
+              //\r
+              return EFI_INVALID_PARAMETER;\r
+            }\r
+\r
+            TailNodeCnt++;\r
+            if (TailNodeCnt >= (AllowedCnt - NodeCnt)) {\r
+              //\r
+              // :: indicates one or more groups of 16 bits of zeros.\r
+              //\r
+              return EFI_INVALID_PARAMETER;\r
+            }\r
+          }\r
+\r
+          TempStr2++;\r
+        }\r
+\r
+        Short  = TRUE;\r
+        Update = TRUE;\r
+\r
+        Ip6Str = Ip6Str + 2;\r
+      } else {\r
+        Ip6Str++;\r
+        NodeCnt++;\r
+        if ((Short && (NodeCnt > 6)) || (!Short && (NodeCnt > 7))) {\r
+          //\r
+          // There are more than 8 groups of 16 bits of zeros.\r
+          //\r
+          return EFI_INVALID_PARAMETER;\r
+        }\r
+      }\r
+    }\r
+\r
+    //\r
+    // Convert the string to IPv6 address. AsciiStrHexToUintn stops at the first\r
+    // character that is not a valid hexadecimal character, ':' or '\0' here.\r
+    //\r
+    NodeVal = AsciiStrHexToUintn (TempStr);\r
+    if ((NodeVal > 0xFFFF) || (Index > 14)) {\r
+      return EFI_INVALID_PARAMETER;\r
+    }\r
+\r
+    Ip6Address->Addr[Index] = (UINT8) (NodeVal >> 8);\r
+    Ip6Address->Addr[Index + 1] = (UINT8) (NodeVal & 0xFF);\r
+\r
+    //\r
+    // Skip the groups of zeros by ::\r
+    //\r
+    if (Short && Update) {\r
+      Index  = (UINT8) (16 - (TailNodeCnt + 2) * 2);\r
+      Update = FALSE;\r
+    }\r
+  }\r
+\r
+  if ((!Short && Index != 16) || (*Ip6Str != '\0')) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+\r
+/**\r
+  Convert one Null-terminated Unicode string (decimal dotted) to EFI_IPv4_ADDRESS.\r
+\r
+  @param[in]      String         The pointer to the Ascii string.\r
+  @param[out]     Ip4Address     The pointer to the converted IPv4 address.\r
+\r
+  @retval EFI_SUCCESS            Convert to IPv4 address successfully.\r
+  @retval EFI_INVALID_PARAMETER  The string is mal-formated or Ip4Address is NULL.\r
+  @retval EFI_OUT_OF_RESOURCES   Fail to perform the operation due to lack of resource.\r
+\r
+**/\r
+EFI_STATUS\r
+NetLibStrToIp4 (\r
+  IN CONST CHAR16                *String,\r
+  OUT      EFI_IPv4_ADDRESS      *Ip4Address\r
+  )\r
+{\r
+  CHAR8                          *Ip4Str;\r
+  EFI_STATUS                     Status;\r
+\r
+  if ((String == NULL) || (Ip4Address == NULL)) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  Ip4Str = (CHAR8 *) AllocatePool ((StrLen (String) + 1) * sizeof (CHAR8));\r
+  if (Ip4Str == NULL) {\r
+    return EFI_OUT_OF_RESOURCES;\r
+  }\r
+\r
+  UnicodeStrToAsciiStr (String, Ip4Str);\r
+\r
+  Status = NetLibAsciiStrToIp4 (Ip4Str, Ip4Address);\r
+\r
+  FreePool (Ip4Str);\r
+\r
+  return Status;\r
+}\r
+\r
+\r
+/**\r
+  Convert one Null-terminated Unicode string to EFI_IPv6_ADDRESS.  The format of\r
+  the string is defined in RFC 4291 - Text Pepresentation of Addresses.\r
+\r
+  @param[in]      String         The pointer to the Ascii string.\r
+  @param[out]     Ip6Address     The pointer to the converted IPv6 address.\r
+\r
+  @retval EFI_SUCCESS            Convert to IPv6 address successfully.\r
+  @retval EFI_INVALID_PARAMETER  The string is mal-formated or Ip6Address is NULL.\r
+  @retval EFI_OUT_OF_RESOURCES   Fail to perform the operation due to lack of resource.\r
+\r
+**/\r
+EFI_STATUS\r
+NetLibStrToIp6 (\r
+  IN CONST CHAR16                *String,\r
+  OUT      EFI_IPv6_ADDRESS      *Ip6Address\r
+  )\r
+{\r
+  CHAR8                          *Ip6Str;\r
+  EFI_STATUS                     Status;\r
+\r
+  if ((String == NULL) || (Ip6Address == NULL)) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  Ip6Str = (CHAR8 *) AllocatePool ((StrLen (String) + 1) * sizeof (CHAR8));\r
+  if (Ip6Str == NULL) {\r
+    return EFI_OUT_OF_RESOURCES;\r
+  }\r
+\r
+  UnicodeStrToAsciiStr (String, Ip6Str);\r
+\r
+  Status = NetLibAsciiStrToIp6 (Ip6Str, Ip6Address);\r
+\r
+  FreePool (Ip6Str);\r
+\r
+  return Status;\r
+}\r
+\r
+/**\r
+  Convert one Null-terminated Unicode string to EFI_IPv6_ADDRESS and prefix length.\r
+  The format of the string is defined in RFC 4291 - Text Pepresentation of Addresses\r
+  Prefixes: ipv6-address/prefix-length.\r
+\r
+  @param[in]      String         The pointer to the Ascii string.\r
+  @param[out]     Ip6Address     The pointer to the converted IPv6 address.\r
+  @param[out]     PrefixLength   The pointer to the converted prefix length.\r
+\r
+  @retval EFI_SUCCESS            Convert to IPv6 address successfully.\r
+  @retval EFI_INVALID_PARAMETER  The string is mal-formated or Ip6Address is NULL.\r
+  @retval EFI_OUT_OF_RESOURCES   Fail to perform the operation due to lack of resource.\r
+\r
+**/\r
+EFI_STATUS\r
+NetLibStrToIp6andPrefix (\r
+  IN CONST CHAR16                *String,\r
+  OUT      EFI_IPv6_ADDRESS      *Ip6Address,\r
+  OUT      UINT8                 *PrefixLength\r
+  )\r
+{\r
+  CHAR8                          *Ip6Str;\r
+  CHAR8                          *PrefixStr;\r
+  CHAR8                          *TempStr;\r
+  EFI_STATUS                     Status;\r
+  UINT8                          Length;\r
+\r
+  if ((String == NULL) || (Ip6Address == NULL) || (PrefixLength == NULL)) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  Ip6Str = (CHAR8 *) AllocatePool ((StrLen (String) + 1) * sizeof (CHAR8));\r
+  if (Ip6Str == NULL) {\r
+    return EFI_OUT_OF_RESOURCES;\r
+  }\r
+\r
+  UnicodeStrToAsciiStr (String, Ip6Str);\r
+\r
+  //\r
+  // Get the sub string describing prefix length.\r
+  //\r
+  TempStr = Ip6Str;\r
+  while (*TempStr != '\0' && (*TempStr != '/')) {\r
+    TempStr++;\r
+  }\r
+\r
+  if (*TempStr == '/') {\r
+    PrefixStr = TempStr + 1;\r
+  } else {\r
+    PrefixStr = NULL;\r
+  }\r
+\r
+  //\r
+  // Get the sub string describing IPv6 address and convert it.\r
+  //\r
+  *TempStr = '\0';\r
+\r
+  Status = NetLibAsciiStrToIp6 (Ip6Str, Ip6Address);\r
+  if (EFI_ERROR (Status)) {\r
+    goto Exit;\r
+  }\r
+\r
+  //\r
+  // If input string doesn't indicate the prefix length, return 0xff.\r
+  //\r
+  Length = 0xFF;\r
+  \r
+  //\r
+  // Convert the string to prefix length\r
+  //\r
+  if (PrefixStr != NULL) {\r
+\r
+    Status = EFI_INVALID_PARAMETER;\r
+    Length = 0;\r
+    while (*PrefixStr != '\0') {\r
+      if (NET_IS_DIGIT (*PrefixStr)) {\r
+        Length = (UINT8) (Length * 10 + (*PrefixStr - '0'));\r
+        if (Length >= IP6_PREFIX_NUM) {\r
+          goto Exit;\r
+        }\r
+      } else {\r
+        goto Exit;\r
+      }\r
+\r
+      PrefixStr++;\r
+    }\r
+  }\r
+\r
+  *PrefixLength = Length;\r
+  Status        = EFI_SUCCESS;\r
+\r
+Exit:\r
+\r
+  FreePool (Ip6Str);\r
+  return Status;\r
+}\r
+\r