NetworkPkg: Move Network library and drivers from MdeModulePkg to NetworkPkg

Signed-off-by: Liming Gao <liming.gao@intel.com>
Cc: Siyuan Fu <siyuan.fu@intel.com>
Cc: Jiaxin Wu <jiaxin.wu@intel.com>
Reviewed-by: Jiaxin Wu <jiaxin.wu@intel.com>
Reviewed-by: Siyuan Fu <siyuan.fu@intel.com>
This commit is contained in:
Liming Gao
2019-05-15 20:02:18 +08:00
parent c0fd7f734e
commit 4542f8b813
147 changed files with 24 additions and 24 deletions

View File

@@ -0,0 +1,425 @@
/** @file
UEFI Component Name(2) protocol implementation for Mtftp4Dxe driver.
Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.<BR>
SPDX-License-Identifier: BSD-2-Clause-Patent
**/
#include "Mtftp4Impl.h"
//
// EFI Component Name Functions
//
/**
Retrieves a Unicode string that is the user readable name of the driver.
This function retrieves the user readable name of a driver in the form of a
Unicode string. If the driver specified by This has a user readable name in
the language specified by Language, then a pointer to the driver name is
returned in DriverName, and EFI_SUCCESS is returned. If the driver specified
by This does not support the language specified by Language,
then EFI_UNSUPPORTED is returned.
@param[in] This A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or
EFI_COMPONENT_NAME_PROTOCOL instance.
@param[in] Language A pointer to a Null-terminated ASCII string
array indicating the language. This is the
language of the driver name that the caller is
requesting, and it must match one of the
languages specified in SupportedLanguages. The
number of languages supported by a driver is up
to the driver writer. Language is specified
in RFC 4646 or ISO 639-2 language code format.
@param[out] DriverName A pointer to the Unicode string to return.
This Unicode string is the name of the
driver specified by This in the language
specified by Language.
@retval EFI_SUCCESS The Unicode string for the Driver specified by
This and the language specified by Language was
returned in DriverName.
@retval EFI_INVALID_PARAMETER Language is NULL.
@retval EFI_INVALID_PARAMETER DriverName is NULL.
@retval EFI_UNSUPPORTED The driver specified by This does not support
the language specified by Language.
**/
EFI_STATUS
EFIAPI
Mtftp4ComponentNameGetDriverName (
IN EFI_COMPONENT_NAME_PROTOCOL *This,
IN CHAR8 *Language,
OUT CHAR16 **DriverName
);
/**
Retrieves a Unicode string that is the user readable name of the controller
that is being managed by a driver.
This function retrieves the user readable name of the controller specified by
ControllerHandle and ChildHandle in the form of a Unicode string. If the
driver specified by This has a user readable name in the language specified by
Language, then a pointer to the controller name is returned in ControllerName,
and EFI_SUCCESS is returned. If the driver specified by This is not currently
managing the controller specified by ControllerHandle and ChildHandle,
then EFI_UNSUPPORTED is returned. If the driver specified by This does not
support the language specified by Language, then EFI_UNSUPPORTED is returned.
@param[in] This A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or
EFI_COMPONENT_NAME_PROTOCOL instance.
@param[in] ControllerHandle The handle of a controller that the driver
specified by This is managing. This handle
specifies the controller whose name is to be
returned.
@param[in] ChildHandle The handle of the child controller to retrieve
the name of. This is an optional parameter that
may be NULL. It will be NULL for device
drivers. It will also be NULL for a bus drivers
that wish to retrieve the name of the bus
controller. It will not be NULL for a bus
driver that wishes to retrieve the name of a
child controller.
@param[in] Language A pointer to a Null-terminated ASCII string
array indicating the language. This is the
language of the driver name that the caller is
requesting, and it must match one of the
languages specified in SupportedLanguages. The
number of languages supported by a driver is up
to the driver writer. Language is specified in
RFC 4646 or ISO 639-2 language code format.
@param[out] ControllerName A pointer to the Unicode string to return.
This Unicode string is the name of the
controller specified by ControllerHandle and
ChildHandle in the language specified by
Language from the point of view of the driver
specified by This.
@retval EFI_SUCCESS The Unicode string for the user readable name in
the language specified by Language for the
driver specified by This was returned in
DriverName.
@retval EFI_INVALID_PARAMETER ControllerHandle is NULL.
@retval EFI_INVALID_PARAMETER ChildHandle is not NULL and it is not a valid
EFI_HANDLE.
@retval EFI_INVALID_PARAMETER Language is NULL.
@retval EFI_INVALID_PARAMETER ControllerName is NULL.
@retval EFI_UNSUPPORTED The driver specified by This is not currently
managing the controller specified by
ControllerHandle and ChildHandle.
@retval EFI_UNSUPPORTED The driver specified by This does not support
the language specified by Language.
**/
EFI_STATUS
EFIAPI
Mtftp4ComponentNameGetControllerName (
IN EFI_COMPONENT_NAME_PROTOCOL *This,
IN EFI_HANDLE ControllerHandle,
IN EFI_HANDLE ChildHandle OPTIONAL,
IN CHAR8 *Language,
OUT CHAR16 **ControllerName
);
///
/// EFI Component Name Protocol
///
GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME_PROTOCOL gMtftp4ComponentName = {
Mtftp4ComponentNameGetDriverName,
Mtftp4ComponentNameGetControllerName,
"eng"
};
///
/// EFI Component Name 2 Protocol
///
GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME2_PROTOCOL gMtftp4ComponentName2 = {
(EFI_COMPONENT_NAME2_GET_DRIVER_NAME) Mtftp4ComponentNameGetDriverName,
(EFI_COMPONENT_NAME2_GET_CONTROLLER_NAME) Mtftp4ComponentNameGetControllerName,
"en"
};
GLOBAL_REMOVE_IF_UNREFERENCED EFI_UNICODE_STRING_TABLE mMtftp4DriverNameTable[] = {
{
"eng;en",
L"MTFTP4 Network Service"
},
{
NULL,
NULL
}
};
GLOBAL_REMOVE_IF_UNREFERENCED EFI_UNICODE_STRING_TABLE *gMtftp4ControllerNameTable = NULL;
/**
Retrieves a Unicode string that is the user readable name of the driver.
This function retrieves the user readable name of a driver in the form of a
Unicode string. If the driver specified by This has a user readable name in
the language specified by Language, then a pointer to the driver name is
returned in DriverName, and EFI_SUCCESS is returned. If the driver specified
by This does not support the language specified by Language,
then EFI_UNSUPPORTED is returned.
@param[in] This A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or
EFI_COMPONENT_NAME_PROTOCOL instance.
@param[in] Language A pointer to a Null-terminated ASCII string
array indicating the language. This is the
language of the driver name that the caller is
requesting, and it must match one of the
languages specified in SupportedLanguages. The
number of languages supported by a driver is up
to the driver writer. Language is specified
in RFC 4646 or ISO 639-2 language code format.
@param[out] DriverName A pointer to the Unicode string to return.
This Unicode string is the name of the
driver specified by This in the language
specified by Language.
@retval EFI_SUCCESS The Unicode string for the Driver specified by
This and the language specified by Language was
returned in DriverName.
@retval EFI_INVALID_PARAMETER Language is NULL.
@retval EFI_INVALID_PARAMETER DriverName is NULL.
@retval EFI_UNSUPPORTED The driver specified by This does not support
the language specified by Language.
**/
EFI_STATUS
EFIAPI
Mtftp4ComponentNameGetDriverName (
IN EFI_COMPONENT_NAME_PROTOCOL *This,
IN CHAR8 *Language,
OUT CHAR16 **DriverName
)
{
return LookupUnicodeString2 (
Language,
This->SupportedLanguages,
mMtftp4DriverNameTable,
DriverName,
(BOOLEAN)(This == &gMtftp4ComponentName)
);
}
/**
Update the component name for the Mtftp4 child handle.
@param Mtftp4[in] A pointer to the EFI_MTFTP4_PROTOCOL.
@retval EFI_SUCCESS Update the ControllerNameTable of this instance successfully.
@retval EFI_INVALID_PARAMETER The input parameter is invalid.
**/
EFI_STATUS
UpdateName (
IN EFI_MTFTP4_PROTOCOL *Mtftp4
)
{
EFI_STATUS Status;
CHAR16 HandleName[80];
EFI_MTFTP4_MODE_DATA ModeData;
if (Mtftp4 == NULL) {
return EFI_INVALID_PARAMETER;
}
//
// Format the child name into the string buffer as:
// MTFTPv4 (ServerIp=192.168.1.10, ServerPort=69)
//
Status = Mtftp4->GetModeData (Mtftp4, &ModeData);
if (EFI_ERROR (Status)) {
return Status;
}
UnicodeSPrint (HandleName, sizeof (HandleName),
L"MTFTPv4 (ServerIp=%d.%d.%d.%d, ServerPort=%d)",
ModeData.ConfigData.ServerIp.Addr[0],
ModeData.ConfigData.ServerIp.Addr[1],
ModeData.ConfigData.ServerIp.Addr[2],
ModeData.ConfigData.ServerIp.Addr[3],
ModeData.ConfigData.InitialServerPort
);
if (gMtftp4ControllerNameTable != NULL) {
FreeUnicodeStringTable (gMtftp4ControllerNameTable);
gMtftp4ControllerNameTable = NULL;
}
Status = AddUnicodeString2 (
"eng",
gMtftp4ComponentName.SupportedLanguages,
&gMtftp4ControllerNameTable,
HandleName,
TRUE
);
if (EFI_ERROR (Status)) {
return Status;
}
return AddUnicodeString2 (
"en",
gMtftp4ComponentName2.SupportedLanguages,
&gMtftp4ControllerNameTable,
HandleName,
FALSE
);
}
/**
Retrieves a Unicode string that is the user readable name of the controller
that is being managed by a driver.
This function retrieves the user readable name of the controller specified by
ControllerHandle and ChildHandle in the form of a Unicode string. If the
driver specified by This has a user readable name in the language specified by
Language, then a pointer to the controller name is returned in ControllerName,
and EFI_SUCCESS is returned. If the driver specified by This is not currently
managing the controller specified by ControllerHandle and ChildHandle,
then EFI_UNSUPPORTED is returned. If the driver specified by This does not
support the language specified by Language, then EFI_UNSUPPORTED is returned.
@param[in] This A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or
EFI_COMPONENT_NAME_PROTOCOL instance.
@param[in] ControllerHandle The handle of a controller that the driver
specified by This is managing. This handle
specifies the controller whose name is to be
returned.
@param[in] ChildHandle The handle of the child controller to retrieve
the name of. This is an optional parameter that
may be NULL. It will be NULL for device
drivers. It will also be NULL for a bus drivers
that wish to retrieve the name of the bus
controller. It will not be NULL for a bus
driver that wishes to retrieve the name of a
child controller.
@param[in] Language A pointer to a Null-terminated ASCII string
array indicating the language. This is the
language of the driver name that the caller is
requesting, and it must match one of the
languages specified in SupportedLanguages. The
number of languages supported by a driver is up
to the driver writer. Language is specified in
RFC 4646 or ISO 639-2 language code format.
@param[out] ControllerName A pointer to the Unicode string to return.
This Unicode string is the name of the
controller specified by ControllerHandle and
ChildHandle in the language specified by
Language from the point of view of the driver
specified by This.
@retval EFI_SUCCESS The Unicode string for the user readable name in
the language specified by Language for the
driver specified by This was returned in
DriverName.
@retval EFI_INVALID_PARAMETER ControllerHandle is NULL.
@retval EFI_INVALID_PARAMETER ChildHandle is not NULL and it is not a valid
EFI_HANDLE.
@retval EFI_INVALID_PARAMETER Language is NULL.
@retval EFI_INVALID_PARAMETER ControllerName is NULL.
@retval EFI_UNSUPPORTED The driver specified by This is not currently
managing the controller specified by
ControllerHandle and ChildHandle.
@retval EFI_UNSUPPORTED The driver specified by This does not support
the language specified by Language.
**/
EFI_STATUS
EFIAPI
Mtftp4ComponentNameGetControllerName (
IN EFI_COMPONENT_NAME_PROTOCOL *This,
IN EFI_HANDLE ControllerHandle,
IN EFI_HANDLE ChildHandle OPTIONAL,
IN CHAR8 *Language,
OUT CHAR16 **ControllerName
)
{
EFI_STATUS Status;
EFI_MTFTP4_PROTOCOL *Mtftp4;
//
// Only provide names for child handles.
//
if (ChildHandle == NULL) {
return EFI_UNSUPPORTED;
}
//
// Make sure this driver produced ChildHandle
//
Status = EfiTestChildHandle (
ControllerHandle,
ChildHandle,
&gEfiUdp4ProtocolGuid
);
if (EFI_ERROR (Status)) {
return Status;
}
//
// Retrieve an instance of a produced protocol from ChildHandle
//
Status = gBS->OpenProtocol (
ChildHandle,
&gEfiMtftp4ProtocolGuid,
(VOID **)&Mtftp4,
NULL,
NULL,
EFI_OPEN_PROTOCOL_GET_PROTOCOL
);
if (EFI_ERROR (Status)) {
return Status;
}
//
// Update the component name for this child handle.
//
Status = UpdateName (Mtftp4);
if (EFI_ERROR (Status)) {
return Status;
}
return LookupUnicodeString2 (
Language,
This->SupportedLanguages,
gMtftp4ControllerNameTable,
ControllerName,
(BOOLEAN)(This == &gMtftp4ComponentName)
);
}

View File

