/** @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; }