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