@@ -0,0 +1,739 @@
/** @file
Implementation of Mtftp drivers.
Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.<BR>
SPDX-License-Identifier: BSD-2-Clause-Patent
**/
#include "Mtftp4Impl.h"
EFI_DRIVER_BINDING_PROTOCOL gMtftp4DriverBinding = {
Mtftp4DriverBindingSupported,
Mtftp4DriverBindingStart,
Mtftp4DriverBindingStop,
0xa,
NULL,
NULL
};
EFI_SERVICE_BINDING_PROTOCOL gMtftp4ServiceBindingTemplete = {
Mtftp4ServiceBindingCreateChild,
Mtftp4ServiceBindingDestroyChild
};
/**
The driver entry point which installs multiple protocols to the ImageHandle.
@param ImageHandle The MTFTP's image handle.
@param SystemTable The system table.
@retval EFI_SUCCESS The handles are successfully installed on the image.
@retval others some EFI_ERROR occured.
**/
EFI_STATUS
EFIAPI
Mtftp4DriverEntryPoint (
IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable
)
{
return EfiLibInstallDriverBindingComponentName2 (
ImageHandle,
SystemTable,
&gMtftp4DriverBinding,
ImageHandle,
&gMtftp4ComponentName,
&gMtftp4ComponentName2
);
}
/**
Test whether MTFTP driver support this controller.
@param This The MTFTP driver binding instance
@param Controller The controller to test
@param RemainingDevicePath The remaining device path
@retval EFI_SUCCESS The controller has UDP service binding protocol
installed, MTFTP can support it.
@retval EFI_ALREADY_STARTED The device specified by ControllerHandle and
RemainingDevicePath is already being managed by
the driver specified by This.
@retval EFI_ACCESS_DENIED The device specified by ControllerHandle and
RemainingDevicePath is already being managed by a
different driver or an application that requires
exclusive access.
@retval EFI_UNSUPPORTED The device specified by ControllerHandle and
RemainingDevicePath is not supported by the driver
specified by This.
**/
EFI_STATUS
EFIAPI
Mtftp4DriverBindingSupported (
IN EFI_DRIVER_BINDING_PROTOCOL *This,
IN EFI_HANDLE Controller,
IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath
)
{
EFI_STATUS Status;
Status = gBS->OpenProtocol (
Controller,
&gEfiUdp4ServiceBindingProtocolGuid,
NULL,
This->DriverBindingHandle,
Controller,
EFI_OPEN_PROTOCOL_TEST_PROTOCOL
);
return Status;
}
/**
Config a NULL UDP that is used to keep the connection between UDP and MTFTP.
Just leave the Udp child unconfigured. When UDP is unloaded,
MTFTP will be informed with DriverBinding Stop.
@param UdpIo The UDP_IO to configure
@param Context The opaque parameter to the callback
@retval EFI_SUCCESS It always return EFI_SUCCESS directly.
**/
EFI_STATUS
EFIAPI
Mtftp4ConfigNullUdp (
IN UDP_IO *UdpIo,
IN VOID *Context
)
{
return EFI_SUCCESS;
}
/**
Create then initialize a MTFTP service binding instance.
@param Controller The controller to install the MTFTP service
binding on
@param Image The driver binding image of the MTFTP driver
@param Service The variable to receive the created service
binding instance.
@retval EFI_OUT_OF_RESOURCES Failed to allocate resource to create the instance
@retval EFI_DEVICE_ERROR Failed to create a NULL UDP port to keep
connection with UDP.
@retval EFI_SUCCESS The service instance is created for the
controller.
**/
EFI_STATUS
Mtftp4CreateService (
IN EFI_HANDLE Controller,
IN EFI_HANDLE Image,
OUT MTFTP4_SERVICE **Service
)
{
MTFTP4_SERVICE *MtftpSb;
EFI_STATUS Status;
*Service = NULL;
MtftpSb = AllocatePool (sizeof (MTFTP4_SERVICE));
if (MtftpSb == NULL) {
return EFI_OUT_OF_RESOURCES;
}
MtftpSb->Signature = MTFTP4_SERVICE_SIGNATURE;
MtftpSb->ServiceBinding = gMtftp4ServiceBindingTemplete;
MtftpSb->ChildrenNum = 0;
InitializeListHead (&MtftpSb->Children);
MtftpSb->Timer = NULL;
MtftpSb->TimerNotifyLevel = NULL;
MtftpSb->TimerToGetMap = NULL;
MtftpSb->Controller = Controller;
MtftpSb->Image = Image;
MtftpSb->ConnectUdp = NULL;
//
// Create the timer and a udp to be notified when UDP is uninstalled
//
Status = gBS->CreateEvent (
EVT_NOTIFY_SIGNAL | EVT_TIMER,
TPL_CALLBACK,
Mtftp4OnTimerTick,
MtftpSb,
&MtftpSb->Timer
);
if (EFI_ERROR (Status)) {
FreePool (MtftpSb);
return Status;
}
Status = gBS->CreateEvent (
EVT_NOTIFY_SIGNAL | EVT_TIMER,
TPL_NOTIFY,
Mtftp4OnTimerTickNotifyLevel,
MtftpSb,
&MtftpSb->TimerNotifyLevel
);
if (EFI_ERROR (Status)) {
gBS->CloseEvent (MtftpSb->Timer);
FreePool (MtftpSb);
return Status;
}
//
// Create the timer used to time out the procedure which is used to
// get the default IP address.
//
Status = gBS->CreateEvent (
EVT_TIMER,
TPL_CALLBACK,
NULL,
NULL,
&MtftpSb->TimerToGetMap
);
if (EFI_ERROR (Status)) {
gBS->CloseEvent (MtftpSb->TimerNotifyLevel);
gBS->CloseEvent (MtftpSb->Timer);
FreePool (MtftpSb);
return Status;
}
MtftpSb->ConnectUdp = UdpIoCreateIo (
Controller,
Image,
Mtftp4ConfigNullUdp,
UDP_IO_UDP4_VERSION,
NULL
);
if (MtftpSb->ConnectUdp == NULL) {
gBS->CloseEvent (MtftpSb->TimerToGetMap);
gBS->CloseEvent (MtftpSb->TimerNotifyLevel);
gBS->CloseEvent (MtftpSb->Timer);
FreePool (MtftpSb);
return EFI_DEVICE_ERROR;
}
*Service = MtftpSb;
return EFI_SUCCESS;
}
/**
Release all the resource used the MTFTP service binding instance.
@param MtftpSb The MTFTP service binding instance.
**/
VOID
Mtftp4CleanService (
IN MTFTP4_SERVICE *MtftpSb
)
{
UdpIoFreeIo (MtftpSb->ConnectUdp);
gBS->CloseEvent (MtftpSb->TimerToGetMap);
gBS->CloseEvent (MtftpSb->TimerNotifyLevel);
gBS->CloseEvent (MtftpSb->Timer);
}
/**
Start the MTFTP driver on this controller.
MTFTP driver will install a MTFTP SERVICE BINDING protocol on the supported
controller, which can be used to create/destroy MTFTP children.
@param This The MTFTP driver binding protocol.
@param Controller The controller to manage.
@param RemainingDevicePath Remaining device path.
@retval EFI_ALREADY_STARTED The MTFTP service binding protocol has been
started on the controller.
@retval EFI_SUCCESS The MTFTP service binding is installed on the
controller.
**/
EFI_STATUS
EFIAPI
Mtftp4DriverBindingStart (
IN EFI_DRIVER_BINDING_PROTOCOL *This,
IN EFI_HANDLE Controller,
IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath
)
{
MTFTP4_SERVICE *MtftpSb;
EFI_STATUS Status;
//
// Directly return if driver is already running.
//
Status = gBS->OpenProtocol (
Controller,
&gEfiMtftp4ServiceBindingProtocolGuid,
NULL,
This->DriverBindingHandle,
Controller,
EFI_OPEN_PROTOCOL_TEST_PROTOCOL
);
if (Status == EFI_SUCCESS) {
return EFI_ALREADY_STARTED;
}
Status = Mtftp4CreateService (Controller, This->DriverBindingHandle, &MtftpSb);
if (EFI_ERROR (Status)) {
return Status;
}
ASSERT (MtftpSb != NULL);
Status = gBS->SetTimer (MtftpSb->Timer, TimerPeriodic, TICKS_PER_SECOND);
if (EFI_ERROR (Status)) {
goto ON_ERROR;
}
Status = gBS->SetTimer (MtftpSb->TimerNotifyLevel, TimerPeriodic, TICKS_PER_SECOND);
if (EFI_ERROR (Status)) {
goto ON_ERROR;
}
//
// Install the Mtftp4ServiceBinding Protocol onto Controller
//
Status = gBS->InstallMultipleProtocolInterfaces (
&Controller,
&gEfiMtftp4ServiceBindingProtocolGuid,
&MtftpSb->ServiceBinding,
NULL
);
if (EFI_ERROR (Status)) {
goto ON_ERROR;
}
return EFI_SUCCESS;
ON_ERROR:
Mtftp4CleanService (MtftpSb);
FreePool (MtftpSb);
return Status;
}
/**
Callback function which provided by user to remove one node in NetDestroyLinkList process.
@param[in] Entry The entry to be removed.
@param[in] Context Pointer to the callback context corresponds to the Context in NetDestroyLinkList.
@retval EFI_SUCCESS The entry has been removed successfully.
@retval Others Fail to remove the entry.
**/
EFI_STATUS
EFIAPI
Mtftp4DestroyChildEntryInHandleBuffer (
IN LIST_ENTRY *Entry,
IN VOID *Context
)
{
MTFTP4_PROTOCOL *Instance;
EFI_SERVICE_BINDING_PROTOCOL *ServiceBinding;
UINTN NumberOfChildren;
EFI_HANDLE *ChildHandleBuffer;
if (Entry == NULL || Context == NULL) {
return EFI_INVALID_PARAMETER;
}
Instance = NET_LIST_USER_STRUCT_S (Entry, MTFTP4_PROTOCOL, Link, MTFTP4_PROTOCOL_SIGNATURE);
ServiceBinding = ((MTFTP4_DESTROY_CHILD_IN_HANDLE_BUF_CONTEXT *) Context)->ServiceBinding;
NumberOfChildren = ((MTFTP4_DESTROY_CHILD_IN_HANDLE_BUF_CONTEXT *) Context)->NumberOfChildren;
ChildHandleBuffer = ((MTFTP4_DESTROY_CHILD_IN_HANDLE_BUF_CONTEXT *) Context)->ChildHandleBuffer;
if (!NetIsInHandleBuffer (Instance->Handle, NumberOfChildren, ChildHandleBuffer)) {
return EFI_SUCCESS;
}
return ServiceBinding->DestroyChild (ServiceBinding, Instance->Handle);
}
/**
Stop the MTFTP driver on controller. The controller is a UDP
child handle.
@param This The MTFTP driver binding protocol
@param Controller The controller to stop
@param NumberOfChildren The number of children
@param ChildHandleBuffer The array of the child handle.
@retval EFI_SUCCESS The driver is stopped on the controller.
@retval EFI_DEVICE_ERROR Failed to stop the driver on the controller.
**/
EFI_STATUS
EFIAPI
Mtftp4DriverBindingStop (
IN EFI_DRIVER_BINDING_PROTOCOL *This,
IN EFI_HANDLE Controller,
IN UINTN NumberOfChildren,
IN EFI_HANDLE *ChildHandleBuffer
)
{
EFI_SERVICE_BINDING_PROTOCOL *ServiceBinding;
MTFTP4_SERVICE *MtftpSb;
EFI_HANDLE NicHandle;
EFI_STATUS Status;
LIST_ENTRY *List;
MTFTP4_DESTROY_CHILD_IN_HANDLE_BUF_CONTEXT Context;
//
// MTFTP driver opens UDP child, So, Controller is a UDP
// child handle. Locate the Nic handle first. Then get the
// MTFTP private data back.
//
NicHandle = NetLibGetNicHandle (Controller, &gEfiUdp4ProtocolGuid);
if (NicHandle == NULL) {
return EFI_SUCCESS;
}
Status = gBS->OpenProtocol (
NicHandle,
&gEfiMtftp4ServiceBindingProtocolGuid,
(VOID **) &ServiceBinding,
This->DriverBindingHandle,
NicHandle,
EFI_OPEN_PROTOCOL_GET_PROTOCOL
);
if (EFI_ERROR (Status)) {
return EFI_DEVICE_ERROR;
}
MtftpSb = MTFTP4_SERVICE_FROM_THIS (ServiceBinding);
if (!IsListEmpty (&MtftpSb->Children)) {
//
// Destroy the Mtftp4 child instance in ChildHandleBuffer.
//
List = &MtftpSb->Children;
Context.ServiceBinding = ServiceBinding;
Context.NumberOfChildren = NumberOfChildren;
Context.ChildHandleBuffer = ChildHandleBuffer;
Status = NetDestroyLinkList (
List,
Mtftp4DestroyChildEntryInHandleBuffer,
&Context,
NULL
);
}
if (NumberOfChildren == 0 && IsListEmpty (&MtftpSb->Children)) {
gBS->UninstallProtocolInterface (
NicHandle,
&gEfiMtftp4ServiceBindingProtocolGuid,
ServiceBinding
);
Mtftp4CleanService (MtftpSb);
if (gMtftp4ControllerNameTable != NULL) {
FreeUnicodeStringTable (gMtftp4ControllerNameTable);
gMtftp4ControllerNameTable = NULL;
}
FreePool (MtftpSb);
Status = EFI_SUCCESS;
}
return Status;
}
/**
Initialize a MTFTP protocol instance which is the child of MtftpSb.
@param MtftpSb The MTFTP service binding protocol.
@param Instance The MTFTP instance to initialize.
**/
VOID
Mtftp4InitProtocol (
IN MTFTP4_SERVICE *MtftpSb,
OUT MTFTP4_PROTOCOL *Instance
)
{
ZeroMem (Instance, sizeof (MTFTP4_PROTOCOL));
Instance->Signature = MTFTP4_PROTOCOL_SIGNATURE;
InitializeListHead (&Instance->Link);
CopyMem (&Instance->Mtftp4, &gMtftp4ProtocolTemplate, sizeof (Instance->Mtftp4));
Instance->State = MTFTP4_STATE_UNCONFIGED;
Instance->Service = MtftpSb;
InitializeListHead (&Instance->Blocks);
}
/**
Create a MTFTP child for the service binding instance, then
install the MTFTP protocol to the ChildHandle.
@param This The MTFTP service binding instance.
@param ChildHandle The Child handle to install the MTFTP protocol.
@retval EFI_INVALID_PARAMETER The parameter is invalid.
@retval EFI_OUT_OF_RESOURCES Failed to allocate resource for the new child.
@retval EFI_SUCCESS The child is successfully create.
**/
EFI_STATUS
EFIAPI
Mtftp4ServiceBindingCreateChild (
IN EFI_SERVICE_BINDING_PROTOCOL *This,
IN EFI_HANDLE *ChildHandle
)
{
MTFTP4_SERVICE *MtftpSb;
MTFTP4_PROTOCOL *Instance;
EFI_STATUS Status;
EFI_TPL OldTpl;
VOID *Udp4;
if ((This == NULL) || (ChildHandle == NULL)) {
return EFI_INVALID_PARAMETER;
}
Instance = AllocatePool (sizeof (*Instance));
if (Instance == NULL) {
return EFI_OUT_OF_RESOURCES;
}
MtftpSb = MTFTP4_SERVICE_FROM_THIS (This);
Mtftp4InitProtocol (MtftpSb, Instance);
Instance->UnicastPort = UdpIoCreateIo (
MtftpSb->Controller,
MtftpSb->Image,
Mtftp4ConfigNullUdp,
UDP_IO_UDP4_VERSION,
Instance
);
if (Instance->UnicastPort == NULL) {
FreePool (Instance);
return EFI_OUT_OF_RESOURCES;
}
//
// Install the MTFTP protocol onto ChildHandle
//
Status = gBS->InstallMultipleProtocolInterfaces (
ChildHandle,
&gEfiMtftp4ProtocolGuid,
&Instance->Mtftp4,
NULL
);
if (EFI_ERROR (Status)) {
UdpIoFreeIo (Instance->UnicastPort);
FreePool (Instance);
return Status;
}
Instance->Handle = *ChildHandle;
//
// Open the Udp4 protocol BY_CHILD.
//
Status = gBS->OpenProtocol (
MtftpSb->ConnectUdp->UdpHandle,
&gEfiUdp4ProtocolGuid,
(VOID **) &Udp4,
gMtftp4DriverBinding.DriverBindingHandle,
Instance->Handle,
EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER
);
if (EFI_ERROR (Status)) {
goto ON_ERROR;
}
//
// Open the Udp4 protocol by child.
//
Status = gBS->OpenProtocol (
Instance->UnicastPort->UdpHandle,
&gEfiUdp4ProtocolGuid,
(VOID **) &Udp4,
gMtftp4DriverBinding.DriverBindingHandle,
Instance->Handle,
EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER
);
if (EFI_ERROR (Status)) {
//
// Close the Udp4 protocol.
//
gBS->CloseProtocol (
MtftpSb->ConnectUdp->UdpHandle,
&gEfiUdp4ProtocolGuid,
gMtftp4DriverBinding.DriverBindingHandle,
ChildHandle
);
goto ON_ERROR;
}
//
// Add it to the parent's child list.
//
OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
InsertTailList (&MtftpSb->Children, &Instance->Link);
MtftpSb->ChildrenNum++;
gBS->RestoreTPL (OldTpl);
return EFI_SUCCESS;
ON_ERROR:
if (Instance->Handle != NULL) {
gBS->UninstallMultipleProtocolInterfaces (
Instance->Handle,
&gEfiMtftp4ProtocolGuid,
&Instance->Mtftp4,
NULL
);
}
UdpIoFreeIo (Instance->UnicastPort);
FreePool (Instance);
return Status;
}
/**
Destroy one of the service binding's child.
@param This The service binding instance
@param ChildHandle The child handle to destroy
@retval EFI_INVALID_PARAMETER The parameter is invaid.
@retval EFI_UNSUPPORTED The child may have already been destroyed.
@retval EFI_SUCCESS The child is destroyed and removed from the
parent's child list.
**/
EFI_STATUS
EFIAPI
Mtftp4ServiceBindingDestroyChild (
IN EFI_SERVICE_BINDING_PROTOCOL *This,
IN EFI_HANDLE ChildHandle
)
{
MTFTP4_SERVICE *MtftpSb;
MTFTP4_PROTOCOL *Instance;
EFI_MTFTP4_PROTOCOL *Mtftp4;
EFI_STATUS Status;
EFI_TPL OldTpl;
if ((This == NULL) || (ChildHandle == NULL)) {
return EFI_INVALID_PARAMETER;
}
//
// Retrieve the private context data structures
//
Status = gBS->OpenProtocol (
ChildHandle,
&gEfiMtftp4ProtocolGuid,
(VOID **) &Mtftp4,
gMtftp4DriverBinding.DriverBindingHandle,
ChildHandle,
EFI_OPEN_PROTOCOL_GET_PROTOCOL
);
if (EFI_ERROR (Status)) {
return EFI_UNSUPPORTED;
}
Instance = MTFTP4_PROTOCOL_FROM_THIS (Mtftp4);
MtftpSb = MTFTP4_SERVICE_FROM_THIS (This);
if (Instance->Service != MtftpSb) {
return EFI_INVALID_PARAMETER;
}
if (Instance->InDestroy) {
return EFI_SUCCESS;
}
Instance->InDestroy = TRUE;
//
// Close the Udp4 protocol.
//
gBS->CloseProtocol (
MtftpSb->ConnectUdp->UdpHandle,
&gEfiUdp4ProtocolGuid,
gMtftp4DriverBinding.DriverBindingHandle,
ChildHandle
);
gBS->CloseProtocol (
Instance->UnicastPort->UdpHandle,
&gEfiUdp4ProtocolGuid,
gMtftp4DriverBinding.DriverBindingHandle,
ChildHandle
);
if (Instance->McastUdpPort != NULL) {
gBS->CloseProtocol (
Instance->McastUdpPort->UdpHandle,
&gEfiUdp4ProtocolGuid,
gMtftp4DriverBinding.DriverBindingHandle,
ChildHandle
);
}
//
// Uninstall the MTFTP4 protocol first to enable a top down destruction.
//
Status = gBS->UninstallProtocolInterface (
ChildHandle,
&gEfiMtftp4ProtocolGuid,
Mtftp4
);
if (EFI_ERROR (Status)) {
Instance->InDestroy = FALSE;
return Status;
}
OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
Mtftp4CleanOperation (Instance, EFI_DEVICE_ERROR);
UdpIoFreeIo (Instance->UnicastPort);
RemoveEntryList (&Instance->Link);
MtftpSb->ChildrenNum--;
gBS->RestoreTPL (OldTpl);
FreePool (Instance);
return EFI_SUCCESS;
}

