]> git.proxmox.com Git - mirror_edk2.git/commitdiff
ArmPkg/BdsLib: Rework TFTP boot
authorRonald Cron <Ronald.Cron@arm.com>
Fri, 12 Dec 2014 19:14:22 +0000 (19:14 +0000)
committeroliviermartin <oliviermartin@Edk2>
Fri, 12 Dec 2014 19:14:22 +0000 (19:14 +0000)
Rework the downloading of an image from a TFTP server to do not
depend on any "PXE specific" setting of the DHCP server.

Contributed-under: TianoCore Contribution Agreement 1.0
Signed-off-by: Ronald Cron <Ronald.Cron@arm.com>
Reviewed-by: Olivier Martin <olivier.martin@arm.com>
git-svn-id: https://svn.code.sf.net/p/edk2/code/trunk/edk2@16516 6f19259b-4bc3-4df7-8a09-765794883524

ArmPkg/ArmPkg.dsc
ArmPkg/Library/BdsLib/BdsFilePath.c
ArmPkg/Library/BdsLib/BdsInternal.h
ArmPkg/Library/BdsLib/BdsLib.inf
ArmPlatformPkg/Bds/Bds.inf
ArmPlatformPkg/Bds/BootOptionSupport.c

index 5d9eb177e046531b9296f2cea6bab8c3b8ae9c69..be5126ccefd9205aa56f1aad06322c968a79f5e7 100644 (file)
   PeCoffLib|MdePkg/Library/BasePeCoffLib/BasePeCoffLib.inf\r
   PeCoffGetEntryPointLib|MdePkg/Library/BasePeCoffGetEntryPointLib/BasePeCoffGetEntryPointLib.inf\r
 \r
+  NetLib|MdeModulePkg/Library/DxeNetLib/DxeNetLib.inf\r
+  UefiHiiServicesLib|MdeModulePkg/Library/UefiHiiServicesLib/UefiHiiServicesLib.inf\r
+  HiiLib|MdeModulePkg/Library/UefiHiiLib/UefiHiiLib.inf\r
+\r
   SemihostLib|ArmPkg/Library/SemihostLib/SemihostLib.inf\r
   UncachedMemoryAllocationLib|ArmPkg/Library/UncachedMemoryAllocationLib/UncachedMemoryAllocationLib.inf\r
   DxeServicesTableLib|MdePkg/Library/DxeServicesTableLib/DxeServicesTableLib.inf\r
index f26ba39ddba8bc1c9122085727336911ea18ee85..0057d941263bb54e4887e8da1f52be0be8146a31 100644 (file)
 \r
 #include "BdsInternal.h"\r
 \r
+#include <Library/NetLib.h>\r
+\r
+#include <Protocol/Bds.h>\r
 #include <Protocol/UsbIo.h>\r
 #include <Protocol/DiskIo.h>\r
 #include <Protocol/LoadedImage.h>\r
 #include <Protocol/SimpleNetwork.h>\r
+#include <Protocol/Dhcp4.h>\r
+#include <Protocol/Mtftp4.h>\r
 \r
 #define IS_DEVICE_PATH_NODE(node,type,subtype) (((node)->Type == (type)) && ((node)->SubType == (subtype)))\r
 \r
