summaryrefslogtreecommitdiffstats
path: root/src/VBox/Devices/EFI/Firmware/NetworkPkg/Mtftp4Dxe
diff options
context:
space:
mode:
Diffstat (limited to 'src/VBox/Devices/EFI/Firmware/NetworkPkg/Mtftp4Dxe')
-rw-r--r--src/VBox/Devices/EFI/Firmware/NetworkPkg/Mtftp4Dxe/ComponentName.c425
-rw-r--r--src/VBox/Devices/EFI/Firmware/NetworkPkg/Mtftp4Dxe/Mtftp4Driver.c739
-rw-r--r--src/VBox/Devices/EFI/Firmware/NetworkPkg/Mtftp4Dxe/Mtftp4Driver.h131
-rw-r--r--src/VBox/Devices/EFI/Firmware/NetworkPkg/Mtftp4Dxe/Mtftp4Dxe.inf69
-rw-r--r--src/VBox/Devices/EFI/Firmware/NetworkPkg/Mtftp4Dxe/Mtftp4Dxe.uni17
-rw-r--r--src/VBox/Devices/EFI/Firmware/NetworkPkg/Mtftp4Dxe/Mtftp4DxeExtra.uni14
-rw-r--r--src/VBox/Devices/EFI/Firmware/NetworkPkg/Mtftp4Dxe/Mtftp4Impl.c1113
-rw-r--r--src/VBox/Devices/EFI/Firmware/NetworkPkg/Mtftp4Dxe/Mtftp4Impl.h226
-rw-r--r--src/VBox/Devices/EFI/Firmware/NetworkPkg/Mtftp4Dxe/Mtftp4Option.c549
-rw-r--r--src/VBox/Devices/EFI/Firmware/NetworkPkg/Mtftp4Dxe/Mtftp4Option.h111
-rw-r--r--src/VBox/Devices/EFI/Firmware/NetworkPkg/Mtftp4Dxe/Mtftp4Rrq.c818
-rw-r--r--src/VBox/Devices/EFI/Firmware/NetworkPkg/Mtftp4Dxe/Mtftp4Support.c663
-rw-r--r--src/VBox/Devices/EFI/Firmware/NetworkPkg/Mtftp4Dxe/Mtftp4Support.h198
-rw-r--r--src/VBox/Devices/EFI/Firmware/NetworkPkg/Mtftp4Dxe/Mtftp4Wrq.c529
14 files changed, 5602 insertions, 0 deletions
diff --git a/src/VBox/Devices/EFI/Firmware/NetworkPkg/Mtftp4Dxe/ComponentName.c b/src/VBox/Devices/EFI/Firmware/NetworkPkg/Mtftp4Dxe/ComponentName.c
new file mode 100644
index 00000000..36416d3a
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/NetworkPkg/Mtftp4Dxe/ComponentName.c
@@ -0,0 +1,425 @@
+/** @file
+ UEFI Component Name(2) protocol implementation for Mtftp4Dxe driver.
+
+Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.<BR>
+SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include "Mtftp4Impl.h"
+
+//
+// EFI Component Name Functions
+//
+/**
+ Retrieves a Unicode string that is the user readable name of the driver.
+
+ This function retrieves the user readable name of a driver in the form of a
+ Unicode string. If the driver specified by This has a user readable name in
+ the language specified by Language, then a pointer to the driver name is
+ returned in DriverName, and EFI_SUCCESS is returned. If the driver specified
+ by This does not support the language specified by Language,
+ then EFI_UNSUPPORTED is returned.
+
+ @param[in] This A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or
+ EFI_COMPONENT_NAME_PROTOCOL instance.
+
+ @param[in] Language A pointer to a Null-terminated ASCII string
+ array indicating the language. This is the
+ language of the driver name that the caller is
+ requesting, and it must match one of the
+ languages specified in SupportedLanguages. The
+ number of languages supported by a driver is up
+ to the driver writer. Language is specified
+ in RFC 4646 or ISO 639-2 language code format.
+
+ @param[out] DriverName A pointer to the Unicode string to return.
+ This Unicode string is the name of the
+ driver specified by This in the language
+ specified by Language.
+
+ @retval EFI_SUCCESS The Unicode string for the Driver specified by
+ This and the language specified by Language was
+ returned in DriverName.
+
+ @retval EFI_INVALID_PARAMETER Language is NULL.
+
+ @retval EFI_INVALID_PARAMETER DriverName is NULL.
+
+ @retval EFI_UNSUPPORTED The driver specified by This does not support
+ the language specified by Language.
+
+**/
+EFI_STATUS
+EFIAPI
+Mtftp4ComponentNameGetDriverName (
+ IN EFI_COMPONENT_NAME_PROTOCOL *This,
+ IN CHAR8 *Language,
+ OUT CHAR16 **DriverName
+ );
+
+
+/**
+ Retrieves a Unicode string that is the user readable name of the controller
+ that is being managed by a driver.
+
+ This function retrieves the user readable name of the controller specified by
+ ControllerHandle and ChildHandle in the form of a Unicode string. If the
+ driver specified by This has a user readable name in the language specified by
+ Language, then a pointer to the controller name is returned in ControllerName,
+ and EFI_SUCCESS is returned. If the driver specified by This is not currently
+ managing the controller specified by ControllerHandle and ChildHandle,
+ then EFI_UNSUPPORTED is returned. If the driver specified by This does not
+ support the language specified by Language, then EFI_UNSUPPORTED is returned.
+
+ @param[in] This A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or
+ EFI_COMPONENT_NAME_PROTOCOL instance.
+
+ @param[in] ControllerHandle The handle of a controller that the driver
+ specified by This is managing. This handle
+ specifies the controller whose name is to be
+ returned.
+
+ @param[in] ChildHandle The handle of the child controller to retrieve
+ the name of. This is an optional parameter that
+ may be NULL. It will be NULL for device
+ drivers. It will also be NULL for a bus drivers
+ that wish to retrieve the name of the bus
+ controller. It will not be NULL for a bus
+ driver that wishes to retrieve the name of a
+ child controller.
+
+ @param[in] Language A pointer to a Null-terminated ASCII string
+ array indicating the language. This is the
+ language of the driver name that the caller is
+ requesting, and it must match one of the
+ languages specified in SupportedLanguages. The
+ number of languages supported by a driver is up
+ to the driver writer. Language is specified in
+ RFC 4646 or ISO 639-2 language code format.
+
+ @param[out] ControllerName A pointer to the Unicode string to return.
+ This Unicode string is the name of the
+ controller specified by ControllerHandle and
+ ChildHandle in the language specified by
+ Language from the point of view of the driver
+ specified by This.
+
+ @retval EFI_SUCCESS The Unicode string for the user readable name in
+ the language specified by Language for the
+ driver specified by This was returned in
+ DriverName.
+
+ @retval EFI_INVALID_PARAMETER ControllerHandle is NULL.
+
+ @retval EFI_INVALID_PARAMETER ChildHandle is not NULL and it is not a valid
+ EFI_HANDLE.
+
+ @retval EFI_INVALID_PARAMETER Language is NULL.
+
+ @retval EFI_INVALID_PARAMETER ControllerName is NULL.
+
+ @retval EFI_UNSUPPORTED The driver specified by This is not currently
+ managing the controller specified by
+ ControllerHandle and ChildHandle.
+
+ @retval EFI_UNSUPPORTED The driver specified by This does not support
+ the language specified by Language.
+
+**/
+EFI_STATUS
+EFIAPI
+Mtftp4ComponentNameGetControllerName (
+ IN EFI_COMPONENT_NAME_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN EFI_HANDLE ChildHandle OPTIONAL,
+ IN CHAR8 *Language,
+ OUT CHAR16 **ControllerName
+ );
+
+
+///
+/// EFI Component Name Protocol
+///
+GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME_PROTOCOL gMtftp4ComponentName = {
+ Mtftp4ComponentNameGetDriverName,
+ Mtftp4ComponentNameGetControllerName,
+ "eng"
+};
+
+///
+/// EFI Component Name 2 Protocol
+///
+GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME2_PROTOCOL gMtftp4ComponentName2 = {
+ (EFI_COMPONENT_NAME2_GET_DRIVER_NAME) Mtftp4ComponentNameGetDriverName,
+ (EFI_COMPONENT_NAME2_GET_CONTROLLER_NAME) Mtftp4ComponentNameGetControllerName,
+ "en"
+};
+
+
+GLOBAL_REMOVE_IF_UNREFERENCED EFI_UNICODE_STRING_TABLE mMtftp4DriverNameTable[] = {
+ {
+ "eng;en",
+ L"MTFTP4 Network Service"
+ },
+ {
+ NULL,
+ NULL
+ }
+};
+
+GLOBAL_REMOVE_IF_UNREFERENCED EFI_UNICODE_STRING_TABLE *gMtftp4ControllerNameTable = NULL;
+
+/**
+ Retrieves a Unicode string that is the user readable name of the driver.
+
+ This function retrieves the user readable name of a driver in the form of a
+ Unicode string. If the driver specified by This has a user readable name in
+ the language specified by Language, then a pointer to the driver name is
+ returned in DriverName, and EFI_SUCCESS is returned. If the driver specified
+ by This does not support the language specified by Language,
+ then EFI_UNSUPPORTED is returned.
+
+ @param[in] This A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or
+ EFI_COMPONENT_NAME_PROTOCOL instance.
+
+ @param[in] Language A pointer to a Null-terminated ASCII string
+ array indicating the language. This is the
+ language of the driver name that the caller is
+ requesting, and it must match one of the
+ languages specified in SupportedLanguages. The
+ number of languages supported by a driver is up
+ to the driver writer. Language is specified
+ in RFC 4646 or ISO 639-2 language code format.
+
+ @param[out] DriverName A pointer to the Unicode string to return.
+ This Unicode string is the name of the
+ driver specified by This in the language
+ specified by Language.
+
+ @retval EFI_SUCCESS The Unicode string for the Driver specified by
+ This and the language specified by Language was
+ returned in DriverName.
+
+ @retval EFI_INVALID_PARAMETER Language is NULL.
+
+ @retval EFI_INVALID_PARAMETER DriverName is NULL.
+
+ @retval EFI_UNSUPPORTED The driver specified by This does not support
+ the language specified by Language.
+
+**/
+EFI_STATUS
+EFIAPI
+Mtftp4ComponentNameGetDriverName (
+ IN EFI_COMPONENT_NAME_PROTOCOL *This,
+ IN CHAR8 *Language,
+ OUT CHAR16 **DriverName
+ )
+{
+ return LookupUnicodeString2 (
+ Language,
+ This->SupportedLanguages,
+ mMtftp4DriverNameTable,
+ DriverName,
+ (BOOLEAN)(This == &gMtftp4ComponentName)
+ );
+}
+
+/**
+ Update the component name for the Mtftp4 child handle.
+
+ @param Mtftp4[in] A pointer to the EFI_MTFTP4_PROTOCOL.
+
+
+ @retval EFI_SUCCESS Update the ControllerNameTable of this instance successfully.
+ @retval EFI_INVALID_PARAMETER The input parameter is invalid.
+
+**/
+EFI_STATUS
+UpdateName (
+ IN EFI_MTFTP4_PROTOCOL *Mtftp4
+ )
+{
+ EFI_STATUS Status;
+ CHAR16 HandleName[80];
+ EFI_MTFTP4_MODE_DATA ModeData;
+
+ if (Mtftp4 == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ //
+ // Format the child name into the string buffer as:
+ // MTFTPv4 (ServerIp=192.168.1.10, ServerPort=69)
+ //
+ Status = Mtftp4->GetModeData (Mtftp4, &ModeData);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ UnicodeSPrint (HandleName, sizeof (HandleName),
+ L"MTFTPv4 (ServerIp=%d.%d.%d.%d, ServerPort=%d)",
+ ModeData.ConfigData.ServerIp.Addr[0],
+ ModeData.ConfigData.ServerIp.Addr[1],
+ ModeData.ConfigData.ServerIp.Addr[2],
+ ModeData.ConfigData.ServerIp.Addr[3],
+ ModeData.ConfigData.InitialServerPort
+ );
+
+ if (gMtftp4ControllerNameTable != NULL) {
+ FreeUnicodeStringTable (gMtftp4ControllerNameTable);
+ gMtftp4ControllerNameTable = NULL;
+ }
+
+ Status = AddUnicodeString2 (
+ "eng",
+ gMtftp4ComponentName.SupportedLanguages,
+ &gMtftp4ControllerNameTable,
+ HandleName,
+ TRUE
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ return AddUnicodeString2 (
+ "en",
+ gMtftp4ComponentName2.SupportedLanguages,
+ &gMtftp4ControllerNameTable,
+ HandleName,
+ FALSE
+ );
+}
+
+/**
+ Retrieves a Unicode string that is the user readable name of the controller
+ that is being managed by a driver.
+
+ This function retrieves the user readable name of the controller specified by
+ ControllerHandle and ChildHandle in the form of a Unicode string. If the
+ driver specified by This has a user readable name in the language specified by
+ Language, then a pointer to the controller name is returned in ControllerName,
+ and EFI_SUCCESS is returned. If the driver specified by This is not currently
+ managing the controller specified by ControllerHandle and ChildHandle,
+ then EFI_UNSUPPORTED is returned. If the driver specified by This does not
+ support the language specified by Language, then EFI_UNSUPPORTED is returned.
+
+ @param[in] This A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or
+ EFI_COMPONENT_NAME_PROTOCOL instance.
+
+ @param[in] ControllerHandle The handle of a controller that the driver
+ specified by This is managing. This handle
+ specifies the controller whose name is to be
+ returned.
+
+ @param[in] ChildHandle The handle of the child controller to retrieve
+ the name of. This is an optional parameter that
+ may be NULL. It will be NULL for device
+ drivers. It will also be NULL for a bus drivers
+ that wish to retrieve the name of the bus
+ controller. It will not be NULL for a bus
+ driver that wishes to retrieve the name of a
+ child controller.
+
+ @param[in] Language A pointer to a Null-terminated ASCII string
+ array indicating the language. This is the
+ language of the driver name that the caller is
+ requesting, and it must match one of the
+ languages specified in SupportedLanguages. The
+ number of languages supported by a driver is up
+ to the driver writer. Language is specified in
+ RFC 4646 or ISO 639-2 language code format.
+
+ @param[out] ControllerName A pointer to the Unicode string to return.
+ This Unicode string is the name of the
+ controller specified by ControllerHandle and
+ ChildHandle in the language specified by
+ Language from the point of view of the driver
+ specified by This.
+
+ @retval EFI_SUCCESS The Unicode string for the user readable name in
+ the language specified by Language for the
+ driver specified by This was returned in
+ DriverName.
+
+ @retval EFI_INVALID_PARAMETER ControllerHandle is NULL.
+
+ @retval EFI_INVALID_PARAMETER ChildHandle is not NULL and it is not a valid
+ EFI_HANDLE.
+
+ @retval EFI_INVALID_PARAMETER Language is NULL.
+
+ @retval EFI_INVALID_PARAMETER ControllerName is NULL.
+
+ @retval EFI_UNSUPPORTED The driver specified by This is not currently
+ managing the controller specified by
+ ControllerHandle and ChildHandle.
+
+ @retval EFI_UNSUPPORTED The driver specified by This does not support
+ the language specified by Language.
+
+**/
+EFI_STATUS
+EFIAPI
+Mtftp4ComponentNameGetControllerName (
+ IN EFI_COMPONENT_NAME_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN EFI_HANDLE ChildHandle OPTIONAL,
+ IN CHAR8 *Language,
+ OUT CHAR16 **ControllerName
+ )
+{
+ EFI_STATUS Status;
+ EFI_MTFTP4_PROTOCOL *Mtftp4;
+
+ //
+ // Only provide names for child handles.
+ //
+ if (ChildHandle == NULL) {
+ return EFI_UNSUPPORTED;
+ }
+
+ //
+ // Make sure this driver produced ChildHandle
+ //
+ Status = EfiTestChildHandle (
+ ControllerHandle,
+ ChildHandle,
+ &gEfiUdp4ProtocolGuid
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ //
+ // Retrieve an instance of a produced protocol from ChildHandle
+ //
+ Status = gBS->OpenProtocol (
+ ChildHandle,
+ &gEfiMtftp4ProtocolGuid,
+ (VOID **)&Mtftp4,
+ NULL,
+ NULL,
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ //
+ // Update the component name for this child handle.
+ //
+ Status = UpdateName (Mtftp4);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ return LookupUnicodeString2 (
+ Language,
+ This->SupportedLanguages,
+ gMtftp4ControllerNameTable,
+ ControllerName,
+ (BOOLEAN)(This == &gMtftp4ComponentName)
+ );
+}
diff --git a/src/VBox/Devices/EFI/Firmware/NetworkPkg/Mtftp4Dxe/Mtftp4Driver.c b/src/VBox/Devices/EFI/Firmware/NetworkPkg/Mtftp4Dxe/Mtftp4Driver.c
new file mode 100644
index 00000000..ae1ef34b
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/NetworkPkg/Mtftp4Dxe/Mtftp4Driver.c
@@ -0,0 +1,739 @@
+/** @file
+ Implementation of Mtftp drivers.
+
+Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.<BR>
+SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include "Mtftp4Impl.h"
+
+EFI_DRIVER_BINDING_PROTOCOL gMtftp4DriverBinding = {
+ Mtftp4DriverBindingSupported,
+ Mtftp4DriverBindingStart,
+ Mtftp4DriverBindingStop,
+ 0xa,
+ NULL,
+ NULL
+};
+
+EFI_SERVICE_BINDING_PROTOCOL gMtftp4ServiceBindingTemplete = {
+ Mtftp4ServiceBindingCreateChild,
+ Mtftp4ServiceBindingDestroyChild
+};
+
+
+/**
+ The driver entry point which installs multiple protocols to the ImageHandle.
+
+ @param ImageHandle The MTFTP's image handle.
+ @param SystemTable The system table.
+
+ @retval EFI_SUCCESS The handles are successfully installed on the image.
+ @retval others some EFI_ERROR occurred.
+
+**/
+EFI_STATUS
+EFIAPI
+Mtftp4DriverEntryPoint (
+ IN EFI_HANDLE ImageHandle,
+ IN EFI_SYSTEM_TABLE *SystemTable
+ )
+{
+ return EfiLibInstallDriverBindingComponentName2 (
+ ImageHandle,
+ SystemTable,
+ &gMtftp4DriverBinding,
+ ImageHandle,
+ &gMtftp4ComponentName,
+ &gMtftp4ComponentName2
+ );
+}
+
+
+/**
+ Test whether MTFTP driver support this controller.
+
+ @param This The MTFTP driver binding instance
+ @param Controller The controller to test
+ @param RemainingDevicePath The remaining device path
+
+ @retval EFI_SUCCESS The controller has UDP service binding protocol
+ installed, MTFTP can support it.
+ @retval EFI_ALREADY_STARTED The device specified by ControllerHandle and
+ RemainingDevicePath is already being managed by
+ the driver specified by This.
+ @retval EFI_ACCESS_DENIED The device specified by ControllerHandle and
+ RemainingDevicePath is already being managed by a
+ different driver or an application that requires
+ exclusive access.
+ @retval EFI_UNSUPPORTED The device specified by ControllerHandle and
+ RemainingDevicePath is not supported by the driver
+ specified by This.
+
+**/
+EFI_STATUS
+EFIAPI
+Mtftp4DriverBindingSupported (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE Controller,
+ IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath
+ )
+{
+ EFI_STATUS Status;
+
+ Status = gBS->OpenProtocol (
+ Controller,
+ &gEfiUdp4ServiceBindingProtocolGuid,
+ NULL,
+ This->DriverBindingHandle,
+ Controller,
+ EFI_OPEN_PROTOCOL_TEST_PROTOCOL
+ );
+
+ return Status;
+}
+
+
+/**
+ Config a NULL UDP that is used to keep the connection between UDP and MTFTP.
+
+ Just leave the Udp child unconfigured. When UDP is unloaded,
+ MTFTP will be informed with DriverBinding Stop.
+
+ @param UdpIo The UDP_IO to configure
+ @param Context The opaque parameter to the callback
+
+ @retval EFI_SUCCESS It always return EFI_SUCCESS directly.
+
+**/
+EFI_STATUS
+EFIAPI
+Mtftp4ConfigNullUdp (
+ IN UDP_IO *UdpIo,
+ IN VOID *Context
+ )
+{
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Create then initialize a MTFTP service binding instance.
+
+ @param Controller The controller to install the MTFTP service
+ binding on
+ @param Image The driver binding image of the MTFTP driver
+ @param Service The variable to receive the created service
+ binding instance.
+
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate resource to create the instance
+ @retval EFI_DEVICE_ERROR Failed to create a NULL UDP port to keep
+ connection with UDP.
+ @retval EFI_SUCCESS The service instance is created for the
+ controller.
+
+**/
+EFI_STATUS
+Mtftp4CreateService (
+ IN EFI_HANDLE Controller,
+ IN EFI_HANDLE Image,
+ OUT MTFTP4_SERVICE **Service
+ )
+{
+ MTFTP4_SERVICE *MtftpSb;
+ EFI_STATUS Status;
+
+ *Service = NULL;
+ MtftpSb = AllocatePool (sizeof (MTFTP4_SERVICE));
+
+ if (MtftpSb == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ MtftpSb->Signature = MTFTP4_SERVICE_SIGNATURE;
+ MtftpSb->ServiceBinding = gMtftp4ServiceBindingTemplete;
+ MtftpSb->ChildrenNum = 0;
+ InitializeListHead (&MtftpSb->Children);
+
+ MtftpSb->Timer = NULL;
+ MtftpSb->TimerNotifyLevel = NULL;
+ MtftpSb->TimerToGetMap = NULL;
+ MtftpSb->Controller = Controller;
+ MtftpSb->Image = Image;
+ MtftpSb->ConnectUdp = NULL;
+
+ //
+ // Create the timer and a udp to be notified when UDP is uninstalled
+ //
+ Status = gBS->CreateEvent (
+ EVT_NOTIFY_SIGNAL | EVT_TIMER,
+ TPL_CALLBACK,
+ Mtftp4OnTimerTick,
+ MtftpSb,
+ &MtftpSb->Timer
+ );
+ if (EFI_ERROR (Status)) {
+ FreePool (MtftpSb);
+ return Status;
+ }
+
+ Status = gBS->CreateEvent (
+ EVT_NOTIFY_SIGNAL | EVT_TIMER,
+ TPL_NOTIFY,
+ Mtftp4OnTimerTickNotifyLevel,
+ MtftpSb,
+ &MtftpSb->TimerNotifyLevel
+ );
+ if (EFI_ERROR (Status)) {
+ gBS->CloseEvent (MtftpSb->Timer);
+ FreePool (MtftpSb);
+ return Status;
+ }
+
+ //
+ // Create the timer used to time out the procedure which is used to
+ // get the default IP address.
+ //
+ Status = gBS->CreateEvent (
+ EVT_TIMER,
+ TPL_CALLBACK,
+ NULL,
+ NULL,
+ &MtftpSb->TimerToGetMap
+ );
+ if (EFI_ERROR (Status)) {
+ gBS->CloseEvent (MtftpSb->TimerNotifyLevel);
+ gBS->CloseEvent (MtftpSb->Timer);
+ FreePool (MtftpSb);
+ return Status;
+ }
+
+ MtftpSb->ConnectUdp = UdpIoCreateIo (
+ Controller,
+ Image,
+ Mtftp4ConfigNullUdp,
+ UDP_IO_UDP4_VERSION,
+ NULL
+ );
+
+ if (MtftpSb->ConnectUdp == NULL) {
+ gBS->CloseEvent (MtftpSb->TimerToGetMap);
+ gBS->CloseEvent (MtftpSb->TimerNotifyLevel);
+ gBS->CloseEvent (MtftpSb->Timer);
+ FreePool (MtftpSb);
+ return EFI_DEVICE_ERROR;
+ }
+
+ *Service = MtftpSb;
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Release all the resource used the MTFTP service binding instance.
+
+ @param MtftpSb The MTFTP service binding instance.
+
+**/
+VOID
+Mtftp4CleanService (
+ IN MTFTP4_SERVICE *MtftpSb
+ )
+{
+ UdpIoFreeIo (MtftpSb->ConnectUdp);
+ gBS->CloseEvent (MtftpSb->TimerToGetMap);
+ gBS->CloseEvent (MtftpSb->TimerNotifyLevel);
+ gBS->CloseEvent (MtftpSb->Timer);
+}
+
+
+/**
+ Start the MTFTP driver on this controller.
+
+ MTFTP driver will install a MTFTP SERVICE BINDING protocol on the supported
+ controller, which can be used to create/destroy MTFTP children.
+
+ @param This The MTFTP driver binding protocol.
+ @param Controller The controller to manage.
+ @param RemainingDevicePath Remaining device path.
+
+ @retval EFI_ALREADY_STARTED The MTFTP service binding protocol has been
+ started on the controller.
+ @retval EFI_SUCCESS The MTFTP service binding is installed on the
+ controller.
+
+**/
+EFI_STATUS
+EFIAPI
+Mtftp4DriverBindingStart (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE Controller,
+ IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath
+ )
+{
+ MTFTP4_SERVICE *MtftpSb;
+ EFI_STATUS Status;
+
+ //
+ // Directly return if driver is already running.
+ //
+ Status = gBS->OpenProtocol (
+ Controller,
+ &gEfiMtftp4ServiceBindingProtocolGuid,
+ NULL,
+ This->DriverBindingHandle,
+ Controller,
+ EFI_OPEN_PROTOCOL_TEST_PROTOCOL
+ );
+
+ if (Status == EFI_SUCCESS) {
+ return EFI_ALREADY_STARTED;
+ }
+
+ Status = Mtftp4CreateService (Controller, This->DriverBindingHandle, &MtftpSb);
+
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+ ASSERT (MtftpSb != NULL);
+
+ Status = gBS->SetTimer (MtftpSb->Timer, TimerPeriodic, TICKS_PER_SECOND);
+
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+
+ Status = gBS->SetTimer (MtftpSb->TimerNotifyLevel, TimerPeriodic, TICKS_PER_SECOND);
+
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+
+ //
+ // Install the Mtftp4ServiceBinding Protocol onto Controller
+ //
+ Status = gBS->InstallMultipleProtocolInterfaces (
+ &Controller,
+ &gEfiMtftp4ServiceBindingProtocolGuid,
+ &MtftpSb->ServiceBinding,
+ NULL
+ );
+
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+
+ return EFI_SUCCESS;
+
+ON_ERROR:
+ Mtftp4CleanService (MtftpSb);
+ FreePool (MtftpSb);
+
+ return Status;
+}
+
+/**
+ Callback function which provided by user to remove one node in NetDestroyLinkList process.
+
+ @param[in] Entry The entry to be removed.
+ @param[in] Context Pointer to the callback context corresponds to the Context in NetDestroyLinkList.
+
+ @retval EFI_SUCCESS The entry has been removed successfully.
+ @retval Others Fail to remove the entry.
+
+**/
+EFI_STATUS
+EFIAPI
+Mtftp4DestroyChildEntryInHandleBuffer (
+ IN LIST_ENTRY *Entry,
+ IN VOID *Context
+ )
+{
+ MTFTP4_PROTOCOL *Instance;
+ EFI_SERVICE_BINDING_PROTOCOL *ServiceBinding;
+ UINTN NumberOfChildren;
+ EFI_HANDLE *ChildHandleBuffer;
+
+ if (Entry == NULL || Context == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Instance = NET_LIST_USER_STRUCT_S (Entry, MTFTP4_PROTOCOL, Link, MTFTP4_PROTOCOL_SIGNATURE);
+ ServiceBinding = ((MTFTP4_DESTROY_CHILD_IN_HANDLE_BUF_CONTEXT *) Context)->ServiceBinding;
+ NumberOfChildren = ((MTFTP4_DESTROY_CHILD_IN_HANDLE_BUF_CONTEXT *) Context)->NumberOfChildren;
+ ChildHandleBuffer = ((MTFTP4_DESTROY_CHILD_IN_HANDLE_BUF_CONTEXT *) Context)->ChildHandleBuffer;
+
+ if (!NetIsInHandleBuffer (Instance->Handle, NumberOfChildren, ChildHandleBuffer)) {
+ return EFI_SUCCESS;
+ }
+
+ return ServiceBinding->DestroyChild (ServiceBinding, Instance->Handle);
+}
+
+/**
+ Stop the MTFTP driver on controller. The controller is a UDP
+ child handle.
+
+ @param This The MTFTP driver binding protocol
+ @param Controller The controller to stop
+ @param NumberOfChildren The number of children
+ @param ChildHandleBuffer The array of the child handle.
+
+ @retval EFI_SUCCESS The driver is stopped on the controller.
+ @retval EFI_DEVICE_ERROR Failed to stop the driver on the controller.
+
+**/
+EFI_STATUS
+EFIAPI
+Mtftp4DriverBindingStop (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE Controller,
+ IN UINTN NumberOfChildren,
+ IN EFI_HANDLE *ChildHandleBuffer
+ )
+{
+ EFI_SERVICE_BINDING_PROTOCOL *ServiceBinding;
+ MTFTP4_SERVICE *MtftpSb;
+ EFI_HANDLE NicHandle;
+ EFI_STATUS Status;
+ LIST_ENTRY *List;
+ MTFTP4_DESTROY_CHILD_IN_HANDLE_BUF_CONTEXT Context;
+
+ //
+ // MTFTP driver opens UDP child, So, Controller is a UDP
+ // child handle. Locate the Nic handle first. Then get the
+ // MTFTP private data back.
+ //
+ NicHandle = NetLibGetNicHandle (Controller, &gEfiUdp4ProtocolGuid);
+
+ if (NicHandle == NULL) {
+ return EFI_SUCCESS;
+ }
+
+ Status = gBS->OpenProtocol (
+ NicHandle,
+ &gEfiMtftp4ServiceBindingProtocolGuid,
+ (VOID **) &ServiceBinding,
+ This->DriverBindingHandle,
+ NicHandle,
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL
+ );
+
+ if (EFI_ERROR (Status)) {
+ return EFI_DEVICE_ERROR;
+ }
+
+ MtftpSb = MTFTP4_SERVICE_FROM_THIS (ServiceBinding);
+
+ if (!IsListEmpty (&MtftpSb->Children)) {
+ //
+ // Destroy the Mtftp4 child instance in ChildHandleBuffer.
+ //
+ List = &MtftpSb->Children;
+ Context.ServiceBinding = ServiceBinding;
+ Context.NumberOfChildren = NumberOfChildren;
+ Context.ChildHandleBuffer = ChildHandleBuffer;
+ Status = NetDestroyLinkList (
+ List,
+ Mtftp4DestroyChildEntryInHandleBuffer,
+ &Context,
+ NULL
+ );
+ }
+
+ if (NumberOfChildren == 0 && IsListEmpty (&MtftpSb->Children)) {
+ gBS->UninstallProtocolInterface (
+ NicHandle,
+ &gEfiMtftp4ServiceBindingProtocolGuid,
+ ServiceBinding
+ );
+
+ Mtftp4CleanService (MtftpSb);
+ if (gMtftp4ControllerNameTable != NULL) {
+ FreeUnicodeStringTable (gMtftp4ControllerNameTable);
+ gMtftp4ControllerNameTable = NULL;
+ }
+ FreePool (MtftpSb);
+
+ Status = EFI_SUCCESS;
+ }
+
+ return Status;
+}
+
+
+/**
+ Initialize a MTFTP protocol instance which is the child of MtftpSb.
+
+ @param MtftpSb The MTFTP service binding protocol.
+ @param Instance The MTFTP instance to initialize.
+
+**/
+VOID
+Mtftp4InitProtocol (
+ IN MTFTP4_SERVICE *MtftpSb,
+ OUT MTFTP4_PROTOCOL *Instance
+ )
+{
+ ZeroMem (Instance, sizeof (MTFTP4_PROTOCOL));
+
+ Instance->Signature = MTFTP4_PROTOCOL_SIGNATURE;
+ InitializeListHead (&Instance->Link);
+ CopyMem (&Instance->Mtftp4, &gMtftp4ProtocolTemplate, sizeof (Instance->Mtftp4));
+ Instance->State = MTFTP4_STATE_UNCONFIGED;
+ Instance->Service = MtftpSb;
+
+ InitializeListHead (&Instance->Blocks);
+}
+
+
+/**
+ Create a MTFTP child for the service binding instance, then
+ install the MTFTP protocol to the ChildHandle.
+
+ @param This The MTFTP service binding instance.
+ @param ChildHandle The Child handle to install the MTFTP protocol.
+
+ @retval EFI_INVALID_PARAMETER The parameter is invalid.
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate resource for the new child.
+ @retval EFI_SUCCESS The child is successfully create.
+
+**/
+EFI_STATUS
+EFIAPI
+Mtftp4ServiceBindingCreateChild (
+ IN EFI_SERVICE_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE *ChildHandle
+ )
+{
+ MTFTP4_SERVICE *MtftpSb;
+ MTFTP4_PROTOCOL *Instance;
+ EFI_STATUS Status;
+ EFI_TPL OldTpl;
+ VOID *Udp4;
+
+ if ((This == NULL) || (ChildHandle == NULL)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Instance = AllocatePool (sizeof (*Instance));
+
+ if (Instance == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ MtftpSb = MTFTP4_SERVICE_FROM_THIS (This);
+
+ Mtftp4InitProtocol (MtftpSb, Instance);
+
+ Instance->UnicastPort = UdpIoCreateIo (
+ MtftpSb->Controller,
+ MtftpSb->Image,
+ Mtftp4ConfigNullUdp,
+ UDP_IO_UDP4_VERSION,
+ Instance
+ );
+
+ if (Instance->UnicastPort == NULL) {
+ FreePool (Instance);
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ //
+ // Install the MTFTP protocol onto ChildHandle
+ //
+ Status = gBS->InstallMultipleProtocolInterfaces (
+ ChildHandle,
+ &gEfiMtftp4ProtocolGuid,
+ &Instance->Mtftp4,
+ NULL
+ );
+
+ if (EFI_ERROR (Status)) {
+ UdpIoFreeIo (Instance->UnicastPort);
+ FreePool (Instance);
+ return Status;
+ }
+
+ Instance->Handle = *ChildHandle;
+
+ //
+ // Open the Udp4 protocol BY_CHILD.
+ //
+ Status = gBS->OpenProtocol (
+ MtftpSb->ConnectUdp->UdpHandle,
+ &gEfiUdp4ProtocolGuid,
+ (VOID **) &Udp4,
+ gMtftp4DriverBinding.DriverBindingHandle,
+ Instance->Handle,
+ EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER
+ );
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+
+ //
+ // Open the Udp4 protocol by child.
+ //
+ Status = gBS->OpenProtocol (
+ Instance->UnicastPort->UdpHandle,
+ &gEfiUdp4ProtocolGuid,
+ (VOID **) &Udp4,
+ gMtftp4DriverBinding.DriverBindingHandle,
+ Instance->Handle,
+ EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER
+ );
+ if (EFI_ERROR (Status)) {
+ //
+ // Close the Udp4 protocol.
+ //
+ gBS->CloseProtocol (
+ MtftpSb->ConnectUdp->UdpHandle,
+ &gEfiUdp4ProtocolGuid,
+ gMtftp4DriverBinding.DriverBindingHandle,
+ *ChildHandle
+ );
+ goto ON_ERROR;
+ }
+
+ //
+ // Add it to the parent's child list.
+ //
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
+
+ InsertTailList (&MtftpSb->Children, &Instance->Link);
+ MtftpSb->ChildrenNum++;
+
+ gBS->RestoreTPL (OldTpl);
+
+ return EFI_SUCCESS;
+
+ON_ERROR:
+ if (Instance->Handle != NULL) {
+ gBS->UninstallMultipleProtocolInterfaces (
+ Instance->Handle,
+ &gEfiMtftp4ProtocolGuid,
+ &Instance->Mtftp4,
+ NULL
+ );
+ }
+
+ UdpIoFreeIo (Instance->UnicastPort);
+ FreePool (Instance);
+
+ return Status;
+}
+
+
+/**
+ Destroy one of the service binding's child.
+
+ @param This The service binding instance
+ @param ChildHandle The child handle to destroy
+
+ @retval EFI_INVALID_PARAMETER The parameter is invalid.
+ @retval EFI_UNSUPPORTED The child may have already been destroyed.
+ @retval EFI_SUCCESS The child is destroyed and removed from the
+ parent's child list.
+
+**/
+EFI_STATUS
+EFIAPI
+Mtftp4ServiceBindingDestroyChild (
+ IN EFI_SERVICE_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE ChildHandle
+ )
+{
+ MTFTP4_SERVICE *MtftpSb;
+ MTFTP4_PROTOCOL *Instance;
+ EFI_MTFTP4_PROTOCOL *Mtftp4;
+ EFI_STATUS Status;
+ EFI_TPL OldTpl;
+
+ if ((This == NULL) || (ChildHandle == NULL)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ //
+ // Retrieve the private context data structures
+ //
+ Status = gBS->OpenProtocol (
+ ChildHandle,
+ &gEfiMtftp4ProtocolGuid,
+ (VOID **) &Mtftp4,
+ gMtftp4DriverBinding.DriverBindingHandle,
+ ChildHandle,
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL
+ );
+
+ if (EFI_ERROR (Status)) {
+ return EFI_UNSUPPORTED;
+ }
+
+ Instance = MTFTP4_PROTOCOL_FROM_THIS (Mtftp4);
+ MtftpSb = MTFTP4_SERVICE_FROM_THIS (This);
+
+ if (Instance->Service != MtftpSb) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (Instance->InDestroy) {
+ return EFI_SUCCESS;
+ }
+
+ Instance->InDestroy = TRUE;
+
+ //
+ // Close the Udp4 protocol.
+ //
+ gBS->CloseProtocol (
+ MtftpSb->ConnectUdp->UdpHandle,
+ &gEfiUdp4ProtocolGuid,
+ gMtftp4DriverBinding.DriverBindingHandle,
+ ChildHandle
+ );
+
+ gBS->CloseProtocol (
+ Instance->UnicastPort->UdpHandle,
+ &gEfiUdp4ProtocolGuid,
+ gMtftp4DriverBinding.DriverBindingHandle,
+ ChildHandle
+ );
+
+ if (Instance->McastUdpPort != NULL) {
+ gBS->CloseProtocol (
+ Instance->McastUdpPort->UdpHandle,
+ &gEfiUdp4ProtocolGuid,
+ gMtftp4DriverBinding.DriverBindingHandle,
+ ChildHandle
+ );
+ }
+
+ //
+ // Uninstall the MTFTP4 protocol first to enable a top down destruction.
+ //
+ Status = gBS->UninstallProtocolInterface (
+ ChildHandle,
+ &gEfiMtftp4ProtocolGuid,
+ Mtftp4
+ );
+
+ if (EFI_ERROR (Status)) {
+ Instance->InDestroy = FALSE;
+ return Status;
+ }
+
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
+
+ Mtftp4CleanOperation (Instance, EFI_DEVICE_ERROR);
+ UdpIoFreeIo (Instance->UnicastPort);
+
+ RemoveEntryList (&Instance->Link);
+ MtftpSb->ChildrenNum--;
+
+ gBS->RestoreTPL (OldTpl);
+
+ FreePool (Instance);
+ return EFI_SUCCESS;
+}
diff --git a/src/VBox/Devices/EFI/Firmware/NetworkPkg/Mtftp4Dxe/Mtftp4Driver.h b/src/VBox/Devices/EFI/Firmware/NetworkPkg/Mtftp4Dxe/Mtftp4Driver.h
new file mode 100644
index 00000000..987a6826
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/NetworkPkg/Mtftp4Dxe/Mtftp4Driver.h
@@ -0,0 +1,131 @@
+/** @file
+ Mtftp drivers function header.
+
+Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.<BR>
+SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#ifndef __EFI_MTFTP4_DRIVER_H__
+#define __EFI_MTFTP4_DRIVER_H__
+
+#include <Uefi.h>
+
+#include <Protocol/ServiceBinding.h>
+
+#include <Library/NetLib.h>
+#include <Library/UefiLib.h>
+#include <Library/UefiDriverEntryPoint.h>
+
+extern EFI_COMPONENT_NAME_PROTOCOL gMtftp4ComponentName;
+extern EFI_COMPONENT_NAME2_PROTOCOL gMtftp4ComponentName2;
+extern EFI_DRIVER_BINDING_PROTOCOL gMtftp4DriverBinding;
+extern EFI_UNICODE_STRING_TABLE *gMtftp4ControllerNameTable;
+
+/**
+ Test whether MTFTP driver support this controller.
+
+ @param This The MTFTP driver binding instance
+ @param Controller The controller to test
+ @param RemainingDevicePath The remaining device path
+
+ @retval EFI_SUCCESS The controller has UDP service binding protocol
+ installed, MTFTP can support it.
+ @retval Others MTFTP can't support the controller.
+
+**/
+EFI_STATUS
+EFIAPI
+Mtftp4DriverBindingSupported (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE Controller,
+ IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath
+ );
+
+/**
+ Start the MTFTP driver on this controller.
+
+ MTFTP driver will install a MTFTP SERVICE BINDING protocol on the supported
+ controller, which can be used to create/destroy MTFTP children.
+
+ @param This The MTFTP driver binding protocol.
+ @param Controller The controller to manage.
+ @param RemainingDevicePath Remaining device path.
+
+ @retval EFI_ALREADY_STARTED The MTFTP service binding protocol has been
+ started on the controller.
+ @retval EFI_SUCCESS The MTFTP service binding is installed on the
+ controller.
+
+**/
+EFI_STATUS
+EFIAPI
+Mtftp4DriverBindingStart (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE Controller,
+ IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath
+ );
+
+/**
+ Stop the MTFTP driver on controller. The controller is a UDP
+ child handle.
+
+ @param This The MTFTP driver binding protocol
+ @param Controller The controller to stop
+ @param NumberOfChildren The number of children
+ @param ChildHandleBuffer The array of the child handle.
+
+ @retval EFI_SUCCESS The driver is stopped on the controller.
+ @retval EFI_DEVICE_ERROR Failed to stop the driver on the controller.
+
+**/
+EFI_STATUS
+EFIAPI
+Mtftp4DriverBindingStop (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE Controller,
+ IN UINTN NumberOfChildren,
+ IN EFI_HANDLE *ChildHandleBuffer
+ );
+
+/**
+ Create a MTFTP child for the service binding instance, then
+ install the MTFTP protocol to the ChildHandle.
+
+ @param This The MTFTP service binding instance.
+ @param ChildHandle The Child handle to install the MTFTP protocol.
+
+ @retval EFI_INVALID_PARAMETER The parameter is invalid.
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate resource for the new child.
+ @retval EFI_SUCCESS The child is successfully create.
+
+**/
+EFI_STATUS
+EFIAPI
+Mtftp4ServiceBindingCreateChild (
+ IN EFI_SERVICE_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE *ChildHandle
+ );
+
+/**
+ Destroy one of the service binding's child.
+
+ @param This The service binding instance
+ @param ChildHandle The child handle to destroy
+
+ @retval EFI_INVALID_PARAMETER The parameter is invalid.
+ @retval EFI_UNSUPPORTED The child may have already been destroyed.
+ @retval EFI_SUCCESS The child is destroyed and removed from the
+ parent's child list.
+
+**/
+EFI_STATUS
+EFIAPI
+Mtftp4ServiceBindingDestroyChild (
+ IN EFI_SERVICE_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE ChildHandle
+ );
+
+
+
+#endif
diff --git a/src/VBox/Devices/EFI/Firmware/NetworkPkg/Mtftp4Dxe/Mtftp4Dxe.inf b/src/VBox/Devices/EFI/Firmware/NetworkPkg/Mtftp4Dxe/Mtftp4Dxe.inf
new file mode 100644
index 00000000..aae0bda8
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/NetworkPkg/Mtftp4Dxe/Mtftp4Dxe.inf
@@ -0,0 +1,69 @@
+## @file
+# This module produces EFI MTFTPv4 Protocol and EFI MTFTPv4 Service Binding Protocol.
+#
+# This module produces EFI MTFTPv4 Protocol upon EFI UDPv4 Protocol, to provide
+# basic services for client-side unicast and/or multicase TFTP operations.
+#
+# Copyright (c) 2007 - 2018, Intel Corporation. All rights reserved.<BR>
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+#
+##
+
+[Defines]
+ INF_VERSION = 0x00010005
+ BASE_NAME = Mtftp4Dxe
+ MODULE_UNI_FILE = Mtftp4Dxe.uni
+ FILE_GUID = DC3641B8-2FA8-4ed3-BC1F-F9962A03454B
+ MODULE_TYPE = UEFI_DRIVER
+ VERSION_STRING = 1.0
+ ENTRY_POINT = Mtftp4DriverEntryPoint
+ UNLOAD_IMAGE = NetLibDefaultUnload
+#
+# The following information is for reference only and not required by the build tools.
+#
+# VALID_ARCHITECTURES = IA32 X64 EBC
+#
+# DRIVER_BINDING = gMtftp4DriverBinding
+# COMPONENT_NAME = gMtftp4ComponentName
+# COMPONENT_NAME2 = gMtftp4ComponentName2
+#
+
+[Sources]
+ Mtftp4Option.c
+ Mtftp4Rrq.c
+ Mtftp4Impl.h
+ ComponentName.c
+ Mtftp4Support.c
+ Mtftp4Impl.c
+ Mtftp4Option.h
+ Mtftp4Support.h
+ Mtftp4Driver.h
+ Mtftp4Driver.c
+ Mtftp4Wrq.c
+
+
+[Packages]
+ MdePkg/MdePkg.dec
+ NetworkPkg/NetworkPkg.dec
+
+
+[LibraryClasses]
+ UefiLib
+ UefiBootServicesTableLib
+ UefiDriverEntryPoint
+ DebugLib
+ NetLib
+ UdpIoLib
+ MemoryAllocationLib
+ BaseMemoryLib
+
+
+[Protocols]
+ gEfiMtftp4ServiceBindingProtocolGuid ## BY_START
+ gEfiUdp4ServiceBindingProtocolGuid ## TO_START
+ gEfiMtftp4ProtocolGuid ## BY_START
+ gEfiUdp4ProtocolGuid ## TO_START
+
+[UserExtensions.TianoCore."ExtraFiles"]
+ Mtftp4DxeExtra.uni
diff --git a/src/VBox/Devices/EFI/Firmware/NetworkPkg/Mtftp4Dxe/Mtftp4Dxe.uni b/src/VBox/Devices/EFI/Firmware/NetworkPkg/Mtftp4Dxe/Mtftp4Dxe.uni
new file mode 100644
index 00000000..1c2c80bc
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/NetworkPkg/Mtftp4Dxe/Mtftp4Dxe.uni
@@ -0,0 +1,17 @@
+// /** @file
+// This module produces EFI MTFTPv4 Protocol and EFI MTFTPv4 Service Binding Protocol.
+//
+// This module produces EFI MTFTPv4 Protocol upon EFI UDPv4 Protocol, to provide
+// basic services for client-side unicast and/or multicase TFTP operations.
+//
+// Copyright (c) 2007 - 2018, Intel Corporation. All rights reserved.<BR>
+//
+// SPDX-License-Identifier: BSD-2-Clause-Patent
+//
+// **/
+
+
+#string STR_MODULE_ABSTRACT #language en-US "Produces EFI MTFTPv4 Protocol and EFI MTFTPv4 Service Binding Protocol"
+
+#string STR_MODULE_DESCRIPTION #language en-US "This module produces EFI MTFTPv4 Protocol upon EFI UDPv4 Protocol, to provide basic services for client-side unicast or multicase TFTP operations or both."
+
diff --git a/src/VBox/Devices/EFI/Firmware/NetworkPkg/Mtftp4Dxe/Mtftp4DxeExtra.uni b/src/VBox/Devices/EFI/Firmware/NetworkPkg/Mtftp4Dxe/Mtftp4DxeExtra.uni
new file mode 100644
index 00000000..cd685c45
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/NetworkPkg/Mtftp4Dxe/Mtftp4DxeExtra.uni
@@ -0,0 +1,14 @@
+// /** @file
+// Mtftp4Dxe Localized Strings and Content
+//
+// Copyright (c) 2013 - 2018, Intel Corporation. All rights reserved.<BR>
+//
+// SPDX-License-Identifier: BSD-2-Clause-Patent
+//
+// **/
+
+#string STR_PROPERTIES_MODULE_NAME
+#language en-US
+"MTFTP v4 DXE Driver"
+
+
diff --git a/src/VBox/Devices/EFI/Firmware/NetworkPkg/Mtftp4Dxe/Mtftp4Impl.c b/src/VBox/Devices/EFI/Firmware/NetworkPkg/Mtftp4Dxe/Mtftp4Impl.c
new file mode 100644
index 00000000..3fce2b52
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/NetworkPkg/Mtftp4Dxe/Mtftp4Impl.c
@@ -0,0 +1,1113 @@
+/** @file
+ Interface routine for Mtftp4.
+
+(C) Copyright 2014 Hewlett-Packard Development Company, L.P.<BR>
+Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.<BR>
+SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+
+#include "Mtftp4Impl.h"
+
+
+/**
+ Clean up the MTFTP session to get ready for new operation.
+
+ @param Instance The MTFTP session to clean up
+ @param Result The result to return to the caller who initiated
+ the operation.
+**/
+VOID
+Mtftp4CleanOperation (
+ IN OUT MTFTP4_PROTOCOL *Instance,
+ IN EFI_STATUS Result
+ )
+{
+ LIST_ENTRY *Entry;
+ LIST_ENTRY *Next;
+ MTFTP4_BLOCK_RANGE *Block;
+ EFI_MTFTP4_TOKEN *Token;
+
+ //
+ // Free various resources.
+ //
+ Token = Instance->Token;
+
+ if (Token != NULL) {
+ Token->Status = Result;
+
+ if (Token->Event != NULL) {
+ gBS->SignalEvent (Token->Event);
+ }
+
+ Instance->Token = NULL;
+ }
+
+ ASSERT (Instance->UnicastPort != NULL);
+ UdpIoCleanIo (Instance->UnicastPort);
+
+ if (Instance->LastPacket != NULL) {
+ NetbufFree (Instance->LastPacket);
+ Instance->LastPacket = NULL;
+ }
+
+ if (Instance->McastUdpPort != NULL) {
+ gBS->CloseProtocol (
+ Instance->McastUdpPort->UdpHandle,
+ &gEfiUdp4ProtocolGuid,
+ gMtftp4DriverBinding.DriverBindingHandle,
+ Instance->Handle
+ );
+ UdpIoFreeIo (Instance->McastUdpPort);
+ Instance->McastUdpPort = NULL;
+ }
+
+ NET_LIST_FOR_EACH_SAFE (Entry, Next, &Instance->Blocks) {
+ Block = NET_LIST_USER_STRUCT (Entry, MTFTP4_BLOCK_RANGE, Link);
+ RemoveEntryList (Entry);
+ FreePool (Block);
+ }
+
+ ZeroMem (&Instance->RequestOption, sizeof (MTFTP4_OPTION));
+
+ Instance->Operation = 0;
+
+ Instance->BlkSize = MTFTP4_DEFAULT_BLKSIZE;
+ Instance->WindowSize = 1;
+ Instance->TotalBlock = 0;
+ Instance->AckedBlock = 0;
+ Instance->LastBlock = 0;
+ Instance->ServerIp = 0;
+ Instance->ListeningPort = 0;
+ Instance->ConnectedPort = 0;
+ Instance->Gateway = 0;
+ Instance->PacketToLive = 0;
+ Instance->MaxRetry = 0;
+ Instance->CurRetry = 0;
+ Instance->Timeout = 0;
+ Instance->McastIp = 0;
+ Instance->McastPort = 0;
+ Instance->Master = TRUE;
+}
+
+
+/**
+ Check packet for GetInfo.
+
+ GetInfo is implemented with EfiMtftp4ReadFile. It use Mtftp4GetInfoCheckPacket
+ to inspect the first packet from server, then abort the session.
+
+ @param This The MTFTP4 protocol instance
+ @param Token The user's token
+ @param PacketLen The length of the packet
+ @param Packet The received packet.
+
+ @retval EFI_ABORTED Abort the ReadFile operation and return.
+
+**/
+EFI_STATUS
+EFIAPI
+Mtftp4GetInfoCheckPacket (
+ IN EFI_MTFTP4_PROTOCOL *This,
+ IN EFI_MTFTP4_TOKEN *Token,
+ IN UINT16 PacketLen,
+ IN EFI_MTFTP4_PACKET *Packet
+ )
+{
+ MTFTP4_GETINFO_STATE *State;
+ EFI_STATUS Status;
+ UINT16 OpCode;
+ EFI_MTFTP4_ERROR_HEADER *ErrorHeader;
+
+ State = (MTFTP4_GETINFO_STATE *) Token->Context;
+ OpCode = NTOHS (Packet->OpCode);
+
+ //
+ // Set the GetInfo's return status according to the OpCode.
+ //
+ switch (OpCode) {
+ case EFI_MTFTP4_OPCODE_ERROR:
+ ErrorHeader = (EFI_MTFTP4_ERROR_HEADER *) Packet;
+ if (ErrorHeader->ErrorCode == EFI_MTFTP4_ERRORCODE_FILE_NOT_FOUND) {
+ DEBUG ((EFI_D_ERROR, "TFTP error code 1 (File Not Found)\n"));
+ } else {
+ DEBUG ((EFI_D_ERROR, "TFTP error code %d\n", ErrorHeader->ErrorCode));
+ }
+ State->Status = EFI_TFTP_ERROR;
+ break;
+
+ case EFI_MTFTP4_OPCODE_OACK:
+ State->Status = EFI_SUCCESS;
+ break;
+
+ default:
+ State->Status = EFI_PROTOCOL_ERROR;
+ }
+
+ //
+ // Allocate buffer then copy the packet over. Use gBS->AllocatePool
+ // in case AllocatePool will implements something tricky.
+ //
+ Status = gBS->AllocatePool (EfiBootServicesData, PacketLen, (VOID **) State->Packet);
+
+ if (EFI_ERROR (Status)) {
+ State->Status = EFI_OUT_OF_RESOURCES;
+ return EFI_ABORTED;
+ }
+
+ *(State->PacketLen) = PacketLen;
+ CopyMem (*(State->Packet), Packet, PacketLen);
+
+ return EFI_ABORTED;
+}
+
+
+/**
+ Check whether the override data is valid.
+
+ It will first validate whether the server is a valid unicast. If a gateway
+ is provided in the Override, it also check that it is a unicast on the
+ connected network.
+
+ @param Instance The MTFTP instance
+ @param Override The override data to validate.
+
+ @retval TRUE The override data is valid
+ @retval FALSE The override data is invalid
+
+**/
+BOOLEAN
+Mtftp4OverrideValid (
+ IN MTFTP4_PROTOCOL *Instance,
+ IN EFI_MTFTP4_OVERRIDE_DATA *Override
+ )
+{
+ EFI_MTFTP4_CONFIG_DATA *Config;
+ IP4_ADDR Ip;
+ IP4_ADDR Netmask;
+ IP4_ADDR Gateway;
+
+ CopyMem (&Ip, &Override->ServerIp, sizeof (IP4_ADDR));
+ if (IP4_IS_UNSPECIFIED (NTOHL (Ip)) || IP4_IS_LOCAL_BROADCAST (NTOHL (Ip))) {
+ return FALSE;
+ }
+
+ Config = &Instance->Config;
+
+ CopyMem (&Gateway, &Override->GatewayIp, sizeof (IP4_ADDR));
+ Gateway = NTOHL (Gateway);
+
+ if (!Config->UseDefaultSetting && (Gateway != 0)) {
+ CopyMem (&Netmask, &Config->SubnetMask, sizeof (IP4_ADDR));
+ CopyMem (&Ip, &Config->StationIp, sizeof (IP4_ADDR));
+
+ Netmask = NTOHL (Netmask);
+ Ip = NTOHL (Ip);
+
+ if ((Netmask != 0 && !NetIp4IsUnicast (Gateway, Netmask)) || !IP4_NET_EQUAL (Gateway, Ip, Netmask)) {
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
+
+
+/**
+ Poll the UDP to get the IP4 default address, which may be retrieved
+ by DHCP.
+
+ The default time out value is 5 seconds. If IP has retrieved the default address,
+ the UDP is reconfigured.
+
+ @param Instance The Mtftp instance
+ @param UdpIo The UDP_IO to poll
+ @param UdpCfgData The UDP configure data to reconfigure the UDP_IO
+
+ @retval TRUE The default address is retrieved and UDP is reconfigured.
+ @retval FALSE Some error occurred.
+
+**/
+BOOLEAN
+Mtftp4GetMapping (
+ IN MTFTP4_PROTOCOL *Instance,
+ IN UDP_IO *UdpIo,
+ IN EFI_UDP4_CONFIG_DATA *UdpCfgData
+ )
+{
+ MTFTP4_SERVICE *Service;
+ EFI_IP4_MODE_DATA Ip4Mode;
+ EFI_UDP4_PROTOCOL *Udp;
+ EFI_STATUS Status;
+
+ ASSERT (Instance->Config.UseDefaultSetting);
+
+ Service = Instance->Service;
+ Udp = UdpIo->Protocol.Udp4;
+
+ Status = gBS->SetTimer (
+ Service->TimerToGetMap,
+ TimerRelative,
+ MTFTP4_TIME_TO_GETMAP * TICKS_PER_SECOND
+ );
+ if (EFI_ERROR (Status)) {
+ return FALSE;
+ }
+
+ while (EFI_ERROR (gBS->CheckEvent (Service->TimerToGetMap))) {
+ Udp->Poll (Udp);
+
+ if (!EFI_ERROR (Udp->GetModeData (Udp, NULL, &Ip4Mode, NULL, NULL)) &&
+ Ip4Mode.IsConfigured) {
+
+ Udp->Configure (Udp, NULL);
+ return (BOOLEAN) (Udp->Configure (Udp, UdpCfgData) == EFI_SUCCESS);
+ }
+ }
+
+ return FALSE;
+}
+
+
+/**
+ Configure the UDP port for unicast receiving.
+
+ @param UdpIo The UDP_IO instance
+ @param Instance The MTFTP session
+
+ @retval EFI_SUCCESS The UDP port is successfully configured for the
+ session to unicast receive.
+
+**/
+EFI_STATUS
+Mtftp4ConfigUnicastPort (
+ IN UDP_IO *UdpIo,
+ IN MTFTP4_PROTOCOL *Instance
+ )
+{
+ EFI_MTFTP4_CONFIG_DATA *Config;
+ EFI_UDP4_CONFIG_DATA UdpConfig;
+ EFI_STATUS Status;
+ IP4_ADDR Ip;
+
+ Config = &Instance->Config;
+
+ UdpConfig.AcceptBroadcast = FALSE;
+ UdpConfig.AcceptPromiscuous = FALSE;
+ UdpConfig.AcceptAnyPort = FALSE;
+ UdpConfig.AllowDuplicatePort = FALSE;
+ UdpConfig.TypeOfService = 0;
+ UdpConfig.TimeToLive = 64;
+ UdpConfig.DoNotFragment = FALSE;
+ UdpConfig.ReceiveTimeout = 0;
+ UdpConfig.TransmitTimeout = 0;
+ UdpConfig.UseDefaultAddress = Config->UseDefaultSetting;
+ IP4_COPY_ADDRESS (&UdpConfig.StationAddress, &Config->StationIp);
+ IP4_COPY_ADDRESS (&UdpConfig.SubnetMask, &Config->SubnetMask);
+ UdpConfig.StationPort = Config->LocalPort;
+ UdpConfig.RemotePort = 0;
+
+ Ip = HTONL (Instance->ServerIp);
+ IP4_COPY_ADDRESS (&UdpConfig.RemoteAddress, &Ip);
+
+ Status = UdpIo->Protocol.Udp4->Configure (UdpIo->Protocol.Udp4, &UdpConfig);
+
+ if ((Status == EFI_NO_MAPPING) && Mtftp4GetMapping (Instance, UdpIo, &UdpConfig)) {
+ return EFI_SUCCESS;
+ }
+
+ if (!Config->UseDefaultSetting && !EFI_IP4_EQUAL (&mZeroIp4Addr, &Config->GatewayIp)) {
+ //
+ // The station IP address is manually configured and the Gateway IP is not 0.
+ // Add the default route for this UDP instance.
+ //
+ Status = UdpIo->Protocol.Udp4->Routes (
+ UdpIo->Protocol.Udp4,
+ FALSE,
+ &mZeroIp4Addr,
+ &mZeroIp4Addr,
+ &Config->GatewayIp
+ );
+ if (EFI_ERROR (Status)) {
+ UdpIo->Protocol.Udp4->Configure (UdpIo->Protocol.Udp4, NULL);
+ }
+ }
+ return Status;
+}
+
+
+/**
+ Start the MTFTP session to do the operation, such as read file,
+ write file, and read directory.
+
+ @param This The MTFTP session
+ @param Token The token than encapsules the user's request.
+ @param Operation The operation to do
+
+ @retval EFI_INVALID_PARAMETER Some of the parameters are invalid.
+ @retval EFI_NOT_STARTED The MTFTP session hasn't been configured.
+ @retval EFI_ALREADY_STARTED There is pending operation for the session.
+ @retval EFI_SUCCESS The operation is successfully started.
+
+**/
+EFI_STATUS
+Mtftp4Start (
+ IN EFI_MTFTP4_PROTOCOL *This,
+ IN EFI_MTFTP4_TOKEN *Token,
+ IN UINT16 Operation
+ )
+{
+ MTFTP4_PROTOCOL *Instance;
+ EFI_MTFTP4_OVERRIDE_DATA *Override;
+ EFI_MTFTP4_CONFIG_DATA *Config;
+ EFI_TPL OldTpl;
+ EFI_STATUS Status;
+ EFI_STATUS TokenStatus;
+
+ //
+ // Validate the parameters
+ //
+ if ((This == NULL) || (Token == NULL) || (Token->Filename == NULL) ||
+ ((Token->OptionCount != 0) && (Token->OptionList == NULL))) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ //
+ // User must provide at least one method to collect the data for download.
+ //
+ if (((Operation == EFI_MTFTP4_OPCODE_RRQ) || (Operation == EFI_MTFTP4_OPCODE_DIR)) &&
+ ((Token->Buffer == NULL) && (Token->CheckPacket == NULL))) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ //
+ // User must provide at least one method to provide the data for upload.
+ //
+ if ((Operation == EFI_MTFTP4_OPCODE_WRQ) &&
+ ((Token->Buffer == NULL) && (Token->PacketNeeded == NULL))) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Instance = MTFTP4_PROTOCOL_FROM_THIS (This);
+
+ Status = EFI_SUCCESS;
+ TokenStatus = EFI_SUCCESS;
+
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
+
+ if (Instance->State != MTFTP4_STATE_CONFIGED) {
+ Status = EFI_NOT_STARTED;
+ }
+
+ if (Instance->Operation != 0) {
+ Status = EFI_ACCESS_DENIED;
+ }
+
+ if ((Token->OverrideData != NULL) && !Mtftp4OverrideValid (Instance, Token->OverrideData)) {
+ Status = EFI_INVALID_PARAMETER;
+ }
+
+ if (EFI_ERROR (Status)) {
+ gBS->RestoreTPL (OldTpl);
+ return Status;
+ }
+
+ //
+ // Set the Operation now to prevent the application start other
+ // operations.
+ //
+ Instance->Operation = Operation;
+ Override = Token->OverrideData;
+
+ if (Token->OptionCount != 0) {
+ Status = Mtftp4ParseOption (
+ Token->OptionList,
+ Token->OptionCount,
+ TRUE,
+ Instance->Operation,
+ &Instance->RequestOption
+ );
+
+ if (EFI_ERROR (Status)) {
+ TokenStatus = EFI_DEVICE_ERROR;
+ goto ON_ERROR;
+ }
+ }
+
+ //
+ // Set the operation parameters from the configuration or override data.
+ //
+ Config = &Instance->Config;
+ Instance->Token = Token;
+ Instance->BlkSize = MTFTP4_DEFAULT_BLKSIZE;
+ Instance->WindowSize = MTFTP4_DEFAULT_WINDOWSIZE;
+
+ CopyMem (&Instance->ServerIp, &Config->ServerIp, sizeof (IP4_ADDR));
+ Instance->ServerIp = NTOHL (Instance->ServerIp);
+
+ Instance->ListeningPort = Config->InitialServerPort;
+ Instance->ConnectedPort = 0;
+
+ CopyMem (&Instance->Gateway, &Config->GatewayIp, sizeof (IP4_ADDR));
+ Instance->Gateway = NTOHL (Instance->Gateway);
+
+ Instance->MaxRetry = Config->TryCount;
+ Instance->Timeout = Config->TimeoutValue;
+ Instance->Master = TRUE;
+
+ if (Override != NULL) {
+ CopyMem (&Instance->ServerIp, &Override->ServerIp, sizeof (IP4_ADDR));
+ CopyMem (&Instance->Gateway, &Override->GatewayIp, sizeof (IP4_ADDR));
+
+ Instance->ServerIp = NTOHL (Instance->ServerIp);
+ Instance->Gateway = NTOHL (Instance->Gateway);
+
+ Instance->ListeningPort = Override->ServerPort;
+ Instance->MaxRetry = Override->TryCount;
+ Instance->Timeout = Override->TimeoutValue;
+ }
+
+ if (Instance->ListeningPort == 0) {
+ Instance->ListeningPort = MTFTP4_DEFAULT_SERVER_PORT;
+ }
+
+ if (Instance->MaxRetry == 0) {
+ Instance->MaxRetry = MTFTP4_DEFAULT_RETRY;
+ }
+
+ if (Instance->Timeout == 0) {
+ Instance->Timeout = MTFTP4_DEFAULT_TIMEOUT;
+ }
+
+ //
+ // Config the unicast UDP child to send initial request
+ //
+ Status = Mtftp4ConfigUnicastPort (Instance->UnicastPort, Instance);
+ if (EFI_ERROR (Status)) {
+ TokenStatus = EFI_DEVICE_ERROR;
+ goto ON_ERROR;
+ }
+
+ //
+ // Set initial status.
+ //
+ Token->Status = EFI_NOT_READY;
+
+ //
+ // Build and send an initial requests
+ //
+ if (Operation == EFI_MTFTP4_OPCODE_WRQ) {
+ Status = Mtftp4WrqStart (Instance, Operation);
+ } else {
+ Status = Mtftp4RrqStart (Instance, Operation);
+ }
+
+ if (EFI_ERROR (Status)) {
+ TokenStatus = EFI_DEVICE_ERROR;
+ goto ON_ERROR;
+ }
+
+ gBS->RestoreTPL(OldTpl);
+
+ if (Token->Event != NULL) {
+ return EFI_SUCCESS;
+ }
+
+ //
+ // Return immediately for asynchronous operation or poll the
+ // instance for synchronous operation.
+ //
+ while (Token->Status == EFI_NOT_READY) {
+ This->Poll (This);
+ }
+
+ return Token->Status;
+
+ON_ERROR:
+ Mtftp4CleanOperation (Instance, TokenStatus);
+ gBS->RestoreTPL (OldTpl);
+
+ return Status;
+}
+
+
+/**
+ Reads the current operational settings.
+
+ The GetModeData()function reads the current operational settings of this
+ EFI MTFTPv4 Protocol driver instance.
+
+ @param This Pointer to the EFI_MTFTP4_PROTOCOL instance.
+ @param ModeData Pointer to storage for the EFI MTFTPv4 Protocol
+ driver mode data.
+
+ @retval EFI_SUCCESS The configuration data was successfully returned.
+ @retval EFI_OUT_OF_RESOURCES The required mode data could not be allocated.
+ @retval EFI_INVALID_PARAMETER This is NULL or ModeData is NULL.
+
+**/
+EFI_STATUS
+EFIAPI
+EfiMtftp4GetModeData (
+ IN EFI_MTFTP4_PROTOCOL *This,
+ OUT EFI_MTFTP4_MODE_DATA *ModeData
+ )
+{
+ MTFTP4_PROTOCOL *Instance;
+ EFI_TPL OldTpl;
+
+ if ((This == NULL) || (ModeData == NULL)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
+
+ Instance = MTFTP4_PROTOCOL_FROM_THIS (This);
+ CopyMem(&ModeData->ConfigData, &Instance->Config, sizeof (Instance->Config));
+ ModeData->SupportedOptionCount = MTFTP4_SUPPORTED_OPTIONS;
+ ModeData->SupportedOptoins = (UINT8 **) mMtftp4SupportedOptions;
+ ModeData->UnsupportedOptionCount = 0;
+ ModeData->UnsupportedOptoins = NULL;
+
+ gBS->RestoreTPL (OldTpl);
+
+ return EFI_SUCCESS;
+}
+
+
+
+/**
+ Initializes, changes, or resets the default operational setting for this
+ EFI MTFTPv4 Protocol driver instance.
+
+ The Configure() function is used to set and change the configuration data for
+ this EFI MTFTPv4 Protocol driver instance. The configuration data can be reset
+ to startup defaults by calling Configure() with MtftpConfigData set to NULL.
+ Whenever the instance is reset, any pending operation is aborted. By changing
+ the EFI MTFTPv4 Protocol driver instance configuration data, the client can
+ connect to different MTFTPv4 servers. The configuration parameters in
+ MtftpConfigData are used as the default parameters in later MTFTPv4 operations
+ and can be overridden in later operations.
+
+ @param This Pointer to the EFI_MTFTP4_PROTOCOL instance
+ @param ConfigData MtftpConfigDataPointer to the configuration data
+ structure
+
+ @retval EFI_SUCCESS The EFI MTFTPv4 Protocol driver was configured
+ successfully.
+ @retval EFI_INVALID_PARAMETER One or more following conditions are TRUE:
+ 1.This is NULL.
+ 2.MtftpConfigData.UseDefaultSetting is FALSE and
+ MtftpConfigData.StationIp is not a valid IPv4
+ unicast address.
+ 3.MtftpConfigData.UseDefaultSetting is FALSE and
+ MtftpConfigData.SubnetMask is invalid.
+ 4.MtftpConfigData.ServerIp is not a valid IPv4
+ unicast address.
+ 5.MtftpConfigData.UseDefaultSetting is FALSE and
+ MtftpConfigData.GatewayIp is not a valid IPv4
+ unicast address or is not in the same subnet
+ with station address.
+ @retval EFI_ACCESS_DENIED The EFI configuration could not be changed at this
+ time because there is one MTFTP background operation
+ in progress.
+ @retval EFI_NO_MAPPING When using a default address, configuration
+ (DHCP, BOOTP, RARP, etc.) has not finished yet.
+ @retval EFI_UNSUPPORTED A configuration protocol (DHCP, BOOTP, RARP, etc.)
+ could not be located when clients choose to use
+ the default address settings.
+ @retval EFI_OUT_OF_RESOURCES The EFI MTFTPv4 Protocol driver instance data could
+ not be allocated.
+ @retval EFI_DEVICE_ERROR An unexpected system or network error occurred.
+ The EFI MTFTPv4 Protocol driver instance is not
+ configured.
+
+**/
+EFI_STATUS
+EFIAPI
+EfiMtftp4Configure (
+ IN EFI_MTFTP4_PROTOCOL *This,
+ IN EFI_MTFTP4_CONFIG_DATA *ConfigData
+ )
+{
+ MTFTP4_PROTOCOL *Instance;
+ EFI_TPL OldTpl;
+ IP4_ADDR Ip;
+ IP4_ADDR Netmask;
+ IP4_ADDR Gateway;
+ IP4_ADDR ServerIp;
+
+ if (This == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Instance = MTFTP4_PROTOCOL_FROM_THIS (This);
+
+ if (ConfigData == NULL) {
+ //
+ // Reset the operation if ConfigData is NULL
+ //
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
+
+ Mtftp4CleanOperation (Instance, EFI_ABORTED);
+ ZeroMem (&Instance->Config, sizeof (EFI_MTFTP4_CONFIG_DATA));
+ Instance->State = MTFTP4_STATE_UNCONFIGED;
+
+ gBS->RestoreTPL (OldTpl);
+
+ } else {
+ //
+ // Configure the parameters for new operation.
+ //
+ CopyMem (&Ip, &ConfigData->StationIp, sizeof (IP4_ADDR));
+ CopyMem (&Netmask, &ConfigData->SubnetMask, sizeof (IP4_ADDR));
+ CopyMem (&Gateway, &ConfigData->GatewayIp, sizeof (IP4_ADDR));
+ CopyMem (&ServerIp, &ConfigData->ServerIp, sizeof (IP4_ADDR));
+
+ Ip = NTOHL (Ip);
+ Netmask = NTOHL (Netmask);
+ Gateway = NTOHL (Gateway);
+ ServerIp = NTOHL (ServerIp);
+
+ if (ServerIp == 0 || IP4_IS_LOCAL_BROADCAST (ServerIp)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (!ConfigData->UseDefaultSetting &&
+ ((!IP4_IS_VALID_NETMASK (Netmask) || (Netmask != 0 && !NetIp4IsUnicast (Ip, Netmask))))) {
+
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if ((Gateway != 0) &&
+ ((Netmask != 0xFFFFFFFF && !IP4_NET_EQUAL (Gateway, Ip, Netmask)) || (Netmask != 0 && !NetIp4IsUnicast (Gateway, Netmask)))) {
+
+ return EFI_INVALID_PARAMETER;
+ }
+
+ OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
+
+ if ((Instance->State == MTFTP4_STATE_CONFIGED) && (Instance->Operation != 0)) {
+ gBS->RestoreTPL (OldTpl);
+ return EFI_ACCESS_DENIED;
+ }
+
+ CopyMem(&Instance->Config, ConfigData, sizeof (*ConfigData));;
+ Instance->State = MTFTP4_STATE_CONFIGED;
+
+ gBS->RestoreTPL (OldTpl);
+ }
+
+ return EFI_SUCCESS;
+}
+
+
+
+/**
+ Parses the options in an MTFTPv4 OACK packet.
+
+ The ParseOptions() function parses the option fields in an MTFTPv4 OACK packet
+ and returns the number of options that were found and optionally a list of
+ pointers to the options in the packet.
+ If one or more of the option fields are not valid, then EFI_PROTOCOL_ERROR is
+ returned and *OptionCount and *OptionList stop at the last valid option.
+ The OptionList is allocated by this function, and caller should free it when used.
+
+ @param This Pointer to the EFI_MTFTP4_PROTOCOL instance.
+ @param PacketLen Length of the OACK packet to be parsed.
+ @param Packet Pointer to the OACK packet to be parsed.
+ @param OptionCount Pointer to the number of options in following OptionList.
+ @param OptionList Pointer to EFI_MTFTP4_OPTION storage. Call the
+ EFI Boot Service FreePool() to release theOptionList
+ if the options in this OptionList are not needed
+ any more
+
+ @retval EFI_SUCCESS The OACK packet was valid and the OptionCount and
+ OptionList parameters have been updated.
+ @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE:
+ 1.PacketLen is 0.
+ 2.Packet is NULL or Packet is not a valid MTFTPv4 packet.
+ 3.OptionCount is NULL.
+ @retval EFI_NOT_FOUND No options were found in the OACK packet.
+ @retval EFI_OUT_OF_RESOURCES Storage for the OptionList array cannot be allocated.
+ @retval EFI_PROTOCOL_ERROR One or more of the option fields is invalid.
+
+**/
+EFI_STATUS
+EFIAPI
+EfiMtftp4ParseOptions (
+ IN EFI_MTFTP4_PROTOCOL *This,
+ IN UINT32 PacketLen,
+ IN EFI_MTFTP4_PACKET *Packet,
+ OUT UINT32 *OptionCount,
+ OUT EFI_MTFTP4_OPTION **OptionList OPTIONAL
+ )
+{
+ EFI_STATUS Status;
+
+ if ((This == NULL) || (PacketLen < MTFTP4_OPCODE_LEN) ||
+ (Packet == NULL) || (OptionCount == NULL)) {
+
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Status = Mtftp4ExtractOptions (Packet, PacketLen, OptionCount, OptionList);
+
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ if (*OptionCount == 0) {
+ return EFI_NOT_FOUND;
+ }
+
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Downloads a file from an MTFTPv4 server.
+
+ The ReadFile() function is used to initialize and start an MTFTPv4 download
+ process and optionally wait for completion. When the download operation completes,
+ whether successfully or not, the Token.Status field is updated by the EFI MTFTPv4
+ Protocol driver and then Token.Event is signaled (if it is not NULL).
+ Data can be downloaded from the MTFTPv4 server into either of the following locations:
+ 1.A fixed buffer that is pointed to by Token.Buffer
+ 2.A download service function that is pointed to by Token.CheckPacket
+ If both Token.Buffer and Token.CheckPacket are used, then Token.CheckPacket
+ will be called first. If the call is successful, the packet will be stored in
+ Token.Buffer.
+
+ @param This Pointer to the EFI_MTFTP4_PROTOCOL instance
+ @param Token Pointer to the token structure to provide the
+ parameters that are used in this operation.
+
+ @retval EFI_SUCCESS The data file has been transferred successfully.
+ @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated.
+ @retval EFI_BUFFER_TOO_SMALL BufferSize is not large enough to hold the downloaded
+ data in downloading process.
+ @retval EFI_ABORTED Current operation is aborted by user.
+ @retval EFI_ICMP_ERROR An ICMP ERROR packet was received.
+ @retval EFI_TIMEOUT No responses were received from the MTFTPv4 server.
+ @retval EFI_TFTP_ERROR An MTFTPv4 ERROR packet was received.
+ @retval EFI_DEVICE_ERROR An unexpected network error or system error occurred.
+ @retval EFI_NO_MEDIA There was a media error.
+
+**/
+EFI_STATUS
+EFIAPI
+EfiMtftp4ReadFile (
+ IN EFI_MTFTP4_PROTOCOL *This,
+ IN EFI_MTFTP4_TOKEN *Token
+ )
+{
+ return Mtftp4Start (This, Token, EFI_MTFTP4_OPCODE_RRQ);
+}
+
+
+/**
+ Sends a data file to an MTFTPv4 server. May be unsupported in some EFI implementations
+
+ The WriteFile() function is used to initialize an uploading operation with the
+ given option list and optionally wait for completion. If one or more of the
+ options is not supported by the server, the unsupported options are ignored and
+ a standard TFTP process starts instead. When the upload process completes,
+ whether successfully or not, Token.Event is signaled, and the EFI MTFTPv4 Protocol
+ driver updates Token.Status.
+ The caller can supply the data to be uploaded in the following two modes:
+ 1.Through the user-provided buffer
+ 2.Through a callback function
+ With the user-provided buffer, the Token.BufferSize field indicates the length
+ of the buffer, and the driver will upload the data in the buffer. With an
+ EFI_MTFTP4_PACKET_NEEDED callback function, the driver will call this callback
+ function to get more data from the user to upload. See the definition of
+ EFI_MTFTP4_PACKET_NEEDED for more information. These two modes cannot be used at
+ the same time. The callback function will be ignored if the user provides the buffer.
+
+ @param This Pointer to the EFI_MTFTP4_PROTOCOL instance.
+ @param Token Pointer to the token structure to provide the
+ parameters that are used in this function
+
+ @retval EFI_SUCCESS The upload session has started.
+ @retval EFI_UNSUPPORTED The operation is not supported by this implementation.
+ @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE:
+ 1. This is NULL.
+ 2. Token is NULL.
+ 3. Token.Filename is NULL.
+ 4. Token.OptionCount is not zero and
+ Token.OptionList is NULL.
+ 5. One or more options in Token.OptionList have wrong
+ format.
+ 6. Token.Buffer and Token.PacketNeeded are both
+ NULL.
+ 7. One or more IPv4 addresses in Token.OverrideData
+ are not valid unicast IPv4 addresses if
+ Token.OverrideData is not NULL.
+ @retval EFI_UNSUPPORTED One or more options in the Token.OptionList are in the
+ unsupported list of structure EFI_MTFTP4_MODE_DATA.
+ @retval EFI_NOT_STARTED The EFI MTFTPv4 Protocol driver has not been started.
+ @retval EFI_NO_MAPPING When using a default address, configuration (DHCP,
+ BOOTP, RARP, etc.) is not finished yet.
+ @retval EFI_ALREADY_STARTED This Token is already being used in another MTFTPv4
+ session.
+ @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated.
+ @retval EFI_ACCESS_DENIED The previous operation has not completed yet.
+ @retval EFI_DEVICE_ERROR An unexpected network error or system error occurred.
+
+**/
+EFI_STATUS
+EFIAPI
+EfiMtftp4WriteFile (
+ IN EFI_MTFTP4_PROTOCOL *This,
+ IN EFI_MTFTP4_TOKEN *Token
+ )
+{
+ return Mtftp4Start (This, Token, EFI_MTFTP4_OPCODE_WRQ);
+}
+
+
+/**
+ Downloads a data file "directory" from an MTFTPv4 server.
+ May be unsupported in some EFI implementations
+
+ The ReadDirectory() function is used to return a list of files on the MTFTPv4
+ server that are logically (or operationally) related to Token.Filename. The
+ directory request packet that is sent to the server is built with the option
+ list that was provided by caller, if present.
+ The file information that the server returns is put into either of the following
+ locations:
+ 1.A fixed buffer that is pointed to by Token.Buffer
+ 2.A download service function that is pointed to by Token.CheckPacket
+ If both Token.Buffer and Token.CheckPacket are used, then Token.CheckPacket will
+ be called first. If the call is successful, the packet will be stored in Token.Buffer.
+ The returned directory listing in the Token.Buffer or EFI_MTFTP4_PACKET consists
+ of a list of two or three variable-length ASCII strings, each terminated by a
+ null character, for each file in the directory. If the multicast option is involved,
+ the first field of each directory entry is the static multicast IP address and
+ UDP port number that is associated with the file name. The format of the field
+ is ip:ip:ip:ip:port. If the multicast option is not involved, this field and its
+ terminating null character are not present.
+ The next field of each directory entry is the file name and the last field is
+ the file information string. The information string contains the file size and
+ the create/modify timestamp. The format of the information string is filesize
+ yyyy-mm-dd hh:mm:ss:ffff. The timestamp is Coordinated Universal Time
+ (UTC; also known as Greenwich Mean Time [GMT]).
+ The only difference between ReadFile and ReadDirectory is the opcode used.
+
+ @param This Pointer to the EFI_MTFTP4_PROTOCOL instance
+ @param Token Pointer to the token structure to provide the
+ parameters that are used in this function
+
+ @retval EFI_SUCCESS The MTFTPv4 related file "directory" has been downloaded.
+ @retval EFI_UNSUPPORTED The operation is not supported by this implementation.
+ @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE:
+ 1. This is NULL.
+ 2. Token is NULL.
+ 3. Token.Filename is NULL.
+ 4. Token.OptionCount is not zero and
+ Token.OptionList is NULL.
+ 5. One or more options in Token.OptionList have wrong
+ format.
+ 6. Token.Buffer and Token.PacketNeeded are both
+ NULL.
+ 7. One or more IPv4 addresses in Token.OverrideData
+ are not valid unicast IPv4 addresses if
+ Token.OverrideData is not NULL.
+ @retval EFI_UNSUPPORTED One or more options in the Token.OptionList are in the
+ unsupported list of structure EFI_MTFTP4_MODE_DATA.
+ @retval EFI_NOT_STARTED The EFI MTFTPv4 Protocol driver has not been started.
+ @retval EFI_NO_MAPPING When using a default address, configuration (DHCP,
+ BOOTP, RARP, etc.) is not finished yet.
+ @retval EFI_ALREADY_STARTED This Token is already being used in another MTFTPv4
+ session.
+ @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated.
+ @retval EFI_ACCESS_DENIED The previous operation has not completed yet.
+ @retval EFI_DEVICE_ERROR An unexpected network error or system error occurred.
+
+**/
+EFI_STATUS
+EFIAPI
+EfiMtftp4ReadDirectory (
+ IN EFI_MTFTP4_PROTOCOL *This,
+ IN EFI_MTFTP4_TOKEN *Token
+ )
+{
+ return Mtftp4Start (This, Token, EFI_MTFTP4_OPCODE_DIR);
+}
+
+
+/**
+ Gets information about a file from an MTFTPv4 server.
+
+ The GetInfo() function assembles an MTFTPv4 request packet with options;
+ sends it to the MTFTPv4 server; and may return an MTFTPv4 OACK, MTFTPv4 ERROR,
+ or ICMP ERROR packet. Retries occur only if no response packets are received
+ from the MTFTPv4 server before the timeout expires.
+ It is implemented with EfiMtftp4ReadFile: build a token, then pass it to
+ EfiMtftp4ReadFile. In its check packet callback abort the operations.
+
+ @param This Pointer to the EFI_MTFTP4_PROTOCOL instance
+ @param OverrideData Data that is used to override the existing
+ parameters. If NULL, the default parameters that
+ were set in the EFI_MTFTP4_PROTOCOL.Configure()
+ function are used
+ @param Filename Pointer to null-terminated ASCII file name string
+ @param ModeStr Pointer to null-terminated ASCII mode string. If NULL, "octet"
+ will be used
+ @param OptionCount Number of option/value string pairs in OptionList
+ @param OptionList Pointer to array of option/value string pairs.
+ Ignored if OptionCount is zero
+ @param PacketLength The number of bytes in the returned packet
+ @param Packet PacketThe pointer to the received packet. This
+ buffer must be freed by the caller.
+
+ @retval EFI_SUCCESS An MTFTPv4 OACK packet was received and is in
+ the Buffer.
+ @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE:
+ 1.This is NULL.
+ 2.Filename is NULL.
+ 3.OptionCount is not zero and OptionList is NULL.
+ 4.One or more options in OptionList have wrong format.
+ 5.PacketLength is NULL.
+ 6.One or more IPv4 addresses in OverrideData are
+ not valid unicast IPv4 addresses if OverrideData
+ is not NULL.
+ @retval EFI_UNSUPPORTED One or more options in the OptionList are in the
+ unsupported list of structure EFI_MTFTP4_MODE_DATA
+ @retval EFI_NOT_STARTED The EFI MTFTPv4 Protocol driver has not been started.
+ @retval EFI_NO_MAPPING When using a default address, configuration (DHCP,
+ BOOTP, RARP, etc.) has not finished yet.
+ @retval EFI_ACCESS_DENIED The previous operation has not completed yet.
+ @retval EFI_OUT_OF_RESOURCES Required system resources could not be allocated.
+ @retval EFI_TFTP_ERROR An MTFTPv4 ERROR packet was received and is in
+ the Buffer.
+ @retval EFI_ICMP_ERROR An ICMP ERROR packet was received and the Packet
+ is set to NULL.
+ @retval EFI_PROTOCOL_ERROR An unexpected MTFTPv4 packet was received and is
+ in the Buffer.
+ @retval EFI_TIMEOUT No responses were received from the MTFTPv4 server.
+ @retval EFI_DEVICE_ERROR An unexpected network error or system error occurred.
+ @retval EFI_NO_MEDIA There was a media error.
+
+**/
+EFI_STATUS
+EFIAPI
+EfiMtftp4GetInfo (
+ IN EFI_MTFTP4_PROTOCOL *This,
+ IN EFI_MTFTP4_OVERRIDE_DATA *OverrideData OPTIONAL,
+ IN UINT8 *Filename,
+ IN UINT8 *ModeStr OPTIONAL,
+ IN UINT8 OptionCount,
+ IN EFI_MTFTP4_OPTION *OptionList OPTIONAL,
+ OUT UINT32 *PacketLength,
+ OUT EFI_MTFTP4_PACKET **Packet OPTIONAL
+ )
+{
+ EFI_MTFTP4_TOKEN Token;
+ MTFTP4_GETINFO_STATE State;
+ EFI_STATUS Status;
+
+ if ((This == NULL) || (Filename == NULL) || (PacketLength == NULL) ||
+ ((OptionCount != 0) && (OptionList == NULL))) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (Packet != NULL) {
+ *Packet = NULL;
+ }
+
+ *PacketLength = 0;
+ State.Packet = Packet;
+ State.PacketLen = PacketLength;
+ State.Status = EFI_SUCCESS;
+
+ //
+ // Fill in the Token to issue an synchronous ReadFile operation
+ //
+ Token.Status = EFI_SUCCESS;
+ Token.Event = NULL;
+ Token.OverrideData = OverrideData;
+ Token.Filename = Filename;
+ Token.ModeStr = ModeStr;
+ Token.OptionCount = OptionCount;
+ Token.OptionList = OptionList;
+ Token.BufferSize = 0;
+ Token.Buffer = NULL;
+ Token.Context = &State;
+ Token.CheckPacket = Mtftp4GetInfoCheckPacket;
+ Token.TimeoutCallback = NULL;
+ Token.PacketNeeded = NULL;
+
+ Status = EfiMtftp4ReadFile (This, &Token);
+
+ if (EFI_ABORTED == Status) {
+ return State.Status;
+ }
+
+ return Status;
+}
+
+/**
+ Polls for incoming data packets and processes outgoing data packets.
+
+ The Poll() function can be used by network drivers and applications to increase
+ the rate that data packets are moved between the communications device and the
+ transmit and receive queues.
+ In some systems, the periodic timer event in the managed network driver may not
+ poll the underlying communications device fast enough to transmit and/or receive
+ all data packets without missing incoming packets or dropping outgoing packets.
+ Drivers and applications that are experiencing packet loss should try calling
+ the Poll() function more often.
+
+ @param This Pointer to the EFI_MTFTP4_PROTOCOL instance
+
+ @retval EFI_SUCCESS Incoming or outgoing data was processed.
+ @retval EFI_NOT_STARTED This EFI MTFTPv4 Protocol instance has not been started.
+ @retval EFI_NO_MAPPING When using a default address, configuration (DHCP,
+ BOOTP, RARP, etc.) is not finished yet.
+ @retval EFI_INVALID_PARAMETER This is NULL.
+ @retval EFI_DEVICE_ERROR An unexpected system or network error occurred.
+ @retval EFI_TIMEOUT Data was dropped out of the transmit and/or receive
+ queue. Consider increasing the polling rate.
+
+**/
+EFI_STATUS
+EFIAPI
+EfiMtftp4Poll (
+ IN EFI_MTFTP4_PROTOCOL *This
+ )
+{
+ MTFTP4_PROTOCOL *Instance;
+ EFI_UDP4_PROTOCOL *Udp;
+ EFI_STATUS Status;
+
+ if (This == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Instance = MTFTP4_PROTOCOL_FROM_THIS (This);
+
+ if (Instance->State == MTFTP4_STATE_UNCONFIGED) {
+ return EFI_NOT_STARTED;
+ } else if (Instance->State == MTFTP4_STATE_DESTROY) {
+ return EFI_DEVICE_ERROR;
+ }
+
+ Udp = Instance->UnicastPort->Protocol.Udp4;
+ Status = Udp->Poll (Udp);
+ Mtftp4OnTimerTick (NULL, Instance->Service);
+ return Status;
+}
+
+EFI_MTFTP4_PROTOCOL gMtftp4ProtocolTemplate = {
+ EfiMtftp4GetModeData,
+ EfiMtftp4Configure,
+ EfiMtftp4GetInfo,
+ EfiMtftp4ParseOptions,
+ EfiMtftp4ReadFile,
+ EfiMtftp4WriteFile,
+ EfiMtftp4ReadDirectory,
+ EfiMtftp4Poll
+};
diff --git a/src/VBox/Devices/EFI/Firmware/NetworkPkg/Mtftp4Dxe/Mtftp4Impl.h b/src/VBox/Devices/EFI/Firmware/NetworkPkg/Mtftp4Dxe/Mtftp4Impl.h
new file mode 100644
index 00000000..f97922ca
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/NetworkPkg/Mtftp4Dxe/Mtftp4Impl.h
@@ -0,0 +1,226 @@
+/** @file
+
+ Mtftp4 Implementation.
+
+ Mtftp4 Implementation, it supports the following RFCs:
+ RFC1350 - THE TFTP PROTOCOL (REVISION 2)
+ RFC2090 - TFTP Multicast Option
+ RFC2347 - TFTP Option Extension
+ RFC2348 - TFTP Blocksize Option
+ RFC2349 - TFTP Timeout Interval and Transfer Size Options
+ RFC7440 - TFTP Windowsize Option
+
+Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.<BR>
+SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+
+#ifndef __EFI_MTFTP4_IMPL_H__
+#define __EFI_MTFTP4_IMPL_H__
+
+#include <Uefi.h>
+
+#include <Protocol/Udp4.h>
+#include <Protocol/Mtftp4.h>
+
+#include <Library/DebugLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Library/UdpIoLib.h>
+#include <Library/PrintLib.h>
+
+extern EFI_MTFTP4_PROTOCOL gMtftp4ProtocolTemplate;
+
+typedef struct _MTFTP4_SERVICE MTFTP4_SERVICE;
+typedef struct _MTFTP4_PROTOCOL MTFTP4_PROTOCOL;
+
+#include "Mtftp4Driver.h"
+#include "Mtftp4Option.h"
+#include "Mtftp4Support.h"
+
+
+///
+/// Some constant value of Mtftp service.
+///
+#define MTFTP4_SERVICE_SIGNATURE SIGNATURE_32 ('T', 'F', 'T', 'P')
+#define MTFTP4_PROTOCOL_SIGNATURE SIGNATURE_32 ('t', 'f', 't', 'p')
+
+#define MTFTP4_DEFAULT_SERVER_PORT 69
+#define MTFTP4_DEFAULT_TIMEOUT 3
+#define MTFTP4_DEFAULT_RETRY 5
+#define MTFTP4_DEFAULT_BLKSIZE 512
+#define MTFTP4_DEFAULT_WINDOWSIZE 1
+#define MTFTP4_TIME_TO_GETMAP 5
+
+#define MTFTP4_STATE_UNCONFIGED 0
+#define MTFTP4_STATE_CONFIGED 1
+#define MTFTP4_STATE_DESTROY 2
+
+///
+/// Mtftp service block
+///
+struct _MTFTP4_SERVICE {
+ UINT32 Signature;
+ EFI_SERVICE_BINDING_PROTOCOL ServiceBinding;
+
+ UINT16 ChildrenNum;
+ LIST_ENTRY Children;
+
+ EFI_EVENT Timer; ///< Ticking timer for all the MTFTP clients to handle the packet timeout case.
+ EFI_EVENT TimerNotifyLevel; ///< Ticking timer for all the MTFTP clients to calculate the packet live time.
+ EFI_EVENT TimerToGetMap;
+
+ EFI_HANDLE Controller;
+ EFI_HANDLE Image;
+
+ //
+ // This UDP child is used to keep the connection between the UDP
+ // and MTFTP, so MTFTP will be notified when UDP is uninstalled.
+ //
+ UDP_IO *ConnectUdp;
+};
+
+
+typedef struct {
+ EFI_MTFTP4_PACKET **Packet;
+ UINT32 *PacketLen;
+ EFI_STATUS Status;
+} MTFTP4_GETINFO_STATE;
+
+struct _MTFTP4_PROTOCOL {
+ UINT32 Signature;
+ LIST_ENTRY Link;
+ EFI_MTFTP4_PROTOCOL Mtftp4;
+
+ INTN State;
+ BOOLEAN InDestroy;
+
+ MTFTP4_SERVICE *Service;
+ EFI_HANDLE Handle;
+
+ EFI_MTFTP4_CONFIG_DATA Config;
+
+ //
+ // Operation parameters: token and requested options.
+ //
+ EFI_MTFTP4_TOKEN *Token;
+ MTFTP4_OPTION RequestOption;
+ UINT16 Operation;
+
+ //
+ // Blocks is a list of MTFTP4_BLOCK_RANGE which contains
+ // holes in the file
+ //
+ UINT16 BlkSize;
+ UINT16 LastBlock;
+ LIST_ENTRY Blocks;
+
+ UINT16 WindowSize;
+
+ //
+ // Record the total received and saved block number.
+ //
+ UINT64 TotalBlock;
+
+ //
+ // Record the acked block number.
+ //
+ UINT64 AckedBlock;
+
+ //
+ // The server's communication end point: IP and two ports. one for
+ // initial request, one for its selected port.
+ //
+ IP4_ADDR ServerIp;
+ UINT16 ListeningPort;
+ UINT16 ConnectedPort;
+ IP4_ADDR Gateway;
+ UDP_IO *UnicastPort;
+
+ //
+ // Timeout and retransmit status
+ //
+ NET_BUF *LastPacket;
+ UINT32 PacketToLive;
+ BOOLEAN HasTimeout;
+ UINT32 CurRetry;
+ UINT32 MaxRetry;
+ UINT32 Timeout;
+
+ //
+ // Parameter used by RRQ's multicast download.
+ //
+ IP4_ADDR McastIp;
+ UINT16 McastPort;
+ BOOLEAN Master;
+ UDP_IO *McastUdpPort;
+};
+
+typedef struct {
+ EFI_SERVICE_BINDING_PROTOCOL *ServiceBinding;
+ UINTN NumberOfChildren;
+ EFI_HANDLE *ChildHandleBuffer;
+} MTFTP4_DESTROY_CHILD_IN_HANDLE_BUF_CONTEXT;
+
+/**
+ Clean up the MTFTP session to get ready for new operation.
+
+ @param Instance The MTFTP session to clean up
+ @param Result The result to return to the caller who initiated
+ the operation.
+**/
+VOID
+Mtftp4CleanOperation (
+ IN OUT MTFTP4_PROTOCOL *Instance,
+ IN EFI_STATUS Result
+ );
+
+/**
+ Start the MTFTP session for upload.
+
+ It will first init some states, then send the WRQ request packet,
+ and start receiving the packet.
+
+ @param Instance The MTFTP session
+ @param Operation Redundant parameter, which is always
+ EFI_MTFTP4_OPCODE_WRQ here.
+
+ @retval EFI_SUCCESS The upload process has been started.
+ @retval Others Failed to start the upload.
+
+**/
+EFI_STATUS
+Mtftp4WrqStart (
+ IN MTFTP4_PROTOCOL *Instance,
+ IN UINT16 Operation
+ );
+
+/**
+ Start the MTFTP session to download.
+
+ It will first initialize some of the internal states then build and send a RRQ
+ request packet, at last, it will start receive for the downloading.
+
+ @param Instance The Mtftp session
+ @param Operation The MTFTP opcode, it may be a EFI_MTFTP4_OPCODE_RRQ
+ or EFI_MTFTP4_OPCODE_DIR.
+
+ @retval EFI_SUCCESS The mtftp download session is started.
+ @retval Others Failed to start downloading.
+
+**/
+EFI_STATUS
+Mtftp4RrqStart (
+ IN MTFTP4_PROTOCOL *Instance,
+ IN UINT16 Operation
+ );
+
+#define MTFTP4_SERVICE_FROM_THIS(a) \
+ CR (a, MTFTP4_SERVICE, ServiceBinding, MTFTP4_SERVICE_SIGNATURE)
+
+#define MTFTP4_PROTOCOL_FROM_THIS(a) \
+ CR (a, MTFTP4_PROTOCOL, Mtftp4, MTFTP4_PROTOCOL_SIGNATURE)
+
+#endif
diff --git a/src/VBox/Devices/EFI/Firmware/NetworkPkg/Mtftp4Dxe/Mtftp4Option.c b/src/VBox/Devices/EFI/Firmware/NetworkPkg/Mtftp4Dxe/Mtftp4Option.c
new file mode 100644
index 00000000..04e4f393
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/NetworkPkg/Mtftp4Dxe/Mtftp4Option.c
@@ -0,0 +1,549 @@
+/** @file
+ Routines to process MTFTP4 options.
+
+Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.<BR>
+SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include "Mtftp4Impl.h"
+
+CHAR8 *mMtftp4SupportedOptions[MTFTP4_SUPPORTED_OPTIONS] = {
+ "blksize",
+ "windowsize",
+ "timeout",
+ "tsize",
+ "multicast"
+};
+
+
+/**
+ Check whether two ascii strings are equal, ignore the case.
+
+ @param Str1 The first ascii string
+ @param Str2 The second ascii string
+
+ @retval TRUE Two strings are equal when case is ignored.
+ @retval FALSE Two strings are not equal.
+
+**/
+BOOLEAN
+NetStringEqualNoCase (
+ IN UINT8 *Str1,
+ IN UINT8 *Str2
+ )
+{
+ UINT8 Ch1;
+ UINT8 Ch2;
+
+ ASSERT ((Str1 != NULL) && (Str2 != NULL));
+
+ for (; (*Str1 != '\0') && (*Str2 != '\0'); Str1++, Str2++) {
+ Ch1 = *Str1;
+ Ch2 = *Str2;
+
+ //
+ // Convert them to lower case then compare two
+ //
+ if (('A' <= Ch1) && (Ch1 <= 'Z')) {
+ Ch1 += 'a' - 'A';
+ }
+
+ if (('A' <= Ch2) && (Ch2 <= 'Z')) {
+ Ch2 += 'a' - 'A';
+ }
+
+ if (Ch1 != Ch2) {
+ return FALSE;
+ }
+ }
+
+ return (BOOLEAN) (*Str1 == *Str2);
+}
+
+
+/**
+ Convert a string to a UINT32 number.
+
+ @param Str The string to convert from
+
+ @return The number get from the string
+
+**/
+UINT32
+NetStringToU32 (
+ IN UINT8 *Str
+ )
+{
+ UINT32 Num;
+
+ ASSERT (Str != NULL);
+
+ Num = 0;
+
+ for (; NET_IS_DIGIT (*Str); Str++) {
+ Num = Num * 10 + (*Str - '0');
+ }
+
+ return Num;
+}
+
+
+/**
+ Convert a string of the format "192.168.0.1" to an IP address.
+
+ @param Str The string representation of IP
+ @param Ip The variable to get IP.
+
+ @retval EFI_INVALID_PARAMETER The IP string is invalid.
+ @retval EFI_SUCCESS The IP is parsed into the Ip
+
+**/
+EFI_STATUS
+NetStringToIp (
+ IN UINT8 *Str,
+ OUT IP4_ADDR *Ip
+ )
+{
+ UINT32 Byte;
+ UINT32 Addr;
+ UINTN Index;
+
+ *Ip = 0;
+ Addr = 0;
+
+ for (Index = 0; Index < 4; Index++) {
+ if (!NET_IS_DIGIT (*Str)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Byte = NetStringToU32 (Str);
+
+ if (Byte > 255) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Addr = (Addr << 8) | Byte;
+
+ //
+ // Skip all the digitals and check whether the separator is the dot
+ //
+ while (NET_IS_DIGIT (*Str)) {
+ Str++;
+ }
+
+ if ((Index < 3) && (*Str != '.')) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Str++;
+ }
+
+ *Ip = Addr;
+
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Go through the packet to fill the Options array with the start
+ addresses of each MTFTP option name/value pair.
+
+ @param Packet The packet to check
+ @param PacketLen The packet's length
+ @param Count The size of the Options on input. The actual
+ options on output
+ @param Options The option array to fill in
+
+ @retval EFI_INVALID_PARAMETER The packet is malformatted
+ @retval EFI_BUFFER_TOO_SMALL The Options array is too small
+ @retval EFI_SUCCESS The packet has been parsed into the Options array.
+
+**/
+EFI_STATUS
+Mtftp4FillOptions (
+ IN EFI_MTFTP4_PACKET *Packet,
+ IN UINT32 PacketLen,
+ IN OUT UINT32 *Count,
+ OUT EFI_MTFTP4_OPTION *Options OPTIONAL
+ )
+{
+ UINT8 *Cur;
+ UINT8 *Last;
+ UINT8 Num;
+ UINT8 *Name;
+ UINT8 *Value;
+
+ Num = 0;
+ Cur = (UINT8 *) Packet + MTFTP4_OPCODE_LEN;
+ Last = (UINT8 *) Packet + PacketLen - 1;
+
+ //
+ // process option name and value pairs. The last byte is always zero
+ //
+ while (Cur < Last) {
+ Name = Cur;
+
+ while (*Cur != 0) {
+ Cur++;
+ }
+
+ if (Cur == Last) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Value = ++Cur;
+
+ while (*Cur != 0) {
+ Cur++;
+ }
+
+ Num++;
+
+ if ((Options != NULL) && (Num <= *Count)) {
+ Options[Num - 1].OptionStr = Name;
+ Options[Num - 1].ValueStr = Value;
+ }
+
+ Cur++;
+ }
+
+ if ((*Count < Num) || (Options == NULL)) {
+ *Count = Num;
+ return EFI_BUFFER_TOO_SMALL;
+ }
+
+ *Count = Num;
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Allocate and fill in a array of Mtftp options from the Packet.
+
+ It first calls Mtftp4FillOption to get the option number, then allocate
+ the array, at last, call Mtftp4FillOption again to save the options.
+
+ @param Packet The packet to parse
+ @param PacketLen The length of the packet
+ @param OptionCount The number of options in the packet
+ @param OptionList The point to get the option array.
+
+ @retval EFI_INVALID_PARAMETER The parametera are invalid or packet isn't a
+ well-formatted OACK packet.
+ @retval EFI_SUCCESS The option array is build
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate memory for the array
+
+**/
+EFI_STATUS
+Mtftp4ExtractOptions (
+ IN EFI_MTFTP4_PACKET *Packet,
+ IN UINT32 PacketLen,
+ OUT UINT32 *OptionCount,
+ OUT EFI_MTFTP4_OPTION **OptionList OPTIONAL
+ )
+{
+ EFI_STATUS Status;
+
+ *OptionCount = 0;
+
+ if (OptionList != NULL) {
+ *OptionList = NULL;
+ }
+
+ if (NTOHS (Packet->OpCode) != EFI_MTFTP4_OPCODE_OACK) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (PacketLen == MTFTP4_OPCODE_LEN) {
+ return EFI_SUCCESS;
+ }
+
+ //
+ // The last byte must be zero to terminate the options
+ //
+ if (*((UINT8 *) Packet + PacketLen - 1) != 0) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ //
+ // Get the number of options
+ //
+ Status = Mtftp4FillOptions (Packet, PacketLen, OptionCount, NULL);
+
+ if ((Status == EFI_SUCCESS) || (Status != EFI_BUFFER_TOO_SMALL)) {
+ return Status;
+ }
+
+ //
+ // Allocate memory for the options, then call Mtftp4FillOptions to
+ // fill it if caller want that.
+ //
+ if (OptionList == NULL) {
+ return EFI_SUCCESS;
+ }
+
+ *OptionList = AllocatePool (*OptionCount * sizeof (EFI_MTFTP4_OPTION));
+
+ if (*OptionList == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ Mtftp4FillOptions (Packet, PacketLen, OptionCount, *OptionList);
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Parse the MTFTP multicast option.
+
+ @param Value The Mtftp multicast value string
+ @param Option The option to save the info into.
+
+ @retval EFI_INVALID_PARAMETER The multicast value string is invalid.
+ @retval EFI_SUCCESS The multicast value is parsed into the Option
+
+**/
+EFI_STATUS
+Mtftp4ExtractMcast (
+ IN UINT8 *Value,
+ IN OUT MTFTP4_OPTION *Option
+ )
+{
+ EFI_STATUS Status;
+ UINT32 Num;
+
+ //
+ // The multicast option is formatted like "204.0.0.1,1857,1"
+ // The server can also omit the ip and port, use ",,1"
+ //
+ if (*Value == ',') {
+ Option->McastIp = 0;
+ } else {
+ Status = NetStringToIp (Value, &Option->McastIp);
+
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ while ((*Value != 0) && (*Value != ',')) {
+ Value++;
+ }
+ }
+
+ if (*Value != ',') {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Value++;
+
+ //
+ // Convert the port setting. the server can send us a port number or
+ // empty string. such as the port in ",,1"
+ //
+ if (*Value == ',') {
+ Option->McastPort = 0;
+ } else {
+ Num = NetStringToU32 (Value);
+
+ if (Num > 65535) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Option->McastPort = (UINT16) Num;
+
+ while (NET_IS_DIGIT (*Value)) {
+ Value++;
+ }
+ }
+
+ if (*Value != ',') {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Value++;
+
+ //
+ // Check the master/slave setting, 1 for master, 0 for slave.
+ //
+ Num = NetStringToU32 (Value);
+
+ if ((Num != 0) && (Num != 1)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Option->Master = (BOOLEAN) (Num == 1);
+
+ while (NET_IS_DIGIT (*Value)) {
+ Value++;
+ }
+
+ if (*Value != '\0') {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Parse the option in Options array to MTFTP4_OPTION which program
+ can access directly.
+
+ @param Options The option array, which contains addresses of each
+ option's name/value string.
+ @param Count The number of options in the Options
+ @param Request Whether this is a request or OACK. The format of
+ multicast is different according to this setting.
+ @param Operation The current performed operation.
+ @param MtftpOption The MTFTP4_OPTION for easy access.
+
+ @retval EFI_INVALID_PARAMETER The option is malformatted
+ @retval EFI_UNSUPPORTED Some option isn't supported
+ @retval EFI_SUCCESS The option are OK and has been parsed.
+
+**/
+EFI_STATUS
+Mtftp4ParseOption (
+ IN EFI_MTFTP4_OPTION *Options,
+ IN UINT32 Count,
+ IN BOOLEAN Request,
+ IN UINT16 Operation,
+ OUT MTFTP4_OPTION *MtftpOption
+ )
+{
+ EFI_STATUS Status;
+ UINT32 Index;
+ UINT32 Value;
+ EFI_MTFTP4_OPTION *This;
+
+ MtftpOption->Exist = 0;
+
+ for (Index = 0; Index < Count; Index++) {
+ This = Options + Index;
+
+ if ((This->OptionStr == NULL) || (This->ValueStr == NULL)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (NetStringEqualNoCase (This->OptionStr, (UINT8 *) "blksize")) {
+ //
+ // block size option, valid value is between [8, 65464]
+ //
+ Value = NetStringToU32 (This->ValueStr);
+
+ if ((Value < 8) || (Value > 65464)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ MtftpOption->BlkSize = (UINT16) Value;
+ MtftpOption->Exist |= MTFTP4_BLKSIZE_EXIST;
+
+ } else if (NetStringEqualNoCase (This->OptionStr, (UINT8 *) "timeout")) {
+ //
+ // timeout option, valid value is between [1, 255]
+ //
+ Value = NetStringToU32 (This->ValueStr);
+
+ if ((Value < 1) || (Value > 255)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ MtftpOption->Timeout = (UINT8) Value;
+
+ } else if (NetStringEqualNoCase (This->OptionStr, (UINT8 *) "tsize")) {
+ //
+ // tsize option, the biggest transfer supported is 4GB with block size option
+ //
+ MtftpOption->Tsize = NetStringToU32 (This->ValueStr);
+ MtftpOption->Exist |= MTFTP4_TSIZE_EXIST;
+
+ } else if (NetStringEqualNoCase (This->OptionStr, (UINT8 *) "multicast")) {
+ //
+ // Multicast option, if it is a request, the value must be a zero
+ // length string, otherwise, it is formatted like "204.0.0.1,1857,1\0"
+ //
+ if (Request) {
+ if (*(This->ValueStr) != '\0') {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ } else {
+ Status = Mtftp4ExtractMcast (This->ValueStr, MtftpOption);
+
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+ }
+
+ MtftpOption->Exist |= MTFTP4_MCAST_EXIST;
+
+ } else if (NetStringEqualNoCase (This->OptionStr, (UINT8 *) "windowsize")) {
+ if (Operation == EFI_MTFTP4_OPCODE_WRQ) {
+ //
+ // Currently, windowsize is not supported in the write operation.
+ //
+ return EFI_UNSUPPORTED;
+ }
+
+ Value = NetStringToU32 (This->ValueStr);
+
+ if (Value < 1) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ MtftpOption->WindowSize = (UINT16) Value;
+ MtftpOption->Exist |= MTFTP4_WINDOWSIZE_EXIST;
+ } else if (Request) {
+ //
+ // Ignore the unsupported option if it is a reply, and return
+ // EFI_UNSUPPORTED if it's a request according to the UEFI spec.
+ //
+ return EFI_UNSUPPORTED;
+ }
+ }
+
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Parse the options in the OACK packet to MTFTP4_OPTION which program
+ can access directly.
+
+ @param Packet The OACK packet to parse
+ @param PacketLen The length of the packet
+ @param Operation The current performed operation.
+ @param MtftpOption The MTFTP_OPTION for easy access.
+
+ @retval EFI_INVALID_PARAMETER The packet option is malformatted
+ @retval EFI_UNSUPPORTED Some option isn't supported
+ @retval EFI_SUCCESS The option are OK and has been parsed.
+
+**/
+EFI_STATUS
+Mtftp4ParseOptionOack (
+ IN EFI_MTFTP4_PACKET *Packet,
+ IN UINT32 PacketLen,
+ IN UINT16 Operation,
+ OUT MTFTP4_OPTION *MtftpOption
+ )
+{
+ EFI_MTFTP4_OPTION *OptionList;
+ EFI_STATUS Status;
+ UINT32 Count;
+
+ MtftpOption->Exist = 0;
+
+ Status = Mtftp4ExtractOptions (Packet, PacketLen, &Count, &OptionList);
+
+ if (EFI_ERROR (Status) || (Count == 0)) {
+ return Status;
+ }
+ ASSERT (OptionList != NULL);
+
+ Status = Mtftp4ParseOption (OptionList, Count, FALSE, Operation, MtftpOption);
+
+ FreePool (OptionList);
+ return Status;
+}
diff --git a/src/VBox/Devices/EFI/Firmware/NetworkPkg/Mtftp4Dxe/Mtftp4Option.h b/src/VBox/Devices/EFI/Firmware/NetworkPkg/Mtftp4Dxe/Mtftp4Option.h
new file mode 100644
index 00000000..9d0530da
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/NetworkPkg/Mtftp4Dxe/Mtftp4Option.h
@@ -0,0 +1,111 @@
+/** @file
+ Routines to process MTFTP4 options.
+
+Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.<BR>
+SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+
+#ifndef __EFI_MTFTP4_OPTION_H__
+#define __EFI_MTFTP4_OPTION_H__
+
+#define MTFTP4_SUPPORTED_OPTIONS 5
+#define MTFTP4_OPCODE_LEN 2
+#define MTFTP4_ERRCODE_LEN 2
+#define MTFTP4_BLKNO_LEN 2
+#define MTFTP4_DATA_HEAD_LEN 4
+
+#define MTFTP4_BLKSIZE_EXIST 0x01
+#define MTFTP4_TIMEOUT_EXIST 0x02
+#define MTFTP4_TSIZE_EXIST 0x04
+#define MTFTP4_MCAST_EXIST 0x08
+#define MTFTP4_WINDOWSIZE_EXIST 0x10
+
+typedef struct {
+ UINT16 BlkSize;
+ UINT16 WindowSize;
+ UINT8 Timeout;
+ UINT32 Tsize;
+ IP4_ADDR McastIp;
+ UINT16 McastPort;
+ BOOLEAN Master;
+ UINT32 Exist;
+} MTFTP4_OPTION;
+
+/**
+ Allocate and fill in a array of Mtftp options from the Packet.
+
+ It first calls Mtftp4FillOption to get the option number, then allocate
+ the array, at last, call Mtftp4FillOption again to save the options.
+
+ @param Packet The packet to parse
+ @param PacketLen The length of the packet
+ @param OptionCount The number of options in the packet
+ @param OptionList The point to get the option array.
+
+ @retval EFI_INVALID_PARAMETER The parametera are invalid or packet isn't a
+ well-formatted OACK packet.
+ @retval EFI_SUCCESS The option array is build
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate memory for the array
+
+**/
+EFI_STATUS
+Mtftp4ExtractOptions (
+ IN EFI_MTFTP4_PACKET *Packet,
+ IN UINT32 PacketLen,
+ OUT UINT32 *OptionCount,
+ OUT EFI_MTFTP4_OPTION **OptionList OPTIONAL
+ );
+
+/**
+ Parse the option in Options array to MTFTP4_OPTION which program
+ can access directly.
+
+ @param Options The option array, which contains addresses of each
+ option's name/value string.
+ @param Count The number of options in the Options
+ @param Request Whether this is a request or OACK. The format of
+ multicast is different according to this setting.
+ @param Operation The current performed operation.
+ @param MtftpOption The MTFTP4_OPTION for easy access.
+
+ @retval EFI_INVALID_PARAMETER The option is malformatted
+ @retval EFI_UNSUPPORTED Some option isn't supported
+ @retval EFI_SUCCESS The option are OK and has been parsed.
+
+**/
+EFI_STATUS
+Mtftp4ParseOption (
+ IN EFI_MTFTP4_OPTION *Options,
+ IN UINT32 Count,
+ IN BOOLEAN Request,
+ IN UINT16 Operation,
+ OUT MTFTP4_OPTION *MtftpOption
+ );
+
+/**
+ Parse the options in the OACK packet to MTFTP4_OPTION which program
+ can access directly.
+
+ @param Packet The OACK packet to parse
+ @param PacketLen The length of the packet
+ @param Operation The current performed operation.
+ @param MtftpOption The MTFTP_OPTION for easy access.
+
+ @retval EFI_INVALID_PARAMETER The packet option is malformatted
+ @retval EFI_UNSUPPORTED Some option isn't supported
+ @retval EFI_SUCCESS The option are OK and has been parsed.
+
+**/
+EFI_STATUS
+Mtftp4ParseOptionOack (
+ IN EFI_MTFTP4_PACKET *Packet,
+ IN UINT32 PacketLen,
+ IN UINT16 Operation,
+ OUT MTFTP4_OPTION *MtftpOption
+ );
+
+extern CHAR8 *mMtftp4SupportedOptions[MTFTP4_SUPPORTED_OPTIONS];
+
+#endif
diff --git a/src/VBox/Devices/EFI/Firmware/NetworkPkg/Mtftp4Dxe/Mtftp4Rrq.c b/src/VBox/Devices/EFI/Firmware/NetworkPkg/Mtftp4Dxe/Mtftp4Rrq.c
new file mode 100644
index 00000000..054070c8
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/NetworkPkg/Mtftp4Dxe/Mtftp4Rrq.c
@@ -0,0 +1,818 @@
+/** @file
+ Routines to process Rrq (download).
+
+(C) Copyright 2014 Hewlett-Packard Development Company, L.P.<BR>
+Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.<BR>
+SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+
+#include "Mtftp4Impl.h"
+
+
+/**
+ The packet process callback for MTFTP download.
+
+ @param UdpPacket The packet received
+ @param EndPoint The local/remote access point of the packet
+ @param IoStatus The status of the receiving
+ @param Context Opaque parameter, which is the MTFTP session
+
+**/
+VOID
+EFIAPI
+Mtftp4RrqInput (
+ IN NET_BUF *UdpPacket,
+ IN UDP_END_POINT *EndPoint,
+ IN EFI_STATUS IoStatus,
+ IN VOID *Context
+ );
+
+
+/**
+ Start the MTFTP session to download.
+
+ It will first initialize some of the internal states then build and send a RRQ
+ request packet, at last, it will start receive for the downloading.
+
+ @param Instance The Mtftp session
+ @param Operation The MTFTP opcode, it may be a EFI_MTFTP4_OPCODE_RRQ
+ or EFI_MTFTP4_OPCODE_DIR.
+
+ @retval EFI_SUCCESS The mtftp download session is started.
+ @retval Others Failed to start downloading.
+
+**/
+EFI_STATUS
+Mtftp4RrqStart (
+ IN MTFTP4_PROTOCOL *Instance,
+ IN UINT16 Operation
+ )
+{
+ EFI_STATUS Status;
+
+ //
+ // The valid block number range are [1, 0xffff]. For example:
+ // the client sends an RRQ request to the server, the server
+ // transfers the DATA1 block. If option negotiation is ongoing,
+ // the server will send back an OACK, then client will send ACK0.
+ //
+ Status = Mtftp4InitBlockRange (&Instance->Blocks, 1, 0xffff);
+
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ Status = Mtftp4SendRequest (Instance);
+
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ return UdpIoRecvDatagram (Instance->UnicastPort, Mtftp4RrqInput, Instance, 0);
+}
+
+
+/**
+ Build and send a ACK packet for the download session.
+
+ @param Instance The Mtftp session
+ @param BlkNo The BlkNo to ack.
+
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate memory for the packet
+ @retval EFI_SUCCESS The ACK has been sent
+ @retval Others Failed to send the ACK.
+
+**/
+EFI_STATUS
+Mtftp4RrqSendAck (
+ IN MTFTP4_PROTOCOL *Instance,
+ IN UINT16 BlkNo
+ )
+{
+ EFI_MTFTP4_PACKET *Ack;
+ NET_BUF *Packet;
+ EFI_STATUS Status;
+
+ Status = EFI_SUCCESS;
+
+ Packet = NetbufAlloc (sizeof (EFI_MTFTP4_ACK_HEADER));
+ if (Packet == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ Ack = (EFI_MTFTP4_PACKET *) NetbufAllocSpace (
+ Packet,
+ sizeof (EFI_MTFTP4_ACK_HEADER),
+ FALSE
+ );
+ ASSERT (Ack != NULL);
+
+ Ack->Ack.OpCode = HTONS (EFI_MTFTP4_OPCODE_ACK);
+ Ack->Ack.Block[0] = HTONS (BlkNo);
+
+ Status = Mtftp4SendPacket (Instance, Packet);
+ if (!EFI_ERROR (Status)) {
+ Instance->AckedBlock = Instance->TotalBlock;
+ }
+
+ return Status;
+}
+
+
+/**
+ Deliver the received data block to the user, which can be saved
+ in the user provide buffer or through the CheckPacket callback.
+
+ @param Instance The Mtftp session
+ @param Packet The received data packet
+ @param Len The packet length
+
+ @retval EFI_SUCCESS The data is saved successfully
+ @retval EFI_ABORTED The user tells to abort by return an error through
+ CheckPacket
+ @retval EFI_BUFFER_TOO_SMALL The user's buffer is too small and buffer length is
+ updated to the actual buffer size needed.
+
+**/
+EFI_STATUS
+Mtftp4RrqSaveBlock (
+ IN OUT MTFTP4_PROTOCOL *Instance,
+ IN EFI_MTFTP4_PACKET *Packet,
+ IN UINT32 Len
+ )
+{
+ EFI_MTFTP4_TOKEN *Token;
+ EFI_STATUS Status;
+ UINT16 Block;
+ UINT64 Start;
+ UINT32 DataLen;
+ UINT64 BlockCounter;
+ BOOLEAN Completed;
+
+ Completed = FALSE;
+ Token = Instance->Token;
+ Block = NTOHS (Packet->Data.Block);
+ DataLen = Len - MTFTP4_DATA_HEAD_LEN;
+
+ //
+ // This is the last block, save the block no
+ //
+ if (DataLen < Instance->BlkSize) {
+ Completed = TRUE;
+ Instance->LastBlock = Block;
+ Mtftp4SetLastBlockNum (&Instance->Blocks, Block);
+ }
+
+ //
+ // Remove this block number from the file hole. If Mtftp4RemoveBlockNum
+ // returns EFI_NOT_FOUND, the block has been saved, don't save it again.
+ // Note that : For bigger files, allowing the block counter to roll over
+ // to accept transfers of unlimited size. So BlockCounter is memorised as
+ // continuous block counter.
+ //
+ Status = Mtftp4RemoveBlockNum (&Instance->Blocks, Block, Completed, &BlockCounter);
+
+ if (Status == EFI_NOT_FOUND) {
+ return EFI_SUCCESS;
+ } else if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ if (Token->CheckPacket != NULL) {
+ Status = Token->CheckPacket (&Instance->Mtftp4, Token, (UINT16) Len, Packet);
+
+ if (EFI_ERROR (Status)) {
+ Mtftp4SendError (
+ Instance,
+ EFI_MTFTP4_ERRORCODE_ILLEGAL_OPERATION,
+ (UINT8 *) "User aborted download"
+ );
+
+ return EFI_ABORTED;
+ }
+ }
+
+ if (Token->Buffer != NULL) {
+ Start = MultU64x32 (BlockCounter - 1, Instance->BlkSize);
+
+ if (Start + DataLen <= Token->BufferSize) {
+ CopyMem ((UINT8 *) Token->Buffer + Start, Packet->Data.Data, DataLen);
+
+ //
+ // Update the file size when received the last block
+ //
+ if ((Instance->LastBlock == Block) && Completed) {
+ Token->BufferSize = Start + DataLen;
+ }
+
+ } else if (Instance->LastBlock != 0) {
+ //
+ // Don't save the data if the buffer is too small, return
+ // EFI_BUFFER_TOO_SMALL if received the last packet. This
+ // will give a accurate file length.
+ //
+ Token->BufferSize = Start + DataLen;
+
+ Mtftp4SendError (
+ Instance,
+ EFI_MTFTP4_ERRORCODE_DISK_FULL,
+ (UINT8 *) "User provided memory block is too small"
+ );
+
+ return EFI_BUFFER_TOO_SMALL;
+ }
+ }
+
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Function to process the received data packets.
+
+ It will save the block then send back an ACK if it is active.
+
+ @param Instance The downloading MTFTP session
+ @param Packet The packet received
+ @param Len The length of the packet
+ @param Multicast Whether this packet is multicast or unicast
+ @param Completed Return whether the download has completed
+
+ @retval EFI_SUCCESS The data packet is successfully processed
+ @retval EFI_ABORTED The download is aborted by the user
+ @retval EFI_BUFFER_TOO_SMALL The user provided buffer is too small
+
+**/
+EFI_STATUS
+Mtftp4RrqHandleData (
+ IN MTFTP4_PROTOCOL *Instance,
+ IN EFI_MTFTP4_PACKET *Packet,
+ IN UINT32 Len,
+ IN BOOLEAN Multicast,
+ OUT BOOLEAN *Completed
+ )
+{
+ EFI_STATUS Status;
+ UINT16 BlockNum;
+ INTN Expected;
+
+ *Completed = FALSE;
+ Status = EFI_SUCCESS;
+ BlockNum = NTOHS (Packet->Data.Block);
+ Expected = Mtftp4GetNextBlockNum (&Instance->Blocks);
+
+ ASSERT (Expected >= 0);
+
+ //
+ // If we are active (Master) and received an unexpected packet, transmit
+ // the ACK for the block we received, then restart receiving the
+ // expected one. If we are passive (Slave), save the block.
+ //
+ if (Instance->Master && (Expected != BlockNum)) {
+ //
+ // If Expected is 0, (UINT16) (Expected - 1) is also the expected Ack number (65535).
+ //
+ return Mtftp4RrqSendAck (Instance, (UINT16) (Expected - 1));
+ }
+
+ Status = Mtftp4RrqSaveBlock (Instance, Packet, Len);
+
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ //
+ // Record the total received and saved block number.
+ //
+ Instance->TotalBlock ++;
+
+ //
+ // Reset the passive client's timer whenever it received a
+ // valid data packet.
+ //
+ if (!Instance->Master) {
+ Mtftp4SetTimeout (Instance);
+ }
+
+ //
+ // Check whether we have received all the blocks. Send the ACK if we
+ // are active (unicast client or master client for multicast download).
+ // If we have received all the blocks, send an ACK even if we are passive
+ // to tell the server that we are done.
+ //
+ Expected = Mtftp4GetNextBlockNum (&Instance->Blocks);
+
+ if (Instance->Master || (Expected < 0)) {
+ if (Expected < 0) {
+ //
+ // If we are passive client, then the just received Block maybe
+ // isn't the last block. We need to send an ACK to the last block
+ // to inform the server that we are done. If we are active client,
+ // the Block == Instance->LastBlock.
+ //
+ BlockNum = Instance->LastBlock;
+ *Completed = TRUE;
+
+ } else {
+ BlockNum = (UINT16) (Expected - 1);
+ }
+
+ if (Instance->WindowSize == (Instance->TotalBlock - Instance->AckedBlock) || Expected < 0) {
+ Status = Mtftp4RrqSendAck (Instance, BlockNum);
+ }
+
+ }
+
+ return Status;
+}
+
+
+/**
+ Validate whether the options received in the server's OACK packet is valid.
+
+ The options are valid only if:
+ 1. The server doesn't include options not requested by us
+ 2. The server can only use smaller blksize than that is requested
+ 3. The server can only use the same timeout as requested
+ 4. The server doesn't change its multicast channel.
+
+ @param This The downloading Mtftp session
+ @param Reply The options in the OACK packet
+ @param Request The requested options
+
+ @retval TRUE The options in the OACK is OK.
+ @retval FALSE The options in the OACK is invalid.
+
+**/
+BOOLEAN
+Mtftp4RrqOackValid (
+ IN MTFTP4_PROTOCOL *This,
+ IN MTFTP4_OPTION *Reply,
+ IN MTFTP4_OPTION *Request
+ )
+{
+
+ //
+ // It is invalid for server to return options we don't request
+ //
+ if ((Reply->Exist &~Request->Exist) != 0) {
+ return FALSE;
+ }
+
+ //
+ // Server can only specify a smaller block size and window size to be used and
+ // return the timeout matches that requested.
+ //
+ if ((((Reply->Exist & MTFTP4_BLKSIZE_EXIST) != 0)&& (Reply->BlkSize > Request->BlkSize)) ||
+ (((Reply->Exist & MTFTP4_WINDOWSIZE_EXIST) != 0)&& (Reply->WindowSize > Request->WindowSize)) ||
+ (((Reply->Exist & MTFTP4_TIMEOUT_EXIST) != 0) && (Reply->Timeout != Request->Timeout))
+ ) {
+ return FALSE;
+ }
+
+ //
+ // The server can send ",,master" to client to change its master
+ // setting. But if it use the specific multicast channel, it can't
+ // change the setting.
+ //
+ if (((Reply->Exist & MTFTP4_MCAST_EXIST) != 0) && (This->McastIp != 0)) {
+ if ((Reply->McastIp != 0) && (Reply->McastIp != This->McastIp)) {
+ return FALSE;
+ }
+
+ if ((Reply->McastPort != 0) && (Reply->McastPort != This->McastPort)) {
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
+
+
+/**
+ Configure a UDP IO port to receive the multicast.
+
+ @param McastIo The UDP IO to configure
+ @param Context The opaque parameter to the function which is the
+ MTFTP session.
+
+ @retval EFI_SUCCESS The UDP child is successfully configured.
+ @retval Others Failed to configure the UDP child.
+
+**/
+EFI_STATUS
+EFIAPI
+Mtftp4RrqConfigMcastPort (
+ IN UDP_IO *McastIo,
+ IN VOID *Context
+ )
+{
+ MTFTP4_PROTOCOL *Instance;
+ EFI_MTFTP4_CONFIG_DATA *Config;
+ EFI_UDP4_CONFIG_DATA UdpConfig;
+ EFI_IPv4_ADDRESS Group;
+ EFI_STATUS Status;
+ IP4_ADDR Ip;
+
+ Instance = (MTFTP4_PROTOCOL *) Context;
+ Config = &Instance->Config;
+
+ UdpConfig.AcceptBroadcast = FALSE;
+ UdpConfig.AcceptPromiscuous = FALSE;
+ UdpConfig.AcceptAnyPort = FALSE;
+ UdpConfig.AllowDuplicatePort = FALSE;
+ UdpConfig.TypeOfService = 0;
+ UdpConfig.TimeToLive = 64;
+ UdpConfig.DoNotFragment = FALSE;
+ UdpConfig.ReceiveTimeout = 0;
+ UdpConfig.TransmitTimeout = 0;
+ UdpConfig.UseDefaultAddress = Config->UseDefaultSetting;
+ IP4_COPY_ADDRESS (&UdpConfig.StationAddress, &Config->StationIp);
+ IP4_COPY_ADDRESS (&UdpConfig.SubnetMask, &Config->SubnetMask);
+ UdpConfig.StationPort = Instance->McastPort;
+ UdpConfig.RemotePort = 0;
+
+ Ip = HTONL (Instance->ServerIp);
+ IP4_COPY_ADDRESS (&UdpConfig.RemoteAddress, &Ip);
+
+ Status = McastIo->Protocol.Udp4->Configure (McastIo->Protocol.Udp4, &UdpConfig);
+
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ if (!Config->UseDefaultSetting &&
+ !EFI_IP4_EQUAL (&mZeroIp4Addr, &Config->GatewayIp)) {
+ //
+ // The station IP address is manually configured and the Gateway IP is not 0.
+ // Add the default route for this UDP instance.
+ //
+ Status = McastIo->Protocol.Udp4->Routes (
+ McastIo->Protocol.Udp4,
+ FALSE,
+ &mZeroIp4Addr,
+ &mZeroIp4Addr,
+ &Config->GatewayIp
+ );
+
+ if (EFI_ERROR (Status)) {
+ McastIo->Protocol.Udp4->Configure (McastIo->Protocol.Udp4, NULL);
+ return Status;
+ }
+ }
+
+ //
+ // join the multicast group
+ //
+ Ip = HTONL (Instance->McastIp);
+ IP4_COPY_ADDRESS (&Group, &Ip);
+
+ return McastIo->Protocol.Udp4->Groups (McastIo->Protocol.Udp4, TRUE, &Group);
+}
+
+
+/**
+ Function to process the OACK.
+
+ It will first validate the OACK packet, then update the various negotiated parameters.
+
+ @param Instance The download MTFTP session
+ @param Packet The packet received
+ @param Len The packet length
+ @param Multicast Whether this packet is received as a multicast
+ @param Completed Returns whether the download has completed. NOT
+ used by this function.
+
+ @retval EFI_DEVICE_ERROR Failed to create/start a multicast UDP child
+ @retval EFI_TFTP_ERROR Some error happened during the process
+ @retval EFI_SUCCESS The OACK is successfully processed.
+
+**/
+EFI_STATUS
+Mtftp4RrqHandleOack (
+ IN OUT MTFTP4_PROTOCOL *Instance,
+ IN EFI_MTFTP4_PACKET *Packet,
+ IN UINT32 Len,
+ IN BOOLEAN Multicast,
+ OUT BOOLEAN *Completed
+ )
+{
+ MTFTP4_OPTION Reply;
+ EFI_STATUS Status;
+ INTN Expected;
+ EFI_UDP4_PROTOCOL *Udp4;
+
+ *Completed = FALSE;
+
+ //
+ // If already started the master download, don't change the
+ // setting. Master download always succeeds.
+ //
+ Expected = Mtftp4GetNextBlockNum (&Instance->Blocks);
+ ASSERT (Expected != -1);
+
+ if (Instance->Master && (Expected != 1)) {
+ return EFI_SUCCESS;
+ }
+
+ //
+ // Parse and validate the options from server
+ //
+ ZeroMem (&Reply, sizeof (MTFTP4_OPTION));
+
+ Status = Mtftp4ParseOptionOack (Packet, Len, Instance->Operation, &Reply);
+
+ if (EFI_ERROR (Status) ||
+ !Mtftp4RrqOackValid (Instance, &Reply, &Instance->RequestOption)) {
+ //
+ // Don't send an ERROR packet if the error is EFI_OUT_OF_RESOURCES.
+ //
+ if (Status != EFI_OUT_OF_RESOURCES) {
+ Mtftp4SendError (
+ Instance,
+ EFI_MTFTP4_ERRORCODE_ILLEGAL_OPERATION,
+ (UINT8 *) "Malformatted OACK packet"
+ );
+ }
+
+ return EFI_TFTP_ERROR;
+ }
+
+ if ((Reply.Exist & MTFTP4_MCAST_EXIST) != 0) {
+
+ //
+ // Save the multicast info. Always update the Master, only update the
+ // multicast IP address, block size, window size, timeout at the first time.
+ // If IP address is updated, create a UDP child to receive the multicast.
+ //
+ Instance->Master = Reply.Master;
+
+ if (Instance->McastIp == 0) {
+ if ((Reply.McastIp == 0) || (Reply.McastPort == 0)) {
+ Mtftp4SendError (
+ Instance,
+ EFI_MTFTP4_ERRORCODE_ILLEGAL_OPERATION,
+ (UINT8 *) "Illegal multicast setting"
+ );
+
+ return EFI_TFTP_ERROR;
+ }
+
+ //
+ // Create a UDP child then start receive the multicast from it.
+ //
+ Instance->McastIp = Reply.McastIp;
+ Instance->McastPort = Reply.McastPort;
+ if (Instance->McastUdpPort == NULL) {
+ Instance->McastUdpPort = UdpIoCreateIo (
+ Instance->Service->Controller,
+ Instance->Service->Image,
+ Mtftp4RrqConfigMcastPort,
+ UDP_IO_UDP4_VERSION,
+ Instance
+ );
+ if (Instance->McastUdpPort != NULL) {
+ Status = gBS->OpenProtocol (
+ Instance->McastUdpPort->UdpHandle,
+ &gEfiUdp4ProtocolGuid,
+ (VOID **) &Udp4,
+ Instance->Service->Image,
+ Instance->Handle,
+ EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER
+ );
+ if (EFI_ERROR (Status)) {
+ UdpIoFreeIo (Instance->McastUdpPort);
+ Instance->McastUdpPort = NULL;
+ return EFI_DEVICE_ERROR;
+ }
+ }
+ }
+
+
+ if (Instance->McastUdpPort == NULL) {
+ return EFI_DEVICE_ERROR;
+ }
+
+ Status = UdpIoRecvDatagram (Instance->McastUdpPort, Mtftp4RrqInput, Instance, 0);
+
+ if (EFI_ERROR (Status)) {
+ Mtftp4SendError (
+ Instance,
+ EFI_MTFTP4_ERRORCODE_ACCESS_VIOLATION,
+ (UINT8 *) "Failed to create socket to receive multicast packet"
+ );
+
+ return Status;
+ }
+
+ //
+ // Update the parameters used.
+ //
+ if (Reply.BlkSize != 0) {
+ Instance->BlkSize = Reply.BlkSize;
+ }
+
+ if (Reply.WindowSize != 0) {
+ Instance->WindowSize = Reply.WindowSize;
+ }
+
+ if (Reply.Timeout != 0) {
+ Instance->Timeout = Reply.Timeout;
+ }
+ }
+
+ } else {
+ Instance->Master = TRUE;
+
+ if (Reply.BlkSize != 0) {
+ Instance->BlkSize = Reply.BlkSize;
+ }
+
+ if (Reply.WindowSize != 0) {
+ Instance->WindowSize = Reply.WindowSize;
+ }
+
+ if (Reply.Timeout != 0) {
+ Instance->Timeout = Reply.Timeout;
+ }
+ }
+
+ //
+ // Send an ACK to (Expected - 1) which is 0 for unicast download,
+ // or tell the server we want to receive the Expected block.
+ //
+ return Mtftp4RrqSendAck (Instance, (UINT16) (Expected - 1));
+}
+
+
+/**
+ The packet process callback for MTFTP download.
+
+ @param UdpPacket The packet received
+ @param EndPoint The local/remote access point of the packet
+ @param IoStatus The status of the receiving
+ @param Context Opaque parameter, which is the MTFTP session
+
+**/
+VOID
+EFIAPI
+Mtftp4RrqInput (
+ IN NET_BUF *UdpPacket,
+ IN UDP_END_POINT *EndPoint,
+ IN EFI_STATUS IoStatus,
+ IN VOID *Context
+ )
+{
+ MTFTP4_PROTOCOL *Instance;
+ EFI_MTFTP4_PACKET *Packet;
+ BOOLEAN Completed;
+ BOOLEAN Multicast;
+ EFI_STATUS Status;
+ UINT16 Opcode;
+ UINT32 Len;
+
+ Instance = (MTFTP4_PROTOCOL *) Context;
+ NET_CHECK_SIGNATURE (Instance, MTFTP4_PROTOCOL_SIGNATURE);
+
+ Status = EFI_SUCCESS;
+ Packet = NULL;
+ Completed = FALSE;
+ Multicast = FALSE;
+
+ if (EFI_ERROR (IoStatus)) {
+ Status = IoStatus;
+ goto ON_EXIT;
+ }
+
+ ASSERT (UdpPacket != NULL);
+
+ //
+ // Find the port this packet is from to restart receive correctly.
+ //
+ Multicast = (BOOLEAN) (EndPoint->LocalAddr.Addr[0] == Instance->McastIp);
+
+ if (UdpPacket->TotalSize < MTFTP4_OPCODE_LEN) {
+ goto ON_EXIT;
+ }
+
+ //
+ // Client send initial request to server's listening port. Server
+ // will select a UDP port to communicate with the client. The server
+ // is required to use the same port as RemotePort to multicast the
+ // data.
+ //
+ if (EndPoint->RemotePort != Instance->ConnectedPort) {
+ if (Instance->ConnectedPort != 0) {
+ goto ON_EXIT;
+ } else {
+ Instance->ConnectedPort = EndPoint->RemotePort;
+ }
+ }
+
+ //
+ // Copy the MTFTP packet to a continuous buffer if it isn't already so.
+ //
+ Len = UdpPacket->TotalSize;
+
+ if (UdpPacket->BlockOpNum > 1) {
+ Packet = AllocatePool (Len);
+
+ if (Packet == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ goto ON_EXIT;
+ }
+
+ NetbufCopy (UdpPacket, 0, Len, (UINT8 *) Packet);
+
+ } else {
+ Packet = (EFI_MTFTP4_PACKET *) NetbufGetByte (UdpPacket, 0, NULL);
+ ASSERT (Packet != NULL);
+ }
+
+ Opcode = NTOHS (Packet->OpCode);
+
+ //
+ // Call the user's CheckPacket if provided. Abort the transmission
+ // if CheckPacket returns an EFI_ERROR code.
+ //
+ if ((Instance->Token->CheckPacket != NULL) &&
+ ((Opcode == EFI_MTFTP4_OPCODE_OACK) || (Opcode == EFI_MTFTP4_OPCODE_ERROR))) {
+
+ Status = Instance->Token->CheckPacket (
+ &Instance->Mtftp4,
+ Instance->Token,
+ (UINT16) Len,
+ Packet
+ );
+
+ if (EFI_ERROR (Status)) {
+ //
+ // Send an error message to the server to inform it
+ //
+ if (Opcode != EFI_MTFTP4_OPCODE_ERROR) {
+ Mtftp4SendError (
+ Instance,
+ EFI_MTFTP4_ERRORCODE_REQUEST_DENIED,
+ (UINT8 *) "User aborted the transfer"
+ );
+ }
+
+ Status = EFI_ABORTED;
+ goto ON_EXIT;
+ }
+ }
+
+ switch (Opcode) {
+ case EFI_MTFTP4_OPCODE_DATA:
+ if ((Len > (UINT32) (MTFTP4_DATA_HEAD_LEN + Instance->BlkSize)) ||
+ (Len < (UINT32) MTFTP4_DATA_HEAD_LEN)) {
+ goto ON_EXIT;
+ }
+
+ Status = Mtftp4RrqHandleData (Instance, Packet, Len, Multicast, &Completed);
+ break;
+
+ case EFI_MTFTP4_OPCODE_OACK:
+ if (Multicast || (Len <= MTFTP4_OPCODE_LEN)) {
+ goto ON_EXIT;
+ }
+
+ Status = Mtftp4RrqHandleOack (Instance, Packet, Len, Multicast, &Completed);
+ break;
+
+ case EFI_MTFTP4_OPCODE_ERROR:
+ Status = EFI_TFTP_ERROR;
+ break;
+
+ default:
+ break;
+ }
+
+ON_EXIT:
+
+ //
+ // Free the resources, then if !EFI_ERROR (Status), restart the
+ // receive, otherwise end the session.
+ //
+ if ((Packet != NULL) && (UdpPacket->BlockOpNum > 1)) {
+ FreePool (Packet);
+ }
+
+ if (UdpPacket != NULL) {
+ NetbufFree (UdpPacket);
+ }
+
+ if (!EFI_ERROR (Status) && !Completed) {
+ if (Multicast) {
+ Status = UdpIoRecvDatagram (Instance->McastUdpPort, Mtftp4RrqInput, Instance, 0);
+ } else {
+ Status = UdpIoRecvDatagram (Instance->UnicastPort, Mtftp4RrqInput, Instance, 0);
+ }
+ }
+
+ if (EFI_ERROR (Status) || Completed) {
+ Mtftp4CleanOperation (Instance, Status);
+ }
+}
diff --git a/src/VBox/Devices/EFI/Firmware/NetworkPkg/Mtftp4Dxe/Mtftp4Support.c b/src/VBox/Devices/EFI/Firmware/NetworkPkg/Mtftp4Dxe/Mtftp4Support.c
new file mode 100644
index 00000000..e7f22845
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/NetworkPkg/Mtftp4Dxe/Mtftp4Support.c
@@ -0,0 +1,663 @@
+/** @file
+ Support routines for Mtftp.
+
+Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.<BR>
+SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include "Mtftp4Impl.h"
+
+
+/**
+ Allocate a MTFTP4 block range, then init it to the range of [Start, End]
+
+ @param Start The start block number
+ @param End The last block number in the range
+
+ @return Pointer to the created block range, NULL if failed to allocate memory.
+
+**/
+MTFTP4_BLOCK_RANGE *
+Mtftp4AllocateRange (
+ IN UINT16 Start,
+ IN UINT16 End
+ )
+{
+ MTFTP4_BLOCK_RANGE *Range;
+
+ Range = AllocateZeroPool (sizeof (MTFTP4_BLOCK_RANGE));
+
+ if (Range == NULL) {
+ return NULL;
+ }
+
+ InitializeListHead (&Range->Link);
+ Range->Start = Start;
+ Range->End = End;
+ Range->Bound = End;
+
+ return Range;
+}
+
+
+/**
+ Initialize the block range for either RRQ or WRQ.
+
+ RRQ and WRQ have different requirements for Start and End.
+ For example, during start up, WRQ initializes its whole valid block range
+ to [0, 0xffff]. This is because the server will send us a ACK0 to inform us
+ to start the upload. When the client received ACK0, it will remove 0 from the
+ range, get the next block number, which is 1, then upload the BLOCK1. For RRQ
+ without option negotiation, the server will directly send us the BLOCK1 in
+ response to the client's RRQ. When received BLOCK1, the client will remove
+ it from the block range and send an ACK. It also works if there is option
+ negotiation.
+
+ @param Head The block range head to initialize
+ @param Start The Start block number.
+ @param End The last block number.
+
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate memory for initial block range
+ @retval EFI_SUCCESS The initial block range is created.
+
+**/
+EFI_STATUS
+Mtftp4InitBlockRange (
+ IN LIST_ENTRY *Head,
+ IN UINT16 Start,
+ IN UINT16 End
+ )
+{
+ MTFTP4_BLOCK_RANGE *Range;
+
+ Range = Mtftp4AllocateRange (Start, End);
+
+ if (Range == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ InsertTailList (Head, &Range->Link);
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Get the first valid block number on the range list.
+
+ @param Head The block range head
+
+ @return The first valid block number, -1 if the block range is empty.
+
+**/
+INTN
+Mtftp4GetNextBlockNum (
+ IN LIST_ENTRY *Head
+ )
+{
+ MTFTP4_BLOCK_RANGE *Range;
+
+ if (IsListEmpty (Head)) {
+ return -1;
+ }
+
+ Range = NET_LIST_HEAD (Head, MTFTP4_BLOCK_RANGE, Link);
+ return Range->Start;
+}
+
+
+/**
+ Set the last block number of the block range list.
+
+ It will remove all the blocks after the Last. MTFTP initialize the block range
+ to the maximum possible range, such as [0, 0xffff] for WRQ. When it gets the
+ last block number, it will call this function to set the last block number.
+
+ @param Head The block range list
+ @param Last The last block number
+
+**/
+VOID
+Mtftp4SetLastBlockNum (
+ IN LIST_ENTRY *Head,
+ IN UINT16 Last
+ )
+{
+ MTFTP4_BLOCK_RANGE *Range;
+
+ //
+ // Iterate from the tail to head to remove the block number
+ // after the last.
+ //
+ while (!IsListEmpty (Head)) {
+ Range = NET_LIST_TAIL (Head, MTFTP4_BLOCK_RANGE, Link);
+
+ if (Range->Start > Last) {
+ RemoveEntryList (&Range->Link);
+ FreePool (Range);
+ continue;
+ }
+
+ if (Range->End > Last) {
+ Range->End = Last;
+ }
+
+ return ;
+ }
+}
+
+
+/**
+ Remove the block number from the block range list.
+
+ @param Head The block range list to remove from
+ @param Num The block number to remove
+ @param Completed Whether Num is the last block number.
+ @param BlockCounter The continuous block counter instead of the value after roll-over.
+
+ @retval EFI_NOT_FOUND The block number isn't in the block range list
+ @retval EFI_SUCCESS The block number has been removed from the list
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate resource
+
+**/
+EFI_STATUS
+Mtftp4RemoveBlockNum (
+ IN LIST_ENTRY *Head,
+ IN UINT16 Num,
+ IN BOOLEAN Completed,
+ OUT UINT64 *BlockCounter
+ )
+{
+ MTFTP4_BLOCK_RANGE *Range;
+ MTFTP4_BLOCK_RANGE *NewRange;
+ LIST_ENTRY *Entry;
+
+ NET_LIST_FOR_EACH (Entry, Head) {
+
+ //
+ // Each block represents a hole [Start, End] in the file,
+ // skip to the first range with End >= Num
+ //
+ Range = NET_LIST_USER_STRUCT (Entry, MTFTP4_BLOCK_RANGE, Link);
+
+ if (Range->End < Num) {
+ continue;
+ }
+
+ //
+ // There are three different cases for Start
+ // 1. (Start > Num) && (End >= Num):
+ // because all the holes before this one has the condition of
+ // End < Num, so this block number has been removed.
+ //
+ // 2. (Start == Num) && (End >= Num):
+ // Need to increase the Start by one, and if End == Num, this
+ // hole has been removed completely, remove it.
+ //
+ // 3. (Start < Num) && (End >= Num):
+ // if End == Num, only need to decrease the End by one because
+ // we have (Start < Num) && (Num == End), so (Start <= End - 1).
+ // if (End > Num), the hold is split into two holes, with
+ // [Start, Num - 1] and [Num + 1, End].
+ //
+ if (Range->Start > Num) {
+ return EFI_NOT_FOUND;
+
+ } else if (Range->Start == Num) {
+ Range->Start++;
+
+ //
+ // Note that: RFC 1350 does not mention block counter roll-over,
+ // but several TFTP hosts implement the roll-over be able to accept
+ // transfers of unlimited size. There is no consensus, however, whether
+ // the counter should wrap around to zero or to one. Many implementations
+ // wrap to zero, because this is the simplest to implement. Here we choose
+ // this solution.
+ //
+ *BlockCounter = Num;
+
+ if (Range->Round > 0) {
+ *BlockCounter += Range->Bound + MultU64x32 ((UINTN) (Range->Round -1), (UINT32) (Range->Bound + 1)) + 1;
+ }
+
+ if (Range->Start > Range->Bound) {
+ Range->Start = 0;
+ Range->Round ++;
+ }
+
+ if ((Range->Start > Range->End) || Completed) {
+ RemoveEntryList (&Range->Link);
+ FreePool (Range);
+ }
+
+ return EFI_SUCCESS;
+
+ } else {
+ if (Range->End == Num) {
+ Range->End--;
+ } else {
+ NewRange = Mtftp4AllocateRange ((UINT16) (Num + 1), (UINT16) Range->End);
+
+ if (NewRange == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ Range->End = Num - 1;
+ NetListInsertAfter (&Range->Link, &NewRange->Link);
+ }
+
+ return EFI_SUCCESS;
+ }
+ }
+
+ return EFI_NOT_FOUND;
+}
+
+
+/**
+ Build then transmit the request packet for the MTFTP session.
+
+ @param Instance The Mtftp session
+
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate memory for the request
+ @retval EFI_SUCCESS The request is built and sent
+ @retval Others Failed to transmit the packet.
+
+**/
+EFI_STATUS
+Mtftp4SendRequest (
+ IN MTFTP4_PROTOCOL *Instance
+ )
+{
+ EFI_MTFTP4_PACKET *Packet;
+ EFI_MTFTP4_OPTION *Options;
+ EFI_MTFTP4_TOKEN *Token;
+ RETURN_STATUS Status;
+ NET_BUF *Nbuf;
+ UINT8 *Mode;
+ UINT8 *Cur;
+ UINTN Index;
+ UINT32 BufferLength;
+ UINTN FileNameLength;
+ UINTN ModeLength;
+ UINTN OptionStrLength;
+ UINTN ValueStrLength;
+
+ Token = Instance->Token;
+ Options = Token->OptionList;
+ Mode = Instance->Token->ModeStr;
+
+ if (Mode == NULL) {
+ Mode = (UINT8 *) "octet";
+ }
+
+ //
+ // Compute the packet length
+ //
+ FileNameLength = AsciiStrLen ((CHAR8 *) Token->Filename);
+ ModeLength = AsciiStrLen ((CHAR8 *) Mode);
+ BufferLength = (UINT32) FileNameLength + (UINT32) ModeLength + 4;
+
+ for (Index = 0; Index < Token->OptionCount; Index++) {
+ OptionStrLength = AsciiStrLen ((CHAR8 *) Options[Index].OptionStr);
+ ValueStrLength = AsciiStrLen ((CHAR8 *) Options[Index].ValueStr);
+ BufferLength += (UINT32) OptionStrLength + (UINT32) ValueStrLength + 2;
+ }
+ //
+ // Allocate a packet then copy the data over
+ //
+ if ((Nbuf = NetbufAlloc (BufferLength)) == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ Packet = (EFI_MTFTP4_PACKET *) NetbufAllocSpace (Nbuf, BufferLength, FALSE);
+ ASSERT (Packet != NULL);
+
+ Packet->OpCode = HTONS (Instance->Operation);
+ BufferLength -= sizeof (Packet->OpCode);
+
+ Cur = Packet->Rrq.Filename;
+ Status = AsciiStrCpyS ((CHAR8 *) Cur, BufferLength, (CHAR8 *) Token->Filename);
+ ASSERT_EFI_ERROR (Status);
+ BufferLength -= (UINT32) (FileNameLength + 1);
+ Cur += FileNameLength + 1;
+ Status = AsciiStrCpyS ((CHAR8 *) Cur, BufferLength, (CHAR8 *) Mode);
+ ASSERT_EFI_ERROR (Status);
+ BufferLength -= (UINT32) (ModeLength + 1);
+ Cur += ModeLength + 1;
+
+ for (Index = 0; Index < Token->OptionCount; ++Index) {
+ OptionStrLength = AsciiStrLen ((CHAR8 *) Options[Index].OptionStr);
+ ValueStrLength = AsciiStrLen ((CHAR8 *) Options[Index].ValueStr);
+
+ Status = AsciiStrCpyS ((CHAR8 *) Cur, BufferLength, (CHAR8 *) Options[Index].OptionStr);
+ ASSERT_EFI_ERROR (Status);
+ BufferLength -= (UINT32) (OptionStrLength + 1);
+ Cur += OptionStrLength + 1;
+
+ Status = AsciiStrCpyS ((CHAR8 *) Cur, BufferLength, (CHAR8 *) Options[Index].ValueStr);
+ ASSERT_EFI_ERROR (Status);
+ BufferLength -= (UINT32) (ValueStrLength + 1);
+ Cur += ValueStrLength + 1;
+
+ }
+
+ return Mtftp4SendPacket (Instance, Nbuf);
+}
+
+
+/**
+ Build then send an error message.
+
+ @param Instance The MTFTP session
+ @param ErrCode The error code
+ @param ErrInfo The error message
+
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate memory for the error packet
+ @retval EFI_SUCCESS The error packet is transmitted.
+ @retval Others Failed to transmit the packet.
+
+**/
+EFI_STATUS
+Mtftp4SendError (
+ IN MTFTP4_PROTOCOL *Instance,
+ IN UINT16 ErrCode,
+ IN UINT8 *ErrInfo
+ )
+{
+ NET_BUF *Packet;
+ EFI_MTFTP4_PACKET *TftpError;
+ UINT32 Len;
+
+ Len = (UINT32) (AsciiStrLen ((CHAR8 *) ErrInfo) + sizeof (EFI_MTFTP4_ERROR_HEADER));
+ Packet = NetbufAlloc (Len);
+ if (Packet == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ TftpError = (EFI_MTFTP4_PACKET *) NetbufAllocSpace (Packet, Len, FALSE);
+ ASSERT (TftpError != NULL);
+
+ TftpError->OpCode = HTONS (EFI_MTFTP4_OPCODE_ERROR);
+ TftpError->Error.ErrorCode = HTONS (ErrCode);
+
+ AsciiStrCpyS ((CHAR8 *) TftpError->Error.ErrorMessage, Len, (CHAR8 *) ErrInfo);
+
+ return Mtftp4SendPacket (Instance, Packet);
+}
+
+
+/**
+ The callback function called when the packet is transmitted.
+
+ It simply frees the packet.
+
+ @param Packet The transmitted (or failed to) packet
+ @param EndPoint The local and remote UDP access point
+ @param IoStatus The result of the transmission
+ @param Context Opaque parameter to the callback
+
+**/
+VOID
+EFIAPI
+Mtftp4OnPacketSent (
+ IN NET_BUF *Packet,
+ IN UDP_END_POINT *EndPoint,
+ IN EFI_STATUS IoStatus,
+ IN VOID *Context
+ )
+{
+ NetbufFree (Packet);
+}
+
+
+/**
+ Set the timeout for the instance. User a longer time for passive instances.
+
+ @param Instance The Mtftp session to set time out
+
+**/
+VOID
+Mtftp4SetTimeout (
+ IN OUT MTFTP4_PROTOCOL *Instance
+ )
+{
+ if (Instance->Master) {
+ Instance->PacketToLive = Instance->Timeout;
+ } else {
+ Instance->PacketToLive = Instance->Timeout * 2;
+ }
+}
+
+
+/**
+ Send the packet for the instance.
+
+ It will first save a reference to the packet for later retransmission.
+ Then determine the destination port, listen port for requests, and connected
+ port for others. At last, send the packet out.
+
+ @param Instance The Mtftp instance
+ @param Packet The packet to send
+
+ @retval EFI_SUCCESS The packet is sent out
+ @retval Others Failed to transmit the packet.
+
+**/
+EFI_STATUS
+Mtftp4SendPacket (
+ IN OUT MTFTP4_PROTOCOL *Instance,
+ IN OUT NET_BUF *Packet
+ )
+{
+ UDP_END_POINT UdpPoint;
+ EFI_STATUS Status;
+ UINT16 OpCode;
+ UINT8 *Buffer;
+
+ //
+ // Save the packet for retransmission
+ //
+ if (Instance->LastPacket != NULL) {
+ NetbufFree (Instance->LastPacket);
+ }
+
+ Instance->LastPacket = Packet;
+
+ Instance->CurRetry = 0;
+ Mtftp4SetTimeout (Instance);
+
+ ZeroMem (&UdpPoint, sizeof (UdpPoint));
+ UdpPoint.RemoteAddr.Addr[0] = Instance->ServerIp;
+
+ //
+ // Send the requests to the listening port, other packets
+ // to the connected port
+ //
+ Buffer = NetbufGetByte (Packet, 0, NULL);
+ ASSERT (Buffer != NULL);
+ OpCode = NTOHS (*(UINT16 *)Buffer);
+
+ if ((OpCode == EFI_MTFTP4_OPCODE_RRQ) ||
+ (OpCode == EFI_MTFTP4_OPCODE_DIR) ||
+ (OpCode == EFI_MTFTP4_OPCODE_WRQ)) {
+ UdpPoint.RemotePort = Instance->ListeningPort;
+ } else {
+ UdpPoint.RemotePort = Instance->ConnectedPort;
+ }
+
+ NET_GET_REF (Packet);
+
+ Status = UdpIoSendDatagram (
+ Instance->UnicastPort,
+ Packet,
+ &UdpPoint,
+ NULL,
+ Mtftp4OnPacketSent,
+ Instance
+ );
+
+ if (EFI_ERROR (Status)) {
+ NET_PUT_REF (Packet);
+ }
+
+ return Status;
+}
+
+
+/**
+ Retransmit the last packet for the instance.
+
+ @param Instance The Mtftp instance
+
+ @retval EFI_SUCCESS The last packet is retransmitted.
+ @retval Others Failed to retransmit.
+
+**/
+EFI_STATUS
+Mtftp4Retransmit (
+ IN MTFTP4_PROTOCOL *Instance
+ )
+{
+ UDP_END_POINT UdpPoint;
+ EFI_STATUS Status;
+ UINT16 OpCode;
+ UINT8 *Buffer;
+
+ ASSERT (Instance->LastPacket != NULL);
+
+ ZeroMem (&UdpPoint, sizeof (UdpPoint));
+ UdpPoint.RemoteAddr.Addr[0] = Instance->ServerIp;
+
+ //
+ // Set the requests to the listening port, other packets to the connected port
+ //
+ Buffer = NetbufGetByte (Instance->LastPacket, 0, NULL);
+ ASSERT (Buffer != NULL);
+ OpCode = NTOHS (*(UINT16 *) Buffer);
+
+ if ((OpCode == EFI_MTFTP4_OPCODE_RRQ) || (OpCode == EFI_MTFTP4_OPCODE_DIR) ||
+ (OpCode == EFI_MTFTP4_OPCODE_WRQ)) {
+ UdpPoint.RemotePort = Instance->ListeningPort;
+ } else {
+ UdpPoint.RemotePort = Instance->ConnectedPort;
+ }
+
+ NET_GET_REF (Instance->LastPacket);
+
+ Status = UdpIoSendDatagram (
+ Instance->UnicastPort,
+ Instance->LastPacket,
+ &UdpPoint,
+ NULL,
+ Mtftp4OnPacketSent,
+ Instance
+ );
+
+ if (EFI_ERROR (Status)) {
+ NET_PUT_REF (Instance->LastPacket);
+ }
+
+ return Status;
+}
+
+
+/**
+ The timer ticking function in TPL_NOTIFY level for the Mtftp service instance.
+
+ @param Event The ticking event
+ @param Context The Mtftp service instance
+
+**/
+VOID
+EFIAPI
+Mtftp4OnTimerTickNotifyLevel (
+ IN EFI_EVENT Event,
+ IN VOID *Context
+ )
+{
+ MTFTP4_SERVICE *MtftpSb;
+ LIST_ENTRY *Entry;
+ LIST_ENTRY *Next;
+ MTFTP4_PROTOCOL *Instance;
+
+ MtftpSb = (MTFTP4_SERVICE *) Context;
+
+ //
+ // Iterate through all the children of the Mtftp service instance. Time
+ // out the current packet transmit.
+ //
+ NET_LIST_FOR_EACH_SAFE (Entry, Next, &MtftpSb->Children) {
+ Instance = NET_LIST_USER_STRUCT (Entry, MTFTP4_PROTOCOL, Link);
+ if ((Instance->PacketToLive == 0) || (--Instance->PacketToLive > 0)) {
+ Instance->HasTimeout = FALSE;
+ } else {
+ Instance->HasTimeout = TRUE;
+ }
+ }
+}
+
+
+/**
+ The timer ticking function for the Mtftp service instance.
+
+ @param Event The ticking event
+ @param Context The Mtftp service instance
+
+**/
+VOID
+EFIAPI
+Mtftp4OnTimerTick (
+ IN EFI_EVENT Event,
+ IN VOID *Context
+ )
+{
+ MTFTP4_SERVICE *MtftpSb;
+ LIST_ENTRY *Entry;
+ LIST_ENTRY *Next;
+ MTFTP4_PROTOCOL *Instance;
+ EFI_MTFTP4_TOKEN *Token;
+
+ MtftpSb = (MTFTP4_SERVICE *) Context;
+
+ //
+ // Iterate through all the children of the Mtftp service instance.
+ //
+ NET_LIST_FOR_EACH_SAFE (Entry, Next, &MtftpSb->Children) {
+ Instance = NET_LIST_USER_STRUCT (Entry, MTFTP4_PROTOCOL, Link);
+ if (!Instance->HasTimeout) {
+ continue;
+ }
+
+ Instance->HasTimeout = FALSE;
+
+ //
+ // Call the user's time out handler
+ //
+ Token = Instance->Token;
+
+ if (Token != NULL && Token->TimeoutCallback != NULL &&
+ EFI_ERROR (Token->TimeoutCallback (&Instance->Mtftp4, Token))) {
+ Mtftp4SendError (
+ Instance,
+ EFI_MTFTP4_ERRORCODE_REQUEST_DENIED,
+ (UINT8 *) "User aborted the transfer in time out"
+ );
+
+ Mtftp4CleanOperation (Instance, EFI_ABORTED);
+ continue;
+ }
+
+ //
+ // Retransmit the packet if haven't reach the maximum retry count,
+ // otherwise exit the transfer.
+ //
+ if (++Instance->CurRetry < Instance->MaxRetry) {
+ Mtftp4Retransmit (Instance);
+ Mtftp4SetTimeout (Instance);
+ } else {
+ Mtftp4CleanOperation (Instance, EFI_TIMEOUT);
+ continue;
+ }
+ }
+}
diff --git a/src/VBox/Devices/EFI/Firmware/NetworkPkg/Mtftp4Dxe/Mtftp4Support.h b/src/VBox/Devices/EFI/Firmware/NetworkPkg/Mtftp4Dxe/Mtftp4Support.h
new file mode 100644
index 00000000..f1421cdf
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/NetworkPkg/Mtftp4Dxe/Mtftp4Support.h
@@ -0,0 +1,198 @@
+/** @file
+ Support routines for MTFTP.
+
+Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.<BR>
+SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#ifndef __EFI_MTFTP4_SUPPORT_H__
+#define __EFI_MTFTP4_SUPPORT_H__
+
+//
+// The structure representing a range of block numbers, [Start, End].
+// It is used to remember the holes in the MTFTP block space. If all
+// the holes are filled in, then the download or upload has completed.
+//
+typedef struct {
+ LIST_ENTRY Link;
+ INTN Start;
+ INTN End;
+ INTN Round;
+ INTN Bound;
+} MTFTP4_BLOCK_RANGE;
+
+
+/**
+ Initialize the block range for either RRQ or WRQ.
+
+ RRQ and WRQ have different requirements for Start and End.
+ For example, during start up, WRQ initializes its whole valid block range
+ to [0, 0xffff]. This is because the server will send us a ACK0 to inform us
+ to start the upload. When the client received ACK0, it will remove 0 from the
+ range, get the next block number, which is 1, then upload the BLOCK1. For RRQ
+ without option negotiation, the server will directly send us the BLOCK1 in
+ response to the client's RRQ. When received BLOCK1, the client will remove
+ it from the block range and send an ACK. It also works if there is option
+ negotiation.
+
+ @param Head The block range head to initialize
+ @param Start The Start block number.
+ @param End The last block number.
+
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate memory for initial block range
+ @retval EFI_SUCCESS The initial block range is created.
+
+**/
+EFI_STATUS
+Mtftp4InitBlockRange (
+ IN LIST_ENTRY *Head,
+ IN UINT16 Start,
+ IN UINT16 End
+ );
+
+/**
+ Get the first valid block number on the range list.
+
+ @param Head The block range head
+
+ @return The first valid block number, -1 if the block range is empty.
+
+**/
+INTN
+Mtftp4GetNextBlockNum (
+ IN LIST_ENTRY *Head
+ );
+
+/**
+ Set the last block number of the block range list.
+
+ It will remove all the blocks after the Last. MTFTP initialize the block range
+ to the maximum possible range, such as [0, 0xffff] for WRQ. When it gets the
+ last block number, it will call this function to set the last block number.
+
+ @param Head The block range list
+ @param Last The last block number
+
+**/
+VOID
+Mtftp4SetLastBlockNum (
+ IN LIST_ENTRY *Head,
+ IN UINT16 Last
+ );
+
+/**
+ Remove the block number from the block range list.
+
+ @param Head The block range list to remove from
+ @param Num The block number to remove
+ @param Completed Whether Num is the last block number.
+ @param BlockCounter The continuous block counter instead of the value after roll-over.
+
+ @retval EFI_NOT_FOUND The block number isn't in the block range list
+ @retval EFI_SUCCESS The block number has been removed from the list
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate resource
+
+**/
+EFI_STATUS
+Mtftp4RemoveBlockNum (
+ IN LIST_ENTRY *Head,
+ IN UINT16 Num,
+ IN BOOLEAN Completed,
+ OUT UINT64 *BlockCounter
+ );
+
+/**
+ Set the timeout for the instance. User a longer time for passive instances.
+
+ @param Instance The Mtftp session to set time out
+
+**/
+VOID
+Mtftp4SetTimeout (
+ IN OUT MTFTP4_PROTOCOL *Instance
+ );
+
+/**
+ Send the packet for the instance.
+
+ It will first save a reference to the packet for later retransmission.
+ Then determine the destination port, listen port for requests, and connected
+ port for others. At last, send the packet out.
+
+ @param Instance The Mtftp instance
+ @param Packet The packet to send
+
+ @retval EFI_SUCCESS The packet is sent out
+ @retval Others Failed to transmit the packet.
+
+**/
+EFI_STATUS
+Mtftp4SendPacket (
+ IN OUT MTFTP4_PROTOCOL *Instance,
+ IN OUT NET_BUF *Packet
+ );
+
+/**
+ Build then transmit the request packet for the MTFTP session.
+
+ @param Instance The Mtftp session
+
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate memory for the request
+ @retval EFI_SUCCESS The request is built and sent
+ @retval Others Failed to transmit the packet.
+
+**/
+EFI_STATUS
+Mtftp4SendRequest (
+ IN MTFTP4_PROTOCOL *Instance
+ );
+
+/**
+ Build then send an error message.
+
+ @param Instance The MTFTP session
+ @param ErrCode The error code
+ @param ErrInfo The error message
+
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate memory for the error packet
+ @retval EFI_SUCCESS The error packet is transmitted.
+ @retval Others Failed to transmit the packet.
+
+**/
+EFI_STATUS
+Mtftp4SendError (
+ IN MTFTP4_PROTOCOL *Instance,
+ IN UINT16 ErrCode,
+ IN UINT8 *ErrInfo
+ );
+
+
+/**
+ The timer ticking function in TPL_NOTIFY level for the Mtftp service instance.
+
+ @param Event The ticking event
+ @param Context The Mtftp service instance
+
+**/
+VOID
+EFIAPI
+Mtftp4OnTimerTickNotifyLevel (
+ IN EFI_EVENT Event,
+ IN VOID *Context
+ );
+
+/**
+ The timer ticking function for the Mtftp service instance.
+
+ @param Event The ticking event
+ @param Context The Mtftp service instance
+
+**/
+VOID
+EFIAPI
+Mtftp4OnTimerTick (
+ IN EFI_EVENT Event,
+ IN VOID *Context
+ );
+#endif
diff --git a/src/VBox/Devices/EFI/Firmware/NetworkPkg/Mtftp4Dxe/Mtftp4Wrq.c b/src/VBox/Devices/EFI/Firmware/NetworkPkg/Mtftp4Dxe/Mtftp4Wrq.c
new file mode 100644
index 00000000..edd8c5e0
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/NetworkPkg/Mtftp4Dxe/Mtftp4Wrq.c
@@ -0,0 +1,529 @@
+/** @file
+ Routines to process Wrq (upload).
+
+Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.<BR>
+SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include "Mtftp4Impl.h"
+
+
+
+/**
+ Build then send a MTFTP data packet for the MTFTP upload session.
+
+ @param Instance The MTFTP upload session.
+ @param BlockNum The block number to send.
+
+ @retval EFI_OUT_OF_RESOURCES Failed to build the packet.
+ @retval EFI_ABORTED The consumer of this child directs to abort the
+ transmission by return an error through PacketNeeded.
+ @retval EFI_SUCCESS The data is sent.
+
+**/
+EFI_STATUS
+Mtftp4WrqSendBlock (
+ IN OUT MTFTP4_PROTOCOL *Instance,
+ IN UINT16 BlockNum
+ )
+{
+ EFI_MTFTP4_PACKET *Packet;
+ EFI_MTFTP4_TOKEN *Token;
+ NET_BUF *UdpPacket;
+ EFI_STATUS Status;
+ UINT16 DataLen;
+ UINT8 *DataBuf;
+ UINT64 Start;
+
+ //
+ // Allocate a buffer to hold the user data
+ //
+ UdpPacket = NetbufAlloc (Instance->BlkSize + MTFTP4_DATA_HEAD_LEN);
+
+ if (UdpPacket == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ Packet = (EFI_MTFTP4_PACKET *) NetbufAllocSpace (UdpPacket, MTFTP4_DATA_HEAD_LEN, FALSE);
+ ASSERT (Packet != NULL);
+
+ Packet->Data.OpCode = HTONS (EFI_MTFTP4_OPCODE_DATA);
+ Packet->Data.Block = HTONS (BlockNum);
+
+ //
+ // Read the block from either the buffer or PacketNeeded callback
+ //
+ Token = Instance->Token;
+ DataLen = Instance->BlkSize;
+
+ if (Token->Buffer != NULL) {
+ Start = MultU64x32 (BlockNum - 1, Instance->BlkSize);
+
+ if (Token->BufferSize < Start + Instance->BlkSize) {
+ DataLen = (UINT16) (Token->BufferSize - Start);
+ Instance->LastBlock = BlockNum;
+ Mtftp4SetLastBlockNum (&Instance->Blocks, BlockNum);
+ }
+
+ if (DataLen > 0) {
+ NetbufAllocSpace (UdpPacket, DataLen, FALSE);
+ CopyMem (Packet->Data.Data, (UINT8 *) Token->Buffer + Start, DataLen);
+ }
+
+ } else {
+ //
+ // Get data from PacketNeeded
+ //
+ DataBuf = NULL;
+ Status = Token->PacketNeeded (
+ &Instance->Mtftp4,
+ Token,
+ &DataLen,
+ (VOID **) &DataBuf
+ );
+
+ if (EFI_ERROR (Status) || (DataLen > Instance->BlkSize)) {
+ if (DataBuf != NULL) {
+ FreePool (DataBuf);
+ }
+
+ if (UdpPacket != NULL) {
+ NetbufFree (UdpPacket);
+ }
+
+ Mtftp4SendError (
+ Instance,
+ EFI_MTFTP4_ERRORCODE_REQUEST_DENIED,
+ (UINT8 *) "User aborted the transfer"
+ );
+
+ return EFI_ABORTED;
+ }
+
+ if (DataLen < Instance->BlkSize) {
+ Instance->LastBlock = BlockNum;
+ Mtftp4SetLastBlockNum (&Instance->Blocks, BlockNum);
+ }
+
+ if (DataLen > 0) {
+ NetbufAllocSpace (UdpPacket, DataLen, FALSE);
+ CopyMem (Packet->Data.Data, DataBuf, DataLen);
+ FreePool (DataBuf);
+ }
+ }
+
+ return Mtftp4SendPacket (Instance, UdpPacket);
+}
+
+
+/**
+ Function to handle received ACK packet.
+
+ If the ACK number matches the expected block number, and there are more
+ data pending, send the next block. Otherwise tell the caller that we are done.
+
+ @param Instance The MTFTP upload session
+ @param Packet The MTFTP packet received
+ @param Len The packet length
+ @param Completed Return whether the upload has finished.
+
+ @retval EFI_SUCCESS The ACK is successfully processed.
+ @retval EFI_TFTP_ERROR The block number loops back.
+ @retval Others Failed to transmit the next data packet.
+
+**/
+EFI_STATUS
+Mtftp4WrqHandleAck (
+ IN MTFTP4_PROTOCOL *Instance,
+ IN EFI_MTFTP4_PACKET *Packet,
+ IN UINT32 Len,
+ OUT BOOLEAN *Completed
+ )
+{
+ UINT16 AckNum;
+ INTN Expected;
+ UINT64 BlockCounter;
+
+ *Completed = FALSE;
+ AckNum = NTOHS (Packet->Ack.Block[0]);
+ Expected = Mtftp4GetNextBlockNum (&Instance->Blocks);
+
+ ASSERT (Expected >= 0);
+
+ //
+ // Get an unwanted ACK, return EFI_SUCCESS to let Mtftp4WrqInput
+ // restart receive.
+ //
+ if (Expected != AckNum) {
+ return EFI_SUCCESS;
+ }
+
+ //
+ // Remove the acked block number, if this is the last block number,
+ // tell the Mtftp4WrqInput to finish the transfer. This is the last
+ // block number if the block range are empty.
+ //
+ Mtftp4RemoveBlockNum (&Instance->Blocks, AckNum, *Completed, &BlockCounter);
+
+ Expected = Mtftp4GetNextBlockNum (&Instance->Blocks);
+
+ if (Expected < 0) {
+
+ //
+ // The block range is empty. It may either because the last
+ // block has been ACKed, or the sequence number just looped back,
+ // that is, there is more than 0xffff blocks.
+ //
+ if (Instance->LastBlock == AckNum) {
+ ASSERT (Instance->LastBlock >= 1);
+ *Completed = TRUE;
+ return EFI_SUCCESS;
+
+ } else {
+ Mtftp4SendError (
+ Instance,
+ EFI_MTFTP4_ERRORCODE_REQUEST_DENIED,
+ (UINT8 *) "Block number rolls back, not supported, try blksize option"
+ );
+
+ return EFI_TFTP_ERROR;
+ }
+ }
+
+ return Mtftp4WrqSendBlock (Instance, (UINT16) Expected);
+}
+
+
+/**
+ Check whether the received OACK is valid.
+
+ The OACK is valid only if:
+ 1. It only include options requested by us
+ 2. It can only include a smaller block size
+ 3. It can't change the proposed time out value.
+ 4. Other requirements of the individal MTFTP options as required.
+
+ @param Reply The options included in the OACK
+ @param Request The options we requested
+
+ @retval TRUE The options included in OACK is valid.
+ @retval FALSE The options included in OACK is invalid.
+
+**/
+BOOLEAN
+Mtftp4WrqOackValid (
+ IN MTFTP4_OPTION *Reply,
+ IN MTFTP4_OPTION *Request
+ )
+{
+ //
+ // It is invalid for server to return options we don't request
+ //
+ if ((Reply->Exist & ~Request->Exist) != 0) {
+ return FALSE;
+ }
+
+ //
+ // Server can only specify a smaller block size to be used and
+ // return the timeout matches that requested.
+ //
+ if ((((Reply->Exist & MTFTP4_BLKSIZE_EXIST) != 0) && (Reply->BlkSize > Request->BlkSize)) ||
+ (((Reply->Exist & MTFTP4_TIMEOUT_EXIST) != 0) && (Reply->Timeout != Request->Timeout))) {
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+
+/**
+ Function to handle the MTFTP OACK packet.
+
+ It parses the packet's options, and update the internal states of the session.
+
+ @param Instance The MTFTP session
+ @param Packet The received OACK packet
+ @param Len The length of the packet
+ @param Completed Whether the transmission has completed. NOT used by
+ this function.
+
+ @retval EFI_SUCCESS The OACK process is OK
+ @retval EFI_TFTP_ERROR Some error occurred, and the session reset.
+
+**/
+EFI_STATUS
+Mtftp4WrqHandleOack (
+ IN OUT MTFTP4_PROTOCOL *Instance,
+ IN EFI_MTFTP4_PACKET *Packet,
+ IN UINT32 Len,
+ OUT BOOLEAN *Completed
+ )
+{
+ MTFTP4_OPTION Reply;
+ EFI_MTFTP4_PACKET Bogus;
+ EFI_STATUS Status;
+ INTN Expected;
+
+ *Completed = FALSE;
+
+ //
+ // Ignore the OACK if already started the upload
+ //
+ Expected = Mtftp4GetNextBlockNum (&Instance->Blocks);
+
+ if (Expected != 0) {
+ return EFI_SUCCESS;
+ }
+
+ //
+ // Parse and validate the options from server
+ //
+ ZeroMem (&Reply, sizeof (MTFTP4_OPTION));
+ Status = Mtftp4ParseOptionOack (Packet, Len, Instance->Operation, &Reply);
+
+ if (EFI_ERROR (Status) || !Mtftp4WrqOackValid (&Reply, &Instance->RequestOption)) {
+ //
+ // Don't send a MTFTP error packet when out of resource, it can
+ // only make it worse.
+ //
+ if (Status != EFI_OUT_OF_RESOURCES) {
+ Mtftp4SendError (
+ Instance,
+ EFI_MTFTP4_ERRORCODE_ILLEGAL_OPERATION,
+ (UINT8 *) "Malformatted OACK packet"
+ );
+ }
+
+ return EFI_TFTP_ERROR;
+ }
+
+ if (Reply.BlkSize != 0) {
+ Instance->BlkSize = Reply.BlkSize;
+ }
+
+ if (Reply.Timeout != 0) {
+ Instance->Timeout = Reply.Timeout;
+ }
+
+ //
+ // Build a bogus ACK0 packet then pass it to the Mtftp4WrqHandleAck,
+ // which will start the transmission of the first data block.
+ //
+ Bogus.Ack.OpCode = HTONS (EFI_MTFTP4_OPCODE_ACK);
+ Bogus.Ack.Block[0] = 0;
+
+ Status = Mtftp4WrqHandleAck (
+ Instance,
+ &Bogus,
+ sizeof (EFI_MTFTP4_ACK_HEADER),
+ Completed
+ );
+
+ return Status;
+}
+
+
+/**
+ The input process routine for MTFTP upload.
+
+ @param UdpPacket The received MTFTP packet.
+ @param EndPoint The local/remote access point
+ @param IoStatus The result of the packet receiving
+ @param Context Opaque parameter for the callback, which is the
+ MTFTP session.
+**/
+VOID
+EFIAPI
+Mtftp4WrqInput (
+ IN NET_BUF *UdpPacket,
+ IN UDP_END_POINT *EndPoint,
+ IN EFI_STATUS IoStatus,
+ IN VOID *Context
+ )
+{
+ MTFTP4_PROTOCOL *Instance;
+ EFI_MTFTP4_PACKET *Packet;
+ BOOLEAN Completed;
+ EFI_STATUS Status;
+ UINT32 Len;
+ UINT16 Opcode;
+
+ Instance = (MTFTP4_PROTOCOL *) Context;
+ NET_CHECK_SIGNATURE (Instance, MTFTP4_PROTOCOL_SIGNATURE);
+
+ Completed = FALSE;
+ Packet = NULL;
+ Status = EFI_SUCCESS;
+
+ if (EFI_ERROR (IoStatus)) {
+ Status = IoStatus;
+ goto ON_EXIT;
+ }
+
+ ASSERT (UdpPacket != NULL);
+
+ if (UdpPacket->TotalSize < MTFTP4_OPCODE_LEN) {
+ goto ON_EXIT;
+ }
+
+ //
+ // Client send initial request to server's listening port. Server
+ // will select a UDP port to communicate with the client.
+ //
+ if (EndPoint->RemotePort != Instance->ConnectedPort) {
+ if (Instance->ConnectedPort != 0) {
+ goto ON_EXIT;
+ } else {
+ Instance->ConnectedPort = EndPoint->RemotePort;
+ }
+ }
+
+ //
+ // Copy the MTFTP packet to a continuous buffer if it isn't already so.
+ //
+ Len = UdpPacket->TotalSize;
+
+ if (UdpPacket->BlockOpNum > 1) {
+ Packet = AllocatePool (Len);
+
+ if (Packet == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ goto ON_EXIT;
+ }
+
+ NetbufCopy (UdpPacket, 0, Len, (UINT8 *) Packet);
+
+ } else {
+ Packet = (EFI_MTFTP4_PACKET *) NetbufGetByte (UdpPacket, 0, NULL);
+ ASSERT (Packet != NULL);
+ }
+
+ Opcode = NTOHS (Packet->OpCode);
+
+ //
+ // Call the user's CheckPacket if provided. Abort the transmission
+ // if CheckPacket returns an EFI_ERROR code.
+ //
+ if ((Instance->Token->CheckPacket != NULL) &&
+ ((Opcode == EFI_MTFTP4_OPCODE_OACK) || (Opcode == EFI_MTFTP4_OPCODE_ERROR))) {
+
+ Status = Instance->Token->CheckPacket (
+ &Instance->Mtftp4,
+ Instance->Token,
+ (UINT16) Len,
+ Packet
+ );
+
+ if (EFI_ERROR (Status)) {
+ //
+ // Send an error message to the server to inform it
+ //
+ if (Opcode != EFI_MTFTP4_OPCODE_ERROR) {
+ Mtftp4SendError (
+ Instance,
+ EFI_MTFTP4_ERRORCODE_REQUEST_DENIED,
+ (UINT8 *) "User aborted the transfer"
+ );
+ }
+
+ Status = EFI_ABORTED;
+ goto ON_EXIT;
+ }
+ }
+
+ switch (Opcode) {
+ case EFI_MTFTP4_OPCODE_ACK:
+ if (Len != MTFTP4_OPCODE_LEN + MTFTP4_BLKNO_LEN) {
+ goto ON_EXIT;
+ }
+
+ Status = Mtftp4WrqHandleAck (Instance, Packet, Len, &Completed);
+ break;
+
+ case EFI_MTFTP4_OPCODE_OACK:
+ if (Len <= MTFTP4_OPCODE_LEN) {
+ goto ON_EXIT;
+ }
+
+ Status = Mtftp4WrqHandleOack (Instance, Packet, Len, &Completed);
+ break;
+
+ case EFI_MTFTP4_OPCODE_ERROR:
+ Status = EFI_TFTP_ERROR;
+ break;
+
+ default:
+ break;
+ }
+
+ON_EXIT:
+ //
+ // Free the resources, then if !EFI_ERROR (Status) and not completed,
+ // restart the receive, otherwise end the session.
+ //
+ if ((Packet != NULL) && (UdpPacket->BlockOpNum > 1)) {
+ FreePool (Packet);
+ }
+
+ if (UdpPacket != NULL) {
+ NetbufFree (UdpPacket);
+ }
+
+ if (!EFI_ERROR (Status) && !Completed) {
+ Status = UdpIoRecvDatagram (Instance->UnicastPort, Mtftp4WrqInput, Instance, 0);
+ }
+
+ //
+ // Status may have been updated by UdpIoRecvDatagram
+ //
+ if (EFI_ERROR (Status) || Completed) {
+ Mtftp4CleanOperation (Instance, Status);
+ }
+}
+
+
+
+/**
+ Start the MTFTP session for upload.
+
+ It will first init some states, then send the WRQ request packet,
+ and start receiving the packet.
+
+ @param Instance The MTFTP session
+ @param Operation Redundant parameter, which is always
+ EFI_MTFTP4_OPCODE_WRQ here.
+
+ @retval EFI_SUCCESS The upload process has been started.
+ @retval Others Failed to start the upload.
+
+**/
+EFI_STATUS
+Mtftp4WrqStart (
+ IN MTFTP4_PROTOCOL *Instance,
+ IN UINT16 Operation
+ )
+{
+ EFI_STATUS Status;
+
+ //
+ // The valid block number range are [0, 0xffff]. For example:
+ // the client sends an WRQ request to the server, the server
+ // ACK with an ACK0 to let client start transfer the first
+ // packet.
+ //
+ Status = Mtftp4InitBlockRange (&Instance->Blocks, 0, 0xffff);
+
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ Status = Mtftp4SendRequest (Instance);
+
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ return UdpIoRecvDatagram (Instance->UnicastPort, Mtftp4WrqInput, Instance, 0);
+}
+