View File

@@ -0,0 +1,131 @@
/** @file
Mtftp drivers function header.
Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.<BR>
SPDX-License-Identifier: BSD-2-Clause-Patent
**/
#ifndef __EFI_MTFTP4_DRIVER_H__
#define __EFI_MTFTP4_DRIVER_H__
#include <Uefi.h>
#include <Protocol/ServiceBinding.h>
#include <Library/NetLib.h>
#include <Library/UefiLib.h>
#include <Library/UefiDriverEntryPoint.h>
extern EFI_COMPONENT_NAME_PROTOCOL gMtftp4ComponentName;
extern EFI_COMPONENT_NAME2_PROTOCOL gMtftp4ComponentName2;
extern EFI_DRIVER_BINDING_PROTOCOL gMtftp4DriverBinding;
extern EFI_UNICODE_STRING_TABLE *gMtftp4ControllerNameTable;
/**
Test whether MTFTP driver support this controller.
@param This The MTFTP driver binding instance
@param Controller The controller to test
@param RemainingDevicePath The remaining device path
@retval EFI_SUCCESS The controller has UDP service binding protocol
installed, MTFTP can support it.
@retval Others MTFTP can't support the controller.
**/
EFI_STATUS
EFIAPI
Mtftp4DriverBindingSupported (
IN EFI_DRIVER_BINDING_PROTOCOL *This,
IN EFI_HANDLE Controller,
IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath
);
/**
Start the MTFTP driver on this controller.
MTFTP driver will install a MTFTP SERVICE BINDING protocol on the supported
controller, which can be used to create/destroy MTFTP children.
@param This The MTFTP driver binding protocol.
@param Controller The controller to manage.
@param RemainingDevicePath Remaining device path.
@retval EFI_ALREADY_STARTED The MTFTP service binding protocol has been
started on the controller.
@retval EFI_SUCCESS The MTFTP service binding is installed on the
controller.
**/
EFI_STATUS
EFIAPI
Mtftp4DriverBindingStart (
IN EFI_DRIVER_BINDING_PROTOCOL *This,
IN EFI_HANDLE Controller,
IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath
);
/**
Stop the MTFTP driver on controller. The controller is a UDP
child handle.
@param This The MTFTP driver binding protocol
@param Controller The controller to stop
@param NumberOfChildren The number of children
@param ChildHandleBuffer The array of the child handle.
@retval EFI_SUCCESS The driver is stopped on the controller.
@retval EFI_DEVICE_ERROR Failed to stop the driver on the controller.
**/
EFI_STATUS
EFIAPI
Mtftp4DriverBindingStop (
IN EFI_DRIVER_BINDING_PROTOCOL *This,
IN EFI_HANDLE Controller,
IN UINTN NumberOfChildren,
IN EFI_HANDLE *ChildHandleBuffer
);
/**
Create a MTFTP child for the service binding instance, then
install the MTFTP protocol to the ChildHandle.
@param This The MTFTP service binding instance.
@param ChildHandle The Child handle to install the MTFTP protocol.
@retval EFI_INVALID_PARAMETER The parameter is invalid.
@retval EFI_OUT_OF_RESOURCES Failed to allocate resource for the new child.
@retval EFI_SUCCESS The child is successfully create.
**/
EFI_STATUS
EFIAPI
Mtftp4ServiceBindingCreateChild (
IN EFI_SERVICE_BINDING_PROTOCOL *This,
IN EFI_HANDLE *ChildHandle
);
/**
Destroy one of the service binding's child.
@param This The service binding instance
@param ChildHandle The child handle to destroy
@retval EFI_INVALID_PARAMETER The parameter is invaid.
@retval EFI_UNSUPPORTED The child may have already been destroyed.
@retval EFI_SUCCESS The child is destroyed and removed from the
parent's child list.
**/
EFI_STATUS
EFIAPI
Mtftp4ServiceBindingDestroyChild (
IN EFI_SERVICE_BINDING_PROTOCOL *This,
IN EFI_HANDLE ChildHandle
);
#endif

View File

@@ -0,0 +1,69 @@
## @file
# This module produces EFI MTFTPv4 Protocol and EFI MTFTPv4 Service Binding Protocol.
#
# This module produces EFI MTFTPv4 Protocol upon EFI UDPv4 Protocol, to provide
# basic services for client-side unicast and/or multicase TFTP operations.
#
# Copyright (c) 2007 - 2018, Intel Corporation. All rights reserved.<BR>
# SPDX-License-Identifier: BSD-2-Clause-Patent
#
#
##
[Defines]
INF_VERSION = 0x00010005
BASE_NAME = Mtftp4Dxe
MODULE_UNI_FILE = Mtftp4Dxe.uni
FILE_GUID = DC3641B8-2FA8-4ed3-BC1F-F9962A03454B
MODULE_TYPE = UEFI_DRIVER
VERSION_STRING = 1.0
ENTRY_POINT = Mtftp4DriverEntryPoint
UNLOAD_IMAGE = NetLibDefaultUnload
#
# The following information is for reference only and not required by the build tools.
#
# VALID_ARCHITECTURES = IA32 X64 EBC
#
# DRIVER_BINDING = gMtftp4DriverBinding
# COMPONENT_NAME = gMtftp4ComponentName
# COMPONENT_NAME2 = gMtftp4ComponentName2
#
[Sources]
Mtftp4Option.c
Mtftp4Rrq.c
Mtftp4Impl.h
ComponentName.c
Mtftp4Support.c
Mtftp4Impl.c
Mtftp4Option.h
Mtftp4Support.h
Mtftp4Driver.h
Mtftp4Driver.c
Mtftp4Wrq.c
[Packages]
MdePkg/MdePkg.dec
MdeModulePkg/MdeModulePkg.dec
[LibraryClasses]
UefiLib
UefiBootServicesTableLib
UefiDriverEntryPoint
DebugLib
NetLib
UdpIoLib
MemoryAllocationLib
BaseMemoryLib
[Protocols]
gEfiMtftp4ServiceBindingProtocolGuid ## BY_START
gEfiUdp4ServiceBindingProtocolGuid ## TO_START
gEfiMtftp4ProtocolGuid ## BY_START
gEfiUdp4ProtocolGuid ## TO_START
[UserExtensions.TianoCore."ExtraFiles"]
Mtftp4DxeExtra.uni

View File

@@ -0,0 +1,17 @@
// /** @file
// This module produces EFI MTFTPv4 Protocol and EFI MTFTPv4 Service Binding Protocol.
//
// This module produces EFI MTFTPv4 Protocol upon EFI UDPv4 Protocol, to provide
// basic services for client-side unicast and/or multicase TFTP operations.
//
// Copyright (c) 2007 - 2018, Intel Corporation. All rights reserved.<BR>
//
// SPDX-License-Identifier: BSD-2-Clause-Patent
//
// **/
#string STR_MODULE_ABSTRACT #language en-US "Produces EFI MTFTPv4 Protocol and EFI MTFTPv4 Service Binding Protocol"
#string STR_MODULE_DESCRIPTION #language en-US "This module produces EFI MTFTPv4 Protocol upon EFI UDPv4 Protocol, to provide basic services for client-side unicast or multicase TFTP operations or both."

View File

@@ -0,0 +1,14 @@
// /** @file
// Mtftp4Dxe Localized Strings and Content
//
// Copyright (c) 2013 - 2018, Intel Corporation. All rights reserved.<BR>
//
// SPDX-License-Identifier: BSD-2-Clause-Patent
//
// **/
#string STR_PROPERTIES_MODULE_NAME
#language en-US
"MTFTP v4 DXE Driver"

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,226 @@
/** @file
Mtftp4 Implementation.
Mtftp4 Implementation, it supports the following RFCs:
RFC1350 - THE TFTP PROTOCOL (REVISION 2)
RFC2090 - TFTP Multicast Option
RFC2347 - TFTP Option Extension
RFC2348 - TFTP Blocksize Option
RFC2349 - TFTP Timeout Interval and Transfer Size Options
RFC7440 - TFTP Windowsize Option
Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.<BR>
SPDX-License-Identifier: BSD-2-Clause-Patent
**/
#ifndef __EFI_MTFTP4_IMPL_H__
#define __EFI_MTFTP4_IMPL_H__
#include <Uefi.h>
#include <Protocol/Udp4.h>
#include <Protocol/Mtftp4.h>
#include <Library/DebugLib.h>
#include <Library/BaseMemoryLib.h>
#include <Library/MemoryAllocationLib.h>
#include <Library/UefiBootServicesTableLib.h>
#include <Library/UdpIoLib.h>
#include <Library/PrintLib.h>
extern EFI_MTFTP4_PROTOCOL gMtftp4ProtocolTemplate;
typedef struct _MTFTP4_SERVICE MTFTP4_SERVICE;
typedef struct _MTFTP4_PROTOCOL MTFTP4_PROTOCOL;
#include "Mtftp4Driver.h"
#include "Mtftp4Option.h"
#include "Mtftp4Support.h"
///
/// Some constant value of Mtftp service.
///
#define MTFTP4_SERVICE_SIGNATURE SIGNATURE_32 ('T', 'F', 'T', 'P')
#define MTFTP4_PROTOCOL_SIGNATURE SIGNATURE_32 ('t', 'f', 't', 'p')
#define MTFTP4_DEFAULT_SERVER_PORT 69
#define MTFTP4_DEFAULT_TIMEOUT 3
#define MTFTP4_DEFAULT_RETRY 5
#define MTFTP4_DEFAULT_BLKSIZE 512
#define MTFTP4_DEFAULT_WINDOWSIZE 1
#define MTFTP4_TIME_TO_GETMAP 5
#define MTFTP4_STATE_UNCONFIGED 0
#define MTFTP4_STATE_CONFIGED 1
#define MTFTP4_STATE_DESTROY 2
///
/// Mtftp service block
///
struct _MTFTP4_SERVICE {
UINT32 Signature;
EFI_SERVICE_BINDING_PROTOCOL ServiceBinding;
UINT16 ChildrenNum;
LIST_ENTRY Children;
EFI_EVENT Timer; ///< Ticking timer for all the MTFTP clients to handle the packet timeout case.
EFI_EVENT TimerNotifyLevel; ///< Ticking timer for all the MTFTP clients to calculate the packet live time.
EFI_EVENT TimerToGetMap;
EFI_HANDLE Controller;
EFI_HANDLE Image;
//
// This UDP child is used to keep the connection between the UDP
// and MTFTP, so MTFTP will be notified when UDP is uninstalled.
//
UDP_IO *ConnectUdp;
};
typedef struct {
EFI_MTFTP4_PACKET **Packet;
UINT32 *PacketLen;
EFI_STATUS Status;
} MTFTP4_GETINFO_STATE;
struct _MTFTP4_PROTOCOL {
UINT32 Signature;
LIST_ENTRY Link;
EFI_MTFTP4_PROTOCOL Mtftp4;
INTN State;
BOOLEAN InDestroy;
MTFTP4_SERVICE *Service;
EFI_HANDLE Handle;
EFI_MTFTP4_CONFIG_DATA Config;
//
// Operation parameters: token and requested options.
//
EFI_MTFTP4_TOKEN *Token;
MTFTP4_OPTION RequestOption;
UINT16 Operation;
//
// Blocks is a list of MTFTP4_BLOCK_RANGE which contains
// holes in the file
//
UINT16 BlkSize;
UINT16 LastBlock;
LIST_ENTRY Blocks;
UINT16 WindowSize;
//
// Record the total received and saved block number.
//
UINT64 TotalBlock;
//
// Record the acked block number.
//
UINT64 AckedBlock;
//
// The server's communication end point: IP and two ports. one for
// initial request, one for its selected port.
//
IP4_ADDR ServerIp;
UINT16 ListeningPort;
UINT16 ConnectedPort;
IP4_ADDR Gateway;
UDP_IO *UnicastPort;
//
// Timeout and retransmit status
//
NET_BUF *LastPacket;
UINT32 PacketToLive;
BOOLEAN HasTimeout;
UINT32 CurRetry;
UINT32 MaxRetry;
UINT32 Timeout;
//
// Parameter used by RRQ's multicast download.
//
IP4_ADDR McastIp;
UINT16 McastPort;
BOOLEAN Master;
UDP_IO *McastUdpPort;
};
typedef struct {
EFI_SERVICE_BINDING_PROTOCOL *ServiceBinding;
UINTN NumberOfChildren;
EFI_HANDLE *ChildHandleBuffer;
} MTFTP4_DESTROY_CHILD_IN_HANDLE_BUF_CONTEXT;
/**
Clean up the MTFTP session to get ready for new operation.
@param Instance The MTFTP session to clean up
@param Result The result to return to the caller who initiated
the operation.
**/
VOID
Mtftp4CleanOperation (
IN OUT MTFTP4_PROTOCOL *Instance,
IN EFI_STATUS Result
);
/**
Start the MTFTP session for upload.
It will first init some states, then send the WRQ request packet,
and start receiving the packet.
@param Instance The MTFTP session
@param Operation Redundant parameter, which is always
EFI_MTFTP4_OPCODE_WRQ here.
@retval EFI_SUCCESS The upload process has been started.
@retval Others Failed to start the upload.
**/
EFI_STATUS
Mtftp4WrqStart (
IN MTFTP4_PROTOCOL *Instance,
IN UINT16 Operation
);
/**
Start the MTFTP session to download.
It will first initialize some of the internal states then build and send a RRQ
reqeuest packet, at last, it will start receive for the downloading.
@param Instance The Mtftp session
@param Operation The MTFTP opcode, it may be a EFI_MTFTP4_OPCODE_RRQ
or EFI_MTFTP4_OPCODE_DIR.
@retval EFI_SUCCESS The mtftp download session is started.
@retval Others Failed to start downloading.
**/
EFI_STATUS
Mtftp4RrqStart (
IN MTFTP4_PROTOCOL *Instance,
IN UINT16 Operation
);
#define MTFTP4_SERVICE_FROM_THIS(a) \
CR (a, MTFTP4_SERVICE, ServiceBinding, MTFTP4_SERVICE_SIGNATURE)
#define MTFTP4_PROTOCOL_FROM_THIS(a) \
CR (a, MTFTP4_PROTOCOL, Mtftp4, MTFTP4_PROTOCOL_SIGNATURE)
#endif