+/*\r
+   Constant strings and define related to the message indicating the amount of\r
+   progress in the dowloading of a TFTP file.\r
+*/\r
+\r
+// Frame for the progression slider\r
+STATIC CONST CHAR16 mTftpProgressFrame[] = L"[                                        ]";\r
+\r
+// Number of steps in the progression slider\r
+#define TFTP_PROGRESS_SLIDER_STEPS  ((sizeof (mTftpProgressFrame) / sizeof (CHAR16)) - 3)\r
+\r
+// Size in number of characters plus one (final zero) of the message to\r
+// indicate the progress of a tftp download. The format is "[(progress slider:\r
+// 40 characters)] (nb of KBytes downloaded so far: 7 characters) Kb". There\r
+// are thus the number of characters in mTftpProgressFrame[] plus 11 characters\r
+// (2 // spaces, "Kb" and seven characters for the number of KBytes).\r
+#define TFTP_PROGRESS_MESSAGE_SIZE  ((sizeof (mTftpProgressFrame) / sizeof (CHAR16)) + 12)\r
+\r
+// String to delete the tftp progress message to be able to update it :\r
+// (TFTP_PROGRESS_MESSAGE_SIZE-1) '\b'\r
+STATIC CONST CHAR16 mTftpProgressDelete[] = L"\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b";\r
+\r
+\r
 // Extract the FilePath from the Device Path\r
 CHAR16*\r
 BdsExtractFilePathFromDevicePath (\r
@@ -723,14 +751,14 @@ BdsPxeLoadImage (
 \r
 BOOLEAN\r
 BdsTftpSupport (\r
-  IN EFI_DEVICE_PATH*           DevicePath,\r
-  IN EFI_HANDLE                 Handle,\r
-  IN EFI_DEVICE_PATH*           RemainingDevicePath\r
+  IN EFI_DEVICE_PATH  *DevicePath,\r
+  IN EFI_HANDLE       Handle,\r
+  IN EFI_DEVICE_PATH  *RemainingDevicePath\r
   )\r
 {\r
-  EFI_STATUS  Status;\r
+  EFI_STATUS       Status;\r
   EFI_DEVICE_PATH  *NextDevicePath;\r
-  EFI_PXE_BASE_CODE_PROTOCOL  *PxeBcProtocol;\r
+  VOID             *Interface;\r
 \r
   // Validate the Remaining Device Path\r
   if (IsDevicePathEnd (RemainingDevicePath)) {\r
@@ -748,151 +776,412 @@ BdsTftpSupport (
     return FALSE;\r
   }\r
 \r
-  Status = gBS->HandleProtocol (Handle, &gEfiPxeBaseCodeProtocolGuid, (VOID **)&PxeBcProtocol);\r
+  Status = gBS->HandleProtocol (\r
+                  Handle, &gEfiDevicePathProtocolGuid,\r
+                  &Interface\r
+                  );\r
   if (EFI_ERROR (Status)) {\r
     return FALSE;\r
-  } else {\r
-    return TRUE;\r
   }\r
+\r
+  //\r
+  // Check that the controller (identified by its handle "Handle") supports the\r
+  // MTFTPv4 Service Binding Protocol. If it does, it means that it supports the\r
+  // EFI MTFTPv4 Protocol needed to download the image through TFTP.\r
+  //\r
+  Status = gBS->HandleProtocol (\r
+                  Handle, &gEfiMtftp4ServiceBindingProtocolGuid,\r
+                  &Interface\r
+                  );\r
+  if (EFI_ERROR (Status)) {\r
+    return FALSE;\r
+  }\r
+\r
+  return TRUE;\r
+}\r
+\r
+/**\r
+  Worker function that get the size in numbers of bytes of a file from a TFTP\r
+  server before to download the file.\r
+\r
+  @param[in]   Mtftp4    MTFTP4 protocol interface\r
+  @param[in]   FilePath  Path of the file, Ascii encoded\r
+  @param[out]  FileSize  Address where to store the file size in number of\r
+                         bytes.\r
+\r
+  @retval  EFI_SUCCESS   The size of the file was returned.\r
+  @retval  !EFI_SUCCESS  The size of the file was not returned.\r
+\r
+**/\r
+STATIC\r
+EFI_STATUS\r
+Mtftp4GetFileSize (\r
+  IN  EFI_MTFTP4_PROTOCOL  *Mtftp4,\r
+  IN  CHAR8                *FilePath,\r
+  OUT UINT64               *FileSize\r
+  )\r
+{\r
+  EFI_STATUS         Status;\r
+  EFI_MTFTP4_OPTION  ReqOpt[1];\r
+  EFI_MTFTP4_PACKET  *Packet;\r
+  UINT32             PktLen;\r
+  EFI_MTFTP4_OPTION  *TableOfOptions;\r
+  EFI_MTFTP4_OPTION  *Option;\r
+  UINT32             OptCnt;\r
+  UINT8              OptBuf[128];\r
+\r
+  ReqOpt[0].OptionStr = (UINT8*)"tsize";\r
+  OptBuf[0] = '0';\r
+  OptBuf[1] = 0;\r
+  ReqOpt[0].ValueStr = OptBuf;\r
+\r
+  Status = Mtftp4->GetInfo (\r
+             Mtftp4,\r
+             NULL,\r
+             (UINT8*)FilePath,\r
+             NULL,\r
+             1,\r
+             ReqOpt,\r
+             &PktLen,\r
+             &Packet\r
+             );\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    goto Error;\r
+  }\r
+\r
+  Status = Mtftp4->ParseOptions (\r
+                     Mtftp4,\r
+                     PktLen,\r
+                     Packet,\r
+                     (UINT32 *) &OptCnt,\r
+                     &TableOfOptions\r
+                     );\r
+  if (EFI_ERROR (Status)) {\r
+    goto Error;\r
+  }\r
+\r
+  Option = TableOfOptions;\r
+  while (OptCnt != 0) {\r
+    if (AsciiStrnCmp ((CHAR8 *)Option->OptionStr, "tsize", 5) == 0) {\r
+      *FileSize = AsciiStrDecimalToUint64 ((CHAR8 *)Option->ValueStr);\r
+      break;\r
+    }\r
+    OptCnt--;\r
+    Option++;\r
+  }\r
+  FreePool (TableOfOptions);\r
+\r
+  if (OptCnt == 0) {\r
+    Status = EFI_UNSUPPORTED;\r
+  }\r
+\r
+Error :\r
+\r
+  return Status;\r
 }\r
 \r
+/**\r
+  Update the progress of a file download\r
+  This procedure is called each time a new TFTP packet is received.\r
+\r
+  @param[in]  This       MTFTP4 protocol interface\r
+  @param[in]  Token      Parameters for the download of the file\r
+  @param[in]  PacketLen  Length of the packet\r
+  @param[in]  Packet     Address of the packet\r
+\r
+  @retval  EFI_SUCCESS  All packets are accepted.\r
+\r
+**/\r
+STATIC\r
+EFI_STATUS\r
+Mtftp4CheckPacket (\r
+  IN EFI_MTFTP4_PROTOCOL  *This,\r
+  IN EFI_MTFTP4_TOKEN     *Token,\r
+  IN UINT16               PacketLen,\r
+  IN EFI_MTFTP4_PACKET    *Packet\r
+  )\r
+{\r
+  BDS_TFTP_CONTEXT  *Context;\r
+  CHAR16            Progress[TFTP_PROGRESS_MESSAGE_SIZE];\r
+  UINT64            NbOfKb;\r
+  UINTN             Index;\r
+  UINTN             LastStep;\r
+  UINTN             Step;\r
+  UINT64            LastNbOf50Kb;\r
+  UINT64            NbOf50Kb;\r
+\r
+  if ((NTOHS (Packet->OpCode)) == EFI_MTFTP4_OPCODE_DATA) {\r
+    Context = (BDS_TFTP_CONTEXT*)Token->Context;\r
+\r
+    if (Context->DownloadedNbOfBytes == 0) {\r
+      if (Context->FileSize > 0) {\r
+        Print (L"%s       0 Kb", mTftpProgressFrame);\r
+      } else {\r
+        Print (L"    0 Kb");\r
+      }\r
+    }\r
+\r
+    //\r
+    // The data is the packet are prepended with two UINT16 :\r
+    // . OpCode = EFI_MTFTP4_OPCODE_DATA\r
+    // . Block  = the number of this block of data\r
+    //\r
+    Context->DownloadedNbOfBytes += PacketLen - sizeof (Packet->OpCode) - sizeof (Packet->Data.Block);\r
+    NbOfKb = Context->DownloadedNbOfBytes / 1024;\r
+\r
+    Progress[0] = L'\0';\r
+    if (Context->FileSize > 0) {\r
+      LastStep  = (Context->LastReportedNbOfBytes * TFTP_PROGRESS_SLIDER_STEPS) / Context->FileSize;\r
+      Step      = (Context->DownloadedNbOfBytes   * TFTP_PROGRESS_SLIDER_STEPS) / Context->FileSize;\r
+      if (Step > LastStep) {\r
+        Print (mTftpProgressDelete);\r
+        StrCpy (Progress, mTftpProgressFrame);\r
+        for (Index = 1; Index < Step; Index++) {\r
+          Progress[Index] = L'=';\r
+        }\r
+        Progress[Step] = L'>';\r
+\r
+        UnicodeSPrint (\r
+          Progress + (sizeof (mTftpProgressFrame) / sizeof (CHAR16)) - 1,\r
+          sizeof (Progress) - sizeof (mTftpProgressFrame),\r
+          L" %7d Kb",\r
+          NbOfKb\r
+          );\r
+        Context->LastReportedNbOfBytes = Context->DownloadedNbOfBytes;\r
+      }\r
+    } else {\r
+      //\r
+      // Case when we do not know the size of the final file.\r
+      // We print the updated size every 50KB of downloaded data\r
+      //\r
+      LastNbOf50Kb = Context->LastReportedNbOfBytes / (50*1024);\r
+      NbOf50Kb     = Context->DownloadedNbOfBytes   / (50*1024);\r
+      if (NbOf50Kb > LastNbOf50Kb) {\r
+        Print (L"\b\b\b\b\b\b\b\b\b\b");\r
+        UnicodeSPrint (Progress, sizeof (Progress), L"%7d Kb", NbOfKb);\r
+        Context->LastReportedNbOfBytes = Context->DownloadedNbOfBytes;\r
+      }\r
+    }\r
+    if (Progress[0] != L'\0') {\r
+      Print (L"%s", Progress);\r
+    }\r
+  }\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+  Download an image from a TFTP server\r
+\r
+  @param[in]   DevicePath           Device path of the TFTP boot option\r
+  @param[in]   ControllerHandle     Handle of the network controller\r
+  @param[in]   RemainingDevicePath  Device path of the TFTP boot option but\r
+                                    the first node that identifies the network controller\r
+  @param[in]   Type                 Type to allocate memory pages\r
+  @param[out]  Image                Address of the bufer where the image is stored in\r
+                                    case of success\r
+  @param[out]  ImageSize            Size in number of bytes of the i;age in case of\r
+                                    success\r
+\r
+  @retval  EFI_SUCCESS   The image was returned.\r
+  @retval  !EFI_SUCCESS  Something went wrong.\r
+\r
+**/\r
 EFI_STATUS\r
 BdsTftpLoadImage (\r
   IN     EFI_DEVICE_PATH*       DevicePath,\r
-  IN     EFI_HANDLE             Handle,\r
+  IN     EFI_HANDLE             ControllerHandle,\r
   IN     EFI_DEVICE_PATH*       RemainingDevicePath,\r
   IN     EFI_ALLOCATE_TYPE      Type,\r
   IN OUT EFI_PHYSICAL_ADDRESS   *Image,\r
   OUT    UINTN                  *ImageSize\r
   )\r
 {\r
-  EFI_STATUS                  Status;\r
-  EFI_PXE_BASE_CODE_PROTOCOL  *Pxe;\r
-  UINT64                      TftpBufferSize;\r
-  UINT64                      TftpTransferSize;\r
-  EFI_IP_ADDRESS              ServerIp;\r
-  IPv4_DEVICE_PATH*           IPv4DevicePathNode;\r
-  FILEPATH_DEVICE_PATH*       FilePathDevicePath;\r
-  EFI_IP_ADDRESS              LocalIp;\r
-  CHAR8*                      AsciiPathName;\r
-  EFI_SIMPLE_NETWORK_PROTOCOL *Snp;\r
+  EFI_STATUS              Status;\r
+  EFI_HANDLE              Dhcp4ChildHandle;\r
+  EFI_DHCP4_PROTOCOL      *Dhcp4;\r
+  BOOLEAN                 Dhcp4ToStop;\r
+  EFI_HANDLE              Mtftp4ChildHandle;\r
+  EFI_MTFTP4_PROTOCOL     *Mtftp4;\r
+  EFI_DHCP4_CONFIG_DATA   Dhcp4CfgData;\r
+  EFI_DHCP4_MODE_DATA     Dhcp4Mode;\r
+  EFI_MTFTP4_CONFIG_DATA  Mtftp4CfgData;\r
+  IPv4_DEVICE_PATH        *IPv4DevicePathNode;\r
+  FILEPATH_DEVICE_PATH    *FilePathDevicePathNode;\r
+  CHAR8                   *AsciiFilePath;\r
+  EFI_MTFTP4_TOKEN        Mtftp4Token;\r
+  UINT64                  FileSize;\r
+  UINT64                  TftpBufferSize;\r
+  BDS_TFTP_CONTEXT        *TftpContext;\r
 \r
   ASSERT(IS_DEVICE_PATH_NODE (RemainingDevicePath, MESSAGING_DEVICE_PATH, MSG_IPv4_DP));\r
-\r
   IPv4DevicePathNode = (IPv4_DEVICE_PATH*)RemainingDevicePath;\r
-  FilePathDevicePath = (FILEPATH_DEVICE_PATH*)(IPv4DevicePathNode + 1);\r
 \r
-  Status = gBS->LocateProtocol (&gEfiPxeBaseCodeProtocolGuid, NULL, (VOID **)&Pxe);\r
-  if (EFI_ERROR (Status)) {\r
-    return Status;\r
+  Dhcp4ChildHandle  = NULL;\r
+  Dhcp4             = NULL;\r
+  Dhcp4ToStop       = FALSE;\r
+  Mtftp4ChildHandle = NULL;\r
+  Mtftp4            = NULL;\r
+  AsciiFilePath     = NULL;\r
+  TftpContext       = NULL;\r
+\r
+  if (!IPv4DevicePathNode->StaticIpAddress) {\r
+    //\r
+    // Using the DHCP4 Service Binding Protocol, create a child handle of the DHCP4 service and\r
+    // install the DHCP4 protocol on it. Then, open the DHCP protocol.\r
+    //\r
+    Status = NetLibCreateServiceChild (\r
+               ControllerHandle,\r
+               gImageHandle,\r
+               &gEfiDhcp4ServiceBindingProtocolGuid,\r
+               &Dhcp4ChildHandle\r
+               );\r
+    if (!EFI_ERROR (Status)) {\r
+      Status = gBS->OpenProtocol (\r
+                      Dhcp4ChildHandle,\r
+                      &gEfiDhcp4ProtocolGuid,\r
+                      (VOID **) &Dhcp4,\r
+                      gImageHandle,\r
+                      ControllerHandle,\r
+                      EFI_OPEN_PROTOCOL_BY_DRIVER\r
+                      );\r
+    }\r
+    if (EFI_ERROR (Status)) {\r
+      Print (L"Unable to open DHCP4 protocol\n");\r
+      goto Error;\r
+    }\r
   }\r
 \r
-  Status = Pxe->Start (Pxe, FALSE);\r
-  if (EFI_ERROR (Status) && (Status != EFI_ALREADY_STARTED)) {\r
-    return Status;\r
+  //\r
+  // Using the MTFTP4 Service Binding Protocol, create a child handle of the MTFTP4 service and\r
+  // install the MTFTP4 protocol on it. Then, open the MTFTP4 protocol.\r
+  //\r
+  Status = NetLibCreateServiceChild (\r
+             ControllerHandle,\r
+             gImageHandle,\r
+             &gEfiMtftp4ServiceBindingProtocolGuid,\r
+             &Mtftp4ChildHandle\r
+             );\r
+  if (!EFI_ERROR (Status)) {\r
+    Status = gBS->OpenProtocol (\r
+                    Mtftp4ChildHandle,\r
+                    &gEfiMtftp4ProtocolGuid,\r
+                    (VOID **) &Mtftp4,\r
+                    gImageHandle,\r
+                    ControllerHandle,\r
+                    EFI_OPEN_PROTOCOL_BY_DRIVER\r
+                    );\r
+  }\r
+  if (EFI_ERROR (Status)) {\r
+    Print (L"Unable to open MTFTP4 protocol\n");\r
+    goto Error;\r
   }\r
 \r
-  do {\r
-    if (!IPv4DevicePathNode->StaticIpAddress) {\r
-      Status = Pxe->Dhcp (Pxe, TRUE);\r
-    } else {\r
-      CopyMem (&LocalIp.v4, &IPv4DevicePathNode->LocalIpAddress, sizeof (EFI_IPv4_ADDRESS));\r
-      Status = Pxe->SetStationIp (Pxe, &LocalIp, NULL);\r
+  if (!IPv4DevicePathNode->StaticIpAddress) {\r
+    //\r
+    // Configure the DHCP4, all default settings. It is acceptable for the configuration to\r
+    // fail if the return code is equal to EFI_ACCESS_DENIED which means that the configuration\r
+    // has been done by another instance of the DHCP4 protocol or that the DHCP configuration\r
+    // process has been started but is not completed yet.\r
+    //\r
+    ZeroMem (&Dhcp4CfgData, sizeof (EFI_DHCP4_CONFIG_DATA));\r
+    Status = Dhcp4->Configure (Dhcp4, &Dhcp4CfgData);\r
+    if (EFI_ERROR (Status)) {\r
+      if (Status != EFI_ACCESS_DENIED) {\r
+        Print (L"Error while configuring the DHCP4 protocol\n");\r
+        goto Error;\r
+      }\r
     }\r
 \r
-    // If an IP Address has already been set and a different static IP address is requested then restart\r
-    // the Network service.\r
-    if (Status == EFI_ALREADY_STARTED) {\r
-      Status = gBS->LocateProtocol (&gEfiSimpleNetworkProtocolGuid, NULL, (VOID **)&Snp);\r
-      if (!EFI_ERROR (Status) && IPv4DevicePathNode->StaticIpAddress &&\r
-          (CompareMem (&Snp->Mode->CurrentAddress, &IPv4DevicePathNode->LocalIpAddress, sizeof(EFI_MAC_ADDRESS)) != 0))\r
-      {\r
-        Pxe->Stop (Pxe);\r
-        Status = Pxe->Start (Pxe, FALSE);\r
-        if (EFI_ERROR(Status)) {\r
-          break;\r
-        }\r
-        // After restarting the PXE protocol, we want to try again with our new IP Address\r
-        Status = EFI_ALREADY_STARTED;\r
+    //\r
+    // Start the DHCP configuration. This may have already been done thus do not leave in error\r
+    // if the return code is EFI_ALREADY_STARTED.\r
+    //\r
+    Status = Dhcp4->Start (Dhcp4, NULL);\r
+    if (EFI_ERROR (Status)) {\r
+      if (Status != EFI_ALREADY_STARTED) {\r
+        Print (L"DHCP configuration failed\n");\r
+        goto Error;\r
       }\r
+    } else {\r
+      Dhcp4ToStop = TRUE;\r
     }\r
-  } while (Status == EFI_ALREADY_STARTED);\r
 \r
-  if (EFI_ERROR(Status)) {\r
-    return Status;\r
+    Status = Dhcp4->GetModeData (Dhcp4, &Dhcp4Mode);\r
+    if (EFI_ERROR (Status)) {\r
+      goto Error;\r
+    }\r
+\r
+    if (Dhcp4Mode.State != Dhcp4Bound) {\r
+      Status = EFI_TIMEOUT;\r
+      Print (L"DHCP configuration failed\n");\r
+      goto Error;\r
+    }\r
   }\r
 \r
-  CopyMem (&ServerIp.v4, &IPv4DevicePathNode->RemoteIpAddress, sizeof (EFI_IPv4_ADDRESS));\r
+  //\r
+  // Configure the TFTP4 protocol\r
+  //\r
 \r
-  // Convert the Unicode PathName to Ascii\r
-  AsciiPathName = AllocatePool ((StrLen (FilePathDevicePath->PathName) + 1) * sizeof (CHAR8));\r
-  if (AsciiPathName == NULL) {\r
-    return EFI_OUT_OF_RESOURCES;\r
+  ZeroMem (&Mtftp4CfgData, sizeof (EFI_MTFTP4_CONFIG_DATA));\r
+  Mtftp4CfgData.UseDefaultSetting = FALSE;\r
+  Mtftp4CfgData.TimeoutValue      = 4;\r
+  Mtftp4CfgData.TryCount          = 6;\r
+\r
+  if (IPv4DevicePathNode->StaticIpAddress) {\r
+    CopyMem (&Mtftp4CfgData.StationIp , &IPv4DevicePathNode->LocalIpAddress, sizeof (EFI_IPv4_ADDRESS));\r
+    CopyMem (&Mtftp4CfgData.SubnetMask, &IPv4DevicePathNode->SubnetMask, sizeof (EFI_IPv4_ADDRESS));\r
+    CopyMem (&Mtftp4CfgData.GatewayIp , &IPv4DevicePathNode->GatewayIpAddress, sizeof (EFI_IPv4_ADDRESS));\r
+  } else {\r
+    CopyMem (&Mtftp4CfgData.StationIp , &Dhcp4Mode.ClientAddress, sizeof (EFI_IPv4_ADDRESS));\r
+    CopyMem (&Mtftp4CfgData.SubnetMask, &Dhcp4Mode.SubnetMask   , sizeof (EFI_IPv4_ADDRESS));\r
+    CopyMem (&Mtftp4CfgData.GatewayIp , &Dhcp4Mode.RouterAddress, sizeof (EFI_IPv4_ADDRESS));\r
   }\r
-  UnicodeStrToAsciiStr (FilePathDevicePath->PathName, AsciiPathName);\r
-\r
-  // Try to get the size (required the TFTP server to have "tsize" extension)\r
-  Status = Pxe->Mtftp (\r
-                  Pxe,\r
-                  EFI_PXE_BASE_CODE_TFTP_GET_FILE_SIZE,\r
-                  NULL,\r
-                  FALSE,\r
-                  &TftpBufferSize,\r
-                  NULL,\r
-                  &ServerIp,\r
-                  (UINT8*)AsciiPathName,\r
-                  NULL,\r
-                  FALSE\r
-                  );\r
-  // Pxe.Mtftp replies EFI_PROTOCOL_ERROR if tsize is not supported by the TFTP server\r
-  if (EFI_ERROR (Status) && (Status != EFI_PROTOCOL_ERROR)) {\r
-    if (Status == EFI_TFTP_ERROR) {\r
-      DEBUG((EFI_D_ERROR, "TFTP Error: Fail to get the size of the file\n"));\r
-    }\r
-    goto EXIT;\r
+\r
+  CopyMem (&Mtftp4CfgData.ServerIp  , &IPv4DevicePathNode->RemoteIpAddress, sizeof (EFI_IPv4_ADDRESS));\r
+\r
+  Status = Mtftp4->Configure (Mtftp4, &Mtftp4CfgData);\r
+  if (EFI_ERROR (Status)) {\r
+    Print (L"Error while configuring the MTFTP4 protocol\n");\r
+    goto Error;\r
   }\r
 \r
   //\r
-  // Two cases:\r
-  //   1) the file size is unknown (tsize extension not supported)\r
-  //   2) tsize returned the file size\r
+  // Convert the Unicode path of the file to Ascii\r
   //\r
-  if (Status == EFI_PROTOCOL_ERROR) {\r
-    for (TftpBufferSize = SIZE_8MB; TftpBufferSize <= FixedPcdGet32 (PcdMaxTftpFileSize); TftpBufferSize += SIZE_8MB) {\r
-      // Allocate a buffer to hold the whole file.\r
-      Status = gBS->AllocatePages (\r
-                      Type,\r
-                      EfiBootServicesCode,\r
-                      EFI_SIZE_TO_PAGES (TftpBufferSize),\r
-                      Image\r
-                      );\r
-      if (EFI_ERROR (Status)) {\r
-        DEBUG ((EFI_D_ERROR, "Failed to allocate space for image: %r\n", Status));\r
-        goto EXIT;\r
-      }\r
 \r
-      TftpTransferSize = TftpBufferSize;\r
-      Status = Pxe->Mtftp (\r
-                      Pxe,\r
-                      EFI_PXE_BASE_CODE_TFTP_READ_FILE,\r
-                      (VOID *)(UINTN)*Image,\r
-                      FALSE,\r
-                      &TftpTransferSize,\r
-                      NULL,\r
-                      &ServerIp,\r
-                      (UINT8*)AsciiPathName,\r
-                      NULL,\r
-                      FALSE\r
-                      );\r
-      if (EFI_ERROR (Status)) {\r
-        gBS->FreePages (*Image, EFI_SIZE_TO_PAGES (TftpBufferSize));\r
-      } else {\r
-        *ImageSize = (UINTN)TftpBufferSize;\r
-        break;\r
-      }\r
-    }\r
+  FilePathDevicePathNode = (FILEPATH_DEVICE_PATH*)(IPv4DevicePathNode + 1);\r
+  AsciiFilePath = AllocatePool ((StrLen (FilePathDevicePathNode->PathName) + 1) * sizeof (CHAR8));\r
+  if (AsciiFilePath == NULL) {\r
+    Status = EFI_OUT_OF_RESOURCES;\r
+    goto Error;\r
+  }\r
+  UnicodeStrToAsciiStr (FilePathDevicePathNode->PathName, AsciiFilePath);\r
+\r
+  //\r
+  // Try to get the size of the file in bytes from the server. If it fails,\r
+  // start with a 8MB buffer to download the file.\r
+  //\r
+  FileSize = 0;\r
+  if (Mtftp4GetFileSize (Mtftp4, AsciiFilePath, &FileSize) == EFI_SUCCESS) {\r
+    TftpBufferSize = FileSize;\r
   } else {\r
+    TftpBufferSize = SIZE_8MB;\r
+  }\r
+\r
+  TftpContext = AllocatePool (sizeof (BDS_TFTP_CONTEXT));\r
+  if (TftpContext == NULL) {\r
+    Status = EFI_OUT_OF_RESOURCES;\r
+    goto Error;\r
+  }\r
+  TftpContext->FileSize = FileSize;\r
+\r
+  for (; TftpBufferSize <= FixedPcdGet32 (PcdMaxTftpFileSize);\r
+         TftpBufferSize = (TftpBufferSize + SIZE_8MB) & (~(SIZE_8MB-1))) {\r
+    //\r
     // Allocate a buffer to hold the whole file.\r
+    //\r
     Status = gBS->AllocatePages (\r
                     Type,\r
                     EfiBootServicesCode,\r
@@ -900,31 +1189,85 @@ BdsTftpLoadImage (
                     Image\r
                     );\r
     if (EFI_ERROR (Status)) {\r
-      DEBUG ((EFI_D_ERROR, "Failed to allocate space for kernel image: %r\n", Status));\r
-      goto EXIT;\r
+      Print (L"Failed to allocate space for image\n");\r
+      goto Error;\r
     }\r
 \r
-    Status = Pxe->Mtftp (\r
-                    Pxe,\r
-                    EFI_PXE_BASE_CODE_TFTP_READ_FILE,\r
-                    (VOID *)(UINTN)*Image,\r
-                    FALSE,\r
-                    &TftpBufferSize,\r
-                    NULL,\r
-                    &ServerIp,\r
-                    (UINT8*)AsciiPathName,\r
-                    NULL,\r
-                    FALSE\r
-                    );\r
+    TftpContext->DownloadedNbOfBytes   = 0;\r
+    TftpContext->LastReportedNbOfBytes = 0;\r
+\r
+    ZeroMem (&Mtftp4Token, sizeof (EFI_MTFTP4_TOKEN));\r
+    Mtftp4Token.Filename    = (UINT8*)AsciiFilePath;\r
+    Mtftp4Token.BufferSize  = TftpBufferSize;\r
+    Mtftp4Token.Buffer      = (VOID *)(UINTN)*Image;\r
+    Mtftp4Token.CheckPacket = Mtftp4CheckPacket;\r
+    Mtftp4Token.Context     = (VOID*)TftpContext;\r
+\r
+    Print (L"Downloading the file <%s> from the TFTP server\n", FilePathDevicePathNode->PathName);\r
+    Status = Mtftp4->ReadFile (Mtftp4, &Mtftp4Token);\r
+    Print (L"\n");\r
     if (EFI_ERROR (Status)) {\r
-      gBS->FreePages (*Image, EFI_SIZE_TO_PAGES (TftpBufferSize));\r
-    } else {\r
-      *ImageSize = (UINTN)TftpBufferSize;\r
+      if (Status == EFI_BUFFER_TOO_SMALL) {\r
+        Print (L"Downloading failed, file larger than expected.\n");\r
+        gBS->FreePages (*Image, EFI_SIZE_TO_PAGES (TftpBufferSize));\r
+        continue;\r
+      } else {\r
+        goto Error;\r
+      }\r
     }\r
+\r
+    *ImageSize = Mtftp4Token.BufferSize;\r
+    break;\r
+  }\r
+\r
+Error:\r
+  if (Dhcp4ChildHandle != NULL) {\r
+    if (Dhcp4 != NULL) {\r
+      if (Dhcp4ToStop) {\r
+        Dhcp4->Stop (Dhcp4);\r
+      }\r
+      gBS->CloseProtocol (\r
+             Dhcp4ChildHandle,\r
+             &gEfiDhcp4ProtocolGuid,\r
+             gImageHandle,\r
+             ControllerHandle\r
+            );\r
+    }\r
+    NetLibDestroyServiceChild (\r
+      ControllerHandle,\r
+      gImageHandle,\r
+      &gEfiDhcp4ServiceBindingProtocolGuid,\r
+      Dhcp4ChildHandle\r
+      );\r
+  }\r
+\r
+  if (Mtftp4ChildHandle != NULL) {\r
+    if (Mtftp4 != NULL) {\r
+      if (AsciiFilePath != NULL) {\r
+        FreePool (AsciiFilePath);\r
+      }\r
+      if (TftpContext != NULL) {\r
+        FreePool (TftpContext);\r
+      }\r
+      gBS->CloseProtocol (\r
+             Mtftp4ChildHandle,\r
+             &gEfiMtftp4ProtocolGuid,\r
+             gImageHandle,\r
+             ControllerHandle\r
+            );\r
+    }\r
+    NetLibDestroyServiceChild (\r
+      ControllerHandle,\r
+      gImageHandle,\r
+      &gEfiMtftp4ServiceBindingProtocolGuid,\r
+      Mtftp4ChildHandle\r
+      );\r
+  }\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    Print (L"Failed to download the file - Error=%r\n", Status);\r
   }\r
 \r
-EXIT:\r
-  FreePool (AsciiPathName);\r
   return Status;\r
 }\r
 \r
index 9b5d3a4ff69597a5a6f9de8fb1e4e8a02e38d304..602fd8dcf34c4ee5de1c2b4eff7aa1bd126aa10a 100644 (file)
@@ -71,6 +71,11 @@ typedef struct _BDS_SYSTEM_MEMORY_RESOURCE {
   UINT64                      ResourceLength;\r
 } BDS_SYSTEM_MEMORY_RESOURCE;\r
 \r
+typedef struct {\r
+  UINT64  FileSize;\r
+  UINT64  DownloadedNbOfBytes;\r
+  UINT64  LastReportedNbOfBytes;\r
+} BDS_TFTP_CONTEXT;\r
 \r
 // BdsHelper.c\r
 EFI_STATUS\r
index 02409e1392fb62019a353fb485a970782b101127..6d6a2dfa8ab5e8e8b2c41a684ff0ba1d0b0b8047 100644 (file)
@@ -37,6 +37,7 @@
 \r
 [Packages]\r
   MdePkg/MdePkg.dec\r
+  MdeModulePkg/MdeModulePkg.dec\r
   EmbeddedPkg/EmbeddedPkg.dec\r
   ArmPkg/ArmPkg.dec\r
   ArmPlatformPkg/ArmPlatformPkg.dec\r
@@ -53,6 +54,7 @@
   SerialPortLib\r
   FdtLib\r
   TimerLib\r
+  NetLib\r
 \r
 [LibraryClasses.AARCH64]\r
   ArmGicLib\r
   gEfiUsbIoProtocolGuid\r
   gEfiLoadedImageProtocolGuid\r
   gEfiSimpleNetworkProtocolGuid\r
+  gEfiDhcp4ServiceBindingProtocolGuid\r
+  gEfiDhcp4ProtocolGuid\r
+  gEfiMtftp4ServiceBindingProtocolGuid\r
+  gEfiMtftp4ProtocolGuid\r
 \r
 [FeaturePcd]\r
   gArmTokenSpaceGuid.PcdArmLinuxSpinTable\r
index 5a2f86bf8c916ab4dcbaf218ae6b327c479899c7..c3de53c2bdbce12239a9637ec5ce70b75c9c5736 100644 (file)
@@ -63,6 +63,8 @@
   gEfiDevicePathToTextProtocolGuid\r
   gEfiFirmwareVolumeBlockProtocolGuid\r
   gEfiFirmwareVolumeBlock2ProtocolGuid\r
+  gEfiDhcp4ServiceBindingProtocolGuid\r
+  gEfiMtftp4ServiceBindingProtocolGuid\r
 \r
 [Pcd]\r
   gArmPlatformTokenSpaceGuid.PcdFirmwareVendor\r
index ee4281855e79a9208ae60d574c921917ab8bfbfa..974f220553a3f746934efb25de48f89b447b9703 100644 (file)
@@ -22,6 +22,8 @@
 #include <Protocol/PxeBaseCode.h>\r
 #include <Protocol/SimpleFileSystem.h>\r
 #include <Protocol/SimpleNetwork.h>\r
+#include <Protocol/Dhcp4.h>\r
+#include <Protocol/Mtftp4.h>\r
 \r
 #include <Guid/FileSystemInfo.h>\r
 \r
@@ -866,49 +868,96 @@ BdsLoadOptionPxeIsSupported (
   }\r
 }\r
 \r
+/**\r
+  Add to the list of boot devices the devices allowing a TFTP boot\r
+\r
+  @param[in]   BdsLoadOptionList  List of devices to boot from\r
+\r
+  @retval  EFI_SUCCESS            Update completed\r
+  @retval  EFI_OUT_OF_RESOURCES   Fail to perform the update due to lack of resource\r
+**/\r
 EFI_STATUS\r
 BdsLoadOptionTftpList (\r
   IN OUT LIST_ENTRY* BdsLoadOptionList\r
   )\r
 {\r
-  EFI_STATUS                        Status;\r
-  UINTN                             HandleCount;\r
-  EFI_HANDLE                        *HandleBuffer;\r
-  UINTN                             Index;\r
-  BDS_SUPPORTED_DEVICE              *SupportedDevice;\r
-  EFI_DEVICE_PATH_PROTOCOL*         DevicePathProtocol;\r
-  EFI_SIMPLE_NETWORK_PROTOCOL*      SimpleNet;\r
-  CHAR16                            DeviceDescription[BOOT_DEVICE_DESCRIPTION_MAX];\r
-  EFI_MAC_ADDRESS                   *Mac;\r
+  EFI_STATUS                   Status;\r
+  UINTN                        HandleCount;\r
+  EFI_HANDLE                   *HandleBuffer;\r
+  EFI_HANDLE                   Handle;\r
+  UINTN                        Index;\r
+  EFI_DEVICE_PATH_PROTOCOL     *DevicePathProtocol;\r
+  VOID                         *Interface;\r
+  EFI_SIMPLE_NETWORK_PROTOCOL  *SimpleNetworkProtocol;\r
+  BDS_SUPPORTED_DEVICE         *SupportedDevice;\r
+  EFI_MAC_ADDRESS              *Mac;\r
 \r
-  // List all the PXE Protocols\r
-  Status = gBS->LocateHandleBuffer (ByProtocol, &gEfiPxeBaseCodeProtocolGuid, NULL, &HandleCount, &HandleBuffer);\r
+  //\r
+  // List all the handles on which the Simple Network Protocol is installed.\r
+  //\r
+  Status = gBS->LocateHandleBuffer (\r
+                  ByProtocol,\r
+                  &gEfiSimpleNetworkProtocolGuid,\r
+                  NULL,\r
+                  &HandleCount,\r
+                  &HandleBuffer\r
+                  );\r
   if (EFI_ERROR (Status)) {\r
     return Status;\r
   }\r
 \r
   for (Index = 0; Index < HandleCount; Index++) {\r
-    // We only select the handle WITH a Device Path AND the PXE Protocol AND the TFTP Protocol (the TFTP protocol is required to start PXE)\r
-    Status = gBS->HandleProtocol (HandleBuffer[Index], &gEfiDevicePathProtocolGuid, (VOID **)&DevicePathProtocol);\r
-    if (!EFI_ERROR(Status)) {\r
-      // Allocate BDS Supported Device structure\r
-      SupportedDevice = (BDS_SUPPORTED_DEVICE*)AllocatePool(sizeof(BDS_SUPPORTED_DEVICE));\r
+    Handle = HandleBuffer[Index];\r
+    //\r
+    // We select the handles that support :\r
+    // . the Device Path Protocol\r
+    // . the MTFTP4 Protocol\r
+    //\r
+    Status = gBS->HandleProtocol (\r
+                    Handle,\r
+                    &gEfiDevicePathProtocolGuid,\r
+                    (VOID **)&DevicePathProtocol\r
+                    );\r
+    if (EFI_ERROR (Status)) {\r
+      continue;\r
+    }\r
 \r
-      Status = gBS->LocateProtocol (&gEfiSimpleNetworkProtocolGuid, NULL, (VOID **)&SimpleNet);\r
-      if (!EFI_ERROR(Status)) {\r
-        Mac = &SimpleNet->Mode->CurrentAddress;\r
-        UnicodeSPrint (DeviceDescription,BOOT_DEVICE_DESCRIPTION_MAX,L"MAC Address: %02x:%02x:%02x:%02x:%02x:%02x", Mac->Addr[0],  Mac->Addr[1],  Mac->Addr[2],  Mac->Addr[3],  Mac->Addr[4],  Mac->Addr[5]);\r
-      } else {\r
-        Status = GenerateDeviceDescriptionName (HandleBuffer[Index], DeviceDescription);\r
-        ASSERT_EFI_ERROR (Status);\r
-      }\r
-      UnicodeSPrint (SupportedDevice->Description,BOOT_DEVICE_DESCRIPTION_MAX,L"TFTP on %s",DeviceDescription);\r
+    Status = gBS->HandleProtocol (\r
+                    Handle,\r
+                    &gEfiMtftp4ServiceBindingProtocolGuid,\r
+                    &Interface\r
+                    );\r
+    if (EFI_ERROR (Status)) {\r
+      continue;\r
+    }\r
 \r
-      SupportedDevice->DevicePathProtocol = DevicePathProtocol;\r
-      SupportedDevice->Support = &BdsLoadOptionSupportList[BDS_DEVICE_TFTP];\r
+    Status = gBS->HandleProtocol (\r
+                    Handle,\r
+                    &gEfiSimpleNetworkProtocolGuid,\r
+                    (VOID **)&SimpleNetworkProtocol\r
+                    );\r
+    if (EFI_ERROR (Status)) {\r
+      continue;\r
+    }\r
 \r
-      InsertTailList (BdsLoadOptionList,&SupportedDevice->Link);\r
+    // Allocate BDS Supported Device structure\r
+    SupportedDevice = (BDS_SUPPORTED_DEVICE*)AllocatePool (sizeof (BDS_SUPPORTED_DEVICE));\r
+    if (SupportedDevice == NULL) {\r
+      continue;\r
     }\r
+\r
+    Mac = &SimpleNetworkProtocol->Mode->CurrentAddress;\r
+    UnicodeSPrint (\r
+      SupportedDevice->Description,\r
+      BOOT_DEVICE_DESCRIPTION_MAX,\r
+      L"TFTP on MAC Address: %02x:%02x:%02x:%02x:%02x:%02x",\r
+      Mac->Addr[0],  Mac->Addr[1],  Mac->Addr[2],  Mac->Addr[3],  Mac->Addr[4],  Mac->Addr[5]\r
+      );\r
+\r
+    SupportedDevice->DevicePathProtocol = DevicePathProtocol;\r
+    SupportedDevice->Support = &BdsLoadOptionSupportList[BDS_DEVICE_TFTP];\r
+\r
+    InsertTailList (BdsLoadOptionList, &SupportedDevice->Link);\r
   }\r
 \r
   return EFI_SUCCESS;\r
@@ -920,38 +969,50 @@ BdsLoadOptionTftpCreateDevicePath (
   OUT EFI_DEVICE_PATH_PROTOCOL  **DevicePathNodes\r
   )\r
 {\r
-  EFI_STATUS    Status;\r
-  BOOLEAN       IsDHCP;\r
-  EFI_IP_ADDRESS  LocalIp;\r
-  EFI_IP_ADDRESS  RemoteIp;\r
-  IPv4_DEVICE_PATH*   IPv4DevicePathNode;\r
-  FILEPATH_DEVICE_PATH* FilePathDevicePath;\r
-  CHAR16      BootFilePath[BOOT_DEVICE_FILEPATH_MAX];\r
-  UINTN       BootFilePathSize;\r
+  EFI_STATUS            Status;\r
+  BOOLEAN               IsDHCP;\r
+  EFI_IP_ADDRESS        LocalIp;\r
+  EFI_IP_ADDRESS        SubnetMask;\r
+  EFI_IP_ADDRESS        GatewayIp;\r
+  EFI_IP_ADDRESS        RemoteIp;\r
+  IPv4_DEVICE_PATH      *IPv4DevicePathNode;\r
+  FILEPATH_DEVICE_PATH  *FilePathDevicePath;\r
+  CHAR16                BootFilePath[BOOT_DEVICE_FILEPATH_MAX];\r
+  UINTN                 BootFilePathSize;\r
 \r
-  Print(L"Get the IP address from DHCP: ");\r
+  Print (L"Get the IP address from DHCP: ");\r
   Status = GetHIInputBoolean (&IsDHCP);\r
-  if (EFI_ERROR(Status)) {\r
+  if (EFI_ERROR (Status)) {\r
     return EFI_ABORTED;\r
   }\r
 \r
   if (!IsDHCP) {\r
-    Print(L"Get the static IP address: ");\r
+    Print (L"Local static IP address: ");\r
     Status = GetHIInputIP (&LocalIp);\r
-    if (EFI_ERROR(Status)) {\r
+    if (EFI_ERROR (Status)) {\r
+      return EFI_ABORTED;\r
+    }\r
+    Print (L"Get the network mask: ");\r
+    Status = GetHIInputIP (&SubnetMask);\r
+    if (EFI_ERROR (Status)) {\r
+      return EFI_ABORTED;\r
+    }\r
+    Print (L"Get the gateway IP address: ");\r
+    Status = GetHIInputIP (&GatewayIp);\r
+    if (EFI_ERROR (Status)) {\r
       return EFI_ABORTED;\r
     }\r
   }\r
 \r
-  Print(L"Get the TFTP server IP address: ");\r
+  Print (L"Get the TFTP server IP address: ");\r
   Status = GetHIInputIP (&RemoteIp);\r
-  if (EFI_ERROR(Status)) {\r
+  if (EFI_ERROR (Status)) {\r
     return EFI_ABORTED;\r
   }\r
 \r
-  Print(L"File path of the %s : ", FileName);\r
+  Print (L"File path of the %s : ", FileName);\r
   Status = GetHIInputStr (BootFilePath, BOOT_DEVICE_FILEPATH_MAX);\r
-  if (EFI_ERROR(Status)) {\r
+  if (EFI_ERROR (Status)) {\r
     return EFI_ABORTED;\r
   }\r
 \r
@@ -967,7 +1028,13 @@ BdsLoadOptionTftpCreateDevicePath (
   IPv4DevicePathNode->Header.Type    = MESSAGING_DEVICE_PATH;\r
   IPv4DevicePathNode->Header.SubType = MSG_IPv4_DP;\r
   SetDevicePathNodeLength (&IPv4DevicePathNode->Header, sizeof(IPv4_DEVICE_PATH));\r
-  CopyMem (&IPv4DevicePathNode->LocalIpAddress, &LocalIp.v4, sizeof (EFI_IPv4_ADDRESS));\r
+\r
+  if (!IsDHCP) {\r
+    CopyMem (&IPv4DevicePathNode->LocalIpAddress, &LocalIp.v4, sizeof (EFI_IPv4_ADDRESS));\r
+    CopyMem (&IPv4DevicePathNode->SubnetMask, &SubnetMask.v4, sizeof (EFI_IPv4_ADDRESS));\r
+    CopyMem (&IPv4DevicePathNode->GatewayIpAddress, &GatewayIp.v4, sizeof (EFI_IPv4_ADDRESS));\r
+  }\r
+\r
   CopyMem (&IPv4DevicePathNode->RemoteIpAddress, &RemoteIp.v4, sizeof (EFI_IPv4_ADDRESS));\r
   IPv4DevicePathNode->LocalPort  = 0;\r
   IPv4DevicePathNode->RemotePort = 0;\r
@@ -1021,7 +1088,11 @@ BdsLoadOptionTftpUpdateDevicePath (
   IPv4_DEVICE_PATH       Ipv4Node;\r
   BOOLEAN                IsDHCP;\r
   EFI_IP_ADDRESS         OldIp;\r
+  EFI_IP_ADDRESS         OldSubnetMask;\r
+  EFI_IP_ADDRESS         OldGatewayIp;\r
   EFI_IP_ADDRESS         LocalIp;\r
+  EFI_IP_ADDRESS         SubnetMask;\r
+  EFI_IP_ADDRESS         GatewayIp;\r
   EFI_IP_ADDRESS         RemoteIp;\r
   UINT8                 *FileNodePtr;\r
   CHAR16                 BootFilePath[BOOT_DEVICE_FILEPATH_MAX];\r
@@ -1074,9 +1145,7 @@ BdsLoadOptionTftpUpdateDevicePath (
   if (!IsDHCP) {\r
     Print (L"Local static IP address: ");\r
     if (Ipv4Node.StaticIpAddress) {\r
-      // Copy local IPv4 address into IPv4 or IPv6 union\r
       CopyMem (&OldIp.v4, &Ipv4Node.LocalIpAddress, sizeof (EFI_IPv4_ADDRESS));\r
-\r
       Status = EditHIInputIP (&OldIp, &LocalIp);\r
     } else {\r
       Status = GetHIInputIP (&LocalIp);\r
@@ -1084,6 +1153,28 @@ BdsLoadOptionTftpUpdateDevicePath (
     if (EFI_ERROR (Status)) {\r
       goto ErrorExit;\r
     }\r
+\r
+    Print (L"Get the network mask: ");\r
+    if (Ipv4Node.StaticIpAddress) {\r
+      CopyMem (&OldSubnetMask.v4, &Ipv4Node.SubnetMask, sizeof (EFI_IPv4_ADDRESS));\r
+      Status = EditHIInputIP (&OldSubnetMask, &SubnetMask);\r
+    } else {\r
+      Status = GetHIInputIP (&SubnetMask);\r
+    }\r
+    if (EFI_ERROR (Status)) {\r
+      goto ErrorExit;\r
+    }\r
+\r
+    Print (L"Get the gateway IP address: ");\r
+    if (Ipv4Node.StaticIpAddress) {\r
+      CopyMem (&OldGatewayIp.v4, &Ipv4Node.GatewayIpAddress, sizeof (EFI_IPv4_ADDRESS));\r
+      Status = EditHIInputIP (&OldGatewayIp, &GatewayIp);\r
+    } else {\r
+      Status = GetHIInputIP (&GatewayIp);\r
+    }\r
+    if (EFI_ERROR (Status)) {\r
+      goto ErrorExit;\r
+    }\r
   }\r
 \r
   Print (L"TFTP server IP address: ");\r
@@ -1126,12 +1217,18 @@ BdsLoadOptionTftpUpdateDevicePath (
   //\r
   // Update the IPv4 node. IPv6 case not handled yet.\r
   //\r
-  if (IsDHCP == TRUE) {\r
+  if (IsDHCP) {\r
     Ipv4Node.StaticIpAddress = FALSE;\r
+    ZeroMem (&Ipv4Node.LocalIpAddress, sizeof (EFI_IPv4_ADDRESS));\r
+    ZeroMem (&Ipv4Node.SubnetMask, sizeof (EFI_IPv4_ADDRESS));\r
+    ZeroMem (&Ipv4Node.GatewayIpAddress, sizeof (EFI_IPv4_ADDRESS));\r
   } else {\r
     Ipv4Node.StaticIpAddress = TRUE;\r
+    CopyMem (&Ipv4Node.LocalIpAddress, &LocalIp.v4, sizeof (EFI_IPv4_ADDRESS));\r
+    CopyMem (&Ipv4Node.SubnetMask, &SubnetMask.v4, sizeof (EFI_IPv4_ADDRESS));\r
+    CopyMem (&Ipv4Node.GatewayIpAddress, &GatewayIp.v4, sizeof (EFI_IPv4_ADDRESS));\r
   }\r
-  CopyMem (&Ipv4Node.LocalIpAddress, &LocalIp.v4, sizeof (EFI_IPv4_ADDRESS));\r
+\r
   CopyMem (&Ipv4Node.RemoteIpAddress, &RemoteIp.v4, sizeof (EFI_IPv4_ADDRESS));\r
   CopyMem (Ipv4NodePtr, &Ipv4Node, sizeof (IPv4_DEVICE_PATH));\r
 \r