/** @file\r
*\r
-* Copyright (c) 2011-2013, ARM Limited. All rights reserved.\r
+* Copyright (c) 2011-2014, ARM Limited. All rights reserved.\r
* \r
* This program and the accompanying materials \r
* are licensed and made available under the terms and conditions of the BSD License \r
#include <Protocol/UsbIo.h>\r
#include <Protocol/DiskIo.h>\r
#include <Protocol/LoadedImage.h>\r
+#include <Protocol/SimpleNetwork.h>\r
\r
#define IS_DEVICE_PATH_NODE(node,type,subtype) (((node)->Type == (type)) && ((node)->SubType == (subtype)))\r
\r
return Status;\r
}\r
\r
-/**\r
- Connect a Device Path and return the handle of the driver that support this DevicePath\r
-\r
- @param DevicePath Device Path of the File to connect\r
- @param Handle Handle of the driver that support this DevicePath\r
- @param RemainingDevicePath Remaining DevicePath nodes that do not match the driver DevicePath\r
-\r
- @retval EFI_SUCCESS A driver that matches the Device Path has been found\r
- @retval EFI_NOT_FOUND No handles match the search.\r
- @retval EFI_INVALID_PARAMETER DevicePath or Handle is NULL\r
-\r
-**/\r
+STATIC\r
EFI_STATUS\r
-BdsConnectDevicePath (\r
- IN EFI_DEVICE_PATH_PROTOCOL* DevicePath,\r
- OUT EFI_HANDLE *Handle,\r
- OUT EFI_DEVICE_PATH_PROTOCOL **RemainingDevicePath\r
+BdsConnectAndUpdateDevicePath (\r
+ IN OUT EFI_DEVICE_PATH_PROTOCOL **DevicePath,\r
+ OUT EFI_HANDLE *Handle,\r
+ OUT EFI_DEVICE_PATH_PROTOCOL **RemainingDevicePath\r
)\r
{\r
EFI_DEVICE_PATH* Remaining;\r
EFI_DEVICE_PATH* NewDevicePath;\r
EFI_STATUS Status;\r
\r
- if ((DevicePath == NULL) || (Handle == NULL)) {\r
+ if ((DevicePath == NULL) || (*DevicePath == NULL) || (Handle == NULL)) {\r
return EFI_INVALID_PARAMETER;\r
}\r
\r
do {\r
- Remaining = DevicePath;\r
+ Remaining = *DevicePath;\r
// The LocateDevicePath() function locates all devices on DevicePath that support Protocol and returns\r
// the handle to the device that is closest to DevicePath. On output, the device path pointer is modified\r
// to point to the remaining part of the device path\r
if (!EFI_ERROR (Status)) {\r
// Now, we have got the whole Device Path connected, call again ConnectController to ensure all the supported Driver\r
// Binding Protocol are connected (such as DiskIo and SimpleFileSystem)\r
- Remaining = DevicePath;\r
+ Remaining = *DevicePath;\r
Status = gBS->LocateDevicePath (&gEfiDevicePathProtocolGuid, &Remaining, Handle);\r
if (!EFI_ERROR (Status)) {\r
Status = gBS->ConnectController (*Handle, NULL, Remaining, FALSE);\r
//TODO: Should we just return success and leave the caller decide if it is the expected RemainingPath\r
Status = EFI_SUCCESS;\r
} else {\r
- Status = TryRemovableDevice (DevicePath, Handle, &NewDevicePath);\r
+ Status = TryRemovableDevice (*DevicePath, Handle, &NewDevicePath);\r
if (!EFI_ERROR (Status)) {\r
- return BdsConnectDevicePath (NewDevicePath, Handle, RemainingDevicePath);\r
+ Status = BdsConnectAndUpdateDevicePath (&NewDevicePath, Handle, RemainingDevicePath);\r
+ *DevicePath = NewDevicePath;\r
+ return Status;\r
}\r
}\r
\r
return Status;\r
}\r
\r
+/**\r
+ Connect a Device Path and return the handle of the driver that support this DevicePath\r
+\r
+ @param DevicePath Device Path of the File to connect\r
+ @param Handle Handle of the driver that support this DevicePath\r
+ @param RemainingDevicePath Remaining DevicePath nodes that do not match the driver DevicePath\r
+\r
+ @retval EFI_SUCCESS A driver that matches the Device Path has been found\r
+ @retval EFI_NOT_FOUND No handles match the search.\r
+ @retval EFI_INVALID_PARAMETER DevicePath or Handle is NULL\r
+\r
+**/\r
+EFI_STATUS\r
+BdsConnectDevicePath (\r
+ IN EFI_DEVICE_PATH_PROTOCOL* DevicePath,\r
+ OUT EFI_HANDLE *Handle,\r
+ OUT EFI_DEVICE_PATH_PROTOCOL **RemainingDevicePath\r
+ )\r
+{\r
+ return BdsConnectAndUpdateDevicePath (&DevicePath, Handle, RemainingDevicePath);\r
+}\r
+\r
BOOLEAN\r
BdsFileSystemSupport (\r
IN EFI_DEVICE_PATH *DevicePath,\r
EFI_STATUS Status;\r
EFI_LOAD_FILE_PROTOCOL *LoadFileProtocol;\r
UINTN BufferSize;\r
+ EFI_PXE_BASE_CODE_PROTOCOL *Pxe;\r
\r
// Get Load File Protocol attached to the PXE protocol\r
Status = gBS->HandleProtocol (Handle, &gEfiLoadFileProtocolGuid, (VOID **)&LoadFileProtocol);\r
}\r
}\r
\r
+ if (Status == EFI_ALREADY_STARTED) {\r
+ Status = gBS->LocateProtocol (&gEfiPxeBaseCodeProtocolGuid, NULL, (VOID **)&Pxe);\r
+ if (!EFI_ERROR(Status)) {\r
+ // If PXE is already started, we stop it\r
+ Pxe->Stop (Pxe);\r
+ // And we try again\r
+ return BdsPxeLoadImage (DevicePath, Handle, RemainingDevicePath, Type, Image, ImageSize);\r
+ }\r
+ }\r
return Status;\r
}\r
\r
EFI_STATUS Status;\r
EFI_PXE_BASE_CODE_PROTOCOL *Pxe;\r
UINT64 TftpBufferSize;\r
- VOID* TftpBuffer;\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
\r
ASSERT(IS_DEVICE_PATH_NODE (RemainingDevicePath, MESSAGING_DEVICE_PATH, MSG_IPv4_DP));\r
\r
return Status;\r
}\r
\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
- }\r
- if (EFI_ERROR (Status)) {\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
+ }\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
+ }\r
+ } while (Status == EFI_ALREADY_STARTED);\r
+\r
+ if (EFI_ERROR(Status)) {\r
return Status;\r
}\r
\r
CopyMem (&ServerIp.v4, &IPv4DevicePathNode->RemoteIpAddress, sizeof (EFI_IPv4_ADDRESS));\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
+ }\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
&TftpBufferSize,\r
NULL,\r
&ServerIp,\r
- (UINT8 *)FilePathDevicePath->PathName,\r
+ (UINT8*)AsciiPathName,\r
NULL,\r
- TRUE\r
+ FALSE\r
);\r
- if (EFI_ERROR (Status)) {\r
- return Status;\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
\r
- // Allocate a buffer to hold the whole file.\r
- TftpBuffer = AllocatePool (TftpBufferSize);\r
- if (TftpBuffer == NULL) {\r
- return EFI_OUT_OF_RESOURCES;\r
- }\r
+ //\r
+ // Two cases:\r
+ // 1) the file size is unknown (tsize extension not supported)\r
+ // 2) tsize returned the file size\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
- Status = Pxe->Mtftp (\r
- Pxe,\r
- EFI_PXE_BASE_CODE_TFTP_READ_FILE,\r
- TftpBuffer,\r
- FALSE,\r
- &TftpBufferSize,\r
- NULL,\r
- &ServerIp,\r
- (UINT8 *)FilePathDevicePath->PathName,\r
- NULL,\r
- FALSE\r
- );\r
- if (EFI_ERROR (Status)) {\r
- FreePool (TftpBuffer);\r
- } else if (ImageSize != NULL) {\r
- *ImageSize = (UINTN)TftpBufferSize;\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
+ } else {\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 kernel image: %r\n", Status));\r
+ goto EXIT;\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
+ if (EFI_ERROR (Status)) {\r
+ gBS->FreePages (*Image, EFI_SIZE_TO_PAGES (TftpBufferSize));\r
+ } else {\r
+ *ImageSize = (UINTN)TftpBufferSize;\r
+ }\r
}\r
\r
+EXIT:\r
+ FreePool (AsciiPathName);\r
return Status;\r
}\r
\r
};\r
\r
EFI_STATUS\r
-BdsLoadImage (\r
- IN EFI_DEVICE_PATH *DevicePath,\r
+BdsLoadImageAndUpdateDevicePath (\r
+ IN OUT EFI_DEVICE_PATH **DevicePath,\r
IN EFI_ALLOCATE_TYPE Type,\r
IN OUT EFI_PHYSICAL_ADDRESS* Image,\r
OUT UINTN *FileSize\r
EFI_DEVICE_PATH *RemainingDevicePath;\r
BDS_FILE_LOADER* FileLoader;\r
\r
- Status = BdsConnectDevicePath (DevicePath, &Handle, &RemainingDevicePath);\r
+ Status = BdsConnectAndUpdateDevicePath (DevicePath, &Handle, &RemainingDevicePath);\r
if (EFI_ERROR (Status)) {\r
return Status;\r
}\r
\r
FileLoader = FileLoaders;\r
while (FileLoader->Support != NULL) {\r
- if (FileLoader->Support (DevicePath, Handle, RemainingDevicePath)) {\r
- return FileLoader->LoadImage (DevicePath, Handle, RemainingDevicePath, Type, Image, FileSize);\r
+ if (FileLoader->Support (*DevicePath, Handle, RemainingDevicePath)) {\r
+ return FileLoader->LoadImage (*DevicePath, Handle, RemainingDevicePath, Type, Image, FileSize);\r
}\r
FileLoader++;\r
}\r
return EFI_UNSUPPORTED;\r
}\r
\r
+EFI_STATUS\r
+BdsLoadImage (\r
+ IN EFI_DEVICE_PATH *DevicePath,\r
+ IN EFI_ALLOCATE_TYPE Type,\r
+ IN OUT EFI_PHYSICAL_ADDRESS* Image,\r
+ OUT UINTN *FileSize\r
+ )\r
+{\r
+ return BdsLoadImageAndUpdateDevicePath (&DevicePath, Type, Image, FileSize);\r
+}\r
+\r
/**\r
Start an EFI Application from a Device Path\r
\r
EFI_LOADED_IMAGE_PROTOCOL* LoadedImage;\r
\r
// Find the nearest supported file loader\r
- Status = BdsLoadImage (DevicePath, AllocateAnyPages, &BinaryBuffer, &BinarySize);\r
+ Status = BdsLoadImageAndUpdateDevicePath (&DevicePath, AllocateAnyPages, &BinaryBuffer, &BinarySize);\r
if (EFI_ERROR (Status)) {\r
return Status;\r
}\r