View File

@@ -0,0 +1,549 @@
/** @file
Routines to process MTFTP4 options.
Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.<BR>
SPDX-License-Identifier: BSD-2-Clause-Patent
**/
#include "Mtftp4Impl.h"
CHAR8 *mMtftp4SupportedOptions[MTFTP4_SUPPORTED_OPTIONS] = {
"blksize",
"windowsize",
"timeout",
"tsize",
"multicast"
};
/**
Check whether two ascii strings are equel, ignore the case.
@param Str1 The first ascii string
@param Str2 The second ascii string
@retval TRUE Two strings are equal when case is ignored.
@retval FALSE Two string are not equal.
**/
BOOLEAN
NetStringEqualNoCase (
IN UINT8 *Str1,
IN UINT8 *Str2
)
{
UINT8 Ch1;
UINT8 Ch2;
ASSERT ((Str1 != NULL) && (Str2 != NULL));
for (; (*Str1 != '\0') && (*Str2 != '\0'); Str1++, Str2++) {
Ch1 = *Str1;
Ch2 = *Str2;
//
// Convert them to lower case then compare two
//
if (('A' <= Ch1) && (Ch1 <= 'Z')) {
Ch1 += 'a' - 'A';
}
if (('A' <= Ch2) && (Ch2 <= 'Z')) {
Ch2 += 'a' - 'A';
}
if (Ch1 != Ch2) {
return FALSE;
}
}
return (BOOLEAN) (*Str1 == *Str2);
}
/**
Convert a string to a UINT32 number.
@param Str The string to convert from
@return The number get from the string
**/
UINT32
NetStringToU32 (
IN UINT8 *Str
)
{
UINT32 Num;
ASSERT (Str != NULL);
Num = 0;
for (; NET_IS_DIGIT (*Str); Str++) {
Num = Num * 10 + (*Str - '0');
}
return Num;
}
/**
Convert a string of the format "192.168.0.1" to an IP address.
@param Str The string representation of IP
@param Ip The varible to get IP.
@retval EFI_INVALID_PARAMETER The IP string is invalid.
@retval EFI_SUCCESS The IP is parsed into the Ip
**/
EFI_STATUS
NetStringToIp (
IN UINT8 *Str,
OUT IP4_ADDR *Ip
)
{
UINT32 Byte;
UINT32 Addr;
UINTN Index;
*Ip = 0;
Addr = 0;
for (Index = 0; Index < 4; Index++) {
if (!NET_IS_DIGIT (*Str)) {
return EFI_INVALID_PARAMETER;
}
Byte = NetStringToU32 (Str);
if (Byte > 255) {
return EFI_INVALID_PARAMETER;
}
Addr = (Addr << 8) | Byte;
//
// Skip all the digitals and check whether the sepeator is the dot
//
while (NET_IS_DIGIT (*Str)) {
Str++;
}
if ((Index < 3) && (*Str != '.')) {
return EFI_INVALID_PARAMETER;
}
Str++;
}
*Ip = Addr;
return EFI_SUCCESS;
}
/**
Go through the packet to fill the Options array with the start
addresses of each MTFTP option name/value pair.
@param Packet The packet to check
@param PacketLen The packet's length
@param Count The size of the Options on input. The actual
options on output
@param Options The option array to fill in
@retval EFI_INVALID_PARAMETER The packet is mal-formated
@retval EFI_BUFFER_TOO_SMALL The Options array is too small
@retval EFI_SUCCESS The packet has been parsed into the Options array.
**/
EFI_STATUS
Mtftp4FillOptions (
IN EFI_MTFTP4_PACKET *Packet,
IN UINT32 PacketLen,
IN OUT UINT32 *Count,
OUT EFI_MTFTP4_OPTION *Options OPTIONAL
)
{
UINT8 *Cur;
UINT8 *Last;
UINT8 Num;
UINT8 *Name;
UINT8 *Value;
Num = 0;
Cur = (UINT8 *) Packet + MTFTP4_OPCODE_LEN;
Last = (UINT8 *) Packet + PacketLen - 1;
//
// process option name and value pairs. The last byte is always zero
//
while (Cur < Last) {
Name = Cur;
while (*Cur != 0) {
Cur++;
}
if (Cur == Last) {
return EFI_INVALID_PARAMETER;
}
Value = ++Cur;
while (*Cur != 0) {
Cur++;
}
Num++;
if ((Options != NULL) && (Num <= *Count)) {
Options[Num - 1].OptionStr = Name;
Options[Num - 1].ValueStr = Value;
}
Cur++;
}
if ((*Count < Num) || (Options == NULL)) {
*Count = Num;
return EFI_BUFFER_TOO_SMALL;
}
*Count = Num;
return EFI_SUCCESS;
}
/**
Allocate and fill in a array of Mtftp options from the Packet.
It first calls Mtftp4FillOption to get the option number, then allocate
the array, at last, call Mtftp4FillOption again to save the options.
@param Packet The packet to parse
@param PacketLen The length of the packet
@param OptionCount The number of options in the packet
@param OptionList The point to get the option array.
@retval EFI_INVALID_PARAMETER The parametera are invalid or packet isn't a
well-formated OACK packet.
@retval EFI_SUCCESS The option array is build
@retval EFI_OUT_OF_RESOURCES Failed to allocate memory for the array
**/
EFI_STATUS
Mtftp4ExtractOptions (
IN EFI_MTFTP4_PACKET *Packet,
IN UINT32 PacketLen,
OUT UINT32 *OptionCount,
OUT EFI_MTFTP4_OPTION **OptionList OPTIONAL
)
{
EFI_STATUS Status;
*OptionCount = 0;
if (OptionList != NULL) {
*OptionList = NULL;
}
if (NTOHS (Packet->OpCode) != EFI_MTFTP4_OPCODE_OACK) {
return EFI_INVALID_PARAMETER;
}
if (PacketLen == MTFTP4_OPCODE_LEN) {
return EFI_SUCCESS;
}
//
// The last byte must be zero to terminate the options
//
if (*((UINT8 *) Packet + PacketLen - 1) != 0) {
return EFI_INVALID_PARAMETER;
}
//
// Get the number of options
//
Status = Mtftp4FillOptions (Packet, PacketLen, OptionCount, NULL);
if ((Status == EFI_SUCCESS) || (Status != EFI_BUFFER_TOO_SMALL)) {
return Status;
}
//
// Allocate memory for the options, then call Mtftp4FillOptions to
// fill it if caller want that.
//
if (OptionList == NULL) {
return EFI_SUCCESS;
}
*OptionList = AllocatePool (*OptionCount * sizeof (EFI_MTFTP4_OPTION));
if (*OptionList == NULL) {
return EFI_OUT_OF_RESOURCES;
}
Mtftp4FillOptions (Packet, PacketLen, OptionCount, *OptionList);
return EFI_SUCCESS;
}
/**
Parse the MTFTP multicast option.
@param Value The Mtftp multicast value string
@param Option The option to save the info into.
@retval EFI_INVALID_PARAMETER The multicast value string is invalid.
@retval EFI_SUCCESS The multicast value is parsed into the Option
**/
EFI_STATUS
Mtftp4ExtractMcast (
IN UINT8 *Value,
IN OUT MTFTP4_OPTION *Option
)
{
EFI_STATUS Status;
UINT32 Num;
//
// The multicast option is formated like "204.0.0.1,1857,1"
// The server can also omit the ip and port, use ",,1"
//
if (*Value == ',') {
Option->McastIp = 0;
} else {
Status = NetStringToIp (Value, &Option->McastIp);
if (EFI_ERROR (Status)) {
return Status;
}
while ((*Value != 0) && (*Value != ',')) {
Value++;
}
}
if (*Value != ',') {
return EFI_INVALID_PARAMETER;
}
Value++;
//
// Convert the port setting. the server can send us a port number or
// empty string. such as the port in ",,1"
//
if (*Value == ',') {
Option->McastPort = 0;
} else {
Num = NetStringToU32 (Value);
if (Num > 65535) {
return EFI_INVALID_PARAMETER;
}
Option->McastPort = (UINT16) Num;
while (NET_IS_DIGIT (*Value)) {
Value++;
}
}
if (*Value != ',') {
return EFI_INVALID_PARAMETER;
}
Value++;
//
// Check the master/slave setting, 1 for master, 0 for slave.
//
Num = NetStringToU32 (Value);
if ((Num != 0) && (Num != 1)) {
return EFI_INVALID_PARAMETER;
}
Option->Master = (BOOLEAN) (Num == 1);
while (NET_IS_DIGIT (*Value)) {
Value++;
}
if (*Value != '\0') {
return EFI_INVALID_PARAMETER;
}
return EFI_SUCCESS;
}
/**
Parse the option in Options array to MTFTP4_OPTION which program
can access directly.
@param Options The option array, which contains addresses of each
option's name/value string.
@param Count The number of options in the Options
@param Request Whether this is a request or OACK. The format of
multicast is different according to this setting.
@param Operation The current performed operation.
@param MtftpOption The MTFTP4_OPTION for easy access.
@retval EFI_INVALID_PARAMETER The option is mal-formated
@retval EFI_UNSUPPORTED Some option isn't supported
@retval EFI_SUCCESS The option are OK and has been parsed.
**/
EFI_STATUS
Mtftp4ParseOption (
IN EFI_MTFTP4_OPTION *Options,
IN UINT32 Count,
IN BOOLEAN Request,
IN UINT16 Operation,
OUT MTFTP4_OPTION *MtftpOption
)
{
EFI_STATUS Status;
UINT32 Index;
UINT32 Value;
EFI_MTFTP4_OPTION *This;
MtftpOption->Exist = 0;
for (Index = 0; Index < Count; Index++) {
This = Options + Index;
if ((This->OptionStr == NULL) || (This->ValueStr == NULL)) {
return EFI_INVALID_PARAMETER;
}
if (NetStringEqualNoCase (This->OptionStr, (UINT8 *) "blksize")) {
//
// block size option, valid value is between [8, 65464]
//
Value = NetStringToU32 (This->ValueStr);
if ((Value < 8) || (Value > 65464)) {
return EFI_INVALID_PARAMETER;
}
MtftpOption->BlkSize = (UINT16) Value;
MtftpOption->Exist |= MTFTP4_BLKSIZE_EXIST;
} else if (NetStringEqualNoCase (This->OptionStr, (UINT8 *) "timeout")) {
//
// timeout option, valid value is between [1, 255]
//
Value = NetStringToU32 (This->ValueStr);
if ((Value < 1) || (Value > 255)) {
return EFI_INVALID_PARAMETER;
}
MtftpOption->Timeout = (UINT8) Value;
} else if (NetStringEqualNoCase (This->OptionStr, (UINT8 *) "tsize")) {
//
// tsize option, the biggest transfer supported is 4GB with block size option
//
MtftpOption->Tsize = NetStringToU32 (This->ValueStr);
MtftpOption->Exist |= MTFTP4_TSIZE_EXIST;
} else if (NetStringEqualNoCase (This->OptionStr, (UINT8 *) "multicast")) {
//
// Multicast option, if it is a request, the value must be a zero
// length string, otherwise, it is formated like "204.0.0.1,1857,1\0"
//
if (Request) {
if (*(This->ValueStr) != '\0') {
return EFI_INVALID_PARAMETER;
}
} else {
Status = Mtftp4ExtractMcast (This->ValueStr, MtftpOption);
if (EFI_ERROR (Status)) {
return Status;
}
}
MtftpOption->Exist |= MTFTP4_MCAST_EXIST;
} else if (NetStringEqualNoCase (This->OptionStr, (UINT8 *) "windowsize")) {
if (Operation == EFI_MTFTP4_OPCODE_WRQ) {
//
// Currently, windowsize is not supported in the write operation.
//
return EFI_UNSUPPORTED;
}
Value = NetStringToU32 (This->ValueStr);
if (Value < 1) {
return EFI_INVALID_PARAMETER;
}
MtftpOption->WindowSize = (UINT16) Value;
MtftpOption->Exist |= MTFTP4_WINDOWSIZE_EXIST;
} else if (Request) {
//
// Ignore the unsupported option if it is a reply, and return
// EFI_UNSUPPORTED if it's a request according to the UEFI spec.
//
return EFI_UNSUPPORTED;
}
}
return EFI_SUCCESS;
}
/**
Parse the options in the OACK packet to MTFTP4_OPTION which program
can access directly.
@param Packet The OACK packet to parse
@param PacketLen The length of the packet
@param Operation The current performed operation.
@param MtftpOption The MTFTP_OPTION for easy access.
@retval EFI_INVALID_PARAMETER The packet option is mal-formated
@retval EFI_UNSUPPORTED Some option isn't supported
@retval EFI_SUCCESS The option are OK and has been parsed.
**/
EFI_STATUS
Mtftp4ParseOptionOack (
IN EFI_MTFTP4_PACKET *Packet,
IN UINT32 PacketLen,
IN UINT16 Operation,
OUT MTFTP4_OPTION *MtftpOption
)
{
EFI_MTFTP4_OPTION *OptionList;
EFI_STATUS Status;
UINT32 Count;
MtftpOption->Exist = 0;
Status = Mtftp4ExtractOptions (Packet, PacketLen, &Count, &OptionList);
if (EFI_ERROR (Status) || (Count == 0)) {
return Status;
}
ASSERT (OptionList != NULL);
Status = Mtftp4ParseOption (OptionList, Count, FALSE, Operation, MtftpOption);
FreePool (OptionList);
return Status;
}

View File

