]> git.proxmox.com Git - mirror_edk2.git/blobdiff - NetworkPkg/UefiPxeBcDxe/PxeBcSupport.c
NetworkPkg/UefiPxeBcDxe: Fix various typos
[mirror_edk2.git] / NetworkPkg / UefiPxeBcDxe / PxeBcSupport.c
index 7ad070c5e3f6f17c354ac81ea837ff2561a5e94b..8eb1558d30a963301652f95b798186980d53d737 100644 (file)
 /** @file\r
   Support functions implementation for UefiPxeBc Driver.\r
 \r
-  Copyright (c) 2007 - 2010, Intel Corporation. All rights reserved.<BR>\r
+  Copyright (c) 2007 - 2018, Intel Corporation. All rights reserved.<BR>\r
 \r
-  This program and the accompanying materials\r
-  are licensed and made available under the terms and conditions of the BSD License\r
-  which accompanies this distribution.  The full text of the license may be found at\r
-  http://opensource.org/licenses/bsd-license.php.\r
-\r
-  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
-  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+  SPDX-License-Identifier: BSD-2-Clause-Patent\r
 \r
 **/\r
 \r
 #include "PxeBcImpl.h"\r
 \r
 \r
-\r
 /**\r
-  This function returns SMBIOS string given the string number.\r
-\r
-  @param[in]  Smbios              The pointer to the SMBIOS structure\r
-  @param[in]  StringNumber        String number to return. 0 is used to skip all\r
-                                  strings and point to the next SMBIOS structure.\r
-\r
-  @return String                  The pointer to the next SMBIOS structure if\r
-                                  StringNumber == 0.\r
-\r
-**/\r
-CHAR8 *\r
-PxeBcGetSmbiosString (\r
-  IN  SMBIOS_STRUCTURE_POINTER  *Smbios,\r
-  IN  UINT16                    StringNumber\r
-  )\r
-{\r
-  UINT16                        Index;\r
-  CHAR8                         *String;\r
-\r
-  //\r
-  // Skip over formatted section.\r
-  //\r
-  String = (CHAR8 *) (Smbios->Raw + Smbios->Hdr->Length);\r
-\r
-  //\r
-  // Look through unformated section.\r
-  //\r
-  for (Index = 1; Index <= StringNumber || StringNumber == 0; Index++) {\r
-    if (StringNumber == Index) {\r
-      return String;\r
-    }\r
-\r
-    //\r
-    // Skip zero string.\r
-    //\r
-    while (*String != 0) {\r
-      String++;\r
-    }\r
-    String++;\r
-\r
-    if (*String == 0) {\r
-      //\r
-      // If double NULL then we are done.\r
-      //  Return pointer to next structure in Smbios.\r
-      //  if you pass in a 0 you will always get here\r
-      //\r
-      Smbios->Raw = (UINT8 *)++String;\r
-      return NULL;\r
-    }\r
-  }\r
-\r
-  return NULL;\r
-}\r
-\r
-\r
-/**\r
-  This function obtains the system guid and the serial number from the smbios table.\r
-\r
-  @param[out]  SystemGuid     The pointer of the returned system guid.\r
-\r
-  @retval EFI_SUCCESS         Successfully obtained the system guid.\r
-  @retval EFI_NOT_FOUND       Did not find the SMBIOS table.\r
-\r
-**/\r
-EFI_STATUS\r
-PxeBcGetSystemGuid (\r
-  OUT EFI_GUID              *SystemGuid\r
-  )\r
-{\r
-  EFI_STATUS                Status;\r
-  SMBIOS_TABLE_ENTRY_POINT  *SmbiosTable;\r
-  SMBIOS_STRUCTURE_POINTER  Smbios;\r
-  SMBIOS_STRUCTURE_POINTER  SmbiosEnd;\r
-  UINT16                    Index;\r
-\r
-  SmbiosTable = NULL;\r
-  Status      = EfiGetSystemConfigurationTable (&gEfiSmbiosTableGuid, (VOID **) &SmbiosTable);\r
-\r
-  if (EFI_ERROR (Status) || SmbiosTable == NULL) {\r
-    return EFI_NOT_FOUND;\r
-  }\r
-\r
-  Smbios.Hdr    = (SMBIOS_STRUCTURE *) (UINTN) SmbiosTable->TableAddress;\r
-  SmbiosEnd.Raw = (UINT8 *) (UINTN) (SmbiosTable->TableAddress + SmbiosTable->TableLength);\r
-\r
-  for (Index = 0; Index < SmbiosTable->TableLength; Index++) {\r
-    if (Smbios.Hdr->Type == 1) {\r
-      if (Smbios.Hdr->Length < 0x19) {\r
-        //\r
-        // Older version did not support Guid and Serial number\r
-        //\r
-        continue;\r
-      }\r
-      //\r
-      // SMBIOS tables are byte packed so we need to do a byte copy to\r
-      // prevend alignment faults on Itanium-based platform.\r
-      //\r
-      CopyMem (SystemGuid, &Smbios.Type1->Uuid, sizeof (EFI_GUID));\r
-      PxeBcGetSmbiosString (&Smbios, Smbios.Type1->SerialNumber);\r
-\r
-      return EFI_SUCCESS;\r
-    }\r
-    //\r
-    // Make Smbios point to the next record\r
-    //\r
-    PxeBcGetSmbiosString (&Smbios, 0);\r
-\r
-    if (Smbios.Raw >= SmbiosEnd.Raw) {\r
-      //\r
-      // SMBIOS 2.1 incorrectly stated the length of SmbiosTable as 0x1e.\r
-      // given this we must double check against the length of the structure.\r
-      //\r
-      return EFI_SUCCESS;\r
-    }\r
-  }\r
-\r
-  return EFI_NOT_FOUND;\r
-}\r
-\r
-\r
-/**\r
-  Flush the previous configration using the new station Ip address.\r
+  Flush the previous configuration using the new station Ip address.\r
 \r
   @param[in]   Private        The pointer to the PxeBc private data.\r
   @param[in]   StationIp      The pointer to the station Ip address.\r
@@ -150,22 +22,24 @@ PxeBcGetSystemGuid (
 \r
 **/\r
 EFI_STATUS\r
