diff options
Diffstat (limited to 'src/VBox/Devices/EFI/Firmware/NetworkPkg/Mtftp6Dxe')
14 files changed, 6254 insertions, 0 deletions
diff --git a/src/VBox/Devices/EFI/Firmware/NetworkPkg/Mtftp6Dxe/ComponentName.c b/src/VBox/Devices/EFI/Firmware/NetworkPkg/Mtftp6Dxe/ComponentName.c new file mode 100644 index 00000000..16e21c4a --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/NetworkPkg/Mtftp6Dxe/ComponentName.c @@ -0,0 +1,424 @@ +/** @file + UEFI Component Name(2) protocol implementation for Mtftp6 driver. + + Copyright (c) 2009 - 2018, Intel Corporation. All rights reserved.<BR> + + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "Mtftp6Impl.h" + + +/** + 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 +Mtftp6ComponentNameGetDriverName ( + 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 bus drivers + attempting to retrieve the name of the bus + controller. It will not be NULL for a bus + driver that attempts 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 +Mtftp6ComponentNameGetControllerName ( + 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 gMtftp6ComponentName = { + Mtftp6ComponentNameGetDriverName, + Mtftp6ComponentNameGetControllerName, + "eng" +}; + +// +// EFI Component Name 2 Protocol +// +GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME2_PROTOCOL gMtftp6ComponentName2 = { + (EFI_COMPONENT_NAME2_GET_DRIVER_NAME) Mtftp6ComponentNameGetDriverName, + (EFI_COMPONENT_NAME2_GET_CONTROLLER_NAME) Mtftp6ComponentNameGetControllerName, + "en" +}; + +GLOBAL_REMOVE_IF_UNREFERENCED EFI_UNICODE_STRING_TABLE mMtftp6DriverNameTable[] = { + { + "eng;en", + L"MTFTP6 Network Service Driver" + }, + { + NULL, + NULL + } +}; + +GLOBAL_REMOVE_IF_UNREFERENCED EFI_UNICODE_STRING_TABLE *gMtftp6ControllerNameTable = 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 +Mtftp6ComponentNameGetDriverName ( + IN EFI_COMPONENT_NAME_PROTOCOL *This, + IN CHAR8 *Language, + OUT CHAR16 **DriverName + ) +{ + return LookupUnicodeString2 ( + Language, + This->SupportedLanguages, + mMtftp6DriverNameTable, + DriverName, + (BOOLEAN)(This == &gMtftp6ComponentName) + ); +} + +/** + Update the component name for the Mtftp6 child handle. + + @param Mtftp6[in] A pointer to the EFI_MTFTP6_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_MTFTP6_PROTOCOL *Mtftp6 + ) +{ + EFI_STATUS Status; + CHAR16 HandleName[128]; + EFI_MTFTP6_MODE_DATA Mtftp6ModeData; + CHAR16 Address[sizeof"ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"]; + + if (Mtftp6 == NULL) { + return EFI_INVALID_PARAMETER; + } + + // + // Format the child name into the string buffer. + // + Status = Mtftp6->GetModeData (Mtftp6, &Mtftp6ModeData); + if (!EFI_ERROR (Status)) { + Status = NetLibIp6ToStr (&Mtftp6ModeData.ConfigData.ServerIp, Address, sizeof(Address)); + if (EFI_ERROR (Status)) { + return Status; + } + UnicodeSPrint (HandleName, sizeof (HandleName), + L"MTFTPv6(ServerIp=%s, InitialServerPort=%d)", + Address, + Mtftp6ModeData.ConfigData.InitialServerPort + ); + } else { + UnicodeSPrint (HandleName, 0x100, L"MTFTPv6(%r)", Status); + } + + if (gMtftp6ControllerNameTable != NULL) { + FreeUnicodeStringTable (gMtftp6ControllerNameTable); + gMtftp6ControllerNameTable = NULL; + } + + Status = AddUnicodeString2 ( + "eng", + gMtftp6ComponentName.SupportedLanguages, + &gMtftp6ControllerNameTable, + HandleName, + TRUE + ); + if (EFI_ERROR (Status)) { + return Status; + } + + return AddUnicodeString2 ( + "en", + gMtftp6ComponentName2.SupportedLanguages, + &gMtftp6ControllerNameTable, + 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 + attempting to retrieve the name of the bus + controller. It will not be NULL for a bus + driver that attempts 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 +Mtftp6ComponentNameGetControllerName ( + 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_MTFTP6_PROTOCOL *Mtftp6; + + // + // Only provide names for child handles. + // + if (ChildHandle == NULL) { + return EFI_UNSUPPORTED; + } + + // + // Make sure this driver produced ChildHandle + // + Status = EfiTestChildHandle ( + ControllerHandle, + ChildHandle, + &gEfiUdp6ProtocolGuid + ); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Retrieve an instance of a produced protocol from ChildHandle + // + Status = gBS->OpenProtocol ( + ChildHandle, + &gEfiMtftp6ProtocolGuid, + (VOID **)&Mtftp6, + NULL, + NULL, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Update the component name for this child handle. + // + Status = UpdateName (Mtftp6); + if (EFI_ERROR (Status)) { + return Status; + } + + return LookupUnicodeString2 ( + Language, + This->SupportedLanguages, + gMtftp6ControllerNameTable, + ControllerName, + (BOOLEAN)(This == &gMtftp6ComponentName) + ); +} + diff --git a/src/VBox/Devices/EFI/Firmware/NetworkPkg/Mtftp6Dxe/Mtftp6Driver.c b/src/VBox/Devices/EFI/Firmware/NetworkPkg/Mtftp6Dxe/Mtftp6Driver.c new file mode 100644 index 00000000..7b3461cf --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/NetworkPkg/Mtftp6Dxe/Mtftp6Driver.c @@ -0,0 +1,749 @@ +/** @file + Driver Binding functions and Service Binding functions + implementation for Mtftp6 Driver. + + Copyright (c) 2009 - 2018, Intel Corporation. All rights reserved.<BR> + + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "Mtftp6Impl.h" + + +EFI_DRIVER_BINDING_PROTOCOL gMtftp6DriverBinding = { + Mtftp6DriverBindingSupported, + Mtftp6DriverBindingStart, + Mtftp6DriverBindingStop, + 0xa, + NULL, + NULL +}; + +EFI_SERVICE_BINDING_PROTOCOL gMtftp6ServiceBindingTemplate = { + Mtftp6ServiceBindingCreateChild, + Mtftp6ServiceBindingDestroyChild +}; + + +/** + Destroy the MTFTP6 service. The MTFTP6 service may be partly initialized, + or partly destroyed. If a resource is destroyed, it is marked as such in + case the destroy failed and is called again later. + + @param[in] Service The MTFTP6 service to be destroyed. + +**/ +VOID +Mtftp6DestroyService ( + IN MTFTP6_SERVICE *Service + ) +{ + // + // Make sure all children instances have been already destroyed. + // + ASSERT (Service->ChildrenNum == 0); + + if (Service->DummyUdpIo != NULL) { + UdpIoFreeIo (Service->DummyUdpIo); + } + + if (Service->Timer != NULL) { + gBS->CloseEvent (Service->Timer); + } + + FreePool (Service); +} + + +/** + Create then initialize a MTFTP6 service binding instance. + + @param[in] Controller The controller to install the MTFTP6 service + binding on. + @param[in] Image The driver binding image of the MTFTP6 driver. + @param[out] Service The variable to receive the created service + binding instance. + + @retval EFI_OUT_OF_RESOURCES Failed to allocate resources 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 +Mtftp6CreateService ( + IN EFI_HANDLE Controller, + IN EFI_HANDLE Image, + OUT MTFTP6_SERVICE **Service + ) +{ + MTFTP6_SERVICE *Mtftp6Srv; + EFI_STATUS Status; + + ASSERT (Service != NULL); + + *Service = NULL; + Mtftp6Srv = AllocateZeroPool (sizeof (MTFTP6_SERVICE)); + + if (Mtftp6Srv == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + Mtftp6Srv->Signature = MTFTP6_SERVICE_SIGNATURE; + Mtftp6Srv->Controller = Controller; + Mtftp6Srv->Image = Image; + Mtftp6Srv->ChildrenNum = 0; + + CopyMem ( + &Mtftp6Srv->ServiceBinding, + &gMtftp6ServiceBindingTemplate, + sizeof (EFI_SERVICE_BINDING_PROTOCOL) + ); + + InitializeListHead (&Mtftp6Srv->Children); + + // + // Create a internal timer for all instances. + // + Status = gBS->CreateEvent ( + EVT_NOTIFY_SIGNAL | EVT_TIMER, + TPL_CALLBACK, + Mtftp6OnTimerTick, + Mtftp6Srv, + &Mtftp6Srv->Timer + ); + + if (EFI_ERROR (Status)) { + FreePool (Mtftp6Srv); + return Status; + } + + // + // Create a dummy Udp6Io to build parent-child relationship between Udp6 driver + // and Mtftp6 driver. + // + Mtftp6Srv->DummyUdpIo = UdpIoCreateIo ( + Controller, + Image, + Mtftp6ConfigDummyUdpIo, + UDP_IO_UDP6_VERSION, + NULL + ); + + if (Mtftp6Srv->DummyUdpIo == NULL) { + gBS->CloseEvent (Mtftp6Srv->Timer); + FreePool (Mtftp6Srv); + return EFI_DEVICE_ERROR; + } + + *Service = Mtftp6Srv; + return EFI_SUCCESS; +} + + +/** + Destroy the MTFTP6 instance and recycle the resources. + + @param[in] Instance The pointer to the MTFTP6 instance. + +**/ +VOID +Mtftp6DestroyInstance ( + IN MTFTP6_INSTANCE *Instance + ) +{ + LIST_ENTRY *Entry; + LIST_ENTRY *Next; + MTFTP6_BLOCK_RANGE *Block; + + if (Instance->Config != NULL) { + FreePool (Instance->Config); + } + + if (Instance->Token != NULL && Instance->Token->Event != NULL) { + gBS->SignalEvent (Instance->Token->Event); + } + + if (Instance->LastPacket != NULL) { + NetbufFree (Instance->LastPacket); + } + + if (Instance->UdpIo!= NULL) { + UdpIoFreeIo (Instance->UdpIo); + } + + if (Instance->McastUdpIo != NULL) { + UdpIoFreeIo (Instance->McastUdpIo); + } + + NET_LIST_FOR_EACH_SAFE (Entry, Next, &Instance->BlkList) { + Block = NET_LIST_USER_STRUCT (Entry, MTFTP6_BLOCK_RANGE, Link); + RemoveEntryList (Entry); + FreePool (Block); + } + + FreePool (Instance); +} + + +/** + Create the MTFTP6 instance and initialize it. + + @param[in] Service The pointer to the MTFTP6 service. + @param[out] Instance The pointer to the MTFTP6 instance. + + @retval EFI_OUT_OF_RESOURCES Failed to allocate resources. + @retval EFI_SUCCESS The MTFTP6 instance is created. + +**/ +EFI_STATUS +Mtftp6CreateInstance ( + IN MTFTP6_SERVICE *Service, + OUT MTFTP6_INSTANCE **Instance + ) +{ + MTFTP6_INSTANCE *Mtftp6Ins; + + *Instance = NULL; + Mtftp6Ins = AllocateZeroPool (sizeof (MTFTP6_INSTANCE)); + + if (Mtftp6Ins == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + Mtftp6Ins->Signature = MTFTP6_INSTANCE_SIGNATURE; + Mtftp6Ins->InDestroy = FALSE; + Mtftp6Ins->Service = Service; + + CopyMem ( + &Mtftp6Ins->Mtftp6, + &gMtftp6ProtocolTemplate, + sizeof (EFI_MTFTP6_PROTOCOL) + ); + + InitializeListHead (&Mtftp6Ins->Link); + InitializeListHead (&Mtftp6Ins->BlkList); + + *Instance = Mtftp6Ins; + + return EFI_SUCCESS; +} + + +/** + 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 +Mtftp6DestroyChildEntryInHandleBuffer ( + IN LIST_ENTRY *Entry, + IN VOID *Context + ) +{ + MTFTP6_INSTANCE *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, MTFTP6_INSTANCE, Link, MTFTP6_INSTANCE_SIGNATURE); + ServiceBinding = ((MTFTP6_DESTROY_CHILD_IN_HANDLE_BUF_CONTEXT *) Context)->ServiceBinding; + NumberOfChildren = ((MTFTP6_DESTROY_CHILD_IN_HANDLE_BUF_CONTEXT *) Context)->NumberOfChildren; + ChildHandleBuffer = ((MTFTP6_DESTROY_CHILD_IN_HANDLE_BUF_CONTEXT *) Context)->ChildHandleBuffer; + + if (!NetIsInHandleBuffer (Instance->Handle, NumberOfChildren, ChildHandleBuffer)) { + return EFI_SUCCESS; + } + + return ServiceBinding->DestroyChild (ServiceBinding, Instance->Handle); +} + + +/** + This is the declaration of an EFI image entry point. This entry point is + the same for UEFI Applications, UEFI OS Loaders, and UEFI Drivers, including + both device drivers and bus drivers. + + Entry point of the MTFTP6 driver to install various protocols. + + @param[in] ImageHandle The firmware allocated handle for the UEFI image. + @param[in] SystemTable The pointer to the EFI System Table. + + @retval EFI_SUCCESS The operation completed successfully. + @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a lack of resources. + +**/ +EFI_STATUS +EFIAPI +Mtftp6DriverEntryPoint ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + return EfiLibInstallDriverBindingComponentName2 ( + ImageHandle, + SystemTable, + &gMtftp6DriverBinding, + ImageHandle, + &gMtftp6ComponentName, + &gMtftp6ComponentName2 + ); +} + + +/** + Test to see if this driver supports Controller. This service + is called by the EFI boot service ConnectController(). In + order to make drivers as small as possible, there are calling + restrictions for this service. ConnectController() must + follow these calling restrictions. If any other agent wishes to call + Supported(), it must also follow these calling restrictions. + + @param[in] This Protocol instance pointer. + @param[in] Controller Handle of device to test + @param[in] RemainingDevicePath Optional parameter use to pick a specific child. + device to start. + + @retval EFI_SUCCESS This driver supports this device. + @retval Others This driver does not support this device. + +**/ +EFI_STATUS +EFIAPI +Mtftp6DriverBindingSupported ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath + ) +{ + return gBS->OpenProtocol ( + Controller, + &gEfiUdp6ServiceBindingProtocolGuid, + NULL, + This->DriverBindingHandle, + Controller, + EFI_OPEN_PROTOCOL_TEST_PROTOCOL + ); +} + + +/** + Start this driver on Controller. This service is called by the + EFI boot service ConnectController(). In order to make + drivers as small as possible, there are calling restrictions for + this service. ConnectController() must follow these + calling restrictions. If any other agent wishes to call Start() it + must also follow these calling restrictions. + + @param[in] This Protocol instance pointer. + @param[in] Controller Handle of device to bind driver to. + @param[in] RemainingDevicePath Optional parameter use to pick a specific child + device to start. + + @retval EFI_SUCCESS This driver is added to Controller. + @retval EFI_ALREADY_STARTED This driver is already running on Controller. + @retval Others This driver does not support this device. + +**/ +EFI_STATUS +EFIAPI +Mtftp6DriverBindingStart ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath + ) +{ + MTFTP6_SERVICE *Service; + EFI_STATUS Status; + + // + // Directly return if driver is already running on this Nic handle. + // + Status = gBS->OpenProtocol ( + Controller, + &gEfiMtftp6ServiceBindingProtocolGuid, + NULL, + This->DriverBindingHandle, + Controller, + EFI_OPEN_PROTOCOL_TEST_PROTOCOL + ); + + if (!EFI_ERROR (Status)) { + return EFI_ALREADY_STARTED; + } + + // + // Create Mtftp6 service for this Nic handle + // + Status = Mtftp6CreateService ( + Controller, + This->DriverBindingHandle, + &Service + ); + + if (EFI_ERROR (Status)) { + return Status; + } + + ASSERT (Service != NULL); + + // + // Start the internal timer to track the packet retransmission. + // + Status = gBS->SetTimer ( + Service->Timer, + TimerPeriodic, + TICKS_PER_SECOND + ); + + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + // + // Install the Mtftp6 service on the Nic handle. + // + Status = gBS->InstallMultipleProtocolInterfaces ( + &Controller, + &gEfiMtftp6ServiceBindingProtocolGuid, + &Service->ServiceBinding, + NULL + ); + + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + return EFI_SUCCESS; + +ON_ERROR: + + Mtftp6DestroyService (Service); + return Status; +} + + +/** + Stop this driver on Controller. This service is called by the + EFI boot service DisconnectController(). In order to + make drivers as small as possible, there are calling + restrictions for this service. DisconnectController() + must follow these calling restrictions. If any other agent wishes + to call Stop(), it must also follow these calling restrictions. + + @param[in] This Protocol instance pointer. + @param[in] Controller Handle of device to stop driver on + @param[in] NumberOfChildren Number of Handles in ChildHandleBuffer. If number of + children is zero, stop the entire bus driver. + @param[in] ChildHandleBuffer List of Child Handles to Stop. + + @retval EFI_SUCCESS This driver is removed Controller. + @retval EFI_DEVICE_ERROR An unexpected error. + @retval Others This driver was not removed from this device. + +**/ +EFI_STATUS +EFIAPI +Mtftp6DriverBindingStop ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN UINTN NumberOfChildren, + IN EFI_HANDLE *ChildHandleBuffer + ) +{ + EFI_SERVICE_BINDING_PROTOCOL *ServiceBinding; + MTFTP6_SERVICE *Service; + EFI_HANDLE NicHandle; + EFI_STATUS Status; + LIST_ENTRY *List; + MTFTP6_DESTROY_CHILD_IN_HANDLE_BUF_CONTEXT Context; + + // + // Locate the Nic handle to retrieve the Mtftp6 private data. + // + NicHandle = NetLibGetNicHandle (Controller, &gEfiUdp6ProtocolGuid); + + if (NicHandle == NULL) { + return EFI_SUCCESS; + } + + Status = gBS->OpenProtocol ( + NicHandle, + &gEfiMtftp6ServiceBindingProtocolGuid, + (VOID **) &ServiceBinding, + This->DriverBindingHandle, + NicHandle, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + + if (EFI_ERROR (Status)) { + return EFI_DEVICE_ERROR; + } + + Service = MTFTP6_SERVICE_FROM_THIS (ServiceBinding); + + if (!IsListEmpty (&Service->Children)) { + // + // Destroy the Mtftp6 child instance in ChildHandleBuffer. + // + List = &Service->Children; + Context.ServiceBinding = ServiceBinding; + Context.NumberOfChildren = NumberOfChildren; + Context.ChildHandleBuffer = ChildHandleBuffer; + Status = NetDestroyLinkList ( + List, + Mtftp6DestroyChildEntryInHandleBuffer, + &Context, + NULL + ); + } + + if (NumberOfChildren == 0 && IsListEmpty (&Service->Children)) { + // + // Destroy the Mtftp6 service if there is no Mtftp6 child instance left. + // + gBS->UninstallProtocolInterface ( + NicHandle, + &gEfiMtftp6ServiceBindingProtocolGuid, + ServiceBinding + ); + + Mtftp6DestroyService (Service); + Status = EFI_SUCCESS; + } + + return Status; +} + + +/** + Creates a child handle and installs a protocol. + + The CreateChild() function installs a protocol on ChildHandle. + If ChildHandle is a pointer to NULL, then a new handle is created and returned in ChildHandle. + If ChildHandle is not a pointer to NULL, then the protocol installs on the existing ChildHandle. + + @param[in] This Pointer to the EFI_SERVICE_BINDING_PROTOCOL instance. + @param[in, out] ChildHandle Pointer to the handle of the child to create. If it is NULL, + then a new handle is created. If it is a pointer to an existing + UEFI handle, then the protocol is added to the existing UEFI handle. + + @retval EFI_SUCCESS The protocol was added to ChildHandle. + @retval EFI_INVALID_PARAMETER ChildHandle is NULL. + @retval Others The child handle was not created. + +**/ +EFI_STATUS +EFIAPI +Mtftp6ServiceBindingCreateChild ( + IN EFI_SERVICE_BINDING_PROTOCOL *This, + IN OUT EFI_HANDLE *ChildHandle + ) +{ + MTFTP6_SERVICE *Service; + MTFTP6_INSTANCE *Instance; + EFI_STATUS Status; + EFI_TPL OldTpl; + VOID *Udp6; + + if (This == NULL || ChildHandle == NULL) { + return EFI_INVALID_PARAMETER; + } + + Service = MTFTP6_SERVICE_FROM_THIS (This); + + Status = Mtftp6CreateInstance (Service, &Instance); + + if (EFI_ERROR (Status)) { + return Status; + } + + ASSERT (Instance != NULL); + + // + // Install the Mtftp6 protocol on the new child handle. + // + Status = gBS->InstallMultipleProtocolInterfaces ( + ChildHandle, + &gEfiMtftp6ProtocolGuid, + &Instance->Mtftp6, + NULL + ); + + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + Instance->Handle = *ChildHandle; + + // + // Open the Udp6 protocol by child. + // + Status = gBS->OpenProtocol ( + Service->DummyUdpIo->UdpHandle, + &gEfiUdp6ProtocolGuid, + (VOID **) &Udp6, + gMtftp6DriverBinding.DriverBindingHandle, + Instance->Handle, + EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER + ); + + if (EFI_ERROR (Status)) { + gBS->UninstallMultipleProtocolInterfaces ( + Instance->Handle, + &gEfiMtftp6ProtocolGuid, + &Instance->Mtftp6, + NULL + ); + + goto ON_ERROR; + } + + // + // Add the new Mtftp6 instance into the children list of Mtftp6 service. + // + OldTpl = gBS->RaiseTPL (TPL_CALLBACK); + + InsertTailList (&Service->Children, &Instance->Link); + Service->ChildrenNum++; + + gBS->RestoreTPL (OldTpl); + return EFI_SUCCESS; + +ON_ERROR: + + Mtftp6DestroyInstance (Instance); + return Status; +} + + +/** + Destroys a child handle with a protocol installed on it. + + The DestroyChild() function does the opposite of CreateChild(). It removes a protocol + that was installed by CreateChild() from ChildHandle. If the removed protocol is the + last protocol on ChildHandle, then ChildHandle is destroyed. + + @param[in] This Pointer to the EFI_SERVICE_BINDING_PROTOCOL instance. + @param[in] ChildHandle Handle of the child to destroy. + + @retval EFI_SUCCESS The protocol was removed from ChildHandle. + @retval EFI_UNSUPPORTED ChildHandle does not support the protocol that is being removed. + @retval EFI_INVALID_PARAMETER Child handle is NULL. + @retval Others The child handle was not destroyed + +**/ +EFI_STATUS +EFIAPI +Mtftp6ServiceBindingDestroyChild ( + IN EFI_SERVICE_BINDING_PROTOCOL *This, + IN EFI_HANDLE ChildHandle + ) +{ + MTFTP6_SERVICE *Service; + MTFTP6_INSTANCE *Instance; + EFI_MTFTP6_PROTOCOL *Mtftp6; + EFI_STATUS Status; + EFI_TPL OldTpl; + + if (This == NULL || ChildHandle == NULL) { + return EFI_INVALID_PARAMETER; + } + + // + // Locate the Nic handle to retrieve the Mtftp6 private data. + // + Status = gBS->OpenProtocol ( + ChildHandle, + &gEfiMtftp6ProtocolGuid, + (VOID **) &Mtftp6, + gMtftp6DriverBinding.DriverBindingHandle, + ChildHandle, + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + + if (EFI_ERROR (Status)) { + return EFI_UNSUPPORTED; + } + + Instance = MTFTP6_INSTANCE_FROM_THIS (Mtftp6); + Service = MTFTP6_SERVICE_FROM_THIS (This); + + if (Instance->Service != Service) { + return EFI_INVALID_PARAMETER; + } + + // + // Check whether the instance already in Destroy state. + // + if (Instance->InDestroy) { + return EFI_SUCCESS; + } + + OldTpl = gBS->RaiseTPL (TPL_CALLBACK); + + Instance->InDestroy = TRUE; + + gBS->CloseProtocol ( + Service->DummyUdpIo->UdpHandle, + &gEfiUdp6ProtocolGuid, + gMtftp6DriverBinding.DriverBindingHandle, + ChildHandle + ); + + if (Instance->UdpIo != NULL) { + gBS->CloseProtocol ( + Instance->UdpIo->UdpHandle, + &gEfiUdp6ProtocolGuid, + gMtftp6DriverBinding.DriverBindingHandle, + Instance->Handle + ); + } + + if (Instance->McastUdpIo != NULL) { + gBS->CloseProtocol ( + Instance->McastUdpIo->UdpHandle, + &gEfiUdp6ProtocolGuid, + gMtftp6DriverBinding.DriverBindingHandle, + Instance->Handle + ); + } + + // + // Uninstall the MTFTP6 protocol first to enable a top down destruction. + // + gBS->RestoreTPL (OldTpl); + Status = gBS->UninstallProtocolInterface ( + ChildHandle, + &gEfiMtftp6ProtocolGuid, + Mtftp6 + ); + OldTpl = gBS->RaiseTPL (TPL_CALLBACK); + if (EFI_ERROR (Status)) { + Instance->InDestroy = FALSE; + gBS->RestoreTPL (OldTpl); + return Status; + } + + // + // Remove the Mtftp6 instance from the children list of Mtftp6 service. + // + RemoveEntryList (&Instance->Link); + Service->ChildrenNum --; + + gBS->RestoreTPL (OldTpl); + + Mtftp6DestroyInstance (Instance); + + return EFI_SUCCESS; +} diff --git a/src/VBox/Devices/EFI/Firmware/NetworkPkg/Mtftp6Dxe/Mtftp6Driver.h b/src/VBox/Devices/EFI/Firmware/NetworkPkg/Mtftp6Dxe/Mtftp6Driver.h new file mode 100644 index 00000000..b51c275a --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/NetworkPkg/Mtftp6Dxe/Mtftp6Driver.h @@ -0,0 +1,146 @@ +/** @file + Driver Binding functions and Service Binding functions + declaration for Mtftp6 Driver. + + Copyright (c) 2009 - 2012, Intel Corporation. All rights reserved.<BR> + + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef __EFI_MTFTP6_DRIVER_H__ +#define __EFI_MTFTP6_DRIVER_H__ + +#include <Protocol/ServiceBinding.h> + +extern EFI_COMPONENT_NAME_PROTOCOL gMtftp6ComponentName; +extern EFI_COMPONENT_NAME2_PROTOCOL gMtftp6ComponentName2; +extern EFI_UNICODE_STRING_TABLE *gMtftp6ControllerNameTable; + +/** + Test to see if this driver supports Controller. This service + is called by the EFI boot service ConnectController(). In + order to make drivers as small as possible, there are a few calling + restrictions for this service. ConnectController() must + follow these calling restrictions. If any other agent wishes to call + Supported(), it must also follow these calling restrictions. + + @param[in] This Protocol instance pointer. + @param[in] Controller Handle of device to test. + @param[in] RemainingDevicePath Optional parameter use to pick a specific child + device to start. + + @retval EFI_SUCCESS This driver supports this device. + @retval Others This driver does not support this device. + +**/ +EFI_STATUS +EFIAPI +Mtftp6DriverBindingSupported ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath + ); + +/** + Start this driver on Controller. This service is called by the + EFI boot service ConnectController(). In order to make + drivers as small as possible, there are calling restrictions for + this service. ConnectController() must follow these + calling restrictions. If any other agent wishes to call Start() it + must also follow these calling restrictions. + + @param[in] This Protocol instance pointer. + @param[in] Controller Handle of device to bind driver to. + @param[in] RemainingDevicePath Optional parameter use to pick a specific child + device to start. + + @retval EFI_SUCCESS This driver is added to Controller. + @retval EFI_ALREADY_STARTED This driver is already running on Controller. + @retval Others This driver does not support this device. + +**/ +EFI_STATUS +EFIAPI +Mtftp6DriverBindingStart ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath + ); + +/** + Stop this driver on Controller. This service is called by the + EFI boot service DisconnectController(). In order to + make drivers as small as possible, there are calling + restrictions for this service. DisconnectController() + must follow these calling restrictions. If any other agent wishes + to call Stop(), it must also follow these calling restrictions. + + @param[in] This Protocol instance pointer. + @param[in] Controller Handle of device to stop driver on + @param[in] NumberOfChildren Number of Handles in ChildHandleBuffer. If number of + children is zero, stop the entire bus driver. + @param[in] ChildHandleBuffer List of Child Handles to Stop. + + @retval EFI_SUCCESS This driver is removed Controller. + @retval EFI_DEVICE_ERROR An unexpected error. + @retval Others This driver was not removed from this device. + +**/ +EFI_STATUS +EFIAPI +Mtftp6DriverBindingStop ( + IN EFI_DRIVER_BINDING_PROTOCOL *This, + IN EFI_HANDLE Controller, + IN UINTN NumberOfChildren, + IN EFI_HANDLE *ChildHandleBuffer + ); + +/** + Creates a child handle and installs a protocol. + + The CreateChild() function installs a protocol on ChildHandle. + If ChildHandle is a pointer to NULL, then a new handle is created and returned in ChildHandle. + If ChildHandle is not a pointer to NULL, then the protocol installs on the existing ChildHandle. + + @param[in] This Pointer to the EFI_SERVICE_BINDING_PROTOCOL instance. + @param[in, out] ChildHandle Pointer to the handle of the child to create. If it is NULL, + then a new handle is created. If it is a pointer to an existing + UEFI handle, then the protocol is added to the existing UEFI handle. + + @retval EFI_SUCCESS The protocol was added to ChildHandle. + @retval EFI_INVALID_PARAMETER ChildHandle is NULL. + @retval Others The child handle was not created. + +**/ +EFI_STATUS +EFIAPI +Mtftp6ServiceBindingCreateChild ( + IN EFI_SERVICE_BINDING_PROTOCOL *This, + IN OUT EFI_HANDLE *ChildHandle + ); + +/** + Destroys a child handle with a protocol installed on it. + + The DestroyChild() function does the opposite of CreateChild(). It removes a protocol + that was installed by CreateChild() from ChildHandle. If the removed protocol is the + last protocol on ChildHandle, then ChildHandle is destroyed. + + @param[in] This Pointer to the EFI_SERVICE_BINDING_PROTOCOL instance. + @param[in] ChildHandle Handle of the child to destroy. + + @retval EFI_SUCCESS The protocol was removed from ChildHandle. + @retval EFI_UNSUPPORTED ChildHandle does not support the protocol that is being removed. + @retval EFI_INVALID_PARAMETER Child handle is NULL. + @retval Others The child handle was not destroyed + +**/ +EFI_STATUS +EFIAPI +Mtftp6ServiceBindingDestroyChild ( + IN EFI_SERVICE_BINDING_PROTOCOL *This, + IN EFI_HANDLE ChildHandle + ); + +#endif diff --git a/src/VBox/Devices/EFI/Firmware/NetworkPkg/Mtftp6Dxe/Mtftp6Dxe.inf b/src/VBox/Devices/EFI/Firmware/NetworkPkg/Mtftp6Dxe/Mtftp6Dxe.inf new file mode 100644 index 00000000..a6730c67 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/NetworkPkg/Mtftp6Dxe/Mtftp6Dxe.inf @@ -0,0 +1,70 @@ +## @file +# Client-side Mtftp6 service. +# +# This module produces EFI MTFTPv6 Protocol which provides basic services for +# client-side unicast and/or multicast TFTP. +# +# Copyright (c) 2009 - 2018, Intel Corporation. All rights reserved.<BR> +# +# SPDX-License-Identifier: BSD-2-Clause-Patent +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = Mtftp6Dxe + FILE_GUID = 99F03B99-98D8-49dd-A8D3-3219D0FFE41E + MODULE_TYPE = UEFI_DRIVER + VERSION_STRING = 1.0 + ENTRY_POINT = Mtftp6DriverEntryPoint + UNLOAD_IMAGE = NetLibDefaultUnload + MODULE_UNI_FILE = Mtftp6Dxe.uni + +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = IA32 X64 EBC +# +# DRIVER_BINDING = gMtftp6DriverBinding +# COMPONENT_NAME = gMtftp6ComponentName +# COMPONENT_NAME2 = gMtftp6ComponentName2 +# + +[Sources] + Mtftp6Driver.c + Mtftp6Driver.h + Mtftp6Impl.c + Mtftp6Impl.h + Mtftp6Option.c + Mtftp6Option.h + Mtftp6Support.h + Mtftp6Support.c + Mtftp6Rrq.c + Mtftp6Wrq.c + ComponentName.c + + +[Packages] + MdePkg/MdePkg.dec + NetworkPkg/NetworkPkg.dec + + +[LibraryClasses] + UefiLib + BaseLib + UefiBootServicesTableLib + UefiRuntimeServicesTableLib + UefiDriverEntryPoint + DebugLib + NetLib + UdpIoLib + + +[Protocols] + gEfiUdp6ServiceBindingProtocolGuid ## TO_START + gEfiUdp6ProtocolGuid ## TO_START + gEfiMtftp6ServiceBindingProtocolGuid ## BY_START + gEfiMtftp6ProtocolGuid ## BY_START + +[UserExtensions.TianoCore."ExtraFiles"] + Mtftp6DxeExtra.uni diff --git a/src/VBox/Devices/EFI/Firmware/NetworkPkg/Mtftp6Dxe/Mtftp6Dxe.uni b/src/VBox/Devices/EFI/Firmware/NetworkPkg/Mtftp6Dxe/Mtftp6Dxe.uni new file mode 100644 index 00000000..627442cf --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/NetworkPkg/Mtftp6Dxe/Mtftp6Dxe.uni @@ -0,0 +1,17 @@ +// /** @file
+// Client-side Mtftp6 service.
+//
+// This module produces EFI MTFTPv6 Protocol which provides basic services for
+// client-side unicast and/or multicast TFTP.
+//
+// Copyright (c) 2009 - 2018, Intel Corporation. All rights reserved.<BR>
+//
+// SPDX-License-Identifier: BSD-2-Clause-Patent
+//
+// **/
+
+
+#string STR_MODULE_ABSTRACT #language en-US "Client-side Mtftp6 service"
+
+#string STR_MODULE_DESCRIPTION #language en-US "This module produces EFI MTFTPv6 Protocol which provides basic services for client-side unicast and/or multicast TFTP."
+
diff --git a/src/VBox/Devices/EFI/Firmware/NetworkPkg/Mtftp6Dxe/Mtftp6DxeExtra.uni b/src/VBox/Devices/EFI/Firmware/NetworkPkg/Mtftp6Dxe/Mtftp6DxeExtra.uni new file mode 100644 index 00000000..cedd2be9 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/NetworkPkg/Mtftp6Dxe/Mtftp6DxeExtra.uni @@ -0,0 +1,14 @@ +// /** @file
+// Mtftp6Dxe Localized Strings and Content
+//
+// Copyright (c) 2013 - 2014, Intel Corporation. All rights reserved.<BR>
+//
+// SPDX-License-Identifier: BSD-2-Clause-Patent
+//
+// **/
+
+#string STR_PROPERTIES_MODULE_NAME
+#language en-US
+"MTFTP6 DXE"
+
+
diff --git a/src/VBox/Devices/EFI/Firmware/NetworkPkg/Mtftp6Dxe/Mtftp6Impl.c b/src/VBox/Devices/EFI/Firmware/NetworkPkg/Mtftp6Dxe/Mtftp6Impl.c new file mode 100644 index 00000000..bbb10e90 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/NetworkPkg/Mtftp6Dxe/Mtftp6Impl.c @@ -0,0 +1,641 @@ +/** @file + This EFI_MTFTP6_PROTOCOL interface 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 + + Copyright (c) 2009 - 2012, Intel Corporation. All rights reserved.<BR> + + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "Mtftp6Impl.h" + +EFI_MTFTP6_PROTOCOL gMtftp6ProtocolTemplate = { + EfiMtftp6GetModeData, + EfiMtftp6Configure, + EfiMtftp6GetInfo, + EfiMtftp6ParseOptions, + EfiMtftp6ReadFile, + EfiMtftp6WriteFile, + EfiMtftp6ReadDirectory, + EfiMtftp6Poll + }; + +/** + Returns the current operating mode data for the MTFTP6 instance. + + The GetModeData() function returns the current operating mode and + cached data packet for the MTFTP6 instance. + + @param[in] This Pointer to the EFI_MTFTP6_PROTOCOL instance. + @param[out] ModeData The buffer in which the EFI MTFTPv6 Protocol driver mode + data is returned. + + @retval EFI_SUCCESS The configuration data was returned successfully. + @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 +EfiMtftp6GetModeData ( + IN EFI_MTFTP6_PROTOCOL *This, + OUT EFI_MTFTP6_MODE_DATA *ModeData + ) +{ + MTFTP6_INSTANCE *Instance; + EFI_TPL OldTpl; + + if (This == NULL || ModeData == NULL) { + return EFI_INVALID_PARAMETER; + } + + OldTpl = gBS->RaiseTPL (TPL_CALLBACK); + Instance = MTFTP6_INSTANCE_FROM_THIS (This); + + // + // Copy back the configure data if the instance already configured. + // + if (Instance->Config != NULL) { + CopyMem ( + &ModeData->ConfigData, + Instance->Config, + sizeof (EFI_MTFTP6_CONFIG_DATA) + ); + } else { + ZeroMem ( + &ModeData->ConfigData, + sizeof (EFI_MTFTP6_CONFIG_DATA) + ); + } + + // + // Set the current support options in mode data. + // + ModeData->SupportedOptionCount = MTFTP6_SUPPORTED_OPTIONS_NUM; + ModeData->SupportedOptions = (UINT8 **) mMtftp6SupportedOptions; + + gBS->RestoreTPL (OldTpl); + + return EFI_SUCCESS; +} + + +/** + Initializes, changes, or resets the default operational setting for + this EFI MTFTPv6 Protocol driver instance. + + The Configure() function is used to set and change the configuration + data for this EFI MTFTPv6 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 MTFTPv6 Protocol + driver instance configuration data, the client can connect to + different MTFTPv6 servers. The configuration parameters in + MtftpConfigData are used as the default parameters in later MTFTPv6 + operations and can be overridden in later operations. + + @param[in] This Pointer to the EFI_MTFTP6_PROTOCOL instance. + @param[in] MtftpConfigData Pointer to the configuration data structure. + + @retval EFI_SUCCESS The EFI MTFTPv6 Protocol instance was configured successfully. + @retval EFI_INVALID_PARAMETER One or more following conditions are TRUE: + - This is NULL. + - MtftpConfigData.StationIp is neither zero nor one + of the configured IP addresses in the underlying IPv6 driver. + - MtftpConfigData.ServerIp is not a valid IPv6 unicast address. + Note: It does not match the UEFI 2.3 Specification. + @retval EFI_ACCESS_DENIED - The configuration could not be changed at this time because there + is some MTFTP background operation in progress. + - MtftpConfigData.LocalPort is already in use. + Note: It does not match the UEFI 2.3 Specification. + @retval EFI_NO_MAPPING The underlying IPv6 driver was responsible for choosing a source + address for this instance, but no source address was available for use. + @retval EFI_OUT_OF_RESOURCES The EFI MTFTPv6 Protocol driver instance data could not be + allocated. + Note: It is not defined in the UEFI 2.3 Specification. + @retval EFI_DEVICE_ERROR An unexpected system or network error occurred. The EFI + MTFTPv6 Protocol driver instance is not configured. + Note: It is not defined in the UEFI 2.3 Specification. + +**/ +EFI_STATUS +EFIAPI +EfiMtftp6Configure ( + IN EFI_MTFTP6_PROTOCOL *This, + IN EFI_MTFTP6_CONFIG_DATA *MtftpConfigData OPTIONAL + ) +{ + MTFTP6_SERVICE *Service; + MTFTP6_INSTANCE *Instance; + EFI_UDP6_PROTOCOL *Udp6; + EFI_UDP6_CONFIG_DATA Udp6Cfg; + EFI_STATUS Status; + EFI_TPL OldTpl; + + if (This == NULL) { + return EFI_INVALID_PARAMETER; + } + + if (MtftpConfigData != NULL && !NetIp6IsValidUnicast (&MtftpConfigData->ServerIp)) { + return EFI_INVALID_PARAMETER; + } + + OldTpl = gBS->RaiseTPL (TPL_CALLBACK); + Instance = MTFTP6_INSTANCE_FROM_THIS (This); + Service = Instance->Service; + Status = EFI_SUCCESS; + + if (MtftpConfigData == NULL) { + // + // Configure the instance as NULL to abort the current session. + // + Mtftp6OperationClean (Instance, EFI_ABORTED); + FreePool (Instance->Config); + Instance->Config = NULL; + } else { + // + // It's not allowed to configure one instance twice without configure null. + // + if (Instance->Config != NULL) { + Status = EFI_ACCESS_DENIED; + goto ON_EXIT; + } + // + // Allocate the configure buffer of the instance and store the user's data. + // + Instance->Config = AllocateZeroPool (sizeof (EFI_MTFTP6_CONFIG_DATA)); + + if (Instance->Config == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto ON_EXIT; + } + + CopyMem (Instance->Config, MtftpConfigData, sizeof (EFI_MTFTP6_CONFIG_DATA)); + + // + // Don't configure the udpio here because each operation might override + // the configuration, so delay udpio configuration in each operation. + // + if (Instance->UdpIo == NULL) { + Instance->UdpIo = UdpIoCreateIo ( + Service->Controller, + Service->Image, + Mtftp6ConfigDummyUdpIo, + UDP_IO_UDP6_VERSION, + NULL + ); + if (Instance->UdpIo != NULL) { + Status = gBS->OpenProtocol ( + Instance->UdpIo->UdpHandle, + &gEfiUdp6ProtocolGuid, + (VOID **) &Udp6, + Service->Image, + Instance->Handle, + EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER + ); + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + } + } + + if (Instance->UdpIo == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto ON_EXIT; + } + + // + // Continue to configure the downside Udp6 instance by user's data. + // + ZeroMem (&Udp6Cfg, sizeof (EFI_UDP6_CONFIG_DATA)); + + Udp6Cfg.AcceptPromiscuous = FALSE; + Udp6Cfg.AcceptAnyPort = FALSE; + Udp6Cfg.AllowDuplicatePort = FALSE; + Udp6Cfg.TrafficClass = 0; + Udp6Cfg.HopLimit = 128; + Udp6Cfg.ReceiveTimeout = 0; + Udp6Cfg.TransmitTimeout = 0; + Udp6Cfg.StationPort = Instance->Config->LocalPort; + Udp6Cfg.RemotePort = Instance->Config->InitialServerPort; + + CopyMem ( + &Udp6Cfg.StationAddress, + &Instance->Config->StationIp, + sizeof(EFI_IPv6_ADDRESS) + ); + + CopyMem ( + &Udp6Cfg.RemoteAddress, + &Instance->Config->ServerIp, + sizeof (EFI_IPv6_ADDRESS) + ); + + Udp6 = Instance->UdpIo->Protocol.Udp6; + Status = Udp6->Configure (Udp6, &Udp6Cfg); + + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + } + +ON_EXIT: + if (EFI_ERROR (Status)) { + if (Instance->Config != NULL) { + FreePool (Instance->Config); + Instance->Config = NULL; + } + if (Instance->UdpIo != NULL) { + UdpIoFreeIo (Instance->UdpIo); + Instance->UdpIo = NULL; + } + } + gBS->RestoreTPL (OldTpl); + return Status; +} + + +/** + Get the information of the download from the server. + + The GetInfo() function assembles an MTFTPv6 request packet + with options, sends it to the MTFTPv6 server, and may return + an MTFTPv6 OACK, MTFTPv6 ERROR, or ICMP ERROR packet. Retries + occur only if no response packets are received from the MTFTPv6 + server before the timeout expires. + + @param[in] This Pointer to the EFI_MTFTP6_PROTOCOL instance. + @param[in] OverrideData Data that is used to override the existing parameters. If NULL, the + default parameters that were set in the EFI_MTFTP6_PROTOCOL.Configure() + function are used. + @param[in] Filename Pointer to null-terminated ASCII file name string. + @param[in] ModeStr Pointer to null-terminated ASCII mode string. If NULL, octet will be used. + @param[in] OptionCount Number of option/value string pairs in OptionList. + @param[in] OptionList Pointer to array of option/value string pairs. Ignored if + OptionCount is zero. + @param[out] PacketLength The number of bytes in the returned packet. + @param[out] Packet The pointer to the received packet. This buffer must be freed by + the caller. + + @retval EFI_SUCCESS An MTFTPv6 OACK packet was received and is in the Packet. + Note: It does not match the UEFI 2.3 Specification. + @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE: + - This is NULL. + - Filename is NULL. + - OptionCount is not zero and OptionList is NULL. + - One or more options in OptionList have wrong format. + - PacketLength is NULL. + - OverrideData.ServerIp is not valid unicast IPv6 addresses. + @retval EFI_UNSUPPORTED One or more options in the OptionList are unsupported by + this implementation. + @retval EFI_NOT_STARTED The EFI MTFTPv6 Protocol driver has not been started. + @retval EFI_NO_MAPPING The underlying IPv6 driver was responsible for choosing a source + address for this instance, but no source address was available for use. + @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 MTFTPv6 ERROR packet was received and is in the Packet. + @retval EFI_NETWORK_UNREACHABLE An ICMP network unreachable error packet was received and the Packet is set to NULL. + Note: It is not defined in UEFI 2.3 Specification. + @retval EFI_HOST_UNREACHABLE An ICMP host unreachable error packet was received and the Packet is set to NULL. + Note: It is not defined in the UEFI 2.3 Specification. + @retval EFI_PROTOCOL_UNREACHABLE An ICMP protocol unreachable error packet was received and the Packet is set to NULL. + Note: It is not defined in the UEFI 2.3 Specification. + @retval EFI_PORT_UNREACHABLE An ICMP port unreachable error packet was received and the Packet is set to NULL. + @retval EFI_ICMP_ERROR Some other ICMP ERROR packet was received and the Packet is set to NULL. + Note: It does not match the UEFI 2.3 Specification. + @retval EFI_PROTOCOL_ERROR An unexpected MTFTPv6 packet was received and is in the Packet. + @retval EFI_TIMEOUT No responses were received from the MTFTPv6 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 +EfiMtftp6GetInfo ( + IN EFI_MTFTP6_PROTOCOL *This, + IN EFI_MTFTP6_OVERRIDE_DATA *OverrideData OPTIONAL, + IN UINT8 *Filename, + IN UINT8 *ModeStr OPTIONAL, + IN UINT8 OptionCount, + IN EFI_MTFTP6_OPTION *OptionList OPTIONAL, + OUT UINT32 *PacketLength, + OUT EFI_MTFTP6_PACKET **Packet OPTIONAL + ) +{ + EFI_STATUS Status; + EFI_MTFTP6_TOKEN Token; + MTFTP6_GETINFO_CONTEXT Context; + + if (This == NULL || + Filename == NULL || + PacketLength == NULL || + (OptionCount != 0 && OptionList == NULL) || + (OverrideData != NULL && !NetIp6IsValidUnicast (&OverrideData->ServerIp)) + ) { + return EFI_INVALID_PARAMETER; + } + + if (Packet != NULL) { + *Packet = NULL; + } + + *PacketLength = 0; + + Context.Packet = Packet; + Context.PacketLen = PacketLength; + Context.Status = EFI_SUCCESS; + + // + // Fill fields of the Token for GetInfo 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 = &Context; + Token.CheckPacket = Mtftp6CheckPacket; + Token.TimeoutCallback = NULL; + Token.PacketNeeded = NULL; + + // + // Start the GetInfo operation by issue the Token. + // + Status = Mtftp6OperationStart (This, &Token, EFI_MTFTP6_OPCODE_RRQ); + + if (Status == EFI_ABORTED) { + // + // Return the status if failed to issue. + // + return Context.Status; + } + + return Status; +} + + +/** + Parse the options in an MTFTPv6 OACK packet. + + The ParseOptions() function parses the option fields in an MTFTPv6 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. + + @param[in] This Pointer to the EFI_MTFTP6_PROTOCOL instance. + @param[in] PacketLen Length of the OACK packet to be parsed. + @param[in] Packet Pointer to the OACK packet to be parsed. + @param[out] OptionCount Pointer to the number of options in the following OptionList. + @param[out] OptionList Pointer to EFI_MTFTP6_OPTION storage. Each pointer in the + OptionList points to the corresponding MTFTP option buffer + in the Packet. Call the EFI Boot Service FreePool() to + release the OptionList if the options in this OptionList + are not needed anymore. + + @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: + - PacketLen is 0. + - Packet is NULL or Packet is not a valid MTFTPv6 packet. + - 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 can not be allocated. + @retval EFI_PROTOCOL_ERROR One or more of the option fields is invalid. + +**/ +EFI_STATUS +EFIAPI +EfiMtftp6ParseOptions ( + IN EFI_MTFTP6_PROTOCOL *This, + IN UINT32 PacketLen, + IN EFI_MTFTP6_PACKET *Packet, + OUT UINT32 *OptionCount, + OUT EFI_MTFTP6_OPTION **OptionList OPTIONAL + ) +{ + if (This == NULL) { + return EFI_INVALID_PARAMETER; + } + + return Mtftp6ParseStart (Packet, PacketLen, OptionCount, OptionList); +} + + +/** + Download a file from an MTFTPv6 server. + + The ReadFile() function is used to initialize and start an MTFTPv6 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 MTFTPv6 Protocol driver, and then Token.Event is signaled if it + is not NULL. + Data can be downloaded from the MTFTPv6 server into either of the following + locations: + - A fixed buffer that is pointed to by Token.Buffer + - 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[in] This Pointer to the EFI_MTFTP6_PROTOCOL instance. + @param[in] 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 zero but not large enough to hold the + downloaded data in downloading process. + Note: It does not match the UEFI 2.3 Specification. + @retval EFI_ABORTED Current operation is aborted by user. + @retval EFI_NETWORK_UNREACHABLE An ICMP network unreachable error packet was received. + Note: It is not defined in the UEFI 2.3 Specification. + @retval EFI_HOST_UNREACHABLE An ICMP host unreachable error packet was received. + Note: It is not defined in the UEFI 2.3 Specification. + @retval EFI_PROTOCOL_UNREACHABLE An ICMP protocol unreachable error packet was received. + Note: It is not defined in the UEFI 2.3 Specification. + @retval EFI_PORT_UNREACHABLE An ICMP port unreachable error packet was received. + Note: It is not defined in the UEFI 2.3 Specification. + @retval EFI_ICMP_ERROR An ICMP ERROR packet was received. + @retval EFI_TIMEOUT No responses were received from the MTFTPv6 server. + @retval EFI_TFTP_ERROR An MTFTPv6 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 +EfiMtftp6ReadFile ( + IN EFI_MTFTP6_PROTOCOL *This, + IN EFI_MTFTP6_TOKEN *Token + ) +{ + return Mtftp6OperationStart (This, Token, EFI_MTFTP6_OPCODE_RRQ); +} + + +/** + Send a file to an MTFTPv6 server. + + 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 MTFTPv6 Protocol driver updates Token.Status. + The caller can supply the data to be uploaded in the following two modes: + - Through the user-provided buffer + - 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_MTFTP6_PACKET_NEEDED callback function, the driver + will call this callback function to get more data from the user to upload. + + @param[in] This Pointer to the EFI_MTFTP6_PROTOCOL instance. + @param[in] Token Pointer to the token structure to provide the parameters that are + used in this operation. + + @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: + - This is NULL. + - Token is NULL. + - Token.Filename is NULL. + - Token.OptionCount is not zero and Token.OptionList is NULL. + - One or more options in Token.OptionList have wrong format. + - Token.Buffer and Token.PacketNeeded are both NULL. + - Token.OverrideData.ServerIp is not a valid unicast IPv6 address. + @retval EFI_UNSUPPORTED One or more options in the Token.OptionList are not + supported by this implementation. + @retval EFI_NOT_STARTED The EFI MTFTPv6 Protocol driver has not been started. + @retval EFI_NO_MAPPING The underlying IPv6 driver was responsible for choosing a source + address for this instance, but no source address was available for use. + @retval EFI_ALREADY_STARTED This Token is already being used in another MTFTPv6 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 +EfiMtftp6WriteFile ( + IN EFI_MTFTP6_PROTOCOL *This, + IN EFI_MTFTP6_TOKEN *Token + ) +{ + return Mtftp6OperationStart (This, Token, EFI_MTFTP6_OPCODE_WRQ); +} + + +/** + Download a data file directory from an MTFTPv6 server. + + The ReadDirectory() function is used to return a list of files on the + MTFTPv6 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 the caller, if present. + The file information that the server returns is put into either of + the following locations: + - A fixed buffer that is pointed to by Token.Buffer. + - 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[in] This Pointer to the EFI_MTFTP6_PROTOCOL instance. + @param[in] Token Pointer to the token structure to provide the parameters that are + used in this operation. + + @retval EFI_SUCCESS The MTFTPv6 related file "directory" has been downloaded. + @retval EFI_UNSUPPORTED The EFI MTFTPv6 Protocol driver does not support this function. + @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE: + - This is NULL. + - Token is NULL. + - Token.Filename is NULL. + - Token.OptionCount is not zero and Token.OptionList is NULL. + - One or more options in Token.OptionList have wrong format. + - Token.Buffer and Token.CheckPacket are both NULL. + - Token.OverrideData.ServerIp is not valid unicast IPv6 addresses. + @retval EFI_UNSUPPORTED One or more options in the Token.OptionList are not + supported by this implementation. + @retval EFI_NOT_STARTED The EFI MTFTPv6 Protocol driver has not been started. + @retval EFI_NO_MAPPING The underlying IPv6 driver was responsible for choosing a source + address for this instance, but no source address was available for use. + @retval EFI_ALREADY_STARTED This Token is already being used in another MTFTPv6 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 +EfiMtftp6ReadDirectory ( + IN EFI_MTFTP6_PROTOCOL *This, + IN EFI_MTFTP6_TOKEN *Token + ) +{ + return Mtftp6OperationStart (This, Token, EFI_MTFTP6_OPCODE_DIR); +} + + +/** + 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[in] This The MTFTP6 protocol instance. + + + @retval EFI_SUCCESS Incoming or outgoing data was processed. + @retval EFI_NOT_STARTED This EFI MTFTPv6 Protocol instance has not been started. + @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 +EfiMtftp6Poll ( + IN EFI_MTFTP6_PROTOCOL *This + ) +{ + MTFTP6_INSTANCE *Instance; + EFI_UDP6_PROTOCOL *Udp6; + + if (This == NULL) { + return EFI_INVALID_PARAMETER; + } + + Instance = MTFTP6_INSTANCE_FROM_THIS (This); + + // + // Check the instance whether configured or in destroy. + // + if (Instance->Config == NULL) { + return EFI_NOT_STARTED; + } + + Udp6 = Instance->UdpIo->Protocol.Udp6; + + return Udp6->Poll (Udp6); +} diff --git a/src/VBox/Devices/EFI/Firmware/NetworkPkg/Mtftp6Dxe/Mtftp6Impl.h b/src/VBox/Devices/EFI/Firmware/NetworkPkg/Mtftp6Dxe/Mtftp6Impl.h new file mode 100644 index 00000000..1466ab53 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/NetworkPkg/Mtftp6Dxe/Mtftp6Impl.h @@ -0,0 +1,484 @@ +/** @file + Mtftp6 internal data structure and definition declaration. + + Copyright (c) 2009 - 2018, Intel Corporation. All rights reserved. <BR> + + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef __EFI_MTFTP6_IMPL_H__ +#define __EFI_MTFTP6_IMPL_H__ + +#include <Uefi.h> + +#include <Protocol/Udp6.h> +#include <Protocol/Mtftp6.h> +#include <Protocol/ServiceBinding.h> +#include <Protocol/DriverBinding.h> + +#include <Library/DebugLib.h> +#include <Library/UefiDriverEntryPoint.h> +#include <Library/UefiBootServicesTableLib.h> +#include <Library/UefiLib.h> +#include <Library/BaseLib.h> +#include <Library/NetLib.h> +#include <Library/PrintLib.h> + +typedef struct _MTFTP6_SERVICE MTFTP6_SERVICE; +typedef struct _MTFTP6_INSTANCE MTFTP6_INSTANCE; + +#include "Mtftp6Driver.h" +#include "Mtftp6Option.h" +#include "Mtftp6Support.h" + +#define MTFTP6_SERVICE_SIGNATURE SIGNATURE_32 ('M', 'F', '6', 'S') +#define MTFTP6_INSTANCE_SIGNATURE SIGNATURE_32 ('M', 'F', '6', 'I') + +#define MTFTP6_DEFAULT_SERVER_CMD_PORT 69 +#define MTFTP6_DEFAULT_TIMEOUT 3 +#define MTFTP6_GET_MAPPING_TIMEOUT 3 +#define MTFTP6_DEFAULT_MAX_RETRY 5 +#define MTFTP6_DEFAULT_BLK_SIZE 512 +#define MTFTP6_DEFAULT_WINDOWSIZE 1 +#define MTFTP6_TICK_PER_SECOND 10000000U + +#define MTFTP6_SERVICE_FROM_THIS(a) CR (a, MTFTP6_SERVICE, ServiceBinding, MTFTP6_SERVICE_SIGNATURE) +#define MTFTP6_INSTANCE_FROM_THIS(a) CR (a, MTFTP6_INSTANCE, Mtftp6, MTFTP6_INSTANCE_SIGNATURE) + +extern EFI_MTFTP6_PROTOCOL gMtftp6ProtocolTemplate; + +typedef struct _MTFTP6_GETINFO_CONTEXT{ + EFI_MTFTP6_PACKET **Packet; + UINT32 *PacketLen; + EFI_STATUS Status; +} MTFTP6_GETINFO_CONTEXT; + +// +// Control block for MTFTP6 instance, it's per configuration data. +// +struct _MTFTP6_INSTANCE { + UINT32 Signature; + EFI_HANDLE Handle; + LIST_ENTRY Link; + EFI_MTFTP6_PROTOCOL Mtftp6; + MTFTP6_SERVICE *Service; + EFI_MTFTP6_CONFIG_DATA *Config; + + EFI_MTFTP6_TOKEN *Token; + MTFTP6_EXT_OPTION_INFO ExtInfo; + + UINT16 BlkSize; + UINT16 LastBlk; + LIST_ENTRY BlkList; + + UINT16 Operation; + + UINT16 WindowSize; + + // + // Record the total received and saved block number. + // + UINT64 TotalBlock; + + // + // Record the acked block number. + // + UINT64 AckedBlock; + + EFI_IPv6_ADDRESS ServerIp; + UINT16 ServerCmdPort; + UINT16 ServerDataPort; + UDP_IO *UdpIo; + + EFI_IPv6_ADDRESS McastIp; + UINT16 McastPort; + UDP_IO *McastUdpIo; + + NET_BUF *LastPacket; + UINT32 CurRetry; + UINT32 MaxRetry; + UINT32 PacketToLive; + UINT32 Timeout; + + EFI_TPL OldTpl; + BOOLEAN IsTransmitted; + BOOLEAN IsMaster; + BOOLEAN InDestroy; +}; + +// +// Control block for MTFTP6 service, it's per Nic handle. +// +struct _MTFTP6_SERVICE { + UINT32 Signature; + EFI_SERVICE_BINDING_PROTOCOL ServiceBinding; + EFI_HANDLE Controller; + EFI_HANDLE Image; + + UINT16 ChildrenNum; + LIST_ENTRY Children; + // + // It is used to be as internal calculagraph for all instances. + // + EFI_EVENT Timer; + // + // It is used to maintain the parent-child relationship between + // mtftp driver and udp driver. + // + UDP_IO *DummyUdpIo; +}; + +typedef struct { + EFI_SERVICE_BINDING_PROTOCOL *ServiceBinding; + UINTN NumberOfChildren; + EFI_HANDLE *ChildHandleBuffer; +} MTFTP6_DESTROY_CHILD_IN_HANDLE_BUF_CONTEXT; + +/** + Returns the current operating mode data for the MTFTP6 instance. + + The GetModeData() function returns the current operating mode and + cached data packet for the MTFTP6 instance. + + @param[in] This Pointer to the EFI_MTFTP6_PROTOCOL instance. + @param[out] ModeData The buffer in which the EFI MTFTPv6 Protocol driver mode + data is returned. + + @retval EFI_SUCCESS The configuration data was returned successfully. + @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 +EfiMtftp6GetModeData ( + IN EFI_MTFTP6_PROTOCOL *This, + OUT EFI_MTFTP6_MODE_DATA *ModeData + ); + +/** + Initializes, changes, or resets the default operational setting for + this EFI MTFTPv6 Protocol driver instance. + + The Configure() function is used to set and change the configuration + data for this EFI MTFTPv6 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 MTFTPv6 Protocol + driver instance configuration data, the client can connect to + different MTFTPv6 servers. The configuration parameters in + MtftpConfigData are used as the default parameters in later MTFTPv6 + operations and can be overridden in later operations. + + @param[in] This Pointer to the EFI_MTFTP6_PROTOCOL instance. + @param[in] MtftpConfigData Pointer to the configuration data structure. + + @retval EFI_SUCCESS The EFI MTFTPv6 Protocol instance was configured successfully. + @retval EFI_INVALID_PARAMETER One or more following conditions are TRUE: + - This is NULL. + - MtftpConfigData.StationIp is neither zero nor one + of the configured IP addresses in the underlying IPv6 driver. + - MtftpConfigData.ServerIp is not a valid IPv6 unicast address. + Note: It does not match the UEFI 2.3 Specification. + @retval EFI_ACCESS_DENIED - The configuration could not be changed at this time because there + is some MTFTP background operation in progress. + - MtftpConfigData.LocalPort is already in use. + Note: It does not match the UEFI 2.3 Specification. + @retval EFI_NO_MAPPING The underlying IPv6 driver was responsible for choosing a source + address for this instance, but no source address was available for use. + @retval EFI_OUT_OF_RESOURCES The EFI MTFTPv6 Protocol driver instance data could not be + allocated. + Note: It is not defined in the UEFI 2.3 Specification. + @retval EFI_DEVICE_ERROR An unexpected system or network error occurred. The EFI + MTFTPv6 Protocol driver instance is not configured. + Note: It is not defined in the UEFI 2.3 Specification. + +**/ +EFI_STATUS +EFIAPI +EfiMtftp6Configure ( + IN EFI_MTFTP6_PROTOCOL *This, + IN EFI_MTFTP6_CONFIG_DATA *MtftpConfigData OPTIONAL + ); + +/** + Get the information of the download from the server. + + The GetInfo() function assembles an MTFTPv6 request packet + with options, sends it to the MTFTPv6 server, and may return + an MTFTPv6 OACK, MTFTPv6 ERROR, or ICMP ERROR packet. Retries + occur only if no response packets are received from the MTFTPv6 + server before the timeout expires. + + @param[in] This Pointer to the EFI_MTFTP6_PROTOCOL instance. + @param[in] OverrideData Data that is used to override the existing parameters. If NULL, the + default parameters that were set in the EFI_MTFTP6_PROTOCOL.Configure() + function are used. + @param[in] Filename Pointer to null-terminated ASCII file name string. + @param[in] ModeStr Pointer to null-terminated ASCII mode string. If NULL, octet will be used + @param[in] OptionCount Number of option/value string pairs in OptionList. + @param[in] OptionList Pointer to array of option/value string pairs. Ignored if + OptionCount is zero. + @param[out] PacketLength The number of bytes in the returned packet. + @param[out] Packet The pointer to the received packet. This buffer must be freed by + the caller. + + @retval EFI_SUCCESS An MTFTPv6 OACK packet was received and is in the Packet. + Note: It does not match the UEFI 2.3 Specification. + @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE: + - This is NULL. + - Filename is NULL. + - OptionCount is not zero and OptionList is NULL. + - One or more options in OptionList have wrong format. + - PacketLength is NULL. + - OverrideData.ServerIp is not a valid unicast IPv6 address. + @retval EFI_UNSUPPORTED One or more options in the OptionList are unsupported by + this implementation. + @retval EFI_NOT_STARTED The EFI MTFTPv6 Protocol driver has not been started. + @retval EFI_NO_MAPPING The underlying IPv6 driver was responsible for choosing a source + address for this instance, but no source address was available for use. + @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 MTFTPv6 ERROR packet was received and is in the Packet. + @retval EFI_NETWORK_UNREACHABLE An ICMP network unreachable error packet was received, and the Packet is set to NULL. + Note: It is not defined in the UEFI 2.3 Specification. + @retval EFI_HOST_UNREACHABLE An ICMP host unreachable error packet was received, and the Packet is set to NULL. + Note: It is not defined in the UEFI 2.3 Specification. + @retval EFI_PROTOCOL_UNREACHABLE An ICMP protocol unreachable error packet was received, and the Packet is set to NULL. + Note: It is not defined in the UEFI 2.3 Specification. + @retval EFI_PORT_UNREACHABLE An ICMP port unreachable error packet was received, and the Packet is set to NULL. + @retval EFI_ICMP_ERROR Some other ICMP ERROR packet was received, and the Packet is set to NULL. + Note: It does not match the UEFI 2.3 Specification. + @retval EFI_PROTOCOL_ERROR An unexpected MTFTPv6 packet was received and is in the Packet. + @retval EFI_TIMEOUT No responses were received from the MTFTPv6 server. + @retval EFI_DEVICE_ERROR An unexpected network error or system error occurred. + +**/ +EFI_STATUS +EFIAPI +EfiMtftp6GetInfo ( + IN EFI_MTFTP6_PROTOCOL *This, + IN EFI_MTFTP6_OVERRIDE_DATA *OverrideData OPTIONAL, + IN UINT8 *Filename, + IN UINT8 *ModeStr OPTIONAL, + IN UINT8 OptionCount, + IN EFI_MTFTP6_OPTION *OptionList OPTIONAL, + OUT UINT32 *PacketLength, + OUT EFI_MTFTP6_PACKET **Packet OPTIONAL + ); + +/** + Parse the options in an MTFTPv6 OACK packet. + + The ParseOptions() function parses the option fields in an MTFTPv6 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. + + @param[in] This Pointer to the EFI_MTFTP6_PROTOCOL instance. + @param[in] PacketLen Length of the OACK packet to be parsed. + @param[in] Packet Pointer to the OACK packet to be parsed. + @param[out] OptionCount Pointer to the number of options in the following OptionList. + @param[out] OptionList Pointer to EFI_MTFTP6_OPTION storage. Each pointer in the + OptionList points to the corresponding MTFTP option buffer + in the Packet. Call the EFI Boot Service FreePool() to + release the OptionList 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: + - PacketLen is 0. + - Packet is NULL or Packet is not a valid MTFTPv6 packet. + - 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 can not be allocated. + @retval EFI_PROTOCOL_ERROR One or more of the option fields is invalid. + +**/ +EFI_STATUS +EFIAPI +EfiMtftp6ParseOptions ( + IN EFI_MTFTP6_PROTOCOL *This, + IN UINT32 PacketLen, + IN EFI_MTFTP6_PACKET *Packet, + OUT UINT32 *OptionCount, + OUT EFI_MTFTP6_OPTION **OptionList OPTIONAL + ); + +/** + Download a file from an MTFTPv6 server. + + The ReadFile() function is used to initialize and start an MTFTPv6 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 MTFTPv6 Protocol driver, and then Token.Event is signaled if it + is not NULL. + Data can be downloaded from the MTFTPv6 server into either of the following + locations: + - A fixed buffer that is pointed to by Token.Buffer. + - 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[in] This Pointer to the EFI_MTFTP6_PROTOCOL instance. + @param[in] 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 zero but not large enough to hold the + downloaded data in downloading process. + Note: It does not match the UEFI 2.3 Specification. + @retval EFI_ABORTED Current operation is aborted by user. + @retval EFI_NETWORK_UNREACHABLE An ICMP network unreachable error packet was received. + Note: It is not defined in the UEFI 2.3 Specification. + @retval EFI_HOST_UNREACHABLE An ICMP host unreachable error packet was received. + Note: It is not defined in the UEFI 2.3 Specification. + @retval EFI_PROTOCOL_UNREACHABLE An ICMP protocol unreachable error packet was received. + Note: It is not defined in the UEFI 2.3 Specification. + @retval EFI_PORT_UNREACHABLE An ICMP port unreachable error packet was received. + Note: It is not defined in the UEFI 2.3 Specification. + @retval EFI_ICMP_ERROR An ICMP ERROR packet was received. + @retval EFI_TIMEOUT No responses were received from the MTFTPv6 server. + @retval EFI_TFTP_ERROR An MTFTPv6 ERROR packet was received. + @retval EFI_DEVICE_ERROR An unexpected network error or system error occurred. + +**/ +EFI_STATUS +EFIAPI +EfiMtftp6ReadFile ( + IN EFI_MTFTP6_PROTOCOL *This, + IN EFI_MTFTP6_TOKEN *Token + ); + +/** + Send a file to an MTFTPv6 server. + + 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 MTFTPv6 Protocol driver updates Token.Status. + The caller can supply the data to be uploaded in the following two modes: + - Through the user-provided buffer. + - 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_MTFTP6_PACKET_NEEDED callback function, the driver + will call this callback function to get more data from the user to upload. + + @param[in] This Pointer to the EFI_MTFTP6_PROTOCOL instance. + @param[in] Token Pointer to the token structure to provide the parameters that are + used in this operation. + + @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: + - This is NULL. + - Token is NULL. + - Token.Filename is NULL. + - Token.OptionCount is not zero and Token.OptionList is NULL. + - One or more options in Token.OptionList have wrong format. + - Token.Buffer and Token.PacketNeeded are both NULL. + - Token.OverrideData.ServerIp is not valid unicast IPv6 addresses. + @retval EFI_UNSUPPORTED One or more options in the Token.OptionList are not + supported by this implementation. + @retval EFI_NOT_STARTED The EFI MTFTPv6 Protocol driver has not been started. + @retval EFI_NO_MAPPING The underlying IPv6 driver was responsible for choosing a source + address for this instance, but no source address was available for use. + @retval EFI_ALREADY_STARTED This Token is already being used in another MTFTPv6 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 +EfiMtftp6WriteFile ( + IN EFI_MTFTP6_PROTOCOL *This, + IN EFI_MTFTP6_TOKEN *Token + ); + +/** + Download a data file directory from an MTFTPv6 server. + + The ReadDirectory() function is used to return a list of files on the + MTFTPv6 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: + - A fixed buffer that is pointed to by Token.Buffer. + - 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[in] This Pointer to the EFI_MTFTP6_PROTOCOL instance. + @param[in] Token Pointer to the token structure to provide the parameters that are + used in this operation. + + @retval EFI_SUCCESS The MTFTPv6 related file "directory" has been downloaded. + @retval EFI_UNSUPPORTED The EFI MTFTPv6 Protocol driver does not support this function. + @retval EFI_INVALID_PARAMETER One or more of the following conditions is TRUE: + - This is NULL. + - Token is NULL. + - Token.Filename is NULL. + - Token.OptionCount is not zero and Token.OptionList is NULL. + - One or more options in Token.OptionList have wrong format. + - Token.Buffer and Token.CheckPacket are both NULL. + - Token.OverrideData.ServerIp is not valid unicast IPv6 addresses. + @retval EFI_UNSUPPORTED One or more options in the Token.OptionList are not + supported by this implementation. + @retval EFI_NOT_STARTED The EFI MTFTPv6 Protocol driver has not been started. + @retval EFI_NO_MAPPING The underlying IPv6 driver was responsible for choosing a source + address for this instance, but no source address was available for use. + @retval EFI_ALREADY_STARTED This Token is already being used in another MTFTPv6 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 +EfiMtftp6ReadDirectory ( + IN EFI_MTFTP6_PROTOCOL *This, + IN EFI_MTFTP6_TOKEN *Token + ); + +/** + 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[in] This The MTFTP6 protocol instance. + + + @retval EFI_SUCCESS Incoming or outgoing data was processed. + @retval EFI_NOT_STARTED This EFI MTFTPv6 Protocol instance has not been started. + @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 +EfiMtftp6Poll ( + IN EFI_MTFTP6_PROTOCOL *This + ); + +#endif diff --git a/src/VBox/Devices/EFI/Firmware/NetworkPkg/Mtftp6Dxe/Mtftp6Option.c b/src/VBox/Devices/EFI/Firmware/NetworkPkg/Mtftp6Dxe/Mtftp6Option.c new file mode 100644 index 00000000..5a713177 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/NetworkPkg/Mtftp6Dxe/Mtftp6Option.c @@ -0,0 +1,430 @@ +/** @file + Mtftp6 option parse functions implementation. + + Copyright (c) 2009 - 2018, Intel Corporation. All rights reserved.<BR> + + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "Mtftp6Impl.h" + +CHAR8 *mMtftp6SupportedOptions[MTFTP6_SUPPORTED_OPTIONS_NUM] = { + "blksize", + "windowsize", + "timeout", + "tsize", + "multicast" +}; + + +/** + Parse the NULL terminated ASCII string of multicast option. + + @param[in] Str The pointer to the Ascii string of multicast option. + @param[in] ExtInfo The pointer to the option information to be filled. + + @retval EFI_SUCCESS Parse the multicast option successfully. + @retval EFI_INVALID_PARAMETER The string is malformatted. + @retval EFI_OUT_OF_RESOURCES Failed to perform the operation due to lack of + resources. + +**/ +EFI_STATUS +Mtftp6ParseMcastOption ( + IN UINT8 *Str, + IN MTFTP6_EXT_OPTION_INFO *ExtInfo + ) +{ + EFI_STATUS Status; + UINT32 Num; + CHAR8 *Ip6Str; + CHAR8 *TempStr; + + // + // The multicast option is formatted like "addr,port,mc" + // The server can also omit the ip and port, use ",,1" + // + if (*Str == ',') { + + ZeroMem (&ExtInfo->McastIp, sizeof (EFI_IPv6_ADDRESS)); + } else { + + Ip6Str = (CHAR8 *) AllocateCopyPool (AsciiStrSize ((CHAR8 *) Str), Str); + if (Ip6Str == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + // + // The IPv6 address locates before comma in the input Str. + // + TempStr = Ip6Str; + while ((*TempStr != '\0') && (*TempStr != ',')) { + TempStr++; + } + + *TempStr = '\0'; + + Status = NetLibAsciiStrToIp6 (Ip6Str, &ExtInfo->McastIp); + FreePool (Ip6Str); + + if (EFI_ERROR (Status)) { + return Status; + } + + while ((*Str != '\0') && (*Str != ',')) { + Str++; + } + } + + if (*Str != ',') { + return EFI_INVALID_PARAMETER; + } + + Str++; + + // + // Convert the port setting. the server can send us a port number or + // empty string. such as the port in ",,1" + // + if (*Str == ',') { + + ExtInfo->McastPort = 0; + } else { + + Num = (UINT32) AsciiStrDecimalToUintn ((CHAR8 *) Str); + + if (Num > 65535) { + return EFI_INVALID_PARAMETER; + } + + ExtInfo->McastPort = (UINT16) Num; + + while (NET_IS_DIGIT (*Str)) { + Str++; + } + } + + if (*Str != ',') { + return EFI_INVALID_PARAMETER; + } + + Str++; + + // + // Check the master/slave setting, 1 for master, 0 for slave. + // + Num = (UINT32) AsciiStrDecimalToUintn ((CHAR8 *) Str); + + if (Num != 0 && Num != 1) { + return EFI_INVALID_PARAMETER; + } + + ExtInfo->IsMaster = (BOOLEAN) (Num == 1); + + while (NET_IS_DIGIT (*Str)) { + Str++; + } + + if (*Str != '\0') { + return EFI_INVALID_PARAMETER; + } + + return EFI_SUCCESS; +} + + +/** + Parse the MTFTP6 extension options. + + @param[in] Options The pointer to the extension options list. + @param[in] Count The num of the extension options. + @param[in] IsRequest If FALSE, the extension options is included + by a request packet. + @param[in] Operation The current performed operation. + @param[in] ExtInfo The pointer to the option information to be filled. + + @retval EFI_SUCCESS Parse the multicast option successfully. + @retval EFI_INVALID_PARAMETER There is one option is malformatted at least. + @retval EFI_UNSUPPORTED There is one option is not supported at least. + +**/ +EFI_STATUS +Mtftp6ParseExtensionOption ( + IN EFI_MTFTP6_OPTION *Options, + IN UINT32 Count, + IN BOOLEAN IsRequest, + IN UINT16 Operation, + IN MTFTP6_EXT_OPTION_INFO *ExtInfo + ) +{ + EFI_STATUS Status; + EFI_MTFTP6_OPTION *Opt; + UINT32 Index; + UINT32 Value; + + ExtInfo->BitMap = 0; + + for (Index = 0; Index < Count; Index++) { + + Opt = Options + Index; + + if (Opt->OptionStr == NULL || Opt->ValueStr == NULL) { + return EFI_INVALID_PARAMETER; + } + + if (AsciiStriCmp ((CHAR8 *) Opt->OptionStr, "blksize") == 0) { + // + // block size option, valid value is between [8, 65464] + // + Value = (UINT32) AsciiStrDecimalToUintn ((CHAR8 *) Opt->ValueStr); + + if ((Value < 8) || (Value > 65464)) { + return EFI_INVALID_PARAMETER; + } + + ExtInfo->BlkSize = (UINT16) Value; + ExtInfo->BitMap |= MTFTP6_OPT_BLKSIZE_BIT; + + } else if (AsciiStriCmp ((CHAR8 *) Opt->OptionStr, "timeout") == 0) { + // + // timeout option, valid value is between [1, 255] + // + Value = (UINT32) AsciiStrDecimalToUintn ((CHAR8 *) Opt->ValueStr); + + if (Value < 1 || Value > 255) { + return EFI_INVALID_PARAMETER; + } + + ExtInfo->Timeout = (UINT8) Value; + ExtInfo->BitMap |= MTFTP6_OPT_TIMEOUT_BIT; + + } else if (AsciiStriCmp ((CHAR8 *) Opt->OptionStr, "tsize") == 0) { + // + // tsize option, the biggest transfer supported is 4GB with block size option + // + ExtInfo->Tsize = (UINT32) AsciiStrDecimalToUintn ((CHAR8 *) Opt->ValueStr); + ExtInfo->BitMap |= MTFTP6_OPT_TSIZE_BIT; + + } else if (AsciiStriCmp ((CHAR8 *) Opt->OptionStr, "multicast") == 0) { + // + // Multicast option, if it is a request, the value must be a zero string, + // otherwise, it must be like "addr,port,mc" string, mc indicates master. + // + if (!IsRequest) { + + Status = Mtftp6ParseMcastOption (Opt->ValueStr, ExtInfo); + + if (EFI_ERROR (Status)) { + return Status; + } + } else if (*(Opt->ValueStr) != '\0') { + + return EFI_INVALID_PARAMETER; + } + + ExtInfo->BitMap |= MTFTP6_OPT_MCAST_BIT; + + } else if (AsciiStriCmp ((CHAR8 *) Opt->OptionStr, "windowsize") == 0) { + if (Operation == EFI_MTFTP6_OPCODE_WRQ) { + // + // Currently, windowsize is not supported in the write operation. + // + return EFI_UNSUPPORTED; + } + + Value = (UINT32) AsciiStrDecimalToUintn ((CHAR8 *) Opt->ValueStr); + + if ((Value < 1)) { + return EFI_INVALID_PARAMETER; + } + + ExtInfo->WindowSize = (UINT16) Value; + ExtInfo->BitMap |= MTFTP6_OPT_WINDOWSIZE_BIT; + + } else if (IsRequest) { + // + // If it's a request, unsupported; else if it's a reply, ignore. + // + return EFI_UNSUPPORTED; + } + } + + return EFI_SUCCESS; +} + + +/** + Go through the packet to fill the options array with the start + addresses of each MTFTP option name/value pair. + + @param[in] Packet The packet to be checked. + @param[in] PacketLen The length of the packet. + @param[in, out] Count The num of the Options on input. + The actual one on output. + @param[in] Options The option array to be filled. + It is optional. + + @retval EFI_SUCCESS The packet has been parsed successfully. + @retval EFI_INVALID_PARAMETER The packet is malformatted. + @retval EFI_BUFFER_TOO_SMALL The Options array is too small. + @retval EFI_PROTOCOL_ERROR An unexpected MTFTPv6 packet was received. + +**/ +EFI_STATUS +Mtftp6ParsePacketOption ( + IN EFI_MTFTP6_PACKET *Packet, + IN UINT32 PacketLen, + IN OUT UINT32 *Count, + IN EFI_MTFTP6_OPTION *Options OPTIONAL + ) +{ + UINT8 *Cur; + UINT8 *Last; + UINT8 Num; + UINT8 *Name; + UINT8 *Value; + + Num = 0; + Cur = (UINT8 *) Packet + MTFTP6_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_PROTOCOL_ERROR; + } + + Value = ++Cur; + + while (*Cur != 0) { + Cur++; + } + + Num++; + + if (Options != NULL && Num <= *Count) { + Options[Num - 1].OptionStr = Name; + Options[Num - 1].ValueStr = Value; + } + + Cur++; + } + + // + // Return buffer too small if the buffer passed-in isn't enough. + // + if (*Count < Num || Options == NULL) { + *Count = Num; + return EFI_BUFFER_TOO_SMALL; + } + + *Count = Num; + return EFI_SUCCESS; +} + + +/** + Go through the packet, generate option list array and fill it + by the result of parse options. + + @param[in] Packet The packet to be checked. + @param[in] PacketLen The length of the packet. + @param[in, out] OptionCount The num of the Options on input. + The actual one on output. + @param[out] OptionList The option list array to be generated + and filled. It is optional. + + @retval EFI_SUCCESS The packet has been parsed successfully. + @retval EFI_INVALID_PARAMETER The packet is malformatted. + @retval EFI_PROTOCOL_ERROR There is one option is malformatted at least. + @retval EFI_NOT_FOUND The packet has no options. + @retval EFI_OUT_OF_RESOURCES Failed to allocate memory for the array. + @retval EFI_BUFFER_TOO_SMALL The size of option list array is too small. + +**/ +EFI_STATUS +Mtftp6ParseStart ( + IN EFI_MTFTP6_PACKET *Packet, + IN UINT32 PacketLen, + IN OUT UINT32 *OptionCount, + OUT EFI_MTFTP6_OPTION **OptionList OPTIONAL + ) +{ + EFI_STATUS Status; + + if (PacketLen == 0 || Packet == NULL || OptionCount == NULL) { + return EFI_INVALID_PARAMETER; + } + + *OptionCount = 0; + + if (OptionList != NULL) { + *OptionList = NULL; + } + + if (NTOHS (Packet->OpCode) != EFI_MTFTP6_OPCODE_OACK) { + return EFI_INVALID_PARAMETER; + } + + // + // The last byte must be zero to terminate the options. + // + if (*((UINT8 *) Packet + PacketLen - 1) != 0) { + return EFI_PROTOCOL_ERROR; + } + + // + // Parse packet with NULL buffer for the first time to get the number + // of options in the packet. + // + Status = Mtftp6ParsePacketOption (Packet, PacketLen, OptionCount, NULL); + + if (Status != EFI_BUFFER_TOO_SMALL) { + return Status; + } + + // + // Return not found if there is no option parsed. + // + if (*OptionCount == 0) { + return EFI_NOT_FOUND; + } + + // + // Only need parse out the number of options. + // + if (OptionList == NULL) { + return EFI_SUCCESS; + } + + // + // Allocate the buffer according to the option number parsed before. + // + *OptionList = AllocateZeroPool (*OptionCount * sizeof (EFI_MTFTP6_OPTION)); + + if (*OptionList == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + // + // Parse packet with allocated buffer for the second time to fill the pointer array + // of the options in the packet. + // + Status = Mtftp6ParsePacketOption (Packet, PacketLen, OptionCount, *OptionList); + + if (EFI_ERROR (Status)) { + return Status; + } + + return EFI_SUCCESS; +} diff --git a/src/VBox/Devices/EFI/Firmware/NetworkPkg/Mtftp6Dxe/Mtftp6Option.h b/src/VBox/Devices/EFI/Firmware/NetworkPkg/Mtftp6Dxe/Mtftp6Option.h new file mode 100644 index 00000000..5f4382f6 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/NetworkPkg/Mtftp6Dxe/Mtftp6Option.h @@ -0,0 +1,146 @@ +/** @file + Mtftp6 option parse functions declaration. + + Copyright (c) 2009 - 2018, Intel Corporation. All rights reserved.<BR> + + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef __EFI_MTFTP6_OPTION_H__ +#define __EFI_MTFTP6_OPTION_H__ + +#include <Uefi.h> + +#include <Protocol/ServiceBinding.h> + +#include <Library/NetLib.h> +#include <Library/UdpIoLib.h> +#include <Library/BaseMemoryLib.h> +#include <Library/MemoryAllocationLib.h> +#include <Library/UefiRuntimeServicesTableLib.h> + +#define MTFTP6_SUPPORTED_OPTIONS_NUM 5 +#define MTFTP6_OPCODE_LEN 2 +#define MTFTP6_ERRCODE_LEN 2 +#define MTFTP6_BLKNO_LEN 2 +#define MTFTP6_DATA_HEAD_LEN 4 + +// +// The bit map definition for Mtftp6 extension options. +// +#define MTFTP6_OPT_BLKSIZE_BIT 0x01 +#define MTFTP6_OPT_TIMEOUT_BIT 0x02 +#define MTFTP6_OPT_TSIZE_BIT 0x04 +#define MTFTP6_OPT_MCAST_BIT 0x08 +#define MTFTP6_OPT_WINDOWSIZE_BIT 0X10 + +extern CHAR8 *mMtftp6SupportedOptions[MTFTP6_SUPPORTED_OPTIONS_NUM]; + +typedef struct { + UINT16 BlkSize; + UINT16 WindowSize; + UINT8 Timeout; + UINT32 Tsize; + EFI_IPv6_ADDRESS McastIp; + UINT16 McastPort; + BOOLEAN IsMaster; + UINT32 BitMap; +} MTFTP6_EXT_OPTION_INFO; + +/** + Parse the Ascii string of multi-cast option. + + @param[in] Str The pointer to the Ascii string of multi-cast option. + @param[in] ExtInfo The pointer to the option information to be filled. + + @retval EFI_SUCCESS Parse the multicast option successfully. + @retval EFI_INVALID_PARAMETER The string is malformatted. + +**/ +EFI_STATUS +Mtftp6ParseMcastOption ( + IN UINT8 *Str, + IN MTFTP6_EXT_OPTION_INFO *ExtInfo + ); + + +/** + Parse the MTFTP6 extension options. + + @param[in] Options The pointer to the extension options list. + @param[in] Count The num of the extension options. + @param[in] IsRequest If FALSE, the extension options is included + by a request packet. + @param[in] Operation The current performed operation. + @param[in] ExtInfo The pointer to the option information to be filled. + + @retval EFI_SUCCESS Parse the multicast option successfully. + @retval EFI_INVALID_PARAMETER There is one option is malformatted at least. + @retval EFI_UNSUPPORTED There is one option is not supported at least. + +**/ +EFI_STATUS +Mtftp6ParseExtensionOption ( + IN EFI_MTFTP6_OPTION *Options, + IN UINT32 Count, + IN BOOLEAN IsRequest, + IN UINT16 Operation, + IN MTFTP6_EXT_OPTION_INFO *ExtInfo + ); + + +/** + Go through the packet to fill the options array with the start + addresses of each MTFTP option name/value pair. + + @param[in] Packet The packet to be checked. + @param[in] PacketLen The length of the packet. + @param[in, out] Count The num of the Options on input. + The actual one on output. + @param[in] Options The option array to be filled + it's optional. + + @retval EFI_SUCCESS The packet has been parsed successfully. + @retval EFI_INVALID_PARAMETER The packet is malformatted + @retval EFI_BUFFER_TOO_SMALL The Options array is too small + @retval EFI_PROTOCOL_ERROR An unexpected MTFTPv6 packet was received. + +**/ +EFI_STATUS +Mtftp6ParsePacketOption ( + IN EFI_MTFTP6_PACKET *Packet, + IN UINT32 PacketLen, + IN OUT UINT32 *Count, + IN EFI_MTFTP6_OPTION *Options OPTIONAL + ); + + +/** + Go through the packet, generate option list array and fill it + by the result of parse options. + + @param[in] Packet The packet to be checked. + @param[in] PacketLen The length of the packet. + @param[in, out] OptionCount The num of the Options on input. + The actual one on output. + @param[out] OptionList The option list array to be generated + and filled. It is optional. + + @retval EFI_SUCCESS The packet has been parsed successfully. + @retval EFI_INVALID_PARAMETER The packet is malformatted. + @retval EFI_PROTOCOL_ERROR An option is malformatted. + @retval EFI_NOT_FOUND The packet has no options. + @retval EFI_OUT_OF_RESOURCES Failed to allocate memory for the array. + @retval EFI_BUFFER_TOO_SMALL The size of option list array is too small. + +**/ +EFI_STATUS +Mtftp6ParseStart ( + IN EFI_MTFTP6_PACKET *Packet, + IN UINT32 PacketLen, + IN OUT UINT32 *OptionCount, + OUT EFI_MTFTP6_OPTION **OptionList OPTIONAL + ); + +#endif diff --git a/src/VBox/Devices/EFI/Firmware/NetworkPkg/Mtftp6Dxe/Mtftp6Rrq.c b/src/VBox/Devices/EFI/Firmware/NetworkPkg/Mtftp6Dxe/Mtftp6Rrq.c new file mode 100644 index 00000000..5c5cbdc0 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/NetworkPkg/Mtftp6Dxe/Mtftp6Rrq.c @@ -0,0 +1,942 @@ +/** @file + Mtftp6 Rrq process functions implementation. + + Copyright (c) 2009 - 2018, Intel Corporation. All rights reserved.<BR> + + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "Mtftp6Impl.h" + + +/** + Build and send a ACK packet for download. + + @param[in] Instance The pointer to the Mtftp6 instance. + @param[in] BlockNum The block number to be acked. + + @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 +Mtftp6RrqSendAck ( + IN MTFTP6_INSTANCE *Instance, + IN UINT16 BlockNum + ) +{ + EFI_MTFTP6_PACKET *Ack; + NET_BUF *Packet; + EFI_STATUS Status; + + Status = EFI_SUCCESS; + + // + // Allocate net buffer to create ack packet. + // + Packet = NetbufAlloc (sizeof (EFI_MTFTP6_ACK_HEADER)); + + if (Packet == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + Ack = (EFI_MTFTP6_PACKET *) NetbufAllocSpace ( + Packet, + sizeof (EFI_MTFTP6_ACK_HEADER), + FALSE + ); + ASSERT (Ack != NULL); + + Ack->Ack.OpCode = HTONS (EFI_MTFTP6_OPCODE_ACK); + Ack->Ack.Block[0] = HTONS (BlockNum); + + // + // Reset current retry count of the instance. + // + Instance->CurRetry = 0; + Instance->LastPacket = Packet; + + Status = Mtftp6TransmitPacket (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[in] Instance The pointer to the Mtftp6 instance. + @param[in] Packet The pointer to the received packet. + @param[in] Len The packet length. + @param[out] UdpPacket The net buf of the received packet. + + @retval EFI_SUCCESS The data was 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 +Mtftp6RrqSaveBlock ( + IN MTFTP6_INSTANCE *Instance, + IN EFI_MTFTP6_PACKET *Packet, + IN UINT32 Len, + OUT NET_BUF **UdpPacket + ) +{ + EFI_MTFTP6_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 - MTFTP6_DATA_HEAD_LEN; + + // + // This is the last block, save the block num + // + if (DataLen < Instance->BlkSize) { + Completed = TRUE; + Instance->LastBlk = Block; + Mtftp6SetLastBlockNum (&Instance->BlkList, Block); + } + + // + // Remove this block number from the file hole. If Mtftp6RemoveBlockNum + // 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 = Mtftp6RemoveBlockNum (&Instance->BlkList, Block, Completed, &BlockCounter); + + if (Status == EFI_NOT_FOUND) { + return EFI_SUCCESS; + } else if (EFI_ERROR (Status)) { + return Status; + } + + if (Token->CheckPacket != NULL) { + // + // Callback to the check packet routine with the received packet. + // + Status = Token->CheckPacket (&Instance->Mtftp6, Token, (UINT16) Len, Packet); + + if (EFI_ERROR (Status)) { + // + // Free the received packet before send new packet in ReceiveNotify, + // since the Udp6Io might need to be reconfigured. + // + NetbufFree (*UdpPacket); + *UdpPacket = NULL; + // + // Send the Mtftp6 error message if user aborted the current session. + // + Mtftp6SendError ( + Instance, + EFI_MTFTP6_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->LastBlk == Block) && Completed) { + Token->BufferSize = Start + DataLen; + } + } else if (Instance->LastBlk != 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; + + // + // Free the received packet before send new packet in ReceiveNotify, + // since the udpio might need to be reconfigured. + // + NetbufFree (*UdpPacket); + *UdpPacket = NULL; + // + // Send the Mtftp6 error message if no enough buffer. + // + Mtftp6SendError ( + Instance, + EFI_MTFTP6_ERRORCODE_DISK_FULL, + (UINT8 *) "User provided memory block is too small" + ); + + return EFI_BUFFER_TOO_SMALL; + } + } + + return EFI_SUCCESS; +} + + +/** + Process the received data packets. It will save the block + then send back an ACK if it is active. + + @param[in] Instance The pointer to the Mtftp6 instance. + @param[in] Packet The pointer to the received packet. + @param[in] Len The length of the packet. + @param[out] UdpPacket The net buf of received packet. + @param[out] IsCompleted If TRUE, the download has been completed. + Otherwise, the download has not been completed. + + @retval EFI_SUCCESS The data packet was successfully processed. + @retval EFI_ABORTED The download was aborted by the user. + @retval EFI_BUFFER_TOO_SMALL The user-provided buffer is too small. + +**/ +EFI_STATUS +Mtftp6RrqHandleData ( + IN MTFTP6_INSTANCE *Instance, + IN EFI_MTFTP6_PACKET *Packet, + IN UINT32 Len, + OUT NET_BUF **UdpPacket, + OUT BOOLEAN *IsCompleted + ) +{ + EFI_STATUS Status; + UINT16 BlockNum; + INTN Expected; + + *IsCompleted = FALSE; + Status = EFI_SUCCESS; + BlockNum = NTOHS (Packet->Data.Block); + Expected = Mtftp6GetNextBlockNum (&Instance->BlkList); + + 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->IsMaster && (Expected != BlockNum)) { + // + // Free the received packet before send new packet in ReceiveNotify, + // since the udpio might need to be reconfigured. + // + NetbufFree (*UdpPacket); + *UdpPacket = NULL; + + // + // If Expected is 0, (UINT16) (Expected - 1) is also the expected Ack number (65535). + // + return Mtftp6RrqSendAck (Instance, (UINT16) (Expected - 1)); + } + + Status = Mtftp6RrqSaveBlock (Instance, Packet, Len, UdpPacket); + + 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->IsMaster) { + Instance->PacketToLive = Instance->Timeout * 2; + } + + // + // 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 = Mtftp6GetNextBlockNum (&Instance->BlkList); + + if (Instance->IsMaster || 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->LastBlk; + *IsCompleted = TRUE; + + } else { + BlockNum = (UINT16) (Expected - 1); + } + // + // Free the received packet before send new packet in ReceiveNotify, + // since the udpio might need to be reconfigured. + // + NetbufFree (*UdpPacket); + *UdpPacket = NULL; + + if (Instance->WindowSize == (Instance->TotalBlock - Instance->AckedBlock) || Expected < 0) { + Status = Mtftp6RrqSendAck (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[in] Instance The pointer to the Mtftp6 instance. + @param[in] ReplyInfo The pointer to options information in reply packet. + @param[in] RequestInfo The pointer to requested options info. + + @retval TRUE If the option in the OACK is valid. + @retval FALSE If the option is invalid. + +**/ +BOOLEAN +Mtftp6RrqOackValid ( + IN MTFTP6_INSTANCE *Instance, + IN MTFTP6_EXT_OPTION_INFO *ReplyInfo, + IN MTFTP6_EXT_OPTION_INFO *RequestInfo + ) +{ + // + // It is invalid for server to return options we don't request + // + if ((ReplyInfo->BitMap & ~RequestInfo->BitMap) != 0) { + return FALSE; + } + + // + // Server can only specify a smaller block size and windowsize to be used and + // return the timeout matches that requested. + // + if ((((ReplyInfo->BitMap & MTFTP6_OPT_BLKSIZE_BIT) != 0) && (ReplyInfo->BlkSize > RequestInfo->BlkSize)) || + (((ReplyInfo->BitMap & MTFTP6_OPT_WINDOWSIZE_BIT) != 0) && (ReplyInfo->BlkSize > RequestInfo->BlkSize)) || + (((ReplyInfo->BitMap & MTFTP6_OPT_TIMEOUT_BIT) != 0) && (ReplyInfo->Timeout != RequestInfo->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 (((ReplyInfo->BitMap & MTFTP6_OPT_MCAST_BIT) != 0) && !NetIp6IsUnspecifiedAddr (&Instance->McastIp)) { + + if (!NetIp6IsUnspecifiedAddr (&ReplyInfo->McastIp) && CompareMem ( + &ReplyInfo->McastIp, + &Instance->McastIp, + sizeof (EFI_IPv6_ADDRESS) + ) != 0) { + return FALSE; + } + + if ((ReplyInfo->McastPort != 0) && (ReplyInfo->McastPort != Instance->McastPort)) { + return FALSE; + } + } + + return TRUE; +} + + +/** + Configure Udp6Io to receive a packet from a multicast address. + + @param[in] McastIo The pointer to the mcast Udp6Io. + @param[in] Context The pointer to the context. + + @retval EFI_SUCCESS The mcast Udp6Io was successfully configured. + @retval Others Failed to configure the Udp6Io. + +**/ +EFI_STATUS +EFIAPI +Mtftp6RrqConfigMcastUdpIo ( + IN UDP_IO *McastIo, + IN VOID *Context + ) +{ + EFI_STATUS Status; + EFI_UDP6_PROTOCOL *Udp6; + EFI_UDP6_CONFIG_DATA *Udp6Cfg; + EFI_IPv6_ADDRESS Group; + MTFTP6_INSTANCE *Instance; + + Udp6 = McastIo->Protocol.Udp6; + Udp6Cfg = &(McastIo->Config.Udp6); + Instance = (MTFTP6_INSTANCE *) Context; + + // + // Set the configure data for the mcast Udp6Io. + // + ZeroMem (Udp6Cfg, sizeof (EFI_UDP6_CONFIG_DATA)); + + Udp6Cfg->AcceptPromiscuous = FALSE; + Udp6Cfg->AcceptAnyPort = FALSE; + Udp6Cfg->AllowDuplicatePort = FALSE; + Udp6Cfg->TrafficClass = 0; + Udp6Cfg->HopLimit = 128; + Udp6Cfg->ReceiveTimeout = 0; + Udp6Cfg->TransmitTimeout = 0; + Udp6Cfg->StationPort = Instance->McastPort; + Udp6Cfg->RemotePort = 0; + + CopyMem ( + &Udp6Cfg->RemoteAddress, + &Instance->ServerIp, + sizeof (EFI_IPv6_ADDRESS) + ); + + // + // Configure the mcast Udp6Io. + // + Status = Udp6->Configure (Udp6, Udp6Cfg); + + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Join the multicast group + // + CopyMem (&Group, &Instance->McastIp, sizeof (EFI_IPv6_ADDRESS)); + + return Udp6->Groups (Udp6, TRUE, &Group); +} + + +/** + Process the OACK packet for Rrq. + + @param[in] Instance The pointer to the Mtftp6 instance. + @param[in] Packet The pointer to the received packet. + @param[in] Len The length of the packet. + @param[out] UdpPacket The net buf of received packet. + @param[out] IsCompleted If TRUE, the download has been completed. + Otherwise, the download has not been completed. + + @retval EFI_DEVICE_ERROR Failed to create/start a multicast Udp6 child. + @retval EFI_TFTP_ERROR An error happened during the process. + @retval EFI_SUCCESS The OACK packet successfully processed. + +**/ +EFI_STATUS +Mtftp6RrqHandleOack ( + IN MTFTP6_INSTANCE *Instance, + IN EFI_MTFTP6_PACKET *Packet, + IN UINT32 Len, + OUT NET_BUF **UdpPacket, + OUT BOOLEAN *IsCompleted + ) +{ + EFI_MTFTP6_OPTION *Options; + UINT32 Count; + MTFTP6_EXT_OPTION_INFO ExtInfo; + EFI_STATUS Status; + INTN Expected; + EFI_UDP6_PROTOCOL *Udp6; + + *IsCompleted = FALSE; + Options = NULL; + + // + // If already started the master download, don't change the + // setting. Master download always succeeds. + // + Expected = Mtftp6GetNextBlockNum (&Instance->BlkList); + ASSERT (Expected != -1); + + if (Instance->IsMaster && Expected != 1) { + return EFI_SUCCESS; + } + + ZeroMem (&ExtInfo, sizeof (MTFTP6_EXT_OPTION_INFO)); + + // + // Parse the options in the packet. + // + Status = Mtftp6ParseStart (Packet, Len, &Count, &Options); + + if (EFI_ERROR (Status)) { + return Status; + } + ASSERT (Options != NULL); + + // + // Parse the extensive options in the packet. + // + Status = Mtftp6ParseExtensionOption (Options, Count, FALSE, Instance->Operation, &ExtInfo); + + if (EFI_ERROR (Status) || !Mtftp6RrqOackValid (Instance, &ExtInfo, &Instance->ExtInfo)) { + // + // Don't send an ERROR packet if the error is EFI_OUT_OF_RESOURCES. + // + if (Status != EFI_OUT_OF_RESOURCES) { + // + // Free the received packet before send new packet in ReceiveNotify, + // since the udpio might need to be reconfigured. + // + NetbufFree (*UdpPacket); + *UdpPacket = NULL; + // + // Send the Mtftp6 error message if invalid packet. + // + Mtftp6SendError ( + Instance, + EFI_MTFTP6_ERRORCODE_ILLEGAL_OPERATION, + (UINT8 *) "Malformatted OACK packet" + ); + } + + return EFI_TFTP_ERROR; + } + + if ((ExtInfo.BitMap & MTFTP6_OPT_MCAST_BIT) != 0) { + + // + // Save the multicast info. Always update the Master, only update the + // multicast IP address, block size, window size, timeoute at the first time. If IP + // address is updated, create a UDP child to receive the multicast. + // + Instance->IsMaster = ExtInfo.IsMaster; + + if (NetIp6IsUnspecifiedAddr (&Instance->McastIp)) { + if (NetIp6IsUnspecifiedAddr (&ExtInfo.McastIp) || ExtInfo.McastPort == 0) { + // + // Free the received packet before send new packet in ReceiveNotify, + // since the udpio might need to be reconfigured. + // + NetbufFree (*UdpPacket); + *UdpPacket = NULL; + // + // Send the Mtftp6 error message if invalid multi-cast setting. + // + Mtftp6SendError ( + Instance, + EFI_MTFTP6_ERRORCODE_ILLEGAL_OPERATION, + (UINT8 *) "Illegal multicast setting" + ); + + return EFI_TFTP_ERROR; + } + + // + // Create a UDP child then start receive the multicast from it. + // + CopyMem ( + &Instance->McastIp, + &ExtInfo.McastIp, + sizeof (EFI_IP_ADDRESS) + ); + + Instance->McastPort = ExtInfo.McastPort; + if (Instance->McastUdpIo == NULL) { + Instance->McastUdpIo = UdpIoCreateIo ( + Instance->Service->Controller, + Instance->Service->Image, + Mtftp6RrqConfigMcastUdpIo, + UDP_IO_UDP6_VERSION, + Instance + ); + if (Instance->McastUdpIo != NULL) { + Status = gBS->OpenProtocol ( + Instance->McastUdpIo->UdpHandle, + &gEfiUdp6ProtocolGuid, + (VOID **) &Udp6, + Instance->Service->Image, + Instance->Handle, + EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER + ); + if (EFI_ERROR (Status)) { + UdpIoFreeIo (Instance->McastUdpIo); + Instance->McastUdpIo = NULL; + return EFI_DEVICE_ERROR; + } + } + } + + if (Instance->McastUdpIo == NULL) { + return EFI_DEVICE_ERROR; + } + + Status = UdpIoRecvDatagram ( + Instance->McastUdpIo, + Mtftp6RrqInput, + Instance, + 0 + ); + + if (EFI_ERROR (Status)) { + // + // Free the received packet before send new packet in ReceiveNotify, + // since the udpio might need to be reconfigured. + // + NetbufFree (*UdpPacket); + *UdpPacket = NULL; + // + // Send the Mtftp6 error message if failed to create Udp6Io to receive. + // + Mtftp6SendError ( + Instance, + EFI_MTFTP6_ERRORCODE_ACCESS_VIOLATION, + (UINT8 *) "Failed to create socket to receive multicast packet" + ); + + return Status; + } + + // + // Update the parameters used. + // + if (ExtInfo.BlkSize != 0) { + Instance->BlkSize = ExtInfo.BlkSize; + } + + if (ExtInfo.WindowSize != 0) { + Instance->WindowSize = ExtInfo.WindowSize; + } + + if (ExtInfo.Timeout != 0) { + Instance->Timeout = ExtInfo.Timeout; + } + } + + } else { + + Instance->IsMaster = TRUE; + + if (ExtInfo.BlkSize != 0) { + Instance->BlkSize = ExtInfo.BlkSize; + } + + if (ExtInfo.WindowSize != 0) { + Instance->WindowSize = ExtInfo.WindowSize; + } + + if (ExtInfo.Timeout != 0) { + Instance->Timeout = ExtInfo.Timeout; + } + } + + // + // Free the received packet before send new packet in ReceiveNotify, + // since the udpio might need to be reconfigured. + // + NetbufFree (*UdpPacket); + *UdpPacket = NULL; + // + // Send an ACK to (Expected - 1) which is 0 for unicast download, + // or tell the server we want to receive the Expected block. + // + return Mtftp6RrqSendAck (Instance, (UINT16) (Expected - 1)); +} + + +/** + The packet process callback for Mtftp6 download. + + @param[in] UdpPacket The pointer to the packet received. + @param[in] UdpEpt The pointer to the Udp6 access point. + @param[in] IoStatus The status from Udp6 instance. + @param[in] Context The pointer to the context. + +**/ +VOID +EFIAPI +Mtftp6RrqInput ( + IN NET_BUF *UdpPacket, + IN UDP_END_POINT *UdpEpt, + IN EFI_STATUS IoStatus, + IN VOID *Context + ) +{ + MTFTP6_INSTANCE *Instance; + EFI_MTFTP6_PACKET *Packet; + BOOLEAN IsCompleted; + BOOLEAN IsMcast; + EFI_STATUS Status; + UINT16 Opcode; + UINT32 TotalNum; + UINT32 Len; + + Instance = (MTFTP6_INSTANCE *) Context; + + NET_CHECK_SIGNATURE (Instance, MTFTP6_INSTANCE_SIGNATURE); + + Status = EFI_SUCCESS; + Packet = NULL; + IsCompleted = FALSE; + IsMcast = FALSE; + TotalNum = 0; + + // + // Return error status if Udp6 instance failed to receive. + // + if (EFI_ERROR (IoStatus)) { + Status = IoStatus; + goto ON_EXIT; + } + + ASSERT (UdpPacket != NULL); + + if (UdpPacket->TotalSize < MTFTP6_OPCODE_LEN) { + goto ON_EXIT; + } + + // + // Find the port this packet is from to restart receive correctly. + // + if (CompareMem ( + Ip6Swap128 (&UdpEpt->LocalAddr.v6), + &Instance->McastIp, + sizeof (EFI_IPv6_ADDRESS) + ) == 0) { + IsMcast = TRUE; + } else { + IsMcast = FALSE; + } + + // + // 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 (UdpEpt->RemotePort != Instance->ServerDataPort) { + if (Instance->ServerDataPort != 0) { + goto ON_EXIT; + } else { + // + // For the subsequent exchange of requests, reconfigure the udpio as + // (serverip, serverport, localip, localport). + // Usually, the client set serverport as 0 to receive and reset it + // once the first packet arrives to send ack. + // + Instance->ServerDataPort = UdpEpt->RemotePort; + } + } + + // + // Copy the MTFTP packet to a continuous buffer if it isn't already so. + // + Len = UdpPacket->TotalSize; + TotalNum = UdpPacket->BlockOpNum; + + if (TotalNum > 1) { + Packet = AllocateZeroPool (Len); + + if (Packet == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto ON_EXIT; + } + + NetbufCopy (UdpPacket, 0, Len, (UINT8 *) Packet); + + } else { + Packet = (EFI_MTFTP6_PACKET *) NetbufGetByte (UdpPacket, 0, NULL); + ASSERT (Packet != NULL); + } + + Opcode = NTOHS (Packet->OpCode); + + // + // Callback to the user's CheckPacket if provided. Abort the transmission + // if CheckPacket returns an EFI_ERROR code. + // + if ((Instance->Token->CheckPacket != NULL) && + (Opcode == EFI_MTFTP6_OPCODE_OACK || Opcode == EFI_MTFTP6_OPCODE_ERROR) + ) { + + Status = Instance->Token->CheckPacket ( + &Instance->Mtftp6, + Instance->Token, + (UINT16) Len, + Packet + ); + + if (EFI_ERROR (Status)) { + // + // Send an error message to the server to inform it + // + if (Opcode != EFI_MTFTP6_OPCODE_ERROR) { + // + // Free the received packet before send new packet in ReceiveNotify, + // since the udpio might need to be reconfigured. + // + NetbufFree (UdpPacket); + UdpPacket = NULL; + // + // Send the Mtftp6 error message if user aborted the current session. + // + Mtftp6SendError ( + Instance, + EFI_MTFTP6_ERRORCODE_REQUEST_DENIED, + (UINT8 *) "User aborted the transfer" + ); + } + + Status = EFI_ABORTED; + goto ON_EXIT; + } + } + + // + // Switch the process routines by the operation code. + // + switch (Opcode) { + case EFI_MTFTP6_OPCODE_DATA: + if ((Len > (UINT32) (MTFTP6_DATA_HEAD_LEN + Instance->BlkSize)) || (Len < (UINT32) MTFTP6_DATA_HEAD_LEN)) { + goto ON_EXIT; + } + // + // Handle the data packet of Rrq. + // + Status = Mtftp6RrqHandleData ( + Instance, + Packet, + Len, + &UdpPacket, + &IsCompleted + ); + break; + + case EFI_MTFTP6_OPCODE_OACK: + if (IsMcast || Len <= MTFTP6_OPCODE_LEN) { + goto ON_EXIT; + } + // + // Handle the Oack packet of Rrq. + // + Status = Mtftp6RrqHandleOack ( + Instance, + Packet, + Len, + &UdpPacket, + &IsCompleted + ); + break; + + default: + // + // Drop and return error if received error message. + // + Status = EFI_TFTP_ERROR; + break; + } + +ON_EXIT: + // + // Free the resources, then if !EFI_ERROR (Status), restart the + // receive, otherwise end the session. + // + if (Packet != NULL && TotalNum > 1) { + FreePool (Packet); + } + if (UdpPacket != NULL) { + NetbufFree (UdpPacket); + } + if (!EFI_ERROR (Status) && !IsCompleted) { + if (IsMcast) { + Status = UdpIoRecvDatagram ( + Instance->McastUdpIo, + Mtftp6RrqInput, + Instance, + 0 + ); + } else { + Status = UdpIoRecvDatagram ( + Instance->UdpIo, + Mtftp6RrqInput, + Instance, + 0 + ); + } + } + // + // Clean up the current session if failed to continue. + // + if (EFI_ERROR (Status) || IsCompleted) { + Mtftp6OperationClean (Instance, Status); + } +} + + +/** + Start the Mtftp6 instance to download. It first initializes some + of the internal states, then builds and sends an RRQ request packet. + Finally, it starts receive for the downloading. + + @param[in] Instance The pointer to the Mtftp6 instance. + @param[in] Operation The operation code of current packet. + + @retval EFI_SUCCESS The Mtftp6 is started to download. + @retval Others Failed to start to download. + +**/ +EFI_STATUS +Mtftp6RrqStart ( + IN MTFTP6_INSTANCE *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 = Mtftp6InitBlockRange (&Instance->BlkList, 1, 0xffff); + + if (EFI_ERROR (Status)) { + return Status; + } + + Status = Mtftp6SendRequest (Instance, Operation); + + if (EFI_ERROR (Status)) { + return Status; + } + + return UdpIoRecvDatagram ( + Instance->UdpIo, + Mtftp6RrqInput, + Instance, + 0 + ); +} + diff --git a/src/VBox/Devices/EFI/Firmware/NetworkPkg/Mtftp6Dxe/Mtftp6Support.c b/src/VBox/Devices/EFI/Firmware/NetworkPkg/Mtftp6Dxe/Mtftp6Support.c new file mode 100644 index 00000000..ba342c67 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/NetworkPkg/Mtftp6Dxe/Mtftp6Support.c @@ -0,0 +1,1240 @@ +/** @file + Mtftp6 support functions implementation. + + Copyright (c) 2009 - 2018, Intel Corporation. All rights reserved.<BR> + + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "Mtftp6Impl.h" + + +/** + Allocate a MTFTP block range, then init it to the range of [Start, End]. + + @param[in] Start The start block number. + @param[in] End The last block number in the range. + + @return Range The range of the allocated block buffer. + +**/ +MTFTP6_BLOCK_RANGE * +Mtftp6AllocateRange ( + IN UINT16 Start, + IN UINT16 End + ) +{ + MTFTP6_BLOCK_RANGE *Range; + + Range = AllocateZeroPool (sizeof (MTFTP6_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 startup, + WRQ initializes its whole valid block range to [0, 0xffff]. This + is because the server will send an ACK0 to inform the user to start the + upload. When the client receives an 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 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[in] Head The block range head to initialize. + @param[in] Start The Start block number. + @param[in] 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 +Mtftp6InitBlockRange ( + IN LIST_ENTRY *Head, + IN UINT16 Start, + IN UINT16 End + ) +{ + MTFTP6_BLOCK_RANGE *Range; + + Range = Mtftp6AllocateRange (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[in] Head The block range head. + + @retval ==-1 If the block range is empty. + @retval >-1 The first valid block number. + +**/ +INTN +Mtftp6GetNextBlockNum ( + IN LIST_ENTRY *Head + ) +{ + MTFTP6_BLOCK_RANGE *Range; + + if (IsListEmpty (Head)) { + return -1; + } + + Range = NET_LIST_HEAD (Head, MTFTP6_BLOCK_RANGE, Link); + return Range->Start; +} + + +/** + Set the last block number of the block range list. It + removes 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 calls + this function to set the last block number. + + @param[in] Head The block range list. + @param[in] Last The last block number. + +**/ +VOID +Mtftp6SetLastBlockNum ( + IN LIST_ENTRY *Head, + IN UINT16 Last + ) +{ + MTFTP6_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, MTFTP6_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[in] Head The block range list to remove from. + @param[in] Num The block number to remove. + @param[in] Completed Whether Num is the last block number. + @param[out] 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 resources. + +**/ +EFI_STATUS +Mtftp6RemoveBlockNum ( + IN LIST_ENTRY *Head, + IN UINT16 Num, + IN BOOLEAN Completed, + OUT UINT64 *BlockCounter + ) +{ + MTFTP6_BLOCK_RANGE *Range; + MTFTP6_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, MTFTP6_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 (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 = Mtftp6AllocateRange ((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; +} + + +/** + Configure the opened Udp6 instance until the corresponding Ip6 instance + has been configured. + + @param[in] UdpIo The pointer to the Udp6 Io. + @param[in] UdpCfgData The pointer to the Udp6 configure data. + + @retval EFI_SUCCESS Configure the Udp6 instance successfully. + @retval EFI_NO_MAPPING The corresponding Ip6 instance has not + been configured yet. + +**/ +EFI_STATUS +Mtftp6GetMapping ( + IN UDP_IO *UdpIo, + IN EFI_UDP6_CONFIG_DATA *UdpCfgData + ) +{ + EFI_IP6_MODE_DATA Ip6Mode; + EFI_UDP6_PROTOCOL *Udp6; + EFI_STATUS Status; + EFI_EVENT Event; + + Event = NULL; + Udp6 = UdpIo->Protocol.Udp6; + + // + // Create a timer to check whether the Ip6 instance configured or not. + // + Status = gBS->CreateEvent ( + EVT_TIMER, + TPL_CALLBACK, + NULL, + NULL, + &Event + ); + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + + Status = gBS->SetTimer ( + Event, + TimerRelative, + MTFTP6_GET_MAPPING_TIMEOUT * MTFTP6_TICK_PER_SECOND + ); + if (EFI_ERROR (Status)) { + goto ON_EXIT; + } + + // + // Check the Ip6 mode data till timeout. + // + while (EFI_ERROR (gBS->CheckEvent (Event))) { + + Udp6->Poll (Udp6); + + Status = Udp6->GetModeData (Udp6, NULL, &Ip6Mode, NULL, NULL); + + if (!EFI_ERROR (Status)) { + if (Ip6Mode.AddressList != NULL) { + FreePool (Ip6Mode.AddressList); + } + + if (Ip6Mode.GroupTable != NULL) { + FreePool (Ip6Mode.GroupTable); + } + + if (Ip6Mode.RouteTable != NULL) { + FreePool (Ip6Mode.RouteTable); + } + + if (Ip6Mode.NeighborCache != NULL) { + FreePool (Ip6Mode.NeighborCache); + } + + if (Ip6Mode.PrefixTable != NULL) { + FreePool (Ip6Mode.PrefixTable); + } + + if (Ip6Mode.IcmpTypeList != NULL) { + FreePool (Ip6Mode.IcmpTypeList); + } + + if (Ip6Mode.IsConfigured) { + // + // Continue to configure the Udp6 instance. + // + Status = Udp6->Configure (Udp6, UdpCfgData); + } else { + Status = EFI_NO_MAPPING; + } + } + } + +ON_EXIT: + + if (Event != NULL) { + gBS->CloseEvent (Event); + } + + return Status; +} + + +/** + The dummy configure routine for create a new Udp6 Io. + + @param[in] UdpIo The pointer to the Udp6 Io. + @param[in] Context The pointer to the context. + + @retval EFI_SUCCESS This value is always returned. + +**/ +EFI_STATUS +EFIAPI +Mtftp6ConfigDummyUdpIo ( + IN UDP_IO *UdpIo, + IN VOID *Context + ) +{ + return EFI_SUCCESS; +} + + +/** + The configure routine for Mtftp6 instance to transmit/receive. + + @param[in] UdpIo The pointer to the Udp6 Io. + @param[in] ServerIp The pointer to the server address. + @param[in] ServerPort The pointer to the server port. + @param[in] LocalIp The pointer to the local address. + @param[in] LocalPort The pointer to the local port. + + @retval EFI_SUCCESS Configured the Udp6 Io for Mtftp6 successfully. + @retval EFI_NO_MAPPING The corresponding Ip6 instance has not been + configured yet. + +**/ +EFI_STATUS +Mtftp6ConfigUdpIo ( + IN UDP_IO *UdpIo, + IN EFI_IPv6_ADDRESS *ServerIp, + IN UINT16 ServerPort, + IN EFI_IPv6_ADDRESS *LocalIp, + IN UINT16 LocalPort + ) +{ + EFI_STATUS Status; + EFI_UDP6_PROTOCOL *Udp6; + EFI_UDP6_CONFIG_DATA *Udp6Cfg; + + Udp6 = UdpIo->Protocol.Udp6; + Udp6Cfg = &(UdpIo->Config.Udp6); + + ZeroMem (Udp6Cfg, sizeof (EFI_UDP6_CONFIG_DATA)); + + // + // Set the Udp6 Io configure data. + // + Udp6Cfg->AcceptPromiscuous = FALSE; + Udp6Cfg->AcceptAnyPort = FALSE; + Udp6Cfg->AllowDuplicatePort = FALSE; + Udp6Cfg->TrafficClass = 0; + Udp6Cfg->HopLimit = 128; + Udp6Cfg->ReceiveTimeout = 0; + Udp6Cfg->TransmitTimeout = 0; + Udp6Cfg->StationPort = LocalPort; + Udp6Cfg->RemotePort = ServerPort; + + CopyMem ( + &Udp6Cfg->StationAddress, + LocalIp, + sizeof (EFI_IPv6_ADDRESS) + ); + + CopyMem ( + &Udp6Cfg->RemoteAddress, + ServerIp, + sizeof (EFI_IPv6_ADDRESS) + ); + + // + // Configure the Udp6 instance with current configure data. + // + Status = Udp6->Configure (Udp6, Udp6Cfg); + + if (Status == EFI_NO_MAPPING) { + + return Mtftp6GetMapping (UdpIo, Udp6Cfg); + } + + return Status; +} + + +/** + Build and transmit the request packet for the Mtftp6 instance. + + @param[in] Instance The pointer to the Mtftp6 instance. + @param[in] Operation The operation code of this packet. + + @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 +Mtftp6SendRequest ( + IN MTFTP6_INSTANCE *Instance, + IN UINT16 Operation + ) +{ + EFI_MTFTP6_PACKET *Packet; + EFI_MTFTP6_OPTION *Options; + EFI_MTFTP6_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 = Token->ModeStr; + + if (Mode == NULL) { + Mode = (UINT8 *) "octet"; + } + + // + // The header format of RRQ/WRQ packet is: + // + // 2 bytes string 1 byte string 1 byte + // ------------------------------------------------ + // | Opcode | Filename | 0 | Mode | 0 | + // ------------------------------------------------ + // + // The common option format is: + // + // string 1 byte string 1 byte + // --------------------------------------- + // | OptionStr | 0 | ValueStr | 0 | + // --------------------------------------- + // + + // + // Compute the size of new Mtftp6 packet. + // + 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. + // + if ((Nbuf = NetbufAlloc (BufferLength)) == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + // + // Copy the opcode, filename and mode into packet. + // + Packet = (EFI_MTFTP6_PACKET *) NetbufAllocSpace (Nbuf, BufferLength, FALSE); + ASSERT (Packet != NULL); + + Packet->OpCode = HTONS (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; + + // + // Copy all the extension options into the packet. + // + 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; + + } + + // + // Save the packet buf for retransmit + // + if (Instance->LastPacket != NULL) { + NetbufFree (Instance->LastPacket); + } + + Instance->LastPacket = Nbuf; + Instance->CurRetry = 0; + + return Mtftp6TransmitPacket (Instance, Nbuf); +} + + +/** + Build and send an error packet. + + @param[in] Instance The pointer to the Mtftp6 instance. + @param[in] ErrCode The error code in the packet. + @param[in] ErrInfo The error message in the packet. + + @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 +Mtftp6SendError ( + IN MTFTP6_INSTANCE *Instance, + IN UINT16 ErrCode, + IN UINT8* ErrInfo + ) +{ + NET_BUF *Nbuf; + EFI_MTFTP6_PACKET *TftpError; + UINT32 Len; + + // + // Allocate a packet then copy the data. + // + Len = (UINT32) (AsciiStrLen ((CHAR8 *) ErrInfo) + sizeof (EFI_MTFTP6_ERROR_HEADER)); + Nbuf = NetbufAlloc (Len); + + if (Nbuf == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + TftpError = (EFI_MTFTP6_PACKET *) NetbufAllocSpace (Nbuf, Len, FALSE); + + if (TftpError == NULL) { + NetbufFree (Nbuf); + return EFI_OUT_OF_RESOURCES; + } + + TftpError->OpCode = HTONS (EFI_MTFTP6_OPCODE_ERROR); + TftpError->Error.ErrorCode = HTONS (ErrCode); + + AsciiStrCpyS ((CHAR8 *) TftpError->Error.ErrorMessage, AsciiStrLen ((CHAR8 *) ErrInfo) + 1 , (CHAR8 *) ErrInfo); + + // + // Save the packet buf for retransmit + // + if (Instance->LastPacket != NULL) { + NetbufFree (Instance->LastPacket); + } + + Instance->LastPacket = Nbuf; + Instance->CurRetry = 0; + + return Mtftp6TransmitPacket (Instance, Nbuf); +} + + +/** + The callback function called when the packet is transmitted. + + @param[in] Packet The pointer to the packet. + @param[in] UdpEpt The pointer to the Udp6 access point. + @param[in] IoStatus The result of the transmission. + @param[in] Context The pointer to the context. + +**/ +VOID +EFIAPI +Mtftp6OnPacketSent ( + IN NET_BUF *Packet, + IN UDP_END_POINT *UdpEpt, + IN EFI_STATUS IoStatus, + IN VOID *Context + ) +{ + NetbufFree (Packet); + *(BOOLEAN *) Context = TRUE; +} + + +/** + Send the packet for the Mtftp6 instance. + + @param[in] Instance The pointer to the Mtftp6 instance. + @param[in] Packet The pointer to the packet to be sent. + + @retval EFI_SUCCESS The packet was sent out + @retval Others Failed to transmit the packet. + +**/ +EFI_STATUS +Mtftp6TransmitPacket ( + IN MTFTP6_INSTANCE *Instance, + IN NET_BUF *Packet + ) +{ + EFI_UDP6_PROTOCOL *Udp6; + EFI_UDP6_CONFIG_DATA Udp6CfgData; + EFI_STATUS Status; + UINT16 *Temp; + UINT16 Value; + UINT16 OpCode; + + ZeroMem (&Udp6CfgData, sizeof(EFI_UDP6_CONFIG_DATA)); + Udp6 = Instance->UdpIo->Protocol.Udp6; + + // + // Set the live time of the packet. + // + Instance->PacketToLive = Instance->IsMaster ? Instance->Timeout : (Instance->Timeout * 2); + + Temp = (UINT16 *) NetbufGetByte (Packet, 0, NULL); + ASSERT (Temp != NULL); + + Value = *Temp; + OpCode = NTOHS (Value); + + if (OpCode == EFI_MTFTP6_OPCODE_RRQ || OpCode == EFI_MTFTP6_OPCODE_DIR || OpCode == EFI_MTFTP6_OPCODE_WRQ) { + // + // For the Rrq, Dir, Wrq requests of the operation, configure the Udp6Io as + // (serverip, 69, localip, localport) to send. + // Usually local address and local port are both default as zero. + // + Status = Udp6->Configure (Udp6, NULL); + + if (EFI_ERROR (Status)) { + return Status; + } + + Status = Mtftp6ConfigUdpIo ( + Instance->UdpIo, + &Instance->ServerIp, + Instance->ServerCmdPort, + &Instance->Config->StationIp, + Instance->Config->LocalPort + ); + + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Get the current local address and port by get Udp6 mode data. + // + Status = Udp6->GetModeData (Udp6, &Udp6CfgData, NULL, NULL, NULL); + if (EFI_ERROR (Status)) { + return Status; + } + + NET_GET_REF (Packet); + + Instance->IsTransmitted = FALSE; + + Status = UdpIoSendDatagram ( + Instance->UdpIo, + Packet, + NULL, + NULL, + Mtftp6OnPacketSent, + &Instance->IsTransmitted + ); + + if (EFI_ERROR (Status)) { + NET_PUT_REF (Packet); + return Status; + } + + // + // Poll till the packet sent out from the ip6 queue. + // + gBS->RestoreTPL (Instance->OldTpl); + + while (!Instance->IsTransmitted) { + Udp6->Poll (Udp6); + } + + Instance->OldTpl = gBS->RaiseTPL (TPL_CALLBACK); + + // + // For the subsequent exchange of such requests, reconfigure the Udp6Io as + // (serverip, 0, localip, localport) to receive. + // Currently local address and local port are specified by Udp6 mode data. + // + Status = Udp6->Configure (Udp6, NULL); + + if (EFI_ERROR (Status)) { + return Status; + } + + Status = Mtftp6ConfigUdpIo ( + Instance->UdpIo, + &Instance->ServerIp, + Instance->ServerDataPort, + &Udp6CfgData.StationAddress, + Udp6CfgData.StationPort + ); + } else { + // + // For the data exchange, configure the Udp6Io as (serverip, dataport, + // localip, localport) to send/receive. + // Currently local address and local port are specified by Udp6 mode data. + // + Status = Udp6->GetModeData (Udp6, &Udp6CfgData, NULL, NULL, NULL); + if (EFI_ERROR (Status)) { + return Status; + } + + if (Udp6CfgData.RemotePort != Instance->ServerDataPort) { + + Status = Udp6->Configure (Udp6, NULL); + + if (EFI_ERROR (Status)) { + return Status; + } + + Status = Mtftp6ConfigUdpIo ( + Instance->UdpIo, + &Instance->ServerIp, + Instance->ServerDataPort, + &Udp6CfgData.StationAddress, + Udp6CfgData.StationPort + ); + + if (EFI_ERROR (Status)) { + return Status; + } + } + + NET_GET_REF (Packet); + + Instance->IsTransmitted = FALSE; + + Status = UdpIoSendDatagram ( + Instance->UdpIo, + Packet, + NULL, + NULL, + Mtftp6OnPacketSent, + &Instance->IsTransmitted + ); + + if (EFI_ERROR (Status)) { + NET_PUT_REF (Packet); + } + + // + // Poll till the packet sent out from the ip6 queue. + // + gBS->RestoreTPL (Instance->OldTpl); + + while (!Instance->IsTransmitted) { + Udp6->Poll (Udp6); + } + + Instance->OldTpl = gBS->RaiseTPL (TPL_CALLBACK); + } + + return Status; +} + + +/** + Check packet for GetInfo callback routine. + + GetInfo is implemented with EfiMtftp6ReadFile. It's used to inspect + the first packet from server, then abort the session. + + @param[in] This The pointer to the Mtftp6 protocol. + @param[in] Token The pointer to the Mtftp6 token. + @param[in] PacketLen The length of the packet. + @param[in] Packet The pointer to the received packet. + + @retval EFI_ABORTED Abort the Mtftp6 operation. + +**/ +EFI_STATUS +EFIAPI +Mtftp6CheckPacket ( + IN EFI_MTFTP6_PROTOCOL *This, + IN EFI_MTFTP6_TOKEN *Token, + IN UINT16 PacketLen, + IN EFI_MTFTP6_PACKET *Packet + ) +{ + MTFTP6_GETINFO_CONTEXT *Context; + UINT16 OpCode; + + Context = (MTFTP6_GETINFO_CONTEXT *) Token->Context; + OpCode = NTOHS (Packet->OpCode); + + // + // Set the GetInfo's return status according to the OpCode. + // + switch (OpCode) { + case EFI_MTFTP6_OPCODE_ERROR: + Context->Status = EFI_TFTP_ERROR; + break; + + case EFI_MTFTP6_OPCODE_OACK: + Context->Status = EFI_SUCCESS; + break; + + default: + Context->Status = EFI_PROTOCOL_ERROR; + } + + // + // Allocate buffer then copy the packet over. Use gBS->AllocatePool + // in case NetAllocatePool will implements something tricky. + // + *(Context->Packet) = AllocateZeroPool (PacketLen); + + if (*(Context->Packet) == NULL) { + Context->Status = EFI_OUT_OF_RESOURCES; + return EFI_ABORTED; + } + + *(Context->PacketLen) = PacketLen; + CopyMem (*(Context->Packet), Packet, PacketLen); + + return EFI_ABORTED; +} + + +/** + Clean up the current Mtftp6 operation. + + @param[in] Instance The pointer to the Mtftp6 instance. + @param[in] Result The result to be returned to the user. + +**/ +VOID +Mtftp6OperationClean ( + IN MTFTP6_INSTANCE *Instance, + IN EFI_STATUS Result + ) +{ + LIST_ENTRY *Entry; + LIST_ENTRY *Next; + MTFTP6_BLOCK_RANGE *Block; + + // + // Clean up the current token and event. + // + if (Instance->Token != NULL) { + Instance->Token->Status = Result; + if (Instance->Token->Event != NULL) { + gBS->SignalEvent (Instance->Token->Event); + } + Instance->Token = NULL; + } + + // + // Clean up the corresponding Udp6Io. + // + if (Instance->UdpIo != NULL) { + UdpIoCleanIo (Instance->UdpIo); + } + + if (Instance->McastUdpIo != NULL) { + gBS->CloseProtocol ( + Instance->McastUdpIo->UdpHandle, + &gEfiUdp6ProtocolGuid, + Instance->McastUdpIo->Image, + Instance->Handle + ); + UdpIoFreeIo (Instance->McastUdpIo); + Instance->McastUdpIo = NULL; + } + + // + // Clean up the stored last packet. + // + if (Instance->LastPacket != NULL) { + NetbufFree (Instance->LastPacket); + Instance->LastPacket = NULL; + } + + NET_LIST_FOR_EACH_SAFE (Entry, Next, &Instance->BlkList) { + Block = NET_LIST_USER_STRUCT (Entry, MTFTP6_BLOCK_RANGE, Link); + RemoveEntryList (Entry); + FreePool (Block); + } + + // + // Reinitialize the corresponding fields of the Mtftp6 operation. + // + ZeroMem (&Instance->ExtInfo, sizeof (MTFTP6_EXT_OPTION_INFO)); + ZeroMem (&Instance->ServerIp, sizeof (EFI_IPv6_ADDRESS)); + ZeroMem (&Instance->McastIp, sizeof (EFI_IPv6_ADDRESS)); + + Instance->ServerCmdPort = 0; + Instance->ServerDataPort = 0; + Instance->McastPort = 0; + Instance->BlkSize = 0; + Instance->Operation = 0; + Instance->WindowSize = 1; + Instance->TotalBlock = 0; + Instance->AckedBlock = 0; + Instance->LastBlk = 0; + Instance->PacketToLive = 0; + Instance->MaxRetry = 0; + Instance->CurRetry = 0; + Instance->Timeout = 0; + Instance->IsMaster = TRUE; +} + + +/** + Start the Mtftp6 instance to perform the operation, such as read file, + write file, and read directory. + + @param[in] This The MTFTP session. + @param[in] Token The token than encapsules the user's request. + @param[in] OpCode The operation to perform. + + @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 +Mtftp6OperationStart ( + IN EFI_MTFTP6_PROTOCOL *This, + IN EFI_MTFTP6_TOKEN *Token, + IN UINT16 OpCode + ) +{ + MTFTP6_INSTANCE *Instance; + EFI_STATUS Status; + + if (This == NULL || + Token == NULL || + Token->Filename == NULL || + (Token->OptionCount != 0 && Token->OptionList == NULL) || + (Token->OverrideData != NULL && !NetIp6IsValidUnicast (&Token->OverrideData->ServerIp)) + ) { + return EFI_INVALID_PARAMETER; + } + + // + // At least define one method to collect the data for download. + // + if ((OpCode == EFI_MTFTP6_OPCODE_RRQ || OpCode == EFI_MTFTP6_OPCODE_DIR) && + Token->Buffer == NULL && + Token->CheckPacket == NULL + ) { + return EFI_INVALID_PARAMETER; + } + + // + // At least define one method to provide the data for upload. + // + if (OpCode == EFI_MTFTP6_OPCODE_WRQ && Token->Buffer == NULL && Token->PacketNeeded == NULL) { + return EFI_INVALID_PARAMETER; + } + + Instance = MTFTP6_INSTANCE_FROM_THIS (This); + + if (Instance->Config == NULL) { + return EFI_NOT_STARTED; + } + + if (Instance->Token != NULL) { + return EFI_ACCESS_DENIED; + } + + Status = EFI_SUCCESS; + Instance->OldTpl = gBS->RaiseTPL (TPL_CALLBACK); + + Instance->Operation = OpCode; + + // + // Parse the extension options in the request packet. + // + if (Token->OptionCount != 0) { + + Status = Mtftp6ParseExtensionOption ( + Token->OptionList, + Token->OptionCount, + TRUE, + Instance->Operation, + &Instance->ExtInfo + ); + + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + } + + // + // Initialize runtime data from config data or override data. + // + Instance->Token = Token; + Instance->ServerCmdPort = Instance->Config->InitialServerPort; + Instance->ServerDataPort = 0; + Instance->MaxRetry = Instance->Config->TryCount; + Instance->Timeout = Instance->Config->TimeoutValue; + Instance->IsMaster = TRUE; + + CopyMem ( + &Instance->ServerIp, + &Instance->Config->ServerIp, + sizeof (EFI_IPv6_ADDRESS) + ); + + if (Token->OverrideData != NULL) { + Instance->ServerCmdPort = Token->OverrideData->ServerPort; + Instance->MaxRetry = Token->OverrideData->TryCount; + Instance->Timeout = Token->OverrideData->TimeoutValue; + + CopyMem ( + &Instance->ServerIp, + &Token->OverrideData->ServerIp, + sizeof (EFI_IPv6_ADDRESS) + ); + } + + // + // Set default value for undefined parameters. + // + if (Instance->ServerCmdPort == 0) { + Instance->ServerCmdPort = MTFTP6_DEFAULT_SERVER_CMD_PORT; + } + if (Instance->BlkSize == 0) { + Instance->BlkSize = MTFTP6_DEFAULT_BLK_SIZE; + } + if (Instance->WindowSize == 0) { + Instance->WindowSize = MTFTP6_DEFAULT_WINDOWSIZE; + } + if (Instance->MaxRetry == 0) { + Instance->MaxRetry = MTFTP6_DEFAULT_MAX_RETRY; + } + if (Instance->Timeout == 0) { + Instance->Timeout = MTFTP6_DEFAULT_TIMEOUT; + } + + Token->Status = EFI_NOT_READY; + + // + // Switch the routines by the operation code. + // + switch (OpCode) { + case EFI_MTFTP6_OPCODE_RRQ: + Status = Mtftp6RrqStart (Instance, OpCode); + break; + + case EFI_MTFTP6_OPCODE_DIR: + Status = Mtftp6RrqStart (Instance, OpCode); + break; + + case EFI_MTFTP6_OPCODE_WRQ: + Status = Mtftp6WrqStart (Instance, OpCode); + break; + + default: + Status = EFI_DEVICE_ERROR; + goto ON_ERROR; + } + + if (EFI_ERROR (Status)) { + goto ON_ERROR; + } + + // + // Return immediately for asynchronous or poll the instance for synchronous. + // + gBS->RestoreTPL (Instance->OldTpl); + + if (Token->Event == NULL) { + while (Token->Status == EFI_NOT_READY) { + This->Poll (This); + } + return Token->Status; + } + + return EFI_SUCCESS; + +ON_ERROR: + + Mtftp6OperationClean (Instance, Status); + gBS->RestoreTPL (Instance->OldTpl); + + return Status; +} + + +/** + The timer ticking routine for the Mtftp6 instance. + + @param[in] Event The pointer to the ticking event. + @param[in] Context The pointer to the context. + +**/ +VOID +EFIAPI +Mtftp6OnTimerTick ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + MTFTP6_SERVICE *Service; + MTFTP6_INSTANCE *Instance; + LIST_ENTRY *Entry; + LIST_ENTRY *Next; + EFI_MTFTP6_TOKEN *Token; + EFI_STATUS Status; + + Service = (MTFTP6_SERVICE *) Context; + + // + // Iterate through all the children of the Mtftp service instance. Time + // out the packet. If maximum retries reached, clean the session up. + // + NET_LIST_FOR_EACH_SAFE (Entry, Next, &Service->Children) { + + Instance = NET_LIST_USER_STRUCT (Entry, MTFTP6_INSTANCE, Link); + + if (Instance->Token == NULL) { + continue; + } + + if (Instance->PacketToLive > 0) { + Instance->PacketToLive--; + continue; + } + + Instance->CurRetry++; + Token = Instance->Token; + + if (Token->TimeoutCallback != NULL) { + // + // Call the timeout callback routine if has. + // + Status = Token->TimeoutCallback (&Instance->Mtftp6, Token); + + if (EFI_ERROR (Status)) { + Mtftp6SendError ( + Instance, + EFI_MTFTP6_ERRORCODE_REQUEST_DENIED, + (UINT8 *) "User aborted the transfer in time out" + ); + Mtftp6OperationClean (Instance, EFI_ABORTED); + continue; + } + } + + // + // Retransmit the packet if haven't reach the maximum retry count, + // otherwise exit the transfer. + // + if (Instance->CurRetry < Instance->MaxRetry) { + Mtftp6TransmitPacket (Instance, Instance->LastPacket); + } else { + Mtftp6OperationClean (Instance, EFI_TIMEOUT); + continue; + } + } +} diff --git a/src/VBox/Devices/EFI/Firmware/NetworkPkg/Mtftp6Dxe/Mtftp6Support.h b/src/VBox/Devices/EFI/Firmware/NetworkPkg/Mtftp6Dxe/Mtftp6Support.h new file mode 100644 index 00000000..3c068724 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/NetworkPkg/Mtftp6Dxe/Mtftp6Support.h @@ -0,0 +1,353 @@ +/** @file + Mtftp6 support functions declaration. + + Copyright (c) 2009 - 2018, Intel Corporation. All rights reserved.<BR> + + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef __EFI_MTFTP6_SUPPORT_H__ +#define __EFI_MTFTP6_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; +} MTFTP6_BLOCK_RANGE; + + +/** + Initialize the block range for either RRQ or WRQ. RRQ and WRQ have + different requirements for Start and End. For example, during startup, + WRQ initializes its whole valid block range to [0, 0xffff]. This + is because the server will send an ACK0 to inform the user to start the + upload. When the client receives an 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 BLOCK1 is received, the client will + remove it from the block range and send an ACK. It also works if there + is option negotiation. + + @param[in] Head The block range head to initialize. + @param[in] Start The Start block number. + @param[in] 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 +Mtftp6InitBlockRange ( + IN LIST_ENTRY *Head, + IN UINT16 Start, + IN UINT16 End + ); + + +/** + Get the first valid block number on the range list. + + @param[in] Head The block range head. + + @retval ==-1 If the block range is empty. + @retval >-1 The first valid block number. + +**/ +INTN +Mtftp6GetNextBlockNum ( + IN LIST_ENTRY *Head + ); + + +/** + Set the last block number of the block range list. It + removes 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 calls + this function to set the last block number. + + @param[in] Head The block range list. + @param[in] Last The last block number. + +**/ +VOID +Mtftp6SetLastBlockNum ( + IN LIST_ENTRY *Head, + IN UINT16 Last + ); + + +/** + Remove the block number from the block range list. + + @param[in] Head The block range list to remove from. + @param[in] Num The block number to remove. + @param[in] Completed Whether Num is the last block number. + @param[out] 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 resources. + +**/ +EFI_STATUS +Mtftp6RemoveBlockNum ( + IN LIST_ENTRY *Head, + IN UINT16 Num, + IN BOOLEAN Completed, + OUT UINT64 *BlockCounter + ); + + +/** + Build and transmit the request packet for the Mtftp6 instance. + + @param[in] Instance The pointer to the Mtftp6 instance. + @param[in] Operation The operation code of this packet. + + @retval EFI_OUT_OF_RESOURCES Failed to allocate memory for the request. + @retval EFI_SUCCESS The request was built and sent. + @retval Others Failed to transmit the packet. + +**/ +EFI_STATUS +Mtftp6SendRequest ( + IN MTFTP6_INSTANCE *Instance, + IN UINT16 Operation + ); + + +/** + Build and send an error packet. + + @param[in] Instance The pointer to the Mtftp6 instance. + @param[in] ErrCode The error code in the packet. + @param[in] ErrInfo The error message in the packet. + + @retval EFI_OUT_OF_RESOURCES Failed to allocate memory for the error packet. + @retval EFI_SUCCESS The error packet was transmitted. + @retval Others Failed to transmit the packet. + +**/ +EFI_STATUS +Mtftp6SendError ( + IN MTFTP6_INSTANCE *Instance, + IN UINT16 ErrCode, + IN UINT8* ErrInfo + ); + + +/** + Send the packet for the Mtftp6 instance. + + @param[in] Instance The pointer to the Mtftp6 instance. + @param[in] Packet The pointer to the packet to be sent. + + @retval EFI_SUCCESS The packet was sent out + @retval Others Failed to transmit the packet. + +**/ +EFI_STATUS +Mtftp6TransmitPacket ( + IN MTFTP6_INSTANCE *Instance, + IN NET_BUF *Packet + ); + + +/** + Check packet for GetInfo callback routine. + + @param[in] This The pointer to the Mtftp6 protocol. + @param[in] Token The pointer to the Mtftp6 token. + @param[in] PacketLen The length of the packet + @param[in] Packet The pointer to the received packet. + + @retval EFI_SUCCESS The check process passed successfully. + @retval EFI_ABORTED Abort the Mtftp6 operation. + +**/ +EFI_STATUS +EFIAPI +Mtftp6CheckPacket ( + IN EFI_MTFTP6_PROTOCOL *This, + IN EFI_MTFTP6_TOKEN *Token, + IN UINT16 PacketLen, + IN EFI_MTFTP6_PACKET *Packet + ); + + +/** + The dummy configure routine for create a new Udp6 Io. + + @param[in] UdpIo The pointer to the Udp6 Io. + @param[in] Context The pointer to the context. + + @retval EFI_SUCCESS The value is always returned. + +**/ +EFI_STATUS +EFIAPI +Mtftp6ConfigDummyUdpIo ( + IN UDP_IO *UdpIo, + IN VOID *Context + ); + + +/** + The configure routine for the Mtftp6 instance to transmit/receive. + + @param[in] UdpIo The pointer to the Udp6 Io. + @param[in] ServerIp The pointer to the server address. + @param[in] ServerPort The pointer to the server port. + @param[in] LocalIp The pointer to the local address. + @param[in] LocalPort The pointer to the local port. + + @retval EFI_SUCCESS Configure the Udp6 Io for Mtftp6 successfully. + @retval EFI_NO_MAPPING The corresponding Ip6 instance has not been + configured yet. + +**/ +EFI_STATUS +Mtftp6ConfigUdpIo ( + IN UDP_IO *UdpIo, + IN EFI_IPv6_ADDRESS *ServerIp, + IN UINT16 ServerPort, + IN EFI_IPv6_ADDRESS *LocalIp, + IN UINT16 LocalPort + ); + + +/** + Clean up the current Mtftp6 operation. + + @param[in] Instance The pointer to the Mtftp6 instance. + @param[in] Result The result to be returned to the user. + +**/ +VOID +Mtftp6OperationClean ( + IN MTFTP6_INSTANCE *Instance, + IN EFI_STATUS Result + ); + + +/** + Start the Mtftp6 instance to perform the operation, such as read file, + write file, and read directory. + + @param[in] This The MTFTP session + @param[in] Token The token that encapsulates the user's request. + @param[in] OpCode The operation to perform. + + @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 was successfully started. + +**/ +EFI_STATUS +Mtftp6OperationStart ( + IN EFI_MTFTP6_PROTOCOL *This, + IN EFI_MTFTP6_TOKEN *Token, + IN UINT16 OpCode + ); + + +/** + The timer ticking routine for the Mtftp6 instance. + + @param[in] Event The pointer to the ticking event. + @param[in] Context The pointer to the context. + +**/ +VOID +EFIAPI +Mtftp6OnTimerTick ( + IN EFI_EVENT Event, + IN VOID *Context + ); + + +/** + The packet process callback for Mtftp6 upload. + + @param[in] UdpPacket The pointer to the packet received. + @param[in] UdpEpt The pointer to the Udp6 access point. + @param[in] IoStatus The status from the Udp6 instance. + @param[in] Context The pointer to the context. + +**/ +VOID +EFIAPI +Mtftp6WrqInput ( + IN NET_BUF *UdpPacket, + IN UDP_END_POINT *UdpEpt, + IN EFI_STATUS IoStatus, + IN VOID *Context + ); + + +/** + Start the Mtftp6 instance to upload. It will first init some states, + then send the WRQ request packet, and start to receive the packet. + + @param[in] Instance The pointer to the Mtftp6 instance. + @param[in] Operation The operation code of current packet. + + @retval EFI_SUCCESS The Mtftp6 was started to upload. + @retval Others Failed to start to upload. + +**/ +EFI_STATUS +Mtftp6WrqStart ( + IN MTFTP6_INSTANCE *Instance, + IN UINT16 Operation + ); + + +/** + The packet process callback for Mtftp6 download. + + @param[in] UdpPacket The pointer to the packet received. + @param[in] UdpEpt The pointer to the Udp6 access point. + @param[in] IoStatus The status from Udp6 instance. + @param[in] Context The pointer to the context. + +**/ +VOID +EFIAPI +Mtftp6RrqInput ( + IN NET_BUF *UdpPacket, + IN UDP_END_POINT *UdpEpt, + IN EFI_STATUS IoStatus, + IN VOID *Context + ); + + +/** + Start the Mtftp6 instance to download. It first initializes some + of the internal states then builds and sends an RRQ request packet. + Finally, it starts receive for the downloading. + + @param[in] Instance The pointer to the Mtftp6 instance. + @param[in] Operation The operation code of current packet. + + @retval EFI_SUCCESS The Mtftp6 was started to download. + @retval Others Failed to start to download. + +**/ +EFI_STATUS +Mtftp6RrqStart ( + IN MTFTP6_INSTANCE *Instance, + IN UINT16 Operation + ); + +#endif diff --git a/src/VBox/Devices/EFI/Firmware/NetworkPkg/Mtftp6Dxe/Mtftp6Wrq.c b/src/VBox/Devices/EFI/Firmware/NetworkPkg/Mtftp6Dxe/Mtftp6Wrq.c new file mode 100644 index 00000000..48e60f65 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/NetworkPkg/Mtftp6Dxe/Mtftp6Wrq.c @@ -0,0 +1,598 @@ +/** @file + Mtftp6 Wrq process functions implementation. + + Copyright (c) 2009 - 2014, Intel Corporation. All rights reserved.<BR> + + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "Mtftp6Impl.h" + + + +/** + Build and send a Mtftp6 data packet for upload. + + @param[in] Instance The pointer to the Mtftp6 instance. + @param[in] BlockNum The block num to be sent. + + @retval EFI_OUT_OF_RESOURCES Failed to allocate memory for the packet. + @retval EFI_SUCCESS The data packet was sent. + @retval EFI_ABORTED The user aborted this process. + +**/ +EFI_STATUS +Mtftp6WrqSendBlock ( + IN MTFTP6_INSTANCE *Instance, + IN UINT16 BlockNum + ) +{ + EFI_MTFTP6_PACKET *Packet; + EFI_MTFTP6_TOKEN *Token; + NET_BUF *UdpPacket; + EFI_STATUS Status; + UINT16 DataLen; + UINT8 *DataBuf; + UINT64 Start; + + // + // Allocate net buffer to create data packet. + // + UdpPacket = NetbufAlloc (Instance->BlkSize + MTFTP6_DATA_HEAD_LEN); + + if (UdpPacket == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + Packet = (EFI_MTFTP6_PACKET *) NetbufAllocSpace ( + UdpPacket, + MTFTP6_DATA_HEAD_LEN, + FALSE + ); + ASSERT (Packet != NULL); + + Packet->Data.OpCode = HTONS (EFI_MTFTP6_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->LastBlk = BlockNum; + Mtftp6SetLastBlockNum (&Instance->BlkList, 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->Mtftp6, Token, &DataLen, (VOID*) &DataBuf); + + if (EFI_ERROR (Status) || (DataLen > Instance->BlkSize)) { + if (DataBuf != NULL) { + gBS->FreePool (DataBuf); + } + // + // The received packet has already been freed. + // + Mtftp6SendError ( + Instance, + EFI_MTFTP6_ERRORCODE_REQUEST_DENIED, + (UINT8 *) "User aborted the transfer" + ); + + return EFI_ABORTED; + } + + if (DataLen < Instance->BlkSize) { + Instance->LastBlk = BlockNum; + Mtftp6SetLastBlockNum (&Instance->BlkList, BlockNum); + } + + if (DataLen > 0) { + NetbufAllocSpace (UdpPacket, DataLen, FALSE); + CopyMem (Packet->Data.Data, DataBuf, DataLen); + gBS->FreePool (DataBuf); + } + } + + // + // Reset current retry count of the instance. + // + Instance->CurRetry = 0; + + return Mtftp6TransmitPacket (Instance, UdpPacket); +} + + +/** + Function to handle received ACK packet. If the ACK number matches the + expected block number, with more data pending, send the next + block. Otherwise, tell the caller that we are done. + + @param[in] Instance The pointer to the Mtftp6 instance. + @param[in] Packet The pointer to the received packet. + @param[in] Len The length of the packet. + @param[out] UdpPacket The net buf of received packet. + @param[out] IsCompleted If TRUE, the upload has been completed. + Otherwise, the upload has not been completed. + + @retval EFI_SUCCESS The ACK packet successfully processed. + @retval EFI_TFTP_ERROR The block number loops back. + @retval Others Failed to transmit the next data packet. + +**/ +EFI_STATUS +Mtftp6WrqHandleAck ( + IN MTFTP6_INSTANCE *Instance, + IN EFI_MTFTP6_PACKET *Packet, + IN UINT32 Len, + OUT NET_BUF **UdpPacket, + OUT BOOLEAN *IsCompleted + ) +{ + UINT16 AckNum; + INTN Expected; + UINT64 BlockCounter; + + *IsCompleted = FALSE; + AckNum = NTOHS (Packet->Ack.Block[0]); + Expected = Mtftp6GetNextBlockNum (&Instance->BlkList); + + ASSERT (Expected >= 0); + + // + // Get an unwanted ACK, return EFI_SUCCESS to let Mtftp6WrqInput + // restart receive. + // + if (Expected != AckNum) { + return EFI_SUCCESS; + } + + // + // Remove the acked block number, if this is the last block number, + // tell the Mtftp6WrqInput to finish the transfer. This is the last + // block number if the block range are empty. + // + Mtftp6RemoveBlockNum (&Instance->BlkList, AckNum, *IsCompleted, &BlockCounter); + + Expected = Mtftp6GetNextBlockNum (&Instance->BlkList); + + 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->LastBlk == AckNum) { + ASSERT (Instance->LastBlk >= 1); + *IsCompleted = TRUE; + return EFI_SUCCESS; + + } else { + // + // Free the received packet before send new packet in ReceiveNotify, + // since the udpio might need to be reconfigured. + // + NetbufFree (*UdpPacket); + *UdpPacket = NULL; + // + // Send the Mtftp6 error message if block number rolls back. + // + Mtftp6SendError ( + Instance, + EFI_MTFTP6_ERRORCODE_REQUEST_DENIED, + (UINT8 *) "Block number rolls back, not supported, try blksize option" + ); + + return EFI_TFTP_ERROR; + } + } + + // + // Free the receive buffer before send new packet since it might need + // reconfigure udpio. + // + NetbufFree (*UdpPacket); + *UdpPacket = NULL; + + return Mtftp6WrqSendBlock (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 MTFTP6 options as required. + + @param[in] ReplyInfo The pointer to options information in reply packet. + @param[in] RequestInfo The pointer to requested options information. + + @retval TRUE If the option in OACK is valid. + @retval FALSE If the option is invalid. + +**/ +BOOLEAN +Mtftp6WrqOackValid ( + IN MTFTP6_EXT_OPTION_INFO *ReplyInfo, + IN MTFTP6_EXT_OPTION_INFO *RequestInfo + ) +{ + // + // It is invalid for server to return options we don't request + // + if ((ReplyInfo->BitMap & ~RequestInfo->BitMap) != 0) { + return FALSE; + } + + // + // Server can only specify a smaller block size to be used and + // return the timeout matches that requested. + // + if ((((ReplyInfo->BitMap & MTFTP6_OPT_BLKSIZE_BIT) != 0) && (ReplyInfo->BlkSize > RequestInfo->BlkSize)) || + (((ReplyInfo->BitMap & MTFTP6_OPT_TIMEOUT_BIT) != 0) && (ReplyInfo->Timeout != RequestInfo->Timeout)) + ) { + + return FALSE; + } + + return TRUE; +} + + +/** + Process the OACK packet for Wrq. + + @param[in] Instance The pointer to the Mtftp6 instance. + @param[in] Packet The pointer to the received packet. + @param[in] Len The length of the packet. + @param[out] UdpPacket The net buf of received packet. + @param[out] IsCompleted If TRUE, the upload has been completed. + Otherwise, the upload has not been completed. + + @retval EFI_SUCCESS The OACK packet successfully processed. + @retval EFI_TFTP_ERROR An TFTP communication error happened. + @retval Others Failed to process the OACK packet. + +**/ +EFI_STATUS +Mtftp6WrqHandleOack ( + IN MTFTP6_INSTANCE *Instance, + IN EFI_MTFTP6_PACKET *Packet, + IN UINT32 Len, + OUT NET_BUF **UdpPacket, + OUT BOOLEAN *IsCompleted + ) +{ + EFI_MTFTP6_OPTION *Options; + UINT32 Count; + MTFTP6_EXT_OPTION_INFO ExtInfo; + EFI_MTFTP6_PACKET Dummy; + EFI_STATUS Status; + INTN Expected; + + *IsCompleted = FALSE; + Options = NULL; + + // + // Ignore the OACK if already started the upload + // + Expected = Mtftp6GetNextBlockNum (&Instance->BlkList); + + if (Expected != 0) { + return EFI_SUCCESS; + } + + // + // Parse and validate the options from server + // + ZeroMem (&ExtInfo, sizeof (MTFTP6_EXT_OPTION_INFO)); + + Status = Mtftp6ParseStart (Packet, Len, &Count, &Options); + + if (EFI_ERROR (Status)) { + return Status; + } + ASSERT (Options != NULL); + + Status = Mtftp6ParseExtensionOption (Options, Count, FALSE, Instance->Operation, &ExtInfo); + + if (EFI_ERROR(Status) || !Mtftp6WrqOackValid (&ExtInfo, &Instance->ExtInfo)) { + // + // Don't send a MTFTP error packet when out of resource, it can + // only make it worse. + // + if (Status != EFI_OUT_OF_RESOURCES) { + // + // Free the received packet before send new packet in ReceiveNotify, + // since the udpio might need to be reconfigured. + // + NetbufFree (*UdpPacket); + *UdpPacket = NULL; + // + // Send the Mtftp6 error message if invalid Oack packet received. + // + Mtftp6SendError ( + Instance, + EFI_MTFTP6_ERRORCODE_ILLEGAL_OPERATION, + (UINT8 *) "Malformatted OACK packet" + ); + } + + return EFI_TFTP_ERROR; + } + + if (ExtInfo.BlkSize != 0) { + Instance->BlkSize = ExtInfo.BlkSize; + } + + if (ExtInfo.Timeout != 0) { + Instance->Timeout = ExtInfo.Timeout; + } + + // + // Build a bogus ACK0 packet then pass it to the Mtftp6WrqHandleAck, + // which will start the transmission of the first data block. + // + Dummy.Ack.OpCode = HTONS (EFI_MTFTP6_OPCODE_ACK); + Dummy.Ack.Block[0] = 0; + + return Mtftp6WrqHandleAck ( + Instance, + &Dummy, + sizeof (EFI_MTFTP6_ACK_HEADER), + UdpPacket, + IsCompleted + ); +} + + +/** + The packet process callback for Mtftp6 upload. + + @param[in] UdpPacket The pointer to the packet received. + @param[in] UdpEpt The pointer to the Udp6 access point. + @param[in] IoStatus The status from Udp6 instance. + @param[in] Context The pointer to the context. + +**/ +VOID +EFIAPI +Mtftp6WrqInput ( + IN NET_BUF *UdpPacket, + IN UDP_END_POINT *UdpEpt, + IN EFI_STATUS IoStatus, + IN VOID *Context + ) +{ + MTFTP6_INSTANCE *Instance; + EFI_MTFTP6_PACKET *Packet; + BOOLEAN IsCompleted; + EFI_STATUS Status; + UINT32 TotalNum; + UINT32 Len; + UINT16 Opcode; + + Instance = (MTFTP6_INSTANCE *) Context; + + NET_CHECK_SIGNATURE (Instance, MTFTP6_INSTANCE_SIGNATURE); + + IsCompleted = FALSE; + Packet = NULL; + Status = EFI_SUCCESS; + TotalNum = 0; + + // + // Return error status if Udp6 instance failed to receive. + // + if (EFI_ERROR (IoStatus)) { + Status = IoStatus; + goto ON_EXIT; + } + + ASSERT (UdpPacket != NULL); + + if (UdpPacket->TotalSize < MTFTP6_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 (UdpEpt->RemotePort != Instance->ServerDataPort) { + if (Instance->ServerDataPort != 0) { + goto ON_EXIT; + } else { + Instance->ServerDataPort = UdpEpt->RemotePort; + } + } + + // + // Copy the MTFTP packet to a continuous buffer if it isn't already so. + // + Len = UdpPacket->TotalSize; + TotalNum = UdpPacket->BlockOpNum; + + if (TotalNum > 1) { + Packet = AllocateZeroPool (Len); + + if (Packet == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto ON_EXIT; + } + + NetbufCopy (UdpPacket, 0, Len, (UINT8 *) Packet); + + } else { + Packet = (EFI_MTFTP6_PACKET *) NetbufGetByte (UdpPacket, 0, NULL); + ASSERT (Packet != NULL); + } + + Opcode = NTOHS (Packet->OpCode); + + // + // Callback to the user's CheckPacket if provided. Abort the transmission + // if CheckPacket returns an EFI_ERROR code. + // + if (Instance->Token->CheckPacket != NULL && + (Opcode == EFI_MTFTP6_OPCODE_OACK || Opcode == EFI_MTFTP6_OPCODE_ERROR) + ) { + + Status = Instance->Token->CheckPacket ( + &Instance->Mtftp6, + Instance->Token, + (UINT16) Len, + Packet + ); + + if (EFI_ERROR (Status)) { + // + // Send an error message to the server to inform it + // + if (Opcode != EFI_MTFTP6_OPCODE_ERROR) { + // + // Free the received packet before send new packet in ReceiveNotify, + // since the udpio might need to be reconfigured. + // + NetbufFree (UdpPacket); + UdpPacket = NULL; + // + // Send the Mtftp6 error message if user aborted the current session. + // + Mtftp6SendError ( + Instance, + EFI_MTFTP6_ERRORCODE_REQUEST_DENIED, + (UINT8 *) "User aborted the transfer" + ); + } + + Status = EFI_ABORTED; + goto ON_EXIT; + } + } + + // + // Switch the process routines by the operation code. + // + switch (Opcode) { + case EFI_MTFTP6_OPCODE_ACK: + if (Len != MTFTP6_OPCODE_LEN + MTFTP6_BLKNO_LEN) { + goto ON_EXIT; + } + // + // Handle the Ack packet of Wrq. + // + Status = Mtftp6WrqHandleAck (Instance, Packet, Len, &UdpPacket, &IsCompleted); + break; + + case EFI_MTFTP6_OPCODE_OACK: + if (Len <= MTFTP6_OPCODE_LEN) { + goto ON_EXIT; + } + // + // Handle the Oack packet of Wrq. + // + Status = Mtftp6WrqHandleOack (Instance, Packet, Len, &UdpPacket, &IsCompleted); + break; + + default: + // + // Drop and return eror if received error message. + // + Status = EFI_TFTP_ERROR; + break; + } + +ON_EXIT: + // + // Free the resources, then if !EFI_ERROR (Status) and not completed, + // restart the receive, otherwise end the session. + // + if (Packet != NULL && TotalNum > 1) { + FreePool (Packet); + } + + if (UdpPacket != NULL) { + NetbufFree (UdpPacket); + } + + if (!EFI_ERROR (Status) && !IsCompleted) { + Status = UdpIoRecvDatagram ( + Instance->UdpIo, + Mtftp6WrqInput, + Instance, + 0 + ); + } + // + // Clean up the current session if failed to continue. + // + if (EFI_ERROR (Status) || IsCompleted) { + Mtftp6OperationClean (Instance, Status); + } +} + + +/** + Start the Mtftp6 instance to upload. It will first init some states, + then send the WRQ request packet, and start to receive the packet. + + @param[in] Instance The pointer to the Mtftp6 instance. + @param[in] Operation The operation code of the current packet. + + @retval EFI_SUCCESS The Mtftp6 was started to upload. + @retval Others Failed to start to upload. + +**/ +EFI_STATUS +Mtftp6WrqStart ( + IN MTFTP6_INSTANCE *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 = Mtftp6InitBlockRange (&Instance->BlkList, 0, 0xffff); + + if (EFI_ERROR (Status)) { + return Status; + } + + Status = Mtftp6SendRequest (Instance, Operation); + + if (EFI_ERROR (Status)) { + return Status; + } + + return UdpIoRecvDatagram ( + Instance->UdpIo, + Mtftp6WrqInput, + Instance, + 0 + ); +} + |