@@ -0,0 +1,111 @@
/** @file
Routines to process MTFTP4 options.
Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.<BR>
SPDX-License-Identifier: BSD-2-Clause-Patent
**/
#ifndef __EFI_MTFTP4_OPTION_H__
#define __EFI_MTFTP4_OPTION_H__
#define MTFTP4_SUPPORTED_OPTIONS 5
#define MTFTP4_OPCODE_LEN 2
#define MTFTP4_ERRCODE_LEN 2
#define MTFTP4_BLKNO_LEN 2
#define MTFTP4_DATA_HEAD_LEN 4
#define MTFTP4_BLKSIZE_EXIST 0x01
#define MTFTP4_TIMEOUT_EXIST 0x02
#define MTFTP4_TSIZE_EXIST 0x04
#define MTFTP4_MCAST_EXIST 0x08
#define MTFTP4_WINDOWSIZE_EXIST 0x10
typedef struct {
UINT16 BlkSize;
UINT16 WindowSize;
UINT8 Timeout;
UINT32 Tsize;
IP4_ADDR McastIp;
UINT16 McastPort;
BOOLEAN Master;
UINT32 Exist;
} MTFTP4_OPTION;
/**
Allocate and fill in a array of Mtftp options from the Packet.
It first calls Mtftp4FillOption to get the option number, then allocate
the array, at last, call Mtftp4FillOption again to save the options.
@param Packet The packet to parse
@param PacketLen The length of the packet
@param OptionCount The number of options in the packet
@param OptionList The point to get the option array.
@retval EFI_INVALID_PARAMETER The parametera are invalid or packet isn't a
well-formated OACK packet.
@retval EFI_SUCCESS The option array is build
@retval EFI_OUT_OF_RESOURCES Failed to allocate memory for the array
**/
EFI_STATUS
Mtftp4ExtractOptions (
IN EFI_MTFTP4_PACKET *Packet,
IN UINT32 PacketLen,
OUT UINT32 *OptionCount,
OUT EFI_MTFTP4_OPTION **OptionList OPTIONAL
);
/**
Parse the option in Options array to MTFTP4_OPTION which program
can access directly.
@param Options The option array, which contains addresses of each
option's name/value string.
@param Count The number of options in the Options
@param Request Whether this is a request or OACK. The format of
multicast is different according to this setting.
@param Operation The current performed operation.
@param MtftpOption The MTFTP4_OPTION for easy access.
@retval EFI_INVALID_PARAMETER The option is mal-formated
@retval EFI_UNSUPPORTED Some option isn't supported
@retval EFI_SUCCESS The option are OK and has been parsed.
**/
EFI_STATUS
Mtftp4ParseOption (
IN EFI_MTFTP4_OPTION *Options,
IN UINT32 Count,
IN BOOLEAN Request,
IN UINT16 Operation,
OUT MTFTP4_OPTION *MtftpOption
);
/**
Parse the options in the OACK packet to MTFTP4_OPTION which program
can access directly.
@param Packet The OACK packet to parse
@param PacketLen The length of the packet
@param Operation The current performed operation.
@param MtftpOption The MTFTP_OPTION for easy access.
@retval EFI_INVALID_PARAMETER The packet option is mal-formated
@retval EFI_UNSUPPORTED Some option isn't supported
@retval EFI_SUCCESS The option are OK and has been parsed.
**/
EFI_STATUS
Mtftp4ParseOptionOack (
IN EFI_MTFTP4_PACKET *Packet,
IN UINT32 PacketLen,
IN UINT16 Operation,
OUT MTFTP4_OPTION *MtftpOption
);
extern CHAR8 *mMtftp4SupportedOptions[MTFTP4_SUPPORTED_OPTIONS];
#endif

View File

@@ -0,0 +1,818 @@
/** @file
Routines to process Rrq (download).
(C) Copyright 2014 Hewlett-Packard Development Company, L.P.<BR>
Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.<BR>
SPDX-License-Identifier: BSD-2-Clause-Patent
**/
#include "Mtftp4Impl.h"
/**
The packet process callback for MTFTP download.
@param UdpPacket The packet received
@param EndPoint The local/remote access point of the packet
@param IoStatus The status of the receiving
@param Context Opaque parameter, which is the MTFTP session
**/
VOID
EFIAPI
Mtftp4RrqInput (
IN NET_BUF *UdpPacket,
IN UDP_END_POINT *EndPoint,
IN EFI_STATUS IoStatus,
IN VOID *Context
);
/**
Start the MTFTP session to download.
It will first initialize some of the internal states then build and send a RRQ
reqeuest packet, at last, it will start receive for the downloading.
@param Instance The Mtftp session
@param Operation The MTFTP opcode, it may be a EFI_MTFTP4_OPCODE_RRQ
or EFI_MTFTP4_OPCODE_DIR.
@retval EFI_SUCCESS The mtftp download session is started.
@retval Others Failed to start downloading.
**/
EFI_STATUS
Mtftp4RrqStart (
IN MTFTP4_PROTOCOL *Instance,
IN UINT16 Operation
)
{
EFI_STATUS Status;
//
// The valid block number range are [1, 0xffff]. For example:
// the client sends an RRQ request to the server, the server
// transfers the DATA1 block. If option negoitation is ongoing,
// the server will send back an OACK, then client will send ACK0.
//
Status = Mtftp4InitBlockRange (&Instance->Blocks, 1, 0xffff);
if (EFI_ERROR (Status)) {
return Status;
}
Status = Mtftp4SendRequest (Instance);
if (EFI_ERROR (Status)) {
return Status;
}
return UdpIoRecvDatagram (Instance->UnicastPort, Mtftp4RrqInput, Instance, 0);
}
/**
Build and send a ACK packet for the download session.
@param Instance The Mtftp session
@param BlkNo The BlkNo to ack.
@retval EFI_OUT_OF_RESOURCES Failed to allocate memory for the packet
@retval EFI_SUCCESS The ACK has been sent
@retval Others Failed to send the ACK.
**/
EFI_STATUS
Mtftp4RrqSendAck (
IN MTFTP4_PROTOCOL *Instance,
IN UINT16 BlkNo
)
{
EFI_MTFTP4_PACKET *Ack;
NET_BUF *Packet;
EFI_STATUS Status;
Status = EFI_SUCCESS;
Packet = NetbufAlloc (sizeof (EFI_MTFTP4_ACK_HEADER));
if (Packet == NULL) {
return EFI_OUT_OF_RESOURCES;
}
Ack = (EFI_MTFTP4_PACKET *) NetbufAllocSpace (
Packet,
sizeof (EFI_MTFTP4_ACK_HEADER),
FALSE
);
ASSERT (Ack != NULL);
Ack->Ack.OpCode = HTONS (EFI_MTFTP4_OPCODE_ACK);
Ack->Ack.Block[0] = HTONS (BlkNo);
Status = Mtftp4SendPacket (Instance, Packet);
if (!EFI_ERROR (Status)) {
Instance->AckedBlock = Instance->TotalBlock;
}
return Status;
}
/**
Deliver the received data block to the user, which can be saved
in the user provide buffer or through the CheckPacket callback.
@param Instance The Mtftp session
@param Packet The received data packet
@param Len The packet length
@retval EFI_SUCCESS The data is saved successfully
@retval EFI_ABORTED The user tells to abort by return an error through
CheckPacket
@retval EFI_BUFFER_TOO_SMALL The user's buffer is too small and buffer length is
updated to the actual buffer size needed.
**/
EFI_STATUS
Mtftp4RrqSaveBlock (
IN OUT MTFTP4_PROTOCOL *Instance,
IN EFI_MTFTP4_PACKET *Packet,
IN UINT32 Len
)
{
EFI_MTFTP4_TOKEN *Token;
EFI_STATUS Status;
UINT16 Block;
UINT64 Start;
UINT32 DataLen;
UINT64 BlockCounter;
BOOLEAN Completed;
Completed = FALSE;
Token = Instance->Token;
Block = NTOHS (Packet->Data.Block);
DataLen = Len - MTFTP4_DATA_HEAD_LEN;
//
// This is the last block, save the block no
//
if (DataLen < Instance->BlkSize) {
Completed = TRUE;
Instance->LastBlock = Block;
Mtftp4SetLastBlockNum (&Instance->Blocks, Block);
}
//
// Remove this block number from the file hole. If Mtftp4RemoveBlockNum
// returns EFI_NOT_FOUND, the block has been saved, don't save it again.
// Note that : For bigger files, allowing the block counter to roll over
// to accept transfers of unlimited size. So BlockCounter is memorised as
// continuous block counter.
//
Status = Mtftp4RemoveBlockNum (&Instance->Blocks, Block, Completed, &BlockCounter);
if (Status == EFI_NOT_FOUND) {
return EFI_SUCCESS;
} else if (EFI_ERROR (Status)) {
return Status;
}
if (Token->CheckPacket != NULL) {
Status = Token->CheckPacket (&Instance->Mtftp4, Token, (UINT16) Len, Packet);
if (EFI_ERROR (Status)) {
Mtftp4SendError (
Instance,
EFI_MTFTP4_ERRORCODE_ILLEGAL_OPERATION,
(UINT8 *) "User aborted download"
);
return EFI_ABORTED;
}
}
if (Token->Buffer != NULL) {
Start = MultU64x32 (BlockCounter - 1, Instance->BlkSize);
if (Start + DataLen <= Token->BufferSize) {
CopyMem ((UINT8 *) Token->Buffer + Start, Packet->Data.Data, DataLen);
//
// Update the file size when received the last block
//
if ((Instance->LastBlock == Block) && Completed) {
Token->BufferSize = Start + DataLen;
}
} else if (Instance->LastBlock != 0) {
//
// Don't save the data if the buffer is too small, return
// EFI_BUFFER_TOO_SMALL if received the last packet. This
// will give a accurate file length.
//
Token->BufferSize = Start + DataLen;
Mtftp4SendError (
Instance,
EFI_MTFTP4_ERRORCODE_DISK_FULL,
(UINT8 *) "User provided memory block is too small"
);
return EFI_BUFFER_TOO_SMALL;
}
}
return EFI_SUCCESS;
}
/**
Function to process the received data packets.
It will save the block then send back an ACK if it is active.
@param Instance The downloading MTFTP session
@param Packet The packet received
@param Len The length of the packet
@param Multicast Whether this packet is multicast or unicast
@param Completed Return whether the download has completed
@retval EFI_SUCCESS The data packet is successfully processed
@retval EFI_ABORTED The download is aborted by the user
@retval EFI_BUFFER_TOO_SMALL The user provided buffer is too small
**/
EFI_STATUS
Mtftp4RrqHandleData (
IN MTFTP4_PROTOCOL *Instance,
IN EFI_MTFTP4_PACKET *Packet,
IN UINT32 Len,
IN BOOLEAN Multicast,
OUT BOOLEAN *Completed
)
{
EFI_STATUS Status;
UINT16 BlockNum;
INTN Expected;
*Completed = FALSE;
Status = EFI_SUCCESS;
BlockNum = NTOHS (Packet->Data.Block);
Expected = Mtftp4GetNextBlockNum (&Instance->Blocks);
ASSERT (Expected >= 0);
//
// If we are active (Master) and received an unexpected packet, transmit
// the ACK for the block we received, then restart receiving the
// expected one. If we are passive (Slave), save the block.
//
if (Instance->Master && (Expected != BlockNum)) {
//
// If Expected is 0, (UINT16) (Expected - 1) is also the expected Ack number (65535).
//
return Mtftp4RrqSendAck (Instance, (UINT16) (Expected - 1));
}
Status = Mtftp4RrqSaveBlock (Instance, Packet, Len);
if (EFI_ERROR (Status)) {
return Status;
}
//
// Record the total received and saved block number.
//
Instance->TotalBlock ++;
//
// Reset the passive client's timer whenever it received a
// valid data packet.
//
if (!Instance->Master) {
Mtftp4SetTimeout (Instance);
}
//
// Check whether we have received all the blocks. Send the ACK if we
// are active (unicast client or master client for multicast download).
// If we have received all the blocks, send an ACK even if we are passive
// to tell the server that we are done.
//
Expected = Mtftp4GetNextBlockNum (&Instance->Blocks);
if (Instance->Master || (Expected < 0)) {
if (Expected < 0) {
//
// If we are passive client, then the just received Block maybe
// isn't the last block. We need to send an ACK to the last block
// to inform the server that we are done. If we are active client,
// the Block == Instance->LastBlock.
//
BlockNum = Instance->LastBlock;
*Completed = TRUE;
} else {
BlockNum = (UINT16) (Expected - 1);
}
if (Instance->WindowSize == (Instance->TotalBlock - Instance->AckedBlock) || Expected < 0) {
Status = Mtftp4RrqSendAck (Instance, BlockNum);
}
}
return Status;
}
/**
Validate whether the options received in the server's OACK packet is valid.
The options are valid only if:
1. The server doesn't include options not requested by us
2. The server can only use smaller blksize than that is requested
3. The server can only use the same timeout as requested
4. The server doesn't change its multicast channel.
@param This The downloading Mtftp session
@param Reply The options in the OACK packet
@param Request The requested options
@retval TRUE The options in the OACK is OK.
@retval FALSE The options in the OACK is invalid.
**/
BOOLEAN
Mtftp4RrqOackValid (
IN MTFTP4_PROTOCOL *This,
IN MTFTP4_OPTION *Reply,
IN MTFTP4_OPTION *Request
)
{
//
// It is invalid for server to return options we don't request
//
if ((Reply->Exist &~Request->Exist) != 0) {
return FALSE;
}
//
// Server can only specify a smaller block size and window size to be used and
// return the timeout matches that requested.
//
if ((((Reply->Exist & MTFTP4_BLKSIZE_EXIST) != 0)&& (Reply->BlkSize > Request->BlkSize)) ||
(((Reply->Exist & MTFTP4_WINDOWSIZE_EXIST) != 0)&& (Reply->WindowSize > Request->WindowSize)) ||
(((Reply->Exist & MTFTP4_TIMEOUT_EXIST) != 0) && (Reply->Timeout != Request->Timeout))
) {
return FALSE;
}
//
// The server can send ",,master" to client to change its master
// setting. But if it use the specific multicast channel, it can't
// change the setting.
//
if (((Reply->Exist & MTFTP4_MCAST_EXIST) != 0) && (This->McastIp != 0)) {
if ((Reply->McastIp != 0) && (Reply->McastIp != This->McastIp)) {
return FALSE;
}
if ((Reply->McastPort != 0) && (Reply->McastPort != This->McastPort)) {
return FALSE;
}
}
return TRUE;
}
/**
Configure a UDP IO port to receive the multicast.
@param McastIo The UDP IO to configure
@param Context The opaque parameter to the function which is the
MTFTP session.
@retval EFI_SUCCESS The UDP child is successfully configured.
@retval Others Failed to configure the UDP child.
**/
EFI_STATUS
EFIAPI
Mtftp4RrqConfigMcastPort (
IN UDP_IO *McastIo,
IN VOID *Context
)
{
MTFTP4_PROTOCOL *Instance;
EFI_MTFTP4_CONFIG_DATA *Config;
EFI_UDP4_CONFIG_DATA UdpConfig;
EFI_IPv4_ADDRESS Group;
EFI_STATUS Status;
IP4_ADDR Ip;
Instance = (MTFTP4_PROTOCOL *) Context;
Config = &Instance->Config;
UdpConfig.AcceptBroadcast = FALSE;
UdpConfig.AcceptPromiscuous = FALSE;
UdpConfig.AcceptAnyPort = FALSE;
UdpConfig.AllowDuplicatePort = FALSE;
UdpConfig.TypeOfService = 0;
UdpConfig.TimeToLive = 64;
UdpConfig.DoNotFragment = FALSE;
UdpConfig.ReceiveTimeout = 0;
UdpConfig.TransmitTimeout = 0;
UdpConfig.UseDefaultAddress = Config->UseDefaultSetting;
IP4_COPY_ADDRESS (&UdpConfig.StationAddress, &Config->StationIp);
IP4_COPY_ADDRESS (&UdpConfig.SubnetMask, &Config->SubnetMask);
UdpConfig.StationPort = Instance->McastPort;
UdpConfig.RemotePort = 0;
Ip = HTONL (Instance->ServerIp);
IP4_COPY_ADDRESS (&UdpConfig.RemoteAddress, &Ip);
Status = McastIo->Protocol.Udp4->Configure (McastIo->Protocol.Udp4, &UdpConfig);
if (EFI_ERROR (Status)) {
return Status;
}
if (!Config->UseDefaultSetting &&
!EFI_IP4_EQUAL (&mZeroIp4Addr, &Config->GatewayIp)) {
//
// The station IP address is manually configured and the Gateway IP is not 0.
// Add the default route for this UDP instance.
//
Status = McastIo->Protocol.Udp4->Routes (
McastIo->Protocol.Udp4,
FALSE,
&mZeroIp4Addr,
&mZeroIp4Addr,
&Config->GatewayIp
);
if (EFI_ERROR (Status)) {
McastIo->Protocol.Udp4->Configure (McastIo->Protocol.Udp4, NULL);
return Status;
}
}
//
// join the multicast group
//
Ip = HTONL (Instance->McastIp);
IP4_COPY_ADDRESS (&Group, &Ip);
return McastIo->Protocol.Udp4->Groups (McastIo->Protocol.Udp4, TRUE, &Group);
}
/**
Function to process the OACK.
It will first validate the OACK packet, then update the various negotiated parameters.
@param Instance The download MTFTP session
@param Packet The packet received
@param Len The packet length
@param Multicast Whether this packet is received as a multicast
@param Completed Returns whether the download has completed. NOT
used by this function.
@retval EFI_DEVICE_ERROR Failed to create/start a multicast UDP child
@retval EFI_TFTP_ERROR Some error happened during the process
@retval EFI_SUCCESS The OACK is successfully processed.
**/
EFI_STATUS
Mtftp4RrqHandleOack (
IN OUT MTFTP4_PROTOCOL *Instance,
IN EFI_MTFTP4_PACKET *Packet,
IN UINT32 Len,
IN BOOLEAN Multicast,
OUT BOOLEAN *Completed
)
{
MTFTP4_OPTION Reply;
EFI_STATUS Status;
INTN Expected;
EFI_UDP4_PROTOCOL *Udp4;
*Completed = FALSE;
//
// If already started the master download, don't change the
// setting. Master download always succeeds.
//
Expected = Mtftp4GetNextBlockNum (&Instance->Blocks);
ASSERT (Expected != -1);
if (Instance->Master && (Expected != 1)) {
return EFI_SUCCESS;
}
//
// Parse and validate the options from server
//
ZeroMem (&Reply, sizeof (MTFTP4_OPTION));
Status = Mtftp4ParseOptionOack (Packet, Len, Instance->Operation, &Reply);
if (EFI_ERROR (Status) ||
!Mtftp4RrqOackValid (Instance, &Reply, &Instance->RequestOption)) {
//
// Don't send an ERROR packet if the error is EFI_OUT_OF_RESOURCES.
//
if (Status != EFI_OUT_OF_RESOURCES) {
Mtftp4SendError (
Instance,
EFI_MTFTP4_ERRORCODE_ILLEGAL_OPERATION,
(UINT8 *) "Mal-formated OACK packet"
);
}
return EFI_TFTP_ERROR;
}
if ((Reply.Exist & MTFTP4_MCAST_EXIST) != 0) {
//
// Save the multicast info. Always update the Master, only update the
// multicast IP address, block size, window size, timeoute at the first time. If IP
// address is updated, create a UDP child to receive the multicast.
//
Instance->Master = Reply.Master;
if (Instance->McastIp == 0) {
if ((Reply.McastIp == 0) || (Reply.McastPort == 0)) {
Mtftp4SendError (
Instance,
EFI_MTFTP4_ERRORCODE_ILLEGAL_OPERATION,
(UINT8 *) "Illegal multicast setting"
);
return EFI_TFTP_ERROR;
}
//
// Create a UDP child then start receive the multicast from it.
//
Instance->McastIp = Reply.McastIp;
Instance->McastPort = Reply.McastPort;
if (Instance->McastUdpPort == NULL) {
Instance->McastUdpPort = UdpIoCreateIo (
Instance->Service->Controller,
Instance->Service->Image,
Mtftp4RrqConfigMcastPort,
UDP_IO_UDP4_VERSION,
Instance
);
if (Instance->McastUdpPort != NULL) {
Status = gBS->OpenProtocol (
Instance->McastUdpPort->UdpHandle,
&gEfiUdp4ProtocolGuid,
(VOID **) &Udp4,
Instance->Service->Image,
Instance->Handle,
EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER
);
if (EFI_ERROR (Status)) {
UdpIoFreeIo (Instance->McastUdpPort);
Instance->McastUdpPort = NULL;
return EFI_DEVICE_ERROR;
}
}
}
if (Instance->McastUdpPort == NULL) {
return EFI_DEVICE_ERROR;
}
Status = UdpIoRecvDatagram (Instance->McastUdpPort, Mtftp4RrqInput, Instance, 0);
if (EFI_ERROR (Status)) {
Mtftp4SendError (
Instance,
EFI_MTFTP4_ERRORCODE_ACCESS_VIOLATION,
(UINT8 *) "Failed to create socket to receive multicast packet"
);
return Status;
}
//
// Update the parameters used.
//
if (Reply.BlkSize != 0) {
Instance->BlkSize = Reply.BlkSize;
}
if (Reply.WindowSize != 0) {
Instance->WindowSize = Reply.WindowSize;
}
if (Reply.Timeout != 0) {
Instance->Timeout = Reply.Timeout;
}
}
} else {
Instance->Master = TRUE;
if (Reply.BlkSize != 0) {
Instance->BlkSize = Reply.BlkSize;
}
if (Reply.WindowSize != 0) {
Instance->WindowSize = Reply.WindowSize;
}
if (Reply.Timeout != 0) {
Instance->Timeout = Reply.Timeout;
}
}
//
// Send an ACK to (Expected - 1) which is 0 for unicast download,
// or tell the server we want to receive the Expected block.
//
return Mtftp4RrqSendAck (Instance, (UINT16) (Expected - 1));
}
/**
The packet process callback for MTFTP download.
@param UdpPacket The packet received
@param EndPoint The local/remote access point of the packet
@param IoStatus The status of the receiving
@param Context Opaque parameter, which is the MTFTP session
**/
VOID
EFIAPI
Mtftp4RrqInput (
IN NET_BUF *UdpPacket,
IN UDP_END_POINT *EndPoint,
IN EFI_STATUS IoStatus,
IN VOID *Context
)
{
MTFTP4_PROTOCOL *Instance;
EFI_MTFTP4_PACKET *Packet;
BOOLEAN Completed;
BOOLEAN Multicast;
EFI_STATUS Status;
UINT16 Opcode;
UINT32 Len;
Instance = (MTFTP4_PROTOCOL *) Context;
NET_CHECK_SIGNATURE (Instance, MTFTP4_PROTOCOL_SIGNATURE);
Status = EFI_SUCCESS;
Packet = NULL;
Completed = FALSE;
Multicast = FALSE;
if (EFI_ERROR (IoStatus)) {
Status = IoStatus;
goto ON_EXIT;
}
ASSERT (UdpPacket != NULL);
//
// Find the port this packet is from to restart receive correctly.
//
Multicast = (BOOLEAN) (EndPoint->LocalAddr.Addr[0] == Instance->McastIp);
if (UdpPacket->TotalSize < MTFTP4_OPCODE_LEN) {
goto ON_EXIT;
}
//
// Client send initial request to server's listening port. Server
// will select a UDP port to communicate with the client. The server
// is required to use the same port as RemotePort to multicast the
// data.
//
if (EndPoint->RemotePort != Instance->ConnectedPort) {
if (Instance->ConnectedPort != 0) {
goto ON_EXIT;
} else {
Instance->ConnectedPort = EndPoint->RemotePort;
}
}
//
// Copy the MTFTP packet to a continuous buffer if it isn't already so.
//
Len = UdpPacket->TotalSize;
if (UdpPacket->BlockOpNum > 1) {
Packet = AllocatePool (Len);
if (Packet == NULL) {
Status = EFI_OUT_OF_RESOURCES;
goto ON_EXIT;
}
NetbufCopy (UdpPacket, 0, Len, (UINT8 *) Packet);
} else {
Packet = (EFI_MTFTP4_PACKET *) NetbufGetByte (UdpPacket, 0, NULL);
ASSERT (Packet != NULL);
}
Opcode = NTOHS (Packet->OpCode);
//
// Call the user's CheckPacket if provided. Abort the transmission
// if CheckPacket returns an EFI_ERROR code.
//
if ((Instance->Token->CheckPacket != NULL) &&
((Opcode == EFI_MTFTP4_OPCODE_OACK) || (Opcode == EFI_MTFTP4_OPCODE_ERROR))) {
Status = Instance->Token->CheckPacket (
&Instance->Mtftp4,
Instance->Token,
(UINT16) Len,
Packet
);
if (EFI_ERROR (Status)) {
//
// Send an error message to the server to inform it
//
if (Opcode != EFI_MTFTP4_OPCODE_ERROR) {
Mtftp4SendError (
Instance,
EFI_MTFTP4_ERRORCODE_REQUEST_DENIED,
(UINT8 *) "User aborted the transfer"
);
}
Status = EFI_ABORTED;
goto ON_EXIT;
}
}
switch (Opcode) {
case EFI_MTFTP4_OPCODE_DATA:
if ((Len > (UINT32) (MTFTP4_DATA_HEAD_LEN + Instance->BlkSize)) ||
(Len < (UINT32) MTFTP4_DATA_HEAD_LEN)) {
goto ON_EXIT;
}
Status = Mtftp4RrqHandleData (Instance, Packet, Len, Multicast, &Completed);
break;
case EFI_MTFTP4_OPCODE_OACK:
if (Multicast || (Len <= MTFTP4_OPCODE_LEN)) {
goto ON_EXIT;
}
Status = Mtftp4RrqHandleOack (Instance, Packet, Len, Multicast, &Completed);
break;
case EFI_MTFTP4_OPCODE_ERROR:
Status = EFI_TFTP_ERROR;
break;
default:
break;
}
ON_EXIT:
//
// Free the resources, then if !EFI_ERROR (Status), restart the
// receive, otherwise end the session.
//
if ((Packet != NULL) && (UdpPacket->BlockOpNum > 1)) {
FreePool (Packet);
}
if (UdpPacket != NULL) {
NetbufFree (UdpPacket);
}
if (!EFI_ERROR (Status) && !Completed) {
if (Multicast) {
Status = UdpIoRecvDatagram (Instance->McastUdpPort, Mtftp4RrqInput, Instance, 0);
} else {
Status = UdpIoRecvDatagram (Instance->UnicastPort, Mtftp4RrqInput, Instance, 0);
}
}
if (EFI_ERROR (Status) || Completed) {
Mtftp4CleanOperation (Instance, Status);
}
}