-PxeBcFlushStaionIp (\r
+PxeBcFlushStationIp (\r
   PXEBC_PRIVATE_DATA       *Private,\r
-  EFI_IP_ADDRESS           *StationIp,\r
+  EFI_IP_ADDRESS           *StationIp,     OPTIONAL\r
   EFI_IP_ADDRESS           *SubnetMask     OPTIONAL\r
   )\r
 {\r
   EFI_PXE_BASE_CODE_MODE   *Mode;\r
   EFI_STATUS               Status;\r
-\r
-  ASSERT (StationIp != NULL);\r
+  EFI_ARP_CONFIG_DATA      ArpConfigData;\r
 \r
   Mode   = Private->PxeBc.Mode;\r
   Status = EFI_SUCCESS;\r
+  ZeroMem (&ArpConfigData, sizeof (EFI_ARP_CONFIG_DATA));\r
 \r
-  if (Mode->UsingIpv6) {\r
-\r
+  if (Mode->UsingIpv6 && StationIp != NULL) {\r
+    //\r
+    // Overwrite Udp6CfgData/Ip6CfgData StationAddress.\r
+    //\r
     CopyMem (&Private->Udp6CfgData.StationAddress, StationIp, sizeof (EFI_IPv6_ADDRESS));\r
     CopyMem (&Private->Ip6CfgData.StationAddress, StationIp, sizeof (EFI_IPv6_ADDRESS));\r
 \r
@@ -181,33 +55,57 @@ PxeBcFlushStaionIp (
     }\r
 \r
     Status = Private->Ip6->Receive (Private->Ip6, &Private->Icmp6Token);\r
-    if (EFI_ERROR (Status)) {\r
-      goto ON_EXIT;\r
-    }\r
-\r
   } else {\r
-    ASSERT (SubnetMask != NULL);\r
-    CopyMem (&Private->Udp4CfgData.StationAddress, StationIp, sizeof (EFI_IPv4_ADDRESS));\r
-    CopyMem (&Private->Udp4CfgData.SubnetMask, SubnetMask, sizeof (EFI_IPv4_ADDRESS));\r
-    CopyMem (&Private->Ip4CfgData.StationAddress, StationIp, sizeof (EFI_IPv4_ADDRESS));\r
-    CopyMem (&Private->Ip4CfgData.SubnetMask, SubnetMask, sizeof (EFI_IPv4_ADDRESS));\r
+    if (StationIp != NULL) {\r
+      //\r
+      // Reconfigure the ARP instance with station Ip address.\r
+      //\r
+      ArpConfigData.SwAddressType   = 0x0800;\r
+      ArpConfigData.SwAddressLength = (UINT8) sizeof (EFI_IPv4_ADDRESS);\r
+      ArpConfigData.StationAddress = StationIp;\r
 \r
-    //\r
-    // Reconfigure the Ip4 instance to capture background ICMP packets with new station Ip address.\r
-    //\r
-    Private->Ip4->Cancel (Private->Ip4, &Private->IcmpToken);\r
-    Private->Ip4->Configure (Private->Ip4, NULL);\r
+      Private->Arp->Configure (Private->Arp, NULL);\r
+      Private->Arp->Configure (Private->Arp, &ArpConfigData);\r
 \r
-    Status = Private->Ip4->Configure (Private->Ip4, &Private->Ip4CfgData);\r
-    if (EFI_ERROR (Status)) {\r
-      goto ON_EXIT;\r
+      //\r
+      // Overwrite Udp4CfgData/Ip4CfgData StationAddress.\r
+      //\r
+      CopyMem (&Private->Udp4CfgData.StationAddress, StationIp, sizeof (EFI_IPv4_ADDRESS));\r
+      CopyMem (&Private->Ip4CfgData.StationAddress, StationIp, sizeof (EFI_IPv4_ADDRESS));\r
     }\r
 \r
-    Status = Private->Ip4->Receive (Private->Ip4, &Private->IcmpToken);\r
-    if (EFI_ERROR (Status)) {\r
-      goto ON_EXIT;\r
+    if (SubnetMask != NULL) {\r
+      //\r
+      // Overwrite Udp4CfgData/Ip4CfgData SubnetMask.\r
+      //\r
+      CopyMem (&Private->Udp4CfgData.SubnetMask, SubnetMask, sizeof (EFI_IPv4_ADDRESS));\r
+      CopyMem (&Private->Ip4CfgData.SubnetMask, SubnetMask, sizeof (EFI_IPv4_ADDRESS));\r
     }\r
 \r
+    if (StationIp != NULL && SubnetMask != NULL) {\r
+      //\r
+      // Updated the route table.\r
+      //\r
+      Mode->RouteTableEntries                = 1;\r
+      Mode->RouteTable[0].IpAddr.Addr[0]     = StationIp->Addr[0] & SubnetMask->Addr[0];\r
+      Mode->RouteTable[0].SubnetMask.Addr[0] = SubnetMask->Addr[0];\r
+      Mode->RouteTable[0].GwAddr.Addr[0]     = 0;\r
+    }\r
+\r
+    if (StationIp != NULL || SubnetMask != NULL) {\r
+      //\r
+      // Reconfigure the Ip4 instance to capture background ICMP packets with new station Ip address.\r
+      //\r
+      Private->Ip4->Cancel (Private->Ip4, &Private->IcmpToken);\r
+      Private->Ip4->Configure (Private->Ip4, NULL);\r
+\r
+      Status = Private->Ip4->Configure (Private->Ip4, &Private->Ip4CfgData);\r
+      if (EFI_ERROR (Status)) {\r
+        goto ON_EXIT;\r
+      }\r
+\r
+      Status = Private->Ip4->Receive (Private->Ip4, &Private->IcmpToken);\r
+    }\r
   }\r
 \r
 ON_EXIT:\r
@@ -238,7 +136,7 @@ PxeBcCommonNotify (
 \r
   @param  Mode           The pointer to EFI_PXE_BASE_CODE_MODE.\r
   @param  Ip4Addr        The Ip4 address for resolution.\r
-  @param  MacAddress     The resoluted MAC address if the resolution is successful.\r
+  @param  MacAddress     The resolved MAC address if the resolution is successful.\r
                          The value is undefined if the resolution fails.\r
 \r
   @retval TRUE           Found an matched entry.\r
@@ -383,34 +281,30 @@ PxeBcIcmpErrorDpcHandle (
     //\r
     // The return status should be recognized as EFI_ICMP_ERROR.\r
     //\r
-    gBS->SignalEvent (RxData->RecycleSignal);\r
-    goto ON_EXIT;\r
+    goto ON_RECYCLE;\r
   }\r
 \r
   if (EFI_IP4 (RxData->Header->SourceAddress) != 0 &&\r
-      !NetIp4IsUnicast (EFI_NTOHL (RxData->Header->SourceAddress), 0)) {\r
+      (NTOHL (Mode->SubnetMask.Addr[0]) != 0) &&\r
+      IP4_NET_EQUAL (NTOHL(Mode->StationIp.Addr[0]), EFI_NTOHL (RxData->Header->SourceAddress), NTOHL (Mode->SubnetMask.Addr[0])) &&\r
+      !NetIp4IsUnicast (EFI_NTOHL (RxData->Header->SourceAddress), NTOHL (Mode->SubnetMask.Addr[0]))) {\r
     //\r
     // The source address of the received packet should be a valid unicast address.\r
     //\r
-    gBS->SignalEvent (RxData->RecycleSignal);\r
-    goto ON_EXIT;\r
+    goto ON_RECYCLE;\r
   }\r
 \r
   if (!EFI_IP4_EQUAL (&RxData->Header->DestinationAddress, &Mode->StationIp.v4)) {\r
     //\r
     // The destination address of the received packet should be equal to the host address.\r
     //\r
-    gBS->SignalEvent (RxData->RecycleSignal);\r
-    goto ON_EXIT;\r
+    goto ON_RECYCLE;\r
   }\r
 \r
-  if (RxData->Header->Protocol != EFI_IP_PROTO_ICMP) {\r
-    //\r
-    // The protocol value in the header of the receveid packet should be EFI_IP_PROTO_ICMP.\r
-    //\r
-    gBS->SignalEvent (RxData->RecycleSignal);\r
-    goto ON_EXIT;\r
-  }\r
+  //\r
+  // The protocol has been configured to only receive ICMP packet.\r
+  //\r
+  ASSERT (RxData->Header->Protocol == EFI_IP_PROTO_ICMP);\r
 \r
   Type = *((UINT8 *) RxData->FragmentTable[0].FragmentBuffer);\r
 \r
@@ -422,8 +316,7 @@ PxeBcIcmpErrorDpcHandle (
     //\r
     // The type of the receveid ICMP message should be ICMP_ERROR_MESSAGE.\r
     //\r
-    gBS->SignalEvent (RxData->RecycleSignal);\r
-    goto ON_EXIT;\r
+    goto ON_RECYCLE;\r
   }\r
 \r
   //\r
@@ -450,6 +343,9 @@ PxeBcIcmpErrorDpcHandle (
     IcmpError += CopiedLen;\r
   }\r
 \r
+ON_RECYCLE:\r
+  gBS->SignalEvent (RxData->RecycleSignal);\r
+\r
 ON_EXIT:\r
   Private->IcmpToken.Status = EFI_NOT_READY;\r
   Ip4->Receive (Ip4, &Private->IcmpToken);\r
@@ -519,16 +415,14 @@ PxeBcIcmp6ErrorDpcHandle (
     //\r
     // The return status should be recognized as EFI_ICMP_ERROR.\r
     //\r
-    gBS->SignalEvent (RxData->RecycleSignal);\r
-    goto ON_EXIT;\r
+    goto ON_RECYCLE;\r
   }\r
 \r
   if (!NetIp6IsValidUnicast (&RxData->Header->SourceAddress)) {\r
     //\r
     // The source address of the received packet should be a valid unicast address.\r
     //\r
-    gBS->SignalEvent (RxData->RecycleSignal);\r
-    goto ON_EXIT;\r
+    goto ON_RECYCLE;\r
   }\r
 \r
   if (!NetIp6IsUnspecifiedAddr (&Mode->StationIp.v6) &&\r
@@ -536,29 +430,24 @@ PxeBcIcmp6ErrorDpcHandle (
     //\r
     // The destination address of the received packet should be equal to the host address.\r
     //\r
-    gBS->SignalEvent (RxData->RecycleSignal);\r
-    goto ON_EXIT;\r
+    goto ON_RECYCLE;\r
   }\r
 \r
-  if (RxData->Header->NextHeader != IP6_ICMP) {\r
-    //\r
-    // The nextheader in the header of the receveid packet should be IP6_ICMP.\r
-    //\r
-    gBS->SignalEvent (RxData->RecycleSignal);\r
-    goto ON_EXIT;\r
-  }\r
+  //\r
+  // The protocol has been configured to only receive ICMP packet.\r
+  //\r
+  ASSERT (RxData->Header->NextHeader == IP6_ICMP);\r
 \r
   Type = *((UINT8 *) RxData->FragmentTable[0].FragmentBuffer);\r
 \r
   if (Type != ICMP_V6_DEST_UNREACHABLE &&\r
       Type != ICMP_V6_PACKET_TOO_BIG &&\r
-      Type != ICMP_V6_PACKET_TOO_BIG &&\r
+      Type != ICMP_V6_TIME_EXCEEDED &&\r
       Type != ICMP_V6_PARAMETER_PROBLEM) {\r
     //\r
     // The type of the receveid packet should be an ICMP6 error message.\r
     //\r
-    gBS->SignalEvent (RxData->RecycleSignal);\r
-    goto ON_EXIT;\r
+    goto ON_RECYCLE;\r
   }\r
 \r
   //\r
@@ -585,6 +474,9 @@ PxeBcIcmp6ErrorDpcHandle (
     Icmp6Error += CopiedLen;\r
   }\r
 \r
+ON_RECYCLE:\r
+  gBS->SignalEvent (RxData->RecycleSignal);\r
+\r
 ON_EXIT:\r
   Private->Icmp6Token.Status = EFI_NOT_READY;\r
   Ip6->Receive (Ip6, &Private->Icmp6Token);\r
@@ -619,6 +511,8 @@ PxeBcIcmp6ErrorUpdate (
   @param[in, out]  SrcPort              The pointer to the source port.\r
   @param[in]       DoNotFragment        If TRUE, fragment is not enabled.\r
                                         Otherwise, fragment is enabled.\r
+  @param[in]       Ttl                  The time to live field of the IP header.\r
+  @param[in]       ToS                  The type of service field of the IP header.\r
 \r
   @retval          EFI_SUCCESS          Successfully configured this instance.\r
   @retval          Others               Failed to configure this instance.\r
@@ -631,7 +525,9 @@ PxeBcConfigUdp4Write (
   IN     EFI_IPv4_ADDRESS   *SubnetMask,\r
   IN     EFI_IPv4_ADDRESS   *Gateway,\r
   IN OUT UINT16             *SrcPort,\r
-  IN     BOOLEAN            DoNotFragment\r
+  IN     BOOLEAN            DoNotFragment,\r
+  IN     UINT8              Ttl,\r
+  IN     UINT8              ToS\r
   )\r
 {\r
   EFI_UDP4_CONFIG_DATA  Udp4CfgData;\r
@@ -641,8 +537,8 @@ PxeBcConfigUdp4Write (
 \r
   Udp4CfgData.TransmitTimeout    = PXEBC_DEFAULT_LIFETIME;\r
   Udp4CfgData.ReceiveTimeout     = PXEBC_DEFAULT_LIFETIME;\r
-  Udp4CfgData.TypeOfService      = DEFAULT_ToS;\r
-  Udp4CfgData.TimeToLive         = DEFAULT_TTL;\r
+  Udp4CfgData.TypeOfService      = ToS;\r
+  Udp4CfgData.TimeToLive         = Ttl;\r
   Udp4CfgData.AllowDuplicatePort = TRUE;\r
   Udp4CfgData.DoNotFragment      = DoNotFragment;\r
 \r
@@ -1081,7 +977,7 @@ PxeBcCheckByDestIp (
 /**\r
   Check the received packet using the destination port.\r
 \r
-  @param[in]       PxeBcMode      The pointer to the mode data of PxeBc.\r
+  @param[in]       Mode           The pointer to the mode data of PxeBc.\r
   @param[in]       Session        The pointer to the current UDPv4 session.\r
   @param[in, out]  DestPort       The pointer to the destination port.\r
   @param[in]       OpFlags        Operation flag for UdpRead/UdpWrite.\r
@@ -1280,7 +1176,7 @@ PxeBcUdp4Read (
          Token->Status == EFI_NOT_READY &&\r
          EFI_ERROR (gBS->CheckEvent (TimeoutEvent))) {\r
     //\r
-    // Poll the token utill reply/ICMPv6 error message received or timeout.\r
+    // Poll the token until reply/ICMPv6 error message received or timeout.\r
     //\r
     Udp4->Poll (Udp4);\r
     if (Token->Status == EFI_ICMP_ERROR ||\r
@@ -1384,7 +1280,7 @@ PxeBcUdp6Read (
          Token->Status == EFI_NOT_READY &&\r
          EFI_ERROR (gBS->CheckEvent (TimeoutEvent))) {\r
     //\r
-    // Poll the token utill reply/ICMPv6 error message received or timeout.\r
+    // Poll the token until reply/ICMPv6 error message received or timeout.\r
     //\r
     Udp6->Poll (Udp6);\r
     if (Token->Status == EFI_ICMP_ERROR ||\r
@@ -1507,11 +1403,10 @@ PxeBcUintnToAscDecWithFormat (
 {\r
   UINTN                          Remainder;\r
 \r
-  while (Length > 0) {\r
-    Length--;\r
+  for (; Length > 0; Length--) {\r
     Remainder      = Number % 10;\r
     Number        /= 10;\r
-    Buffer[Length] = (UINT8) ('0' + Remainder);\r
+    Buffer[Length - 1] = (UINT8) ('0' + Remainder);\r
   }\r
 }\r
 \r
@@ -1522,6 +1417,7 @@ PxeBcUintnToAscDecWithFormat (
 \r
   @param[in]  Number         Numeric value to be converted.\r
   @param[in]  Buffer         The pointer to the buffer for ASCII string.\r
+  @param[in]  BufferSize     The maxsize of the buffer.\r
 \r
   @return     Length         The actual length of the ASCII string.\r
 \r
@@ -1529,7 +1425,8 @@ PxeBcUintnToAscDecWithFormat (
 UINTN\r
 PxeBcUintnToAscDec (\r
   IN UINTN               Number,\r
-  IN UINT8               *Buffer\r
+  IN UINT8               *Buffer,\r
+  IN UINTN               BufferSize\r
   )\r
 {\r
   UINTN           Index;\r
@@ -1545,7 +1442,7 @@ PxeBcUintnToAscDec (
     Number         = (UINTN) (Number / 10);\r
   } while (Number != 0);\r
 \r
-  AsciiStrCpy ((CHAR8 *) Buffer, &TempStr[Index]);\r
+  AsciiStrCpyS ((CHAR8 *) Buffer, BufferSize, &TempStr[Index]);\r
 \r
   Length = AsciiStrLen ((CHAR8 *) Buffer);\r
 \r
@@ -1586,3 +1483,56 @@ PxeBcUniHexToUint8 (
 \r
   return EFI_INVALID_PARAMETER;\r
 }\r
+\r
+/**\r
+  Calculate the elapsed time.\r
+\r
+  @param[in]      Private      The pointer to PXE private data\r
+\r
+**/\r
+VOID\r
+CalcElapsedTime (\r
+  IN     PXEBC_PRIVATE_DATA     *Private\r
+  )\r
+{\r
+  EFI_TIME          Time;\r
+  UINT64            CurrentStamp;\r
+  UINT64            ElapsedTimeValue;\r
+\r
+  //\r
+  // Generate a time stamp of the centiseconds from 1900/1/1, assume 30day/month.\r
+  //\r
+  ZeroMem (&Time, sizeof (EFI_TIME));\r
+  gRT->GetTime (&Time, NULL);\r
+  CurrentStamp = MultU64x32 (\r
+                   ((((UINT32)(Time.Year - 1900) * 360 + (Time.Month - 1) * 30 + (Time.Day - 1)) * 24 + Time.Hour) * 60 + Time.Minute) * 60 + Time.Second,\r
+                   100\r
+                   ) +\r
+                 DivU64x32 (\r
+                   Time.Nanosecond,\r
+                   10000000\r
+                   );\r
+\r
+  //\r
+  // Sentinel value of 0 means that this is the first DHCP packet that we are\r
+  // sending and that we need to initialize the value.  First DHCP Solicit\r
+  // gets 0 elapsed-time.  Otherwise, calculate based on StartTime.\r
+  //\r
+  if (Private->ElapsedTime == 0) {\r
+    Private->ElapsedTime = CurrentStamp;\r
+  } else {\r
+    ElapsedTimeValue = CurrentStamp - Private->ElapsedTime;\r
+\r
+    //\r
+    // If elapsed time cannot fit in two bytes, set it to 0xffff.\r
+    //\r
+    if (ElapsedTimeValue > 0xffff) {\r
+      ElapsedTimeValue = 0xffff;\r
+    }\r
+    //\r
+    // Save the elapsed time\r
+    //\r
+    Private->ElapsedTime = ElapsedTimeValue;\r
+  }\r
+}\r
+\r