/** @file | |
Driver Binding functions and Service Binding functions | |
implementation for Dhcp6 Driver. | |
Copyright (c) 2009 - 2018, Intel Corporation. All rights reserved.<BR> | |
SPDX-License-Identifier: BSD-2-Clause-Patent | |
**/ | |
#include "Dhcp6Impl.h" | |
EFI_DRIVER_BINDING_PROTOCOL gDhcp6DriverBinding = { | |
Dhcp6DriverBindingSupported, | |
Dhcp6DriverBindingStart, | |
Dhcp6DriverBindingStop, | |
0xa, | |
NULL, | |
NULL | |
}; | |
EFI_SERVICE_BINDING_PROTOCOL gDhcp6ServiceBindingTemplate = { | |
Dhcp6ServiceBindingCreateChild, | |
Dhcp6ServiceBindingDestroyChild | |
}; | |
/** | |
Configure the default Udp6Io to receive all the DHCP6 traffic | |
on this network interface. | |
@param[in] UdpIo The pointer to Udp6Io to be configured. | |
@param[in] Context The pointer to the context. | |
@retval EFI_SUCCESS The Udp6Io is successfully configured. | |
@retval Others Failed to configure the Udp6Io. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
Dhcp6ConfigureUdpIo ( | |
IN UDP_IO *UdpIo, | |
IN VOID *Context | |
) | |
{ | |
EFI_UDP6_PROTOCOL *Udp6; | |
EFI_UDP6_CONFIG_DATA *Config; | |
Udp6 = UdpIo->Protocol.Udp6; | |
Config = &(UdpIo->Config.Udp6); | |
ZeroMem (Config, sizeof (EFI_UDP6_CONFIG_DATA)); | |
// | |
// Set Udp6 configure data for the Dhcp6 instance. | |
// | |
Config->AcceptPromiscuous = FALSE; | |
Config->AcceptAnyPort = FALSE; | |
Config->AllowDuplicatePort = FALSE; | |
Config->TrafficClass = 0; | |
Config->HopLimit = 128; | |
Config->ReceiveTimeout = 0; | |
Config->TransmitTimeout = 0; | |
// | |
// Configure an endpoint of client(0, 546), server(0, 0), the addresses | |
// will be overridden later. Note that we MUST not limit RemotePort. | |
// More details, refer to RFC 3315 section 5.2. | |
// | |
Config->StationPort = DHCP6_PORT_CLIENT; | |
Config->RemotePort = 0; | |
return Udp6->Configure (Udp6, Config);; | |
} | |
/** | |
Destroy the Dhcp6 service. The Dhcp6 service may be partly initialized, | |
or partly destroyed. If a resource is destroyed, it is marked as such in | |
case the destroy failed and being called again later. | |
@param[in, out] Service The pointer to Dhcp6 service to be destroyed. | |
**/ | |
VOID | |
Dhcp6DestroyService ( | |
IN OUT DHCP6_SERVICE *Service | |
) | |
{ | |
// | |
// All children instances should have been already destroyed here. | |
// | |
ASSERT (Service->NumOfChild == 0); | |
if (Service->ClientId != NULL) { | |
FreePool (Service->ClientId); | |
} | |
if (Service->UdpIo != NULL) { | |
UdpIoFreeIo (Service->UdpIo); | |
} | |
FreePool (Service); | |
} | |
/** | |
Create a new Dhcp6 service for the Nic controller. | |
@param[in] Controller The controller to be installed DHCP6 service | |
binding protocol. | |
@param[in] ImageHandle The image handle of the Dhcp6 driver. | |
@param[out] Service The return pointer of the new Dhcp6 service. | |
@retval EFI_SUCCESS The Dhcp6 service is created successfully. | |
@retval EFI_DEVICE_ERROR An unexpected system or network error occurred. | |
@retval EFI_OUT_OF_RESOURCES Failed to allocate resource. | |
**/ | |
EFI_STATUS | |
Dhcp6CreateService ( | |
IN EFI_HANDLE Controller, | |
IN EFI_HANDLE ImageHandle, | |
OUT DHCP6_SERVICE **Service | |
) | |
{ | |
DHCP6_SERVICE *Dhcp6Srv; | |
EFI_STATUS Status; | |
*Service = NULL; | |
Dhcp6Srv = AllocateZeroPool (sizeof (DHCP6_SERVICE)); | |
if (Dhcp6Srv == NULL) { | |
return EFI_OUT_OF_RESOURCES; | |
} | |
// | |
// Open the SNP protocol to get mode data later. | |
// | |
Dhcp6Srv->Snp = NULL; | |
NetLibGetSnpHandle (Controller, &Dhcp6Srv->Snp); | |
if (Dhcp6Srv->Snp == NULL) { | |
FreePool (Dhcp6Srv); | |
return EFI_DEVICE_ERROR; | |
} | |
// | |
// Initialize the fields of the new Dhcp6 service. | |
// | |
Dhcp6Srv->Signature = DHCP6_SERVICE_SIGNATURE; | |
Dhcp6Srv->Controller = Controller; | |
Dhcp6Srv->Image = ImageHandle; | |
Dhcp6Srv->Xid = (0xffffff & NET_RANDOM (NetRandomInitSeed ())); | |
CopyMem ( | |
&Dhcp6Srv->ServiceBinding, | |
&gDhcp6ServiceBindingTemplate, | |
sizeof (EFI_SERVICE_BINDING_PROTOCOL) | |
); | |
// | |
// Locate Ip6->Ip6Config and store it for get IP6 Duplicate Address Detection transmits. | |
// | |
Status = gBS->HandleProtocol ( | |
Controller, | |
&gEfiIp6ConfigProtocolGuid, | |
(VOID **) &Dhcp6Srv->Ip6Cfg | |
); | |
if (EFI_ERROR (Status)) { | |
FreePool (Dhcp6Srv); | |
return Status; | |
} | |
// | |
// Generate client Duid: If SMBIOS system UUID is located, generate DUID in DUID-UUID format. | |
// Otherwise, in DUID-LLT format. | |
// | |
Dhcp6Srv->ClientId = Dhcp6GenerateClientId (Dhcp6Srv->Snp->Mode); | |
if (Dhcp6Srv->ClientId == NULL) { | |
FreePool (Dhcp6Srv); | |
return EFI_DEVICE_ERROR; | |
} | |
// | |
// Create an Udp6Io for stateful transmit/receive of each Dhcp6 instance. | |
// | |
Dhcp6Srv->UdpIo = UdpIoCreateIo ( | |
Controller, | |
ImageHandle, | |
Dhcp6ConfigureUdpIo, | |
UDP_IO_UDP6_VERSION, | |
NULL | |
); | |
if (Dhcp6Srv->UdpIo == NULL) { | |
FreePool (Dhcp6Srv->ClientId); | |
FreePool (Dhcp6Srv); | |
return EFI_DEVICE_ERROR; | |
} | |
InitializeListHead (&Dhcp6Srv->Child); | |
*Service = Dhcp6Srv; | |
return EFI_SUCCESS; | |
} | |
/** | |
Destroy the Dhcp6 instance and recycle the resources. | |
@param[in, out] Instance The pointer to the Dhcp6 instance. | |
**/ | |
VOID | |
Dhcp6DestroyInstance ( | |
IN OUT DHCP6_INSTANCE *Instance | |
) | |
{ | |
// | |
// Clean up the retry list first. | |
// | |
Dhcp6CleanupRetry (Instance, DHCP6_PACKET_ALL); | |
gBS->CloseEvent (Instance->Timer); | |
// | |
// Clean up the current configure data. | |
// | |
if (Instance->Config != NULL) { | |
Dhcp6CleanupConfigData (Instance->Config); | |
FreePool (Instance->Config); | |
} | |
// | |
// Clean up the current Ia. | |
// | |
if (Instance->IaCb.Ia != NULL) { | |
if (Instance->IaCb.Ia->ReplyPacket != NULL) { | |
FreePool (Instance->IaCb.Ia->ReplyPacket); | |
} | |
FreePool (Instance->IaCb.Ia); | |
} | |
if (Instance->Unicast != NULL) { | |
FreePool (Instance->Unicast); | |
} | |
if (Instance->AdSelect != NULL) { | |
FreePool (Instance->AdSelect); | |
} | |
FreePool (Instance); | |
} | |
/** | |
Create the Dhcp6 instance and initialize it. | |
@param[in] Service The pointer to the Dhcp6 service. | |
@param[out] Instance The pointer to the Dhcp6 instance. | |
@retval EFI_SUCCESS The Dhcp6 instance is created. | |
@retval EFI_OUT_OF_RESOURCES Failed to allocate resources. | |
**/ | |
EFI_STATUS | |
Dhcp6CreateInstance ( | |
IN DHCP6_SERVICE *Service, | |
OUT DHCP6_INSTANCE **Instance | |
) | |
{ | |
EFI_STATUS Status; | |
DHCP6_INSTANCE *Dhcp6Ins; | |
*Instance = NULL; | |
Dhcp6Ins = AllocateZeroPool (sizeof (DHCP6_INSTANCE)); | |
if (Dhcp6Ins == NULL) { | |
return EFI_OUT_OF_RESOURCES; | |
} | |
// | |
// Initialize the fields of the new Dhcp6 instance. | |
// | |
Dhcp6Ins->Signature = DHCP6_INSTANCE_SIGNATURE; | |
Dhcp6Ins->UdpSts = EFI_ALREADY_STARTED; | |
Dhcp6Ins->Service = Service; | |
Dhcp6Ins->InDestroy = FALSE; | |
Dhcp6Ins->MediaPresent = TRUE; | |
CopyMem ( | |
&Dhcp6Ins->Dhcp6, | |
&gDhcp6ProtocolTemplate, | |
sizeof (EFI_DHCP6_PROTOCOL) | |
); | |
InitializeListHead (&Dhcp6Ins->TxList); | |
InitializeListHead (&Dhcp6Ins->InfList); | |
// | |
// There is a timer for each Dhcp6 instance, which is used to track the | |
// lease time of Ia and the retransmission time of all sent packets. | |
// | |
Status = gBS->CreateEvent ( | |
EVT_NOTIFY_SIGNAL | EVT_TIMER, | |
TPL_CALLBACK, | |
Dhcp6OnTimerTick, | |
Dhcp6Ins, | |
&Dhcp6Ins->Timer | |
); | |
if (EFI_ERROR (Status)) { | |
FreePool (Dhcp6Ins); | |
return Status; | |
} | |
*Instance = Dhcp6Ins; | |
return EFI_SUCCESS; | |
} | |
/** | |
Callback function which provided by user to remove one node in NetDestroyLinkList process. | |
@param[in] Entry The entry to be removed. | |
@param[in] Context Pointer to the callback context corresponds to the Context in NetDestroyLinkList. | |
@retval EFI_SUCCESS The entry has been removed successfully. | |
@retval Others Fail to remove the entry. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
Dhcp6DestroyChildEntry ( | |
IN LIST_ENTRY *Entry, | |
IN VOID *Context | |
) | |
{ | |
DHCP6_INSTANCE *Instance; | |
EFI_SERVICE_BINDING_PROTOCOL *ServiceBinding; | |
if (Entry == NULL || Context == NULL) { | |
return EFI_INVALID_PARAMETER; | |
} | |
Instance = NET_LIST_USER_STRUCT_S (Entry, DHCP6_INSTANCE, Link, DHCP6_INSTANCE_SIGNATURE); | |
ServiceBinding = (EFI_SERVICE_BINDING_PROTOCOL *) Context; | |
return ServiceBinding->DestroyChild (ServiceBinding, Instance->Handle); | |
} | |
/** | |
Entry point of the DHCP6 driver to install various protocols. | |
@param[in] ImageHandle The handle of the UEFI image file. | |
@param[in] SystemTable The pointer to the EFI System Table. | |
@retval EFI_SUCCESS The operation completed successfully. | |
@retval Others Unexpected error occurs. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
Dhcp6DriverEntryPoint ( | |
IN EFI_HANDLE ImageHandle, | |
IN EFI_SYSTEM_TABLE *SystemTable | |
) | |
{ | |
return EfiLibInstallDriverBindingComponentName2 ( | |
ImageHandle, | |
SystemTable, | |
&gDhcp6DriverBinding, | |
ImageHandle, | |
&gDhcp6ComponentName, | |
&gDhcp6ComponentName2 | |
); | |
} | |
/** | |
Test to see if this driver supports ControllerHandle. This service | |
is called by the EFI boot service ConnectController(). In | |
order to make drivers as small as possible, there are a few calling | |
restrictions for this service. ConnectController() must | |
follow these calling restrictions. If any other agent wishes to call | |
Supported() it must also follow these calling restrictions. | |
@param[in] This The pointer to the driver binding protocol. | |
@param[in] ControllerHandle The handle of device to be tested. | |
@param[in] RemainingDevicePath Optional parameter use to pick a specific child | |
device to be started. | |
@retval EFI_SUCCESS This driver supports this device. | |
@retval Others This driver does not support this device. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
Dhcp6DriverBindingSupported ( | |
IN EFI_DRIVER_BINDING_PROTOCOL *This, | |
IN EFI_HANDLE ControllerHandle, | |
IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL | |
) | |
{ | |
return gBS->OpenProtocol ( | |
ControllerHandle, | |
&gEfiUdp6ServiceBindingProtocolGuid, | |
NULL, | |
This->DriverBindingHandle, | |
ControllerHandle, | |
EFI_OPEN_PROTOCOL_TEST_PROTOCOL | |
); | |
} | |
/** | |
Start this driver on ControllerHandle. This service is called by the | |
EFI boot service ConnectController(). In order to make | |
drivers as small as possible, there are a few calling restrictions for | |
this service. ConnectController() must follow these | |
calling restrictions. If any other agent wishes to call Start() it | |
must also follow these calling restrictions. | |
@param[in] This The pointer to the driver binding protocol. | |
@param[in] ControllerHandle The handle of device to be started. | |
@param[in] RemainingDevicePath Optional parameter use to pick a specific child | |
device to be started. | |
@retval EFI_SUCCESS This driver is installed to ControllerHandle. | |
@retval EFI_ALREADY_STARTED This driver is already running on ControllerHandle. | |
@retval other This driver does not support this device. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
Dhcp6DriverBindingStart ( | |
IN EFI_DRIVER_BINDING_PROTOCOL *This, | |
IN EFI_HANDLE ControllerHandle, | |
IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL | |
) | |
{ | |
EFI_STATUS Status; | |
DHCP6_SERVICE *Service; | |
// | |
// Check the Dhcp6 service whether already started. | |
// | |
Status = gBS->OpenProtocol ( | |
ControllerHandle, | |
&gEfiDhcp6ServiceBindingProtocolGuid, | |
NULL, | |
This->DriverBindingHandle, | |
ControllerHandle, | |
EFI_OPEN_PROTOCOL_TEST_PROTOCOL | |
); | |
if (!EFI_ERROR (Status)) { | |
return EFI_ALREADY_STARTED; | |
} | |
// | |
// Create and initialize the Dhcp6 service. | |
// | |
Status = Dhcp6CreateService ( | |
ControllerHandle, | |
This->DriverBindingHandle, | |
&Service | |
); | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
ASSERT (Service != NULL); | |
Status = gBS->InstallMultipleProtocolInterfaces ( | |
&ControllerHandle, | |
&gEfiDhcp6ServiceBindingProtocolGuid, | |
&Service->ServiceBinding, | |
NULL | |
); | |
if (EFI_ERROR (Status)) { | |
Dhcp6DestroyService (Service); | |
return Status; | |
} | |
return EFI_SUCCESS; | |
} | |
/** | |
Stop this driver on ControllerHandle. This service is called by the | |
EFI boot service DisconnectController(). In order to | |
make drivers as small as possible, there are a few calling | |
restrictions for this service. DisconnectController() | |
must follow these calling restrictions. If any other agent wishes | |
to call Stop() it must also follow these calling restrictions. | |
@param[in] This Protocol instance pointer. | |
@param[in] ControllerHandle Handle of device to stop driver on | |
@param[in] NumberOfChildren Number of Handles in ChildHandleBuffer. If number of | |
children is zero stop the entire bus driver. | |
@param[in] ChildHandleBuffer List of Child Handles to Stop. | |
@retval EFI_SUCCESS This driver is removed ControllerHandle | |
@retval EFI_DEVICE_ERROR An unexpected system or network error occurred. | |
@retval other This driver was not removed from this device | |
**/ | |
EFI_STATUS | |
EFIAPI | |
Dhcp6DriverBindingStop ( | |
IN EFI_DRIVER_BINDING_PROTOCOL *This, | |
IN EFI_HANDLE ControllerHandle, | |
IN UINTN NumberOfChildren, | |
IN EFI_HANDLE *ChildHandleBuffer OPTIONAL | |
) | |
{ | |
EFI_STATUS Status; | |
EFI_HANDLE NicHandle; | |
EFI_SERVICE_BINDING_PROTOCOL *ServiceBinding; | |
DHCP6_SERVICE *Service; | |
LIST_ENTRY *List; | |
UINTN ListLength; | |
// | |
// Find and check the Nic handle by the controller handle. | |
// | |
NicHandle = NetLibGetNicHandle (ControllerHandle, &gEfiUdp6ProtocolGuid); | |
if (NicHandle == NULL) { | |
return EFI_SUCCESS; | |
} | |
Status = gBS->OpenProtocol ( | |
NicHandle, | |
&gEfiDhcp6ServiceBindingProtocolGuid, | |
(VOID **) &ServiceBinding, | |
This->DriverBindingHandle, | |
NicHandle, | |
EFI_OPEN_PROTOCOL_GET_PROTOCOL | |
); | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
Service = DHCP6_SERVICE_FROM_THIS (ServiceBinding); | |
if (!IsListEmpty (&Service->Child)) { | |
// | |
// Destroy all the children instances before destroy the service. | |
// | |
List = &Service->Child; | |
Status = NetDestroyLinkList ( | |
List, | |
Dhcp6DestroyChildEntry, | |
ServiceBinding, | |
&ListLength | |
); | |
if (EFI_ERROR (Status) || ListLength != 0) { | |
Status = EFI_DEVICE_ERROR; | |
} | |
} | |
if (NumberOfChildren == 0 && !IsListEmpty (&Service->Child)) { | |
Status = EFI_DEVICE_ERROR; | |
} | |
if (NumberOfChildren == 0 && IsListEmpty (&Service->Child)) { | |
// | |
// Destroy the service itself if no child instance left. | |
// | |
Status = gBS->UninstallProtocolInterface ( | |
NicHandle, | |
&gEfiDhcp6ServiceBindingProtocolGuid, | |
ServiceBinding | |
); | |
if (EFI_ERROR (Status)) { | |
goto ON_EXIT; | |
} | |
Dhcp6DestroyService (Service); | |
Status = EFI_SUCCESS; | |
} | |
ON_EXIT: | |
return Status; | |
} | |
/** | |
Creates a child handle and installs a protocol. | |
The CreateChild() function installs a protocol on ChildHandle. | |
If ChildHandle is a pointer to NULL, then a new handle is created and returned in ChildHandle. | |
If ChildHandle is not a pointer to NULL, then the protocol installs on the existing ChildHandle. | |
@param[in] This Pointer to the EFI_SERVICE_BINDING_PROTOCOL instance. | |
@param[in, out] ChildHandle Pointer to the handle of the child to create. If it is NULL, | |
then a new handle is created. If it is a pointer to an existing | |
UEFI handle, then the protocol is added to the existing UEFI handle. | |
@retval EFI_SUCCESS The protocol was added to ChildHandle. | |
@retval EFI_INVALID_PARAMETER ChildHandle is NULL. | |
@retval other The child handle was not created. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
Dhcp6ServiceBindingCreateChild ( | |
IN EFI_SERVICE_BINDING_PROTOCOL *This, | |
IN OUT EFI_HANDLE *ChildHandle | |
) | |
{ | |
EFI_STATUS Status; | |
EFI_TPL OldTpl; | |
DHCP6_SERVICE *Service; | |
DHCP6_INSTANCE *Instance; | |
VOID *Udp6; | |
if (This == NULL || ChildHandle == NULL) { | |
return EFI_INVALID_PARAMETER; | |
} | |
Service = DHCP6_SERVICE_FROM_THIS (This); | |
Status = Dhcp6CreateInstance (Service, &Instance); | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
ASSERT (Instance != NULL); | |
// | |
// Start the timer when the instance is ready to use. | |
// | |
Status = gBS->SetTimer ( | |
Instance->Timer, | |
TimerPeriodic, | |
TICKS_PER_SECOND | |
); | |
if (EFI_ERROR (Status)) { | |
goto ON_ERROR; | |
} | |
// | |
// Install the DHCP6 protocol onto ChildHandle. | |
// | |
Status = gBS->InstallMultipleProtocolInterfaces ( | |
ChildHandle, | |
&gEfiDhcp6ProtocolGuid, | |
&Instance->Dhcp6, | |
NULL | |
); | |
if (EFI_ERROR (Status)) { | |
goto ON_ERROR; | |
} | |
Instance->Handle = *ChildHandle; | |
// | |
// Open the UDP6 protocol BY_CHILD. | |
// | |
Status = gBS->OpenProtocol ( | |
Service->UdpIo->UdpHandle, | |
&gEfiUdp6ProtocolGuid, | |
(VOID **) &Udp6, | |
gDhcp6DriverBinding.DriverBindingHandle, | |
Instance->Handle, | |
EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER | |
); | |
if (EFI_ERROR (Status)) { | |
gBS->UninstallMultipleProtocolInterfaces ( | |
Instance->Handle, | |
&gEfiDhcp6ProtocolGuid, | |
&Instance->Dhcp6, | |
NULL | |
); | |
goto ON_ERROR; | |
} | |
// | |
// Add into the children list of its parent service. | |
// | |
OldTpl = gBS->RaiseTPL (TPL_CALLBACK); | |
InsertTailList (&Service->Child, &Instance->Link); | |
Service->NumOfChild++; | |
gBS->RestoreTPL (OldTpl); | |
return EFI_SUCCESS; | |
ON_ERROR: | |
Dhcp6DestroyInstance (Instance); | |
return Status; | |
} | |
/** | |
Destroys a child handle with a protocol installed on it. | |
The DestroyChild() function does the opposite of CreateChild(). It removes a protocol | |
that was installed by CreateChild() from ChildHandle. If the removed protocol is the | |
last protocol on ChildHandle, then ChildHandle is destroyed. | |
@param[in] This Pointer to the EFI_SERVICE_BINDING_PROTOCOL instance. | |
@param[in] ChildHandle Handle of the child to destroy | |
@retval EFI_SUCCESS The protocol was removed from ChildHandle. | |
@retval EFI_UNSUPPORTED ChildHandle does not support the protocol that is being removed. | |
@retval EFI_INVALID_PARAMETER Child handle is NULL. | |
@retval EFI_ACCESS_DENIED The protocol could not be removed from the ChildHandle | |
because its services are being used. | |
@retval other The child handle was not destroyed | |
**/ | |
EFI_STATUS | |
EFIAPI | |
Dhcp6ServiceBindingDestroyChild ( | |
IN EFI_SERVICE_BINDING_PROTOCOL *This, | |
IN EFI_HANDLE ChildHandle | |
) | |
{ | |
EFI_STATUS Status; | |
EFI_TPL OldTpl; | |
EFI_DHCP6_PROTOCOL *Dhcp6; | |
DHCP6_SERVICE *Service; | |
DHCP6_INSTANCE *Instance; | |
if (This == NULL || ChildHandle == NULL) { | |
return EFI_INVALID_PARAMETER; | |
} | |
// | |
// Retrieve the private context data structures | |
// | |
Status = gBS->OpenProtocol ( | |
ChildHandle, | |
&gEfiDhcp6ProtocolGuid, | |
(VOID **) &Dhcp6, | |
gDhcp6DriverBinding.DriverBindingHandle, | |
ChildHandle, | |
EFI_OPEN_PROTOCOL_GET_PROTOCOL | |
); | |
if (EFI_ERROR (Status)) { | |
return EFI_UNSUPPORTED; | |
} | |
Instance = DHCP6_INSTANCE_FROM_THIS (Dhcp6); | |
Service = DHCP6_SERVICE_FROM_THIS (This); | |
if (Instance->Service != Service) { | |
return EFI_INVALID_PARAMETER; | |
} | |
if (Instance->InDestroy) { | |
return EFI_SUCCESS; | |
} | |
OldTpl = gBS->RaiseTPL (TPL_CALLBACK); | |
Instance->InDestroy = TRUE; | |
Status = gBS->CloseProtocol ( | |
Service->UdpIo->UdpHandle, | |
&gEfiUdp6ProtocolGuid, | |
gDhcp6DriverBinding.DriverBindingHandle, | |
ChildHandle | |
); | |
if (EFI_ERROR (Status)) { | |
Instance->InDestroy = FALSE; | |
gBS->RestoreTPL (OldTpl); | |
return Status; | |
} | |
// | |
// Uninstall the MTFTP6 protocol first to enable a top down destruction. | |
// | |
gBS->RestoreTPL (OldTpl); | |
Status = gBS->UninstallProtocolInterface ( | |
ChildHandle, | |
&gEfiDhcp6ProtocolGuid, | |
Dhcp6 | |
); | |
OldTpl = gBS->RaiseTPL (TPL_CALLBACK); | |
if (EFI_ERROR (Status)) { | |
Instance->InDestroy = FALSE; | |
gBS->RestoreTPL (OldTpl); | |
return Status; | |
} | |
// | |
// Remove it from the children list of its parent service. | |
// | |
RemoveEntryList (&Instance->Link); | |
Service->NumOfChild--; | |
gBS->RestoreTPL (OldTpl); | |
Dhcp6DestroyInstance (Instance); | |
return EFI_SUCCESS; | |
} |