View File

@@ -0,0 +1,663 @@
/** @file
Support routines for Mtftp.
Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.<BR>
SPDX-License-Identifier: BSD-2-Clause-Patent
**/
#include "Mtftp4Impl.h"
/**
Allocate a MTFTP4 block range, then init it to the range of [Start, End]
@param Start The start block number
@param End The last block number in the range
@return Pointer to the created block range, NULL if failed to allocate memory.
**/
MTFTP4_BLOCK_RANGE *
Mtftp4AllocateRange (
IN UINT16 Start,
IN UINT16 End
)
{
MTFTP4_BLOCK_RANGE *Range;
Range = AllocateZeroPool (sizeof (MTFTP4_BLOCK_RANGE));
if (Range == NULL) {
return NULL;
}
InitializeListHead (&Range->Link);
Range->Start = Start;
Range->End = End;
Range->Bound = End;
return Range;
}
/**
Initialize the block range for either RRQ or WRQ.
RRQ and WRQ have different requirements for Start and End.
For example, during start up, WRQ initializes its whole valid block range
to [0, 0xffff]. This is bacause the server will send us a ACK0 to inform us
to start the upload. When the client received ACK0, it will remove 0 from the
range, get the next block number, which is 1, then upload the BLOCK1. For RRQ
without option negotiation, the server will directly send us the BLOCK1 in
response to the client's RRQ. When received BLOCK1, the client will remove
it from the block range and send an ACK. It also works if there is option
negotiation.
@param Head The block range head to initialize
@param Start The Start block number.
@param End The last block number.
@retval EFI_OUT_OF_RESOURCES Failed to allocate memory for initial block range
@retval EFI_SUCCESS The initial block range is created.
**/
EFI_STATUS
Mtftp4InitBlockRange (
IN LIST_ENTRY *Head,
IN UINT16 Start,
IN UINT16 End
)
{
MTFTP4_BLOCK_RANGE *Range;
Range = Mtftp4AllocateRange (Start, End);
if (Range == NULL) {
return EFI_OUT_OF_RESOURCES;
}
InsertTailList (Head, &Range->Link);
return EFI_SUCCESS;
}
/**
Get the first valid block number on the range list.
@param Head The block range head
@return The first valid block number, -1 if the block range is empty.
**/
INTN
Mtftp4GetNextBlockNum (
IN LIST_ENTRY *Head
)
{
MTFTP4_BLOCK_RANGE *Range;
if (IsListEmpty (Head)) {
return -1;
}
Range = NET_LIST_HEAD (Head, MTFTP4_BLOCK_RANGE, Link);
return Range->Start;
}
/**
Set the last block number of the block range list.
It will remove all the blocks after the Last. MTFTP initialize the block range
to the maximum possible range, such as [0, 0xffff] for WRQ. When it gets the
last block number, it will call this function to set the last block number.
@param Head The block range list
@param Last The last block number
**/
VOID
Mtftp4SetLastBlockNum (
IN LIST_ENTRY *Head,
IN UINT16 Last
)
{
MTFTP4_BLOCK_RANGE *Range;
//
// Iterate from the tail to head to remove the block number
// after the last.
//
while (!IsListEmpty (Head)) {
Range = NET_LIST_TAIL (Head, MTFTP4_BLOCK_RANGE, Link);
if (Range->Start > Last) {
RemoveEntryList (&Range->Link);
FreePool (Range);
continue;
}
if (Range->End > Last) {
Range->End = Last;
}
return ;
}
}
/**
Remove the block number from the block range list.
@param Head The block range list to remove from
@param Num The block number to remove
@param Completed Whether Num is the last block number.
@param BlockCounter The continuous block counter instead of the value after roll-over.
@retval EFI_NOT_FOUND The block number isn't in the block range list
@retval EFI_SUCCESS The block number has been removed from the list
@retval EFI_OUT_OF_RESOURCES Failed to allocate resource
**/
EFI_STATUS
Mtftp4RemoveBlockNum (
IN LIST_ENTRY *Head,
IN UINT16 Num,
IN BOOLEAN Completed,
OUT UINT64 *BlockCounter
)
{
MTFTP4_BLOCK_RANGE *Range;
MTFTP4_BLOCK_RANGE *NewRange;
LIST_ENTRY *Entry;
NET_LIST_FOR_EACH (Entry, Head) {
//
// Each block represents a hole [Start, End] in the file,
// skip to the first range with End >= Num
//
Range = NET_LIST_USER_STRUCT (Entry, MTFTP4_BLOCK_RANGE, Link);
if (Range->End < Num) {
continue;
}
//
// There are three different cases for Start
// 1. (Start > Num) && (End >= Num):
// because all the holes before this one has the condition of
// End < Num, so this block number has been removed.
//
// 2. (Start == Num) && (End >= Num):
// Need to increase the Start by one, and if End == Num, this
// hole has been removed completely, remove it.
//
// 3. (Start < Num) && (End >= Num):
// if End == Num, only need to decrease the End by one because
// we have (Start < Num) && (Num == End), so (Start <= End - 1).
// if (End > Num), the hold is splited into two holes, with
// [Start, Num - 1] and [Num + 1, End].
//
if (Range->Start > Num) {
return EFI_NOT_FOUND;
} else if (Range->Start == Num) {
Range->Start++;
//
// Note that: RFC 1350 does not mention block counter roll-over,
// but several TFTP hosts implement the roll-over be able to accept
// transfers of unlimited size. There is no consensus, however, whether
// the counter should wrap around to zero or to one. Many implementations
// wrap to zero, because this is the simplest to implement. Here we choose
// this solution.
//
*BlockCounter = Num;
if (Range->Round > 0) {
*BlockCounter += Range->Bound + MultU64x32 ((UINTN) (Range->Round -1), (UINT32) (Range->Bound + 1)) + 1;
}
if (Range->Start > Range->Bound) {
Range->Start = 0;
Range->Round ++;
}
if ((Range->Start > Range->End) || Completed) {
RemoveEntryList (&Range->Link);
FreePool (Range);
}
return EFI_SUCCESS;
} else {
if (Range->End == Num) {
Range->End--;
} else {
NewRange = Mtftp4AllocateRange ((UINT16) (Num + 1), (UINT16) Range->End);
if (NewRange == NULL) {
return EFI_OUT_OF_RESOURCES;
}
Range->End = Num - 1;
NetListInsertAfter (&Range->Link, &NewRange->Link);
}
return EFI_SUCCESS;
}
}
return EFI_NOT_FOUND;
}
/**
Build then transmit the request packet for the MTFTP session.
@param Instance The Mtftp session
@retval EFI_OUT_OF_RESOURCES Failed to allocate memory for the request
@retval EFI_SUCCESS The request is built and sent
@retval Others Failed to transmit the packet.
**/
EFI_STATUS
Mtftp4SendRequest (
IN MTFTP4_PROTOCOL *Instance
)
{
EFI_MTFTP4_PACKET *Packet;
EFI_MTFTP4_OPTION *Options;
EFI_MTFTP4_TOKEN *Token;
RETURN_STATUS Status;
NET_BUF *Nbuf;
UINT8 *Mode;
UINT8 *Cur;
UINTN Index;
UINT32 BufferLength;
UINTN FileNameLength;
UINTN ModeLength;
UINTN OptionStrLength;
UINTN ValueStrLength;
Token = Instance->Token;
Options = Token->OptionList;
Mode = Instance->Token->ModeStr;
if (Mode == NULL) {
Mode = (UINT8 *) "octet";
}
//
// Compute the packet length
//
FileNameLength = AsciiStrLen ((CHAR8 *) Token->Filename);
ModeLength = AsciiStrLen ((CHAR8 *) Mode);
BufferLength = (UINT32) FileNameLength + (UINT32) ModeLength + 4;
for (Index = 0; Index < Token->OptionCount; Index++) {
OptionStrLength = AsciiStrLen ((CHAR8 *) Options[Index].OptionStr);
ValueStrLength = AsciiStrLen ((CHAR8 *) Options[Index].ValueStr);
BufferLength += (UINT32) OptionStrLength + (UINT32) ValueStrLength + 2;
}
//
// Allocate a packet then copy the data over
//
if ((Nbuf = NetbufAlloc (BufferLength)) == NULL) {
return EFI_OUT_OF_RESOURCES;
}
Packet = (EFI_MTFTP4_PACKET *) NetbufAllocSpace (Nbuf, BufferLength, FALSE);
ASSERT (Packet != NULL);
Packet->OpCode = HTONS (Instance->Operation);
BufferLength -= sizeof (Packet->OpCode);
Cur = Packet->Rrq.Filename;
Status = AsciiStrCpyS ((CHAR8 *) Cur, BufferLength, (CHAR8 *) Token->Filename);
ASSERT_EFI_ERROR (Status);
BufferLength -= (UINT32) (FileNameLength + 1);
Cur += FileNameLength + 1;
Status = AsciiStrCpyS ((CHAR8 *) Cur, BufferLength, (CHAR8 *) Mode);
ASSERT_EFI_ERROR (Status);
BufferLength -= (UINT32) (ModeLength + 1);
Cur += ModeLength + 1;
for (Index = 0; Index < Token->OptionCount; ++Index) {
OptionStrLength = AsciiStrLen ((CHAR8 *) Options[Index].OptionStr);
ValueStrLength = AsciiStrLen ((CHAR8 *) Options[Index].ValueStr);
Status = AsciiStrCpyS ((CHAR8 *) Cur, BufferLength, (CHAR8 *) Options[Index].OptionStr);
ASSERT_EFI_ERROR (Status);
BufferLength -= (UINT32) (OptionStrLength + 1);
Cur += OptionStrLength + 1;
Status = AsciiStrCpyS ((CHAR8 *) Cur, BufferLength, (CHAR8 *) Options[Index].ValueStr);
ASSERT_EFI_ERROR (Status);
BufferLength -= (UINT32) (ValueStrLength + 1);
Cur += ValueStrLength + 1;
}
return Mtftp4SendPacket (Instance, Nbuf);
}
/**
Build then send an error message.
@param Instance The MTFTP session
@param ErrCode The error code
@param ErrInfo The error message
@retval EFI_OUT_OF_RESOURCES Failed to allocate memory for the error packet
@retval EFI_SUCCESS The error packet is transmitted.
@retval Others Failed to transmit the packet.
**/
EFI_STATUS
Mtftp4SendError (
IN MTFTP4_PROTOCOL *Instance,
IN UINT16 ErrCode,
IN UINT8 *ErrInfo
)
{
NET_BUF *Packet;
EFI_MTFTP4_PACKET *TftpError;
UINT32 Len;
Len = (UINT32) (AsciiStrLen ((CHAR8 *) ErrInfo) + sizeof (EFI_MTFTP4_ERROR_HEADER));
Packet = NetbufAlloc (Len);
if (Packet == NULL) {
return EFI_OUT_OF_RESOURCES;
}
TftpError = (EFI_MTFTP4_PACKET *) NetbufAllocSpace (Packet, Len, FALSE);
ASSERT (TftpError != NULL);
TftpError->OpCode = HTONS (EFI_MTFTP4_OPCODE_ERROR);
TftpError->Error.ErrorCode = HTONS (ErrCode);
AsciiStrCpyS ((CHAR8 *) TftpError->Error.ErrorMessage, Len, (CHAR8 *) ErrInfo);
return Mtftp4SendPacket (Instance, Packet);
}
/**
The callback function called when the packet is transmitted.
It simply frees the packet.
@param Packet The transmitted (or failed to) packet
@param EndPoint The local and remote UDP access point
@param IoStatus The result of the transmission
@param Context Opaque parameter to the callback
**/
VOID
EFIAPI
Mtftp4OnPacketSent (
IN NET_BUF *Packet,
IN UDP_END_POINT *EndPoint,
IN EFI_STATUS IoStatus,
IN VOID *Context
)
{
NetbufFree (Packet);
}
/**
Set the timeout for the instance. User a longer time for passive instances.
@param Instance The Mtftp session to set time out
**/
VOID
Mtftp4SetTimeout (
IN OUT MTFTP4_PROTOCOL *Instance
)
{
if (Instance->Master) {
Instance->PacketToLive = Instance->Timeout;
} else {
Instance->PacketToLive = Instance->Timeout * 2;
}
}
/**
Send the packet for the instance.
It will first save a reference to the packet for later retransmission.
Then determine the destination port, listen port for requests, and connected
port for others. At last, send the packet out.
@param Instance The Mtftp instance
@param Packet The packet to send
@retval EFI_SUCCESS The packet is sent out
@retval Others Failed to transmit the packet.
**/
EFI_STATUS
Mtftp4SendPacket (
IN OUT MTFTP4_PROTOCOL *Instance,
IN OUT NET_BUF *Packet
)
{
UDP_END_POINT UdpPoint;
EFI_STATUS Status;
UINT16 OpCode;
UINT8 *Buffer;
//
// Save the packet for retransmission
//
if (Instance->LastPacket != NULL) {
NetbufFree (Instance->LastPacket);
}
Instance->LastPacket = Packet;
Instance->CurRetry = 0;
Mtftp4SetTimeout (Instance);
ZeroMem (&UdpPoint, sizeof (UdpPoint));
UdpPoint.RemoteAddr.Addr[0] = Instance->ServerIp;
//
// Send the requests to the listening port, other packets
// to the connected port
//
Buffer = NetbufGetByte (Packet, 0, NULL);
ASSERT (Buffer != NULL);
OpCode = NTOHS (*(UINT16 *)Buffer);
if ((OpCode == EFI_MTFTP4_OPCODE_RRQ) ||
(OpCode == EFI_MTFTP4_OPCODE_DIR) ||
(OpCode == EFI_MTFTP4_OPCODE_WRQ)) {
UdpPoint.RemotePort = Instance->ListeningPort;
} else {
UdpPoint.RemotePort = Instance->ConnectedPort;
}
NET_GET_REF (Packet);
Status = UdpIoSendDatagram (
Instance->UnicastPort,
Packet,
&UdpPoint,
NULL,
Mtftp4OnPacketSent,
Instance
);
if (EFI_ERROR (Status)) {
NET_PUT_REF (Packet);
}
return Status;
}
/**
Retransmit the last packet for the instance.
@param Instance The Mtftp instance
@retval EFI_SUCCESS The last packet is retransmitted.
@retval Others Failed to retransmit.
**/
EFI_STATUS
Mtftp4Retransmit (
IN MTFTP4_PROTOCOL *Instance
)
{
UDP_END_POINT UdpPoint;
EFI_STATUS Status;
UINT16 OpCode;
UINT8 *Buffer;
ASSERT (Instance->LastPacket != NULL);
ZeroMem (&UdpPoint, sizeof (UdpPoint));
UdpPoint.RemoteAddr.Addr[0] = Instance->ServerIp;
//
// Set the requests to the listening port, other packets to the connected port
//
Buffer = NetbufGetByte (Instance->LastPacket, 0, NULL);
ASSERT (Buffer != NULL);
OpCode = NTOHS (*(UINT16 *) Buffer);
if ((OpCode == EFI_MTFTP4_OPCODE_RRQ) || (OpCode == EFI_MTFTP4_OPCODE_DIR) ||
(OpCode == EFI_MTFTP4_OPCODE_WRQ)) {
UdpPoint.RemotePort = Instance->ListeningPort;
} else {
UdpPoint.RemotePort = Instance->ConnectedPort;
}
NET_GET_REF (Instance->LastPacket);
Status = UdpIoSendDatagram (
Instance->UnicastPort,
Instance->LastPacket,
&UdpPoint,
NULL,
Mtftp4OnPacketSent,
Instance
);
if (EFI_ERROR (Status)) {
NET_PUT_REF (Instance->LastPacket);
}
return Status;
}
/**
The timer ticking function in TPL_NOTIFY level for the Mtftp service instance.
@param Event The ticking event
@param Context The Mtftp service instance
**/
VOID
EFIAPI
Mtftp4OnTimerTickNotifyLevel (
IN EFI_EVENT Event,
IN VOID *Context
)
{
MTFTP4_SERVICE *MtftpSb;
LIST_ENTRY *Entry;
LIST_ENTRY *Next;
MTFTP4_PROTOCOL *Instance;
MtftpSb = (MTFTP4_SERVICE *) Context;
//
// Iterate through all the children of the Mtftp service instance. Time
// out the current packet transmit.
//
NET_LIST_FOR_EACH_SAFE (Entry, Next, &MtftpSb->Children) {
Instance = NET_LIST_USER_STRUCT (Entry, MTFTP4_PROTOCOL, Link);
if ((Instance->PacketToLive == 0) || (--Instance->PacketToLive > 0)) {
Instance->HasTimeout = FALSE;
} else {
Instance->HasTimeout = TRUE;
}
}
}
/**
The timer ticking function for the Mtftp service instance.
@param Event The ticking event
@param Context The Mtftp service instance
**/
VOID
EFIAPI
Mtftp4OnTimerTick (
IN EFI_EVENT Event,
IN VOID *Context
)
{
MTFTP4_SERVICE *MtftpSb;
LIST_ENTRY *Entry;
LIST_ENTRY *Next;
MTFTP4_PROTOCOL *Instance;
EFI_MTFTP4_TOKEN *Token;
MtftpSb = (MTFTP4_SERVICE *) Context;
//
// Iterate through all the children of the Mtftp service instance.
//
NET_LIST_FOR_EACH_SAFE (Entry, Next, &MtftpSb->Children) {
Instance = NET_LIST_USER_STRUCT (Entry, MTFTP4_PROTOCOL, Link);
if (!Instance->HasTimeout) {
continue;
}
Instance->HasTimeout = FALSE;
//
// Call the user's time out handler
//
Token = Instance->Token;
if (Token != NULL && Token->TimeoutCallback != NULL &&
EFI_ERROR (Token->TimeoutCallback (&Instance->Mtftp4, Token))) {
Mtftp4SendError (
Instance,
EFI_MTFTP4_ERRORCODE_REQUEST_DENIED,
(UINT8 *) "User aborted the transfer in time out"
);
Mtftp4CleanOperation (Instance, EFI_ABORTED);
continue;
}
//
// Retransmit the packet if haven't reach the maxmium retry count,
// otherwise exit the transfer.
//
if (++Instance->CurRetry < Instance->MaxRetry) {
Mtftp4Retransmit (Instance);
Mtftp4SetTimeout (Instance);
} else {
Mtftp4CleanOperation (Instance, EFI_TIMEOUT);
continue;
}
}
}

