diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-11 08:17:27 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-11 08:17:27 +0000 |
commit | f215e02bf85f68d3a6106c2a1f4f7f063f819064 (patch) | |
tree | 6bb5b92c046312c4e95ac2620b10ddf482d3fa8b /src/VBox/Devices/EFI/Firmware/NetworkPkg/Mtftp4Dxe | |
parent | Initial commit. (diff) | |
download | virtualbox-f215e02bf85f68d3a6106c2a1f4f7f063f819064.tar.xz virtualbox-f215e02bf85f68d3a6106c2a1f4f7f063f819064.zip |
Adding upstream version 7.0.14-dfsg.upstream/7.0.14-dfsg
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/VBox/Devices/EFI/Firmware/NetworkPkg/Mtftp4Dxe')
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); +} + |