View File

@@ -0,0 +1,198 @@
/** @file
Support routines for MTFTP.
Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.<BR>
SPDX-License-Identifier: BSD-2-Clause-Patent
**/
#ifndef __EFI_MTFTP4_SUPPORT_H__
#define __EFI_MTFTP4_SUPPORT_H__
//
// The structure representing a range of block numbers, [Start, End].
// It is used to remember the holes in the MTFTP block space. If all
// the holes are filled in, then the download or upload has completed.
//
typedef struct {
LIST_ENTRY Link;
INTN Start;
INTN End;
INTN Round;
INTN Bound;
} MTFTP4_BLOCK_RANGE;
/**
Initialize the block range for either RRQ or WRQ.
RRQ and WRQ have different requirements for Start and End.
For example, during start up, WRQ initializes its whole valid block range
to [0, 0xffff]. This is bacause the server will send us a ACK0 to inform us
to start the upload. When the client received ACK0, it will remove 0 from the
range, get the next block number, which is 1, then upload the BLOCK1. For RRQ
without option negotiation, the server will directly send us the BLOCK1 in
response to the client's RRQ. When received BLOCK1, the client will remove
it from the block range and send an ACK. It also works if there is option
negotiation.
@param Head The block range head to initialize
@param Start The Start block number.
@param End The last block number.
@retval EFI_OUT_OF_RESOURCES Failed to allocate memory for initial block range
@retval EFI_SUCCESS The initial block range is created.
**/
EFI_STATUS
Mtftp4InitBlockRange (
IN LIST_ENTRY *Head,
IN UINT16 Start,
IN UINT16 End
);
/**
Get the first valid block number on the range list.
@param Head The block range head
@return The first valid block number, -1 if the block range is empty.
**/
INTN
Mtftp4GetNextBlockNum (
IN LIST_ENTRY *Head
);
/**
Set the last block number of the block range list.
It will remove all the blocks after the Last. MTFTP initialize the block range
to the maximum possible range, such as [0, 0xffff] for WRQ. When it gets the
last block number, it will call this function to set the last block number.
@param Head The block range list
@param Last The last block number
**/
VOID
Mtftp4SetLastBlockNum (
IN LIST_ENTRY *Head,
IN UINT16 Last
);
/**
Remove the block number from the block range list.
@param Head The block range list to remove from
@param Num The block number to remove
@param Completed Whether Num is the last block number.
@param BlockCounter The continuous block counter instead of the value after roll-over.
@retval EFI_NOT_FOUND The block number isn't in the block range list
@retval EFI_SUCCESS The block number has been removed from the list
@retval EFI_OUT_OF_RESOURCES Failed to allocate resource
**/
EFI_STATUS
Mtftp4RemoveBlockNum (
IN LIST_ENTRY *Head,
IN UINT16 Num,
IN BOOLEAN Completed,
OUT UINT64 *BlockCounter
);
/**
Set the timeout for the instance. User a longer time for passive instances.
@param Instance The Mtftp session to set time out
**/
VOID
Mtftp4SetTimeout (
IN OUT MTFTP4_PROTOCOL *Instance
);
/**
Send the packet for the instance.
It will first save a reference to the packet for later retransmission.
Then determine the destination port, listen port for requests, and connected
port for others. At last, send the packet out.
@param Instance The Mtftp instance
@param Packet The packet to send
@retval EFI_SUCCESS The packet is sent out
@retval Others Failed to transmit the packet.
**/
EFI_STATUS
Mtftp4SendPacket (
IN OUT MTFTP4_PROTOCOL *Instance,
IN OUT NET_BUF *Packet
);
/**
Build then transmit the request packet for the MTFTP session.
@param Instance The Mtftp session
@retval EFI_OUT_OF_RESOURCES Failed to allocate memory for the request
@retval EFI_SUCCESS The request is built and sent
@retval Others Failed to transmit the packet.
**/
EFI_STATUS
Mtftp4SendRequest (
IN MTFTP4_PROTOCOL *Instance
);
/**
Build then send an error message.
@param Instance The MTFTP session
@param ErrCode The error code
@param ErrInfo The error message
@retval EFI_OUT_OF_RESOURCES Failed to allocate memory for the error packet
@retval EFI_SUCCESS The error packet is transmitted.
@retval Others Failed to transmit the packet.
**/
EFI_STATUS
Mtftp4SendError (
IN MTFTP4_PROTOCOL *Instance,
IN UINT16 ErrCode,
IN UINT8 *ErrInfo
);
/**
The timer ticking function in TPL_NOTIFY level for the Mtftp service instance.
@param Event The ticking event
@param Context The Mtftp service instance
**/
VOID
EFIAPI
Mtftp4OnTimerTickNotifyLevel (
IN EFI_EVENT Event,
IN VOID *Context
);
/**
The timer ticking function for the Mtftp service instance.
@param Event The ticking event
@param Context The Mtftp service instance
**/
VOID
EFIAPI
Mtftp4OnTimerTick (
IN EFI_EVENT Event,
IN VOID *Context
);
#endif

View File

@@ -0,0 +1,529 @@
/** @file
Routines to process Wrq (upload).
Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.<BR>
SPDX-License-Identifier: BSD-2-Clause-Patent
**/
#include "Mtftp4Impl.h"
/**
Build then send a MTFTP data packet for the MTFTP upload session.
@param Instance The MTFTP upload session.
@param BlockNum The block number to send.
@retval EFI_OUT_OF_RESOURCES Failed to build the packet.
@retval EFI_ABORTED The consumer of this child directs to abort the
transmission by return an error through PacketNeeded.
@retval EFI_SUCCESS The data is sent.
**/
EFI_STATUS
Mtftp4WrqSendBlock (
IN OUT MTFTP4_PROTOCOL *Instance,
IN UINT16 BlockNum
)
{
EFI_MTFTP4_PACKET *Packet;
EFI_MTFTP4_TOKEN *Token;
NET_BUF *UdpPacket;
EFI_STATUS Status;
UINT16 DataLen;
UINT8 *DataBuf;
UINT64 Start;
//
// Allocate a buffer to hold the user data
//
UdpPacket = NetbufAlloc (Instance->BlkSize + MTFTP4_DATA_HEAD_LEN);
if (UdpPacket == NULL) {
return EFI_OUT_OF_RESOURCES;
}
Packet = (EFI_MTFTP4_PACKET *) NetbufAllocSpace (UdpPacket, MTFTP4_DATA_HEAD_LEN, FALSE);
ASSERT (Packet != NULL);
Packet->Data.OpCode = HTONS (EFI_MTFTP4_OPCODE_DATA);
Packet->Data.Block = HTONS (BlockNum);
//
// Read the block from either the buffer or PacketNeeded callback
//
Token = Instance->Token;
DataLen = Instance->BlkSize;
if (Token->Buffer != NULL) {
Start = MultU64x32 (BlockNum - 1, Instance->BlkSize);
if (Token->BufferSize < Start + Instance->BlkSize) {
DataLen = (UINT16) (Token->BufferSize - Start);
Instance->LastBlock = BlockNum;
Mtftp4SetLastBlockNum (&Instance->Blocks, BlockNum);
}
if (DataLen > 0) {
NetbufAllocSpace (UdpPacket, DataLen, FALSE);
CopyMem (Packet->Data.Data, (UINT8 *) Token->Buffer + Start, DataLen);
}
} else {
//
// Get data from PacketNeeded
//
DataBuf = NULL;
Status = Token->PacketNeeded (
&Instance->Mtftp4,
Token,
&DataLen,
(VOID **) &DataBuf
);
if (EFI_ERROR (Status) || (DataLen > Instance->BlkSize)) {
if (DataBuf != NULL) {
FreePool (DataBuf);
}
if (UdpPacket != NULL) {
NetbufFree (UdpPacket);
}
Mtftp4SendError (
Instance,
EFI_MTFTP4_ERRORCODE_REQUEST_DENIED,
(UINT8 *) "User aborted the transfer"
);
return EFI_ABORTED;
}
if (DataLen < Instance->BlkSize) {
Instance->LastBlock = BlockNum;
Mtftp4SetLastBlockNum (&Instance->Blocks, BlockNum);
}
if (DataLen > 0) {
NetbufAllocSpace (UdpPacket, DataLen, FALSE);
CopyMem (Packet->Data.Data, DataBuf, DataLen);
FreePool (DataBuf);
}
}
return Mtftp4SendPacket (Instance, UdpPacket);
}
/**
Function to handle received ACK packet.
If the ACK number matches the expected block number, and there are more
data pending, send the next block. Otherwise tell the caller that we are done.
@param Instance The MTFTP upload session
@param Packet The MTFTP packet received
@param Len The packet length
@param Completed Return whether the upload has finished.
@retval EFI_SUCCESS The ACK is successfully processed.
@retval EFI_TFTP_ERROR The block number loops back.
@retval Others Failed to transmit the next data packet.
**/
EFI_STATUS
Mtftp4WrqHandleAck (
IN MTFTP4_PROTOCOL *Instance,
IN EFI_MTFTP4_PACKET *Packet,
IN UINT32 Len,
OUT BOOLEAN *Completed
)
{
UINT16 AckNum;
INTN Expected;
UINT64 BlockCounter;
*Completed = FALSE;
AckNum = NTOHS (Packet->Ack.Block[0]);
Expected = Mtftp4GetNextBlockNum (&Instance->Blocks);
ASSERT (Expected >= 0);
//
// Get an unwanted ACK, return EFI_SUCCESS to let Mtftp4WrqInput
// restart receive.
//
if (Expected != AckNum) {
return EFI_SUCCESS;
}
//
// Remove the acked block number, if this is the last block number,
// tell the Mtftp4WrqInput to finish the transfer. This is the last
// block number if the block range are empty.
//
Mtftp4RemoveBlockNum (&Instance->Blocks, AckNum, *Completed, &BlockCounter);
Expected = Mtftp4GetNextBlockNum (&Instance->Blocks);
if (Expected < 0) {
//
// The block range is empty. It may either because the the last
// block has been ACKed, or the sequence number just looped back,
// that is, there is more than 0xffff blocks.
//
if (Instance->LastBlock == AckNum) {
ASSERT (Instance->LastBlock >= 1);
*Completed = TRUE;
return EFI_SUCCESS;
} else {
Mtftp4SendError (
Instance,
EFI_MTFTP4_ERRORCODE_REQUEST_DENIED,
(UINT8 *) "Block number rolls back, not supported, try blksize option"
);
return EFI_TFTP_ERROR;
}
}
return Mtftp4WrqSendBlock (Instance, (UINT16) Expected);
}
/**
Check whether the received OACK is valid.
The OACK is valid only if:
1. It only include options requested by us
2. It can only include a smaller block size
3. It can't change the proposed time out value.
4. Other requirements of the individal MTFTP options as required.
@param Reply The options included in the OACK
@param Request The options we requested
@retval TRUE The options included in OACK is valid.
@retval FALSE The options included in OACK is invalid.
**/
BOOLEAN
Mtftp4WrqOackValid (
IN MTFTP4_OPTION *Reply,
IN MTFTP4_OPTION *Request
)
{
//
// It is invalid for server to return options we don't request
//
if ((Reply->Exist & ~Request->Exist) != 0) {
return FALSE;
}
//
// Server can only specify a smaller block size to be used and
// return the timeout matches that requested.
//
if ((((Reply->Exist & MTFTP4_BLKSIZE_EXIST) != 0) && (Reply->BlkSize > Request->BlkSize)) ||
(((Reply->Exist & MTFTP4_TIMEOUT_EXIST) != 0) && (Reply->Timeout != Request->Timeout))) {
return FALSE;
}
return TRUE;
}
/**
Function to handle the MTFTP OACK packet.
It parses the packet's options, and update the internal states of the session.
@param Instance The MTFTP session
@param Packet The received OACK packet
@param Len The length of the packet
@param Completed Whether the transmisson has completed. NOT used by
this function.
@retval EFI_SUCCESS The OACK process is OK
@retval EFI_TFTP_ERROR Some error occured, and the session reset.
**/
EFI_STATUS
Mtftp4WrqHandleOack (
IN OUT MTFTP4_PROTOCOL *Instance,
IN EFI_MTFTP4_PACKET *Packet,
IN UINT32 Len,
OUT BOOLEAN *Completed
)
{
MTFTP4_OPTION Reply;
EFI_MTFTP4_PACKET Bogus;
EFI_STATUS Status;
INTN Expected;
*Completed = FALSE;
//
// Ignore the OACK if already started the upload
//
Expected = Mtftp4GetNextBlockNum (&Instance->Blocks);
if (Expected != 0) {
return EFI_SUCCESS;
}
//
// Parse and validate the options from server
//
ZeroMem (&Reply, sizeof (MTFTP4_OPTION));
Status = Mtftp4ParseOptionOack (Packet, Len, Instance->Operation, &Reply);
if (EFI_ERROR (Status) || !Mtftp4WrqOackValid (&Reply, &Instance->RequestOption)) {
//
// Don't send a MTFTP error packet when out of resource, it can
// only make it worse.
//
if (Status != EFI_OUT_OF_RESOURCES) {
Mtftp4SendError (
Instance,
EFI_MTFTP4_ERRORCODE_ILLEGAL_OPERATION,
(UINT8 *) "Mal-formated OACK packet"
);
}
return EFI_TFTP_ERROR;
}
if (Reply.BlkSize != 0) {
Instance->BlkSize = Reply.BlkSize;
}
if (Reply.Timeout != 0) {
Instance->Timeout = Reply.Timeout;
}
//
// Build a bogus ACK0 packet then pass it to the Mtftp4WrqHandleAck,
// which will start the transmission of the first data block.
//
Bogus.Ack.OpCode = HTONS (EFI_MTFTP4_OPCODE_ACK);
Bogus.Ack.Block[0] = 0;
Status = Mtftp4WrqHandleAck (
Instance,
&Bogus,
sizeof (EFI_MTFTP4_ACK_HEADER),
Completed
);
return Status;
}
/**
The input process routine for MTFTP upload.
@param UdpPacket The received MTFTP packet.
@param EndPoint The local/remote access point
@param IoStatus The result of the packet receiving
@param Context Opaque parameter for the callback, which is the
MTFTP session.
**/
VOID
EFIAPI
Mtftp4WrqInput (
IN NET_BUF *UdpPacket,
IN UDP_END_POINT *EndPoint,
IN EFI_STATUS IoStatus,
IN VOID *Context
)
{
MTFTP4_PROTOCOL *Instance;
EFI_MTFTP4_PACKET *Packet;
BOOLEAN Completed;
EFI_STATUS Status;
UINT32 Len;
UINT16 Opcode;
Instance = (MTFTP4_PROTOCOL *) Context;
NET_CHECK_SIGNATURE (Instance, MTFTP4_PROTOCOL_SIGNATURE);
Completed = FALSE;
Packet = NULL;
Status = EFI_SUCCESS;
if (EFI_ERROR (IoStatus)) {
Status = IoStatus;
goto ON_EXIT;
}
ASSERT (UdpPacket != NULL);
if (UdpPacket->TotalSize < MTFTP4_OPCODE_LEN) {
goto ON_EXIT;
}
//
// Client send initial request to server's listening port. Server
// will select a UDP port to communicate with the client.
//
if (EndPoint->RemotePort != Instance->ConnectedPort) {
if (Instance->ConnectedPort != 0) {
goto ON_EXIT;
} else {
Instance->ConnectedPort = EndPoint->RemotePort;
}
}
//
// Copy the MTFTP packet to a continuous buffer if it isn't already so.
//
Len = UdpPacket->TotalSize;
if (UdpPacket->BlockOpNum > 1) {
Packet = AllocatePool (Len);
if (Packet == NULL) {
Status = EFI_OUT_OF_RESOURCES;
goto ON_EXIT;
}
NetbufCopy (UdpPacket, 0, Len, (UINT8 *) Packet);
} else {
Packet = (EFI_MTFTP4_PACKET *) NetbufGetByte (UdpPacket, 0, NULL);
ASSERT (Packet != NULL);
}
Opcode = NTOHS (Packet->OpCode);
//
// Call the user's CheckPacket if provided. Abort the transmission
// if CheckPacket returns an EFI_ERROR code.
//
if ((Instance->Token->CheckPacket != NULL) &&
((Opcode == EFI_MTFTP4_OPCODE_OACK) || (Opcode == EFI_MTFTP4_OPCODE_ERROR))) {
Status = Instance->Token->CheckPacket (
&Instance->Mtftp4,
Instance->Token,
(UINT16) Len,
Packet
);
if (EFI_ERROR (Status)) {
//
// Send an error message to the server to inform it
//
if (Opcode != EFI_MTFTP4_OPCODE_ERROR) {
Mtftp4SendError (
Instance,
EFI_MTFTP4_ERRORCODE_REQUEST_DENIED,
(UINT8 *) "User aborted the transfer"
);
}
Status = EFI_ABORTED;
goto ON_EXIT;
}
}
switch (Opcode) {
case EFI_MTFTP4_OPCODE_ACK:
if (Len != MTFTP4_OPCODE_LEN + MTFTP4_BLKNO_LEN) {
goto ON_EXIT;
}
Status = Mtftp4WrqHandleAck (Instance, Packet, Len, &Completed);
break;
case EFI_MTFTP4_OPCODE_OACK:
if (Len <= MTFTP4_OPCODE_LEN) {
goto ON_EXIT;
}
Status = Mtftp4WrqHandleOack (Instance, Packet, Len, &Completed);
break;
case EFI_MTFTP4_OPCODE_ERROR:
Status = EFI_TFTP_ERROR;
break;
default:
break;
}
ON_EXIT:
//
// Free the resources, then if !EFI_ERROR (Status) and not completed,
// restart the receive, otherwise end the session.
//
if ((Packet != NULL) && (UdpPacket->BlockOpNum > 1)) {
FreePool (Packet);
}
if (UdpPacket != NULL) {
NetbufFree (UdpPacket);
}
if (!EFI_ERROR (Status) && !Completed) {
Status = UdpIoRecvDatagram (Instance->UnicastPort, Mtftp4WrqInput, Instance, 0);
}
//
// Status may have been updated by UdpIoRecvDatagram
//
if (EFI_ERROR (Status) || Completed) {
Mtftp4CleanOperation (Instance, Status);
}
}
/**
Start the MTFTP session for upload.
It will first init some states, then send the WRQ request packet,
and start receiving the packet.
@param Instance The MTFTP session
@param Operation Redundant parameter, which is always
EFI_MTFTP4_OPCODE_WRQ here.
@retval EFI_SUCCESS The upload process has been started.
@retval Others Failed to start the upload.
**/
EFI_STATUS
Mtftp4WrqStart (
IN MTFTP4_PROTOCOL *Instance,
IN UINT16 Operation
)
{
EFI_STATUS Status;
//
// The valid block number range are [0, 0xffff]. For example:
// the client sends an WRQ request to the server, the server
// ACK with an ACK0 to let client start transfer the first
// packet.
//
Status = Mtftp4InitBlockRange (&Instance->Blocks, 0, 0xffff);
if (EFI_ERROR (Status)) {
return Status;
}
Status = Mtftp4SendRequest (Instance);
if (EFI_ERROR (Status)) {
return Status;
}
return UdpIoRecvDatagram (Instance->UnicastPort, Mtftp4WrqInput, Instance, 0);
}