mirror of https://github.com/acidanthera/audk.git
931 lines
27 KiB
C
931 lines
27 KiB
C
/** @file
|
|
The driver binding and service binding protocol for IP6 driver.
|
|
|
|
Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>
|
|
|
|
This program and the accompanying materials
|
|
are licensed and made available under the terms and conditions of the BSD License
|
|
which accompanies this distribution. The full text of the license may be found at
|
|
http://opensource.org/licenses/bsd-license.php.
|
|
|
|
THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
|
|
WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
|
|
|
|
**/
|
|
|
|
#include "Ip6Impl.h"
|
|
|
|
EFI_DRIVER_BINDING_PROTOCOL gIp6DriverBinding = {
|
|
Ip6DriverBindingSupported,
|
|
Ip6DriverBindingStart,
|
|
Ip6DriverBindingStop,
|
|
0xa,
|
|
NULL,
|
|
NULL
|
|
};
|
|
|
|
/**
|
|
This is the declaration of an EFI image entry point. This entry point is
|
|
the same for UEFI Applications, UEFI OS Loaders, and UEFI Drivers including
|
|
both device drivers and bus drivers.
|
|
|
|
The entry point for IP6 driver which installs the driver
|
|
binding and component name protocol on its image.
|
|
|
|
@param[in] ImageHandle The firmware allocated handle for the UEFI image.
|
|
@param[in] SystemTable A pointer to the EFI System Table.
|
|
|
|
@retval EFI_SUCCESS The operation completed successfully.
|
|
@retval EFI_OUT_OF_RESOURCES The request could not be completed due to a lack of resources.
|
|
|
|
**/
|
|
EFI_STATUS
|
|
EFIAPI
|
|
Ip6DriverEntryPoint (
|
|
IN EFI_HANDLE ImageHandle,
|
|
IN EFI_SYSTEM_TABLE *SystemTable
|
|
)
|
|
{
|
|
return EfiLibInstallDriverBindingComponentName2 (
|
|
ImageHandle,
|
|
SystemTable,
|
|
&gIp6DriverBinding,
|
|
ImageHandle,
|
|
&gIp6ComponentName,
|
|
&gIp6ComponentName2
|
|
);
|
|
}
|
|
|
|
/**
|
|
Test to see if this driver supports ControllerHandle.
|
|
|
|
@param[in] This Protocol instance pointer.
|
|
@param[in] ControllerHandle Handle of device to test.
|
|
@param[in] RemainingDevicePath Optional parameter use to pick a specific child
|
|
device to start.
|
|
|
|
@retval EFI_SUCCESS This driver supports this device.
|
|
@retval EFI_ALREADY_STARTED This driver is already running on this device.
|
|
@retval other This driver does not support this device.
|
|
|
|
**/
|
|
EFI_STATUS
|
|
EFIAPI
|
|
Ip6DriverBindingSupported (
|
|
IN EFI_DRIVER_BINDING_PROTOCOL *This,
|
|
IN EFI_HANDLE ControllerHandle,
|
|
IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL
|
|
)
|
|
{
|
|
//
|
|
// Test for the MNP service binding Protocol
|
|
//
|
|
return gBS->OpenProtocol (
|
|
ControllerHandle,
|
|
&gEfiManagedNetworkServiceBindingProtocolGuid,
|
|
NULL,
|
|
This->DriverBindingHandle,
|
|
ControllerHandle,
|
|
EFI_OPEN_PROTOCOL_TEST_PROTOCOL
|
|
);
|
|
}
|
|
|
|
/**
|
|
Clean up an IP6 service binding instance. It releases all
|
|
the resource allocated by the instance. The instance may be
|
|
partly initialized, or partly destroyed. If a resource is
|
|
destroyed, it is marked as that in case the destory failed and
|
|
being called again later.
|
|
|
|
@param[in] IpSb The IP6 service binding instance to clean up.
|
|
|
|
@retval EFI_SUCCESS The resource used by the instance are cleaned up.
|
|
@retval Others Failed to clean up some of the resources.
|
|
|
|
**/
|
|
EFI_STATUS
|
|
Ip6CleanService (
|
|
IN IP6_SERVICE *IpSb
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
EFI_IPv6_ADDRESS AllNodes;
|
|
IP6_NEIGHBOR_ENTRY *NeighborCache;
|
|
|
|
Ip6ConfigCleanInstance (&IpSb->Ip6ConfigInstance);
|
|
|
|
//
|
|
// Leave link-scope all-nodes multicast address (FF02::1)
|
|
//
|
|
Ip6SetToAllNodeMulticast (FALSE, IP6_LINK_LOCAL_SCOPE, &AllNodes);
|
|
|
|
Status = Ip6LeaveGroup (IpSb, &AllNodes);
|
|
if (EFI_ERROR (Status)) {
|
|
return Status;
|
|
}
|
|
|
|
if (IpSb->DefaultInterface != NULL) {
|
|
Ip6CleanInterface (IpSb->DefaultInterface, NULL);
|
|
IpSb->DefaultInterface = NULL;
|
|
}
|
|
|
|
Ip6CleanDefaultRouterList (IpSb);
|
|
|
|
Ip6CleanPrefixListTable (IpSb, &IpSb->OnlinkPrefix);
|
|
Ip6CleanPrefixListTable (IpSb, &IpSb->AutonomousPrefix);
|
|
|
|
if (IpSb->RouteTable != NULL) {
|
|
Ip6CleanRouteTable (IpSb->RouteTable);
|
|
IpSb->RouteTable = NULL;
|
|
}
|
|
|
|
if (IpSb->InterfaceId != NULL) {
|
|
FreePool (IpSb->InterfaceId);
|
|
}
|
|
|
|
IpSb->InterfaceId = NULL;
|
|
|
|
Ip6CleanAssembleTable (&IpSb->Assemble);
|
|
|
|
if (IpSb->MnpChildHandle != NULL) {
|
|
if (IpSb->Mnp != NULL) {
|
|
IpSb->Mnp->Cancel (IpSb->Mnp, NULL);
|
|
IpSb->Mnp->Configure (IpSb->Mnp, NULL);
|
|
gBS->CloseProtocol (
|
|
IpSb->MnpChildHandle,
|
|
&gEfiManagedNetworkProtocolGuid,
|
|
IpSb->Image,
|
|
IpSb->Controller
|
|
);
|
|
|
|
IpSb->Mnp = NULL;
|
|
}
|
|
|
|
NetLibDestroyServiceChild (
|
|
IpSb->Controller,
|
|
IpSb->Image,
|
|
&gEfiManagedNetworkServiceBindingProtocolGuid,
|
|
IpSb->MnpChildHandle
|
|
);
|
|
|
|
IpSb->MnpChildHandle = NULL;
|
|
}
|
|
|
|
if (IpSb->RecvRequest.MnpToken.Event != NULL) {
|
|
gBS->CloseEvent (IpSb->RecvRequest.MnpToken.Event);
|
|
}
|
|
|
|
if (IpSb->Timer != NULL) {
|
|
gBS->SetTimer (IpSb->Timer, TimerCancel, 0);
|
|
gBS->CloseEvent (IpSb->Timer);
|
|
|
|
IpSb->Timer = NULL;
|
|
}
|
|
|
|
if (IpSb->FasterTimer != NULL) {
|
|
gBS->SetTimer (IpSb->FasterTimer, TimerCancel, 0);
|
|
gBS->CloseEvent (IpSb->FasterTimer);
|
|
|
|
IpSb->FasterTimer = NULL;
|
|
}
|
|
//
|
|
// Free the Neighbor Discovery resources
|
|
//
|
|
while (!IsListEmpty (&IpSb->NeighborTable)) {
|
|
NeighborCache = NET_LIST_HEAD (&IpSb->NeighborTable, IP6_NEIGHBOR_ENTRY, Link);
|
|
Ip6FreeNeighborEntry (IpSb, NeighborCache, FALSE, TRUE, EFI_SUCCESS, NULL, NULL);
|
|
}
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
Create a new IP6 driver service binding protocol.
|
|
|
|
@param[in] Controller The controller that has MNP service binding
|
|
installed.
|
|
@param[in] ImageHandle The IP6 driver's image handle.
|
|
@param[out] Service The variable to receive the newly created IP6
|
|
service.
|
|
|
|
@retval EFI_OUT_OF_RESOURCES Failed to allocate some resources.
|
|
@retval EFI_SUCCESS A new IP6 service binding private is created.
|
|
|
|
**/
|
|
EFI_STATUS
|
|
Ip6CreateService (
|
|
IN EFI_HANDLE Controller,
|
|
IN EFI_HANDLE ImageHandle,
|
|
OUT IP6_SERVICE **Service
|
|
)
|
|
{
|
|
IP6_SERVICE *IpSb;
|
|
EFI_STATUS Status;
|
|
EFI_MANAGED_NETWORK_COMPLETION_TOKEN *MnpToken;
|
|
EFI_MANAGED_NETWORK_CONFIG_DATA *Config;
|
|
IP6_CONFIG_DATA_ITEM *DataItem;
|
|
|
|
ASSERT (Service != NULL);
|
|
|
|
*Service = NULL;
|
|
|
|
//
|
|
// allocate a service private data then initialize all the filed to
|
|
// empty resources, so if any thing goes wrong when allocating
|
|
// resources, Ip6CleanService can be called to clean it up.
|
|
//
|
|
IpSb = AllocateZeroPool (sizeof (IP6_SERVICE));
|
|
|
|
if (IpSb == NULL) {
|
|
return EFI_OUT_OF_RESOURCES;
|
|
}
|
|
|
|
IpSb->Signature = IP6_SERVICE_SIGNATURE;
|
|
IpSb->ServiceBinding.CreateChild = Ip6ServiceBindingCreateChild;
|
|
IpSb->ServiceBinding.DestroyChild = Ip6ServiceBindingDestroyChild;
|
|
IpSb->State = IP6_SERVICE_UNSTARTED;
|
|
IpSb->InDestroy = FALSE;
|
|
|
|
IpSb->NumChildren = 0;
|
|
InitializeListHead (&IpSb->Children);
|
|
|
|
InitializeListHead (&IpSb->Interfaces);
|
|
IpSb->DefaultInterface = NULL;
|
|
IpSb->RouteTable = NULL;
|
|
|
|
IpSb->RecvRequest.Signature = IP6_LINK_RX_SIGNATURE;
|
|
IpSb->RecvRequest.CallBack = NULL;
|
|
IpSb->RecvRequest.Context = NULL;
|
|
MnpToken = &IpSb->RecvRequest.MnpToken;
|
|
MnpToken->Event = NULL;
|
|
MnpToken->Status = EFI_NOT_READY;
|
|
MnpToken->Packet.RxData = NULL;
|
|
|
|
Ip6CreateAssembleTable (&IpSb->Assemble);
|
|
|
|
IpSb->MldCtrl.Mldv1QuerySeen = 0;
|
|
InitializeListHead (&IpSb->MldCtrl.Groups);
|
|
|
|
ZeroMem (&IpSb->LinkLocalAddr, sizeof (EFI_IPv6_ADDRESS));
|
|
IpSb->LinkLocalOk = FALSE;
|
|
IpSb->LinkLocalDadFail = FALSE;
|
|
IpSb->Dhcp6NeedStart = FALSE;
|
|
IpSb->Dhcp6NeedInfoRequest = FALSE;
|
|
|
|
IpSb->CurHopLimit = IP6_HOP_LIMIT;
|
|
IpSb->LinkMTU = IP6_MIN_LINK_MTU;
|
|
IpSb->BaseReachableTime = IP6_REACHABLE_TIME;
|
|
Ip6UpdateReachableTime (IpSb);
|
|
//
|
|
// RFC4861 RETRANS_TIMER: 1,000 milliseconds
|
|
//
|
|
IpSb->RetransTimer = IP6_RETRANS_TIMER;
|
|
|
|
IpSb->RoundRobin = 0;
|
|
|
|
InitializeListHead (&IpSb->NeighborTable);
|
|
InitializeListHead (&IpSb->DefaultRouterList);
|
|
InitializeListHead (&IpSb->OnlinkPrefix);
|
|
InitializeListHead (&IpSb->AutonomousPrefix);
|
|
|
|
IpSb->InterfaceIdLen = IP6_IF_ID_LEN;
|
|
IpSb->InterfaceId = NULL;
|
|
|
|
IpSb->RouterAdvertiseReceived = FALSE;
|
|
IpSb->SolicitTimer = IP6_MAX_RTR_SOLICITATIONS;
|
|
IpSb->Ticks = 0;
|
|
|
|
IpSb->Image = ImageHandle;
|
|
IpSb->Controller = Controller;
|
|
|
|
IpSb->MnpChildHandle = NULL;
|
|
IpSb->Mnp = NULL;
|
|
|
|
Config = &IpSb->MnpConfigData;
|
|
Config->ReceivedQueueTimeoutValue = 0;
|
|
Config->TransmitQueueTimeoutValue = 0;
|
|
Config->ProtocolTypeFilter = IP6_ETHER_PROTO;
|
|
Config->EnableUnicastReceive = TRUE;
|
|
Config->EnableMulticastReceive = TRUE;
|
|
Config->EnableBroadcastReceive = TRUE;
|
|
Config->EnablePromiscuousReceive = FALSE;
|
|
Config->FlushQueuesOnReset = TRUE;
|
|
Config->EnableReceiveTimestamps = FALSE;
|
|
Config->DisableBackgroundPolling = FALSE;
|
|
|
|
ZeroMem (&IpSb->SnpMode, sizeof (EFI_SIMPLE_NETWORK_MODE));
|
|
|
|
IpSb->Timer = NULL;
|
|
IpSb->FasterTimer = NULL;
|
|
|
|
ZeroMem (&IpSb->Ip6ConfigInstance, sizeof (IP6_CONFIG_INSTANCE));
|
|
|
|
IpSb->MacString = NULL;
|
|
|
|
//
|
|
// Create various resources. First create the route table, timer
|
|
// event, MNP token event and MNP child.
|
|
//
|
|
|
|
IpSb->RouteTable = Ip6CreateRouteTable ();
|
|
if (IpSb->RouteTable == NULL) {
|
|
Status = EFI_OUT_OF_RESOURCES;
|
|
goto ON_ERROR;
|
|
}
|
|
|
|
Status = gBS->CreateEvent (
|
|
EVT_NOTIFY_SIGNAL | EVT_TIMER,
|
|
TPL_CALLBACK,
|
|
Ip6TimerTicking,
|
|
IpSb,
|
|
&IpSb->Timer
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
goto ON_ERROR;
|
|
}
|
|
|
|
Status = gBS->CreateEvent (
|
|
EVT_NOTIFY_SIGNAL | EVT_TIMER,
|
|
TPL_CALLBACK,
|
|
Ip6NdFasterTimerTicking,
|
|
IpSb,
|
|
&IpSb->FasterTimer
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
goto ON_ERROR;
|
|
}
|
|
|
|
Status = NetLibCreateServiceChild (
|
|
Controller,
|
|
ImageHandle,
|
|
&gEfiManagedNetworkServiceBindingProtocolGuid,
|
|
&IpSb->MnpChildHandle
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
goto ON_ERROR;
|
|
}
|
|
|
|
Status = gBS->OpenProtocol (
|
|
IpSb->MnpChildHandle,
|
|
&gEfiManagedNetworkProtocolGuid,
|
|
(VOID **) (&IpSb->Mnp),
|
|
ImageHandle,
|
|
Controller,
|
|
EFI_OPEN_PROTOCOL_BY_DRIVER
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
goto ON_ERROR;
|
|
}
|
|
|
|
Status = Ip6ServiceConfigMnp (IpSb, TRUE);
|
|
if (EFI_ERROR (Status)) {
|
|
goto ON_ERROR;
|
|
}
|
|
|
|
Status = IpSb->Mnp->GetModeData (IpSb->Mnp, NULL, &IpSb->SnpMode);
|
|
if (EFI_ERROR (Status)) {
|
|
goto ON_ERROR;
|
|
}
|
|
|
|
IpSb->MaxPacketSize = IP6_MIN_LINK_MTU - sizeof (EFI_IP6_HEADER);
|
|
if (NetLibGetVlanId (IpSb->Controller) != 0) {
|
|
//
|
|
// This is a VLAN device, reduce MTU by VLAN tag length
|
|
//
|
|
IpSb->MaxPacketSize -= NET_VLAN_TAG_LEN;
|
|
}
|
|
IpSb->OldMaxPacketSize = IpSb->MaxPacketSize;
|
|
|
|
//
|
|
// Currently only ETHERNET is supported in IPv6 stack, since
|
|
// link local address requires an IEEE 802 48-bit MACs for
|
|
// EUI-64 format interface identifier mapping.
|
|
//
|
|
if (IpSb->SnpMode.IfType != NET_IFTYPE_ETHERNET) {
|
|
Status = EFI_UNSUPPORTED;
|
|
goto ON_ERROR;
|
|
}
|
|
|
|
Status = Ip6InitMld (IpSb);
|
|
if (EFI_ERROR (Status)) {
|
|
goto ON_ERROR;
|
|
}
|
|
|
|
//
|
|
// The timer expires every 100 (IP6_TIMER_INTERVAL_IN_MS) milliseconds.
|
|
//
|
|
Status = gBS->SetTimer (IpSb->FasterTimer, TimerPeriodic, TICKS_PER_MS * IP6_TIMER_INTERVAL_IN_MS);
|
|
if (EFI_ERROR (Status)) {
|
|
goto ON_ERROR;
|
|
}
|
|
|
|
//
|
|
// The timer expires every 1000 (IP6_ONE_SECOND_IN_MS) milliseconds.
|
|
//
|
|
Status = gBS->SetTimer (IpSb->Timer, TimerPeriodic, TICKS_PER_MS * IP6_ONE_SECOND_IN_MS);
|
|
if (EFI_ERROR (Status)) {
|
|
goto ON_ERROR;
|
|
}
|
|
|
|
|
|
Status = gBS->CreateEvent (
|
|
EVT_NOTIFY_SIGNAL,
|
|
TPL_NOTIFY,
|
|
Ip6OnFrameReceived,
|
|
&IpSb->RecvRequest,
|
|
&MnpToken->Event
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
goto ON_ERROR;
|
|
}
|
|
|
|
Status = Ip6ReceiveFrame (Ip6AcceptFrame, IpSb);
|
|
if (EFI_ERROR (Status)) {
|
|
goto ON_ERROR;
|
|
}
|
|
|
|
Status = NetLibGetMacString (IpSb->Controller, IpSb->Image, &IpSb->MacString);
|
|
if (EFI_ERROR (Status)) {
|
|
goto ON_ERROR;
|
|
}
|
|
|
|
Status = Ip6ConfigInitInstance (&IpSb->Ip6ConfigInstance);
|
|
if (EFI_ERROR (Status)) {
|
|
goto ON_ERROR;
|
|
}
|
|
|
|
IpSb->DefaultInterface = Ip6CreateInterface (IpSb, TRUE);
|
|
if (IpSb->DefaultInterface == NULL) {
|
|
Status = EFI_DEVICE_ERROR;
|
|
goto ON_ERROR;
|
|
}
|
|
|
|
//
|
|
// If there is any manual address, set it.
|
|
//
|
|
DataItem = &IpSb->Ip6ConfigInstance.DataItem[Ip6ConfigDataTypeManualAddress];
|
|
if (DataItem->Data.Ptr != NULL) {
|
|
DataItem->SetData (
|
|
&IpSb->Ip6ConfigInstance,
|
|
DataItem->DataSize,
|
|
DataItem->Data.Ptr
|
|
);
|
|
}
|
|
|
|
InsertHeadList (&IpSb->Interfaces, &IpSb->DefaultInterface->Link);
|
|
|
|
*Service = IpSb;
|
|
return EFI_SUCCESS;
|
|
|
|
ON_ERROR:
|
|
Ip6CleanService (IpSb);
|
|
FreePool (IpSb);
|
|
return Status;
|
|
}
|
|
|
|
|
|
/**
|
|
Start this driver on ControllerHandle.
|
|
|
|
@param[in] This Protocol instance pointer.
|
|
@param[in] ControllerHandle Handle of device to bind driver to.
|
|
@param[in] RemainingDevicePath Optional parameter used to pick a specific child
|
|
device to start.
|
|
|
|
@retval EFI_SUCCES This driver is added 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
|
|
Ip6DriverBindingStart (
|
|
IN EFI_DRIVER_BINDING_PROTOCOL *This,
|
|
IN EFI_HANDLE ControllerHandle,
|
|
IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL
|
|
)
|
|
{
|
|
IP6_SERVICE *IpSb;
|
|
EFI_STATUS Status;
|
|
|
|
//
|
|
// Test for the Ip6 service binding protocol
|
|
//
|
|
Status = gBS->OpenProtocol (
|
|
ControllerHandle,
|
|
&gEfiIp6ServiceBindingProtocolGuid,
|
|
NULL,
|
|
This->DriverBindingHandle,
|
|
ControllerHandle,
|
|
EFI_OPEN_PROTOCOL_TEST_PROTOCOL
|
|
);
|
|
|
|
if (Status == EFI_SUCCESS) {
|
|
return EFI_ALREADY_STARTED;
|
|
}
|
|
|
|
Status = Ip6CreateService (ControllerHandle, This->DriverBindingHandle, &IpSb);
|
|
|
|
if (EFI_ERROR (Status)) {
|
|
return Status;
|
|
}
|
|
|
|
ASSERT (IpSb != NULL);
|
|
|
|
//
|
|
// Install the Ip6ServiceBinding Protocol onto ControlerHandle
|
|
//
|
|
Status = gBS->InstallMultipleProtocolInterfaces (
|
|
&ControllerHandle,
|
|
&gEfiIp6ServiceBindingProtocolGuid,
|
|
&IpSb->ServiceBinding,
|
|
&gEfiIp6ConfigProtocolGuid,
|
|
&IpSb->Ip6ConfigInstance.Ip6Config,
|
|
NULL
|
|
);
|
|
|
|
if (EFI_ERROR (Status)) {
|
|
|
|
Ip6CleanService (IpSb);
|
|
FreePool (IpSb);
|
|
} else {
|
|
//
|
|
// Initialize the IP6 ID
|
|
//
|
|
mIp6Id = NET_RANDOM (NetRandomInitSeed ());
|
|
|
|
Ip6SetVariableData (IpSb);
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
/**
|
|
Stop this driver on ControllerHandle.
|
|
|
|
@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 An array of child handles to be freed. May be NULL
|
|
if NumberOfChildren is 0.
|
|
|
|
@retval EFI_SUCCESS The device was stopped.
|
|
@retval EFI_DEVICE_ERROR The device could not be stopped due to a device error.
|
|
|
|
**/
|
|
EFI_STATUS
|
|
EFIAPI
|
|
Ip6DriverBindingStop (
|
|
IN EFI_DRIVER_BINDING_PROTOCOL *This,
|
|
IN EFI_HANDLE ControllerHandle,
|
|
IN UINTN NumberOfChildren,
|
|
IN EFI_HANDLE *ChildHandleBuffer OPTIONAL
|
|
)
|
|
{
|
|
EFI_SERVICE_BINDING_PROTOCOL *ServiceBinding;
|
|
IP6_SERVICE *IpSb;
|
|
IP6_PROTOCOL *IpInstance;
|
|
EFI_HANDLE NicHandle;
|
|
EFI_STATUS Status;
|
|
BOOLEAN IsDhcp6;
|
|
EFI_TPL OldTpl;
|
|
INTN State;
|
|
|
|
IsDhcp6 = FALSE;
|
|
NicHandle = NetLibGetNicHandle (ControllerHandle, &gEfiDhcp6ProtocolGuid);
|
|
|
|
if (NicHandle != NULL) {
|
|
//
|
|
// DriverBindingStop is triggered by the uninstallation of the EFI DHCPv6
|
|
// Protocol used by Ip6Config.
|
|
//
|
|
IsDhcp6 = TRUE;
|
|
} else {
|
|
|
|
NicHandle = NetLibGetNicHandle (ControllerHandle, &gEfiManagedNetworkProtocolGuid);
|
|
|
|
if (NicHandle == NULL) {
|
|
return EFI_DEVICE_ERROR;
|
|
}
|
|
}
|
|
|
|
Status = gBS->OpenProtocol (
|
|
NicHandle,
|
|
&gEfiIp6ServiceBindingProtocolGuid,
|
|
(VOID **) &ServiceBinding,
|
|
This->DriverBindingHandle,
|
|
NicHandle,
|
|
EFI_OPEN_PROTOCOL_GET_PROTOCOL
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
return EFI_DEVICE_ERROR;
|
|
}
|
|
|
|
IpSb = IP6_SERVICE_FROM_PROTOCOL (ServiceBinding);
|
|
|
|
OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
|
|
|
|
if (IpSb->InDestroy) {
|
|
Status = EFI_SUCCESS;
|
|
goto Exit;
|
|
}
|
|
|
|
if (IsDhcp6) {
|
|
|
|
Status = Ip6ConfigDestroyDhcp6 (&IpSb->Ip6ConfigInstance);
|
|
gBS->CloseEvent (IpSb->Ip6ConfigInstance.Dhcp6Event);
|
|
IpSb->Ip6ConfigInstance.Dhcp6Event = NULL;
|
|
} else if (NumberOfChildren == 0) {
|
|
|
|
IpSb->InDestroy = TRUE;
|
|
State = IpSb->State;
|
|
IpSb->State = IP6_SERVICE_DESTROY;
|
|
|
|
//
|
|
// Clear the variable data.
|
|
//
|
|
Ip6ClearVariableData (IpSb);
|
|
|
|
Status = Ip6CleanService (IpSb);
|
|
if (EFI_ERROR (Status)) {
|
|
IpSb->State = State;
|
|
goto Exit;
|
|
}
|
|
|
|
Status = gBS->UninstallMultipleProtocolInterfaces (
|
|
NicHandle,
|
|
&gEfiIp6ServiceBindingProtocolGuid,
|
|
ServiceBinding,
|
|
&gEfiIp6ConfigProtocolGuid,
|
|
&IpSb->Ip6ConfigInstance.Ip6Config,
|
|
NULL
|
|
);
|
|
ASSERT_EFI_ERROR (Status);
|
|
FreePool (IpSb);
|
|
} else {
|
|
//
|
|
// NumberOfChildren is not zero, destroy all IP6 children instances.
|
|
//
|
|
while (!IsListEmpty (&IpSb->Children)) {
|
|
IpInstance = NET_LIST_HEAD (&IpSb->Children, IP6_PROTOCOL, Link);
|
|
ServiceBinding->DestroyChild (ServiceBinding, IpInstance->Handle);
|
|
}
|
|
|
|
if (IpSb->NumChildren != 0) {
|
|
Status = EFI_DEVICE_ERROR;
|
|
}
|
|
}
|
|
|
|
Exit:
|
|
|
|
gBS->RestoreTPL (OldTpl);
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
/**
|
|
Creates a child handle with a set of I/O services.
|
|
|
|
@param[in] This Protocol instance pointer.
|
|
@param[in] ChildHandle Pointer to the handle of the child to create. If
|
|
it is NULL, then a new handle is created. If it
|
|
is not NULL, then the I/O services are added to
|
|
the existing child handle.
|
|
|
|
@retval EFI_SUCCES The child handle was created with the I/O services.
|
|
@retval EFI_OUT_OF_RESOURCES There are not enough resources availabe to create
|
|
the child.
|
|
@retval other The child handle was not created.
|
|
|
|
**/
|
|
EFI_STATUS
|
|
EFIAPI
|
|
Ip6ServiceBindingCreateChild (
|
|
IN EFI_SERVICE_BINDING_PROTOCOL *This,
|
|
IN EFI_HANDLE *ChildHandle
|
|
)
|
|
{
|
|
IP6_SERVICE *IpSb;
|
|
IP6_PROTOCOL *IpInstance;
|
|
EFI_TPL OldTpl;
|
|
EFI_STATUS Status;
|
|
VOID *Mnp;
|
|
|
|
if ((This == NULL) || (ChildHandle == NULL)) {
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
IpSb = IP6_SERVICE_FROM_PROTOCOL (This);
|
|
|
|
if (IpSb->LinkLocalDadFail) {
|
|
return EFI_DEVICE_ERROR;
|
|
}
|
|
|
|
IpInstance = AllocatePool (sizeof (IP6_PROTOCOL));
|
|
|
|
if (IpInstance == NULL) {
|
|
return EFI_OUT_OF_RESOURCES;
|
|
}
|
|
|
|
Ip6InitProtocol (IpSb, IpInstance);
|
|
|
|
//
|
|
// Install Ip6 onto ChildHandle
|
|
//
|
|
Status = gBS->InstallMultipleProtocolInterfaces (
|
|
ChildHandle,
|
|
&gEfiIp6ProtocolGuid,
|
|
&IpInstance->Ip6Proto,
|
|
NULL
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
goto ON_ERROR;
|
|
}
|
|
|
|
IpInstance->Handle = *ChildHandle;
|
|
|
|
//
|
|
// Open the Managed Network protocol BY_CHILD.
|
|
//
|
|
Status = gBS->OpenProtocol (
|
|
IpSb->MnpChildHandle,
|
|
&gEfiManagedNetworkProtocolGuid,
|
|
(VOID **) &Mnp,
|
|
gIp6DriverBinding.DriverBindingHandle,
|
|
IpInstance->Handle,
|
|
EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
gBS->UninstallMultipleProtocolInterfaces (
|
|
ChildHandle,
|
|
&gEfiIp6ProtocolGuid,
|
|
&IpInstance->Ip6Proto,
|
|
NULL
|
|
);
|
|
|
|
goto ON_ERROR;
|
|
}
|
|
|
|
//
|
|
// Insert it into the service binding instance.
|
|
//
|
|
OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
|
|
|
|
InsertTailList (&IpSb->Children, &IpInstance->Link);
|
|
IpSb->NumChildren++;
|
|
|
|
gBS->RestoreTPL (OldTpl);
|
|
|
|
ON_ERROR:
|
|
|
|
if (EFI_ERROR (Status)) {
|
|
|
|
Ip6CleanProtocol (IpInstance);
|
|
|
|
FreePool (IpInstance);
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
/**
|
|
Destroys a child handle with a set of I/O services.
|
|
|
|
@param[in] This Protocol instance pointer.
|
|
@param[in] ChildHandle Handle of the child to destroy.
|
|
|
|
@retval EFI_SUCCES The I/O services were removed from the child
|
|
handle.
|
|
@retval EFI_UNSUPPORTED The child handle does not support the I/O services
|
|
that are being removed.
|
|
@retval EFI_INVALID_PARAMETER Child handle is not a valid EFI Handle.
|
|
@retval EFI_ACCESS_DENIED The child handle could not be destroyed because
|
|
its I/O services are being used.
|
|
@retval other The child handle was not destroyed.
|
|
|
|
**/
|
|
EFI_STATUS
|
|
EFIAPI
|
|
Ip6ServiceBindingDestroyChild (
|
|
IN EFI_SERVICE_BINDING_PROTOCOL *This,
|
|
IN EFI_HANDLE ChildHandle
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
IP6_SERVICE *IpSb;
|
|
IP6_PROTOCOL *IpInstance;
|
|
EFI_IP6_PROTOCOL *Ip6;
|
|
EFI_TPL OldTpl;
|
|
INTN State;
|
|
|
|
if ((This == NULL) || (ChildHandle == NULL)) {
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
//
|
|
// Retrieve the private context data structures
|
|
//
|
|
IpSb = IP6_SERVICE_FROM_PROTOCOL (This);
|
|
|
|
Status = gBS->OpenProtocol (
|
|
ChildHandle,
|
|
&gEfiIp6ProtocolGuid,
|
|
(VOID **) &Ip6,
|
|
gIp6DriverBinding.DriverBindingHandle,
|
|
ChildHandle,
|
|
EFI_OPEN_PROTOCOL_GET_PROTOCOL
|
|
);
|
|
|
|
if (EFI_ERROR (Status)) {
|
|
return EFI_UNSUPPORTED;
|
|
}
|
|
|
|
IpInstance = IP6_INSTANCE_FROM_PROTOCOL (Ip6);
|
|
|
|
if (IpInstance->Service != IpSb) {
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
|
|
|
|
//
|
|
// A child can be destroyed more than once. For example,
|
|
// Ip6DriverBindingStop will destory all of its children.
|
|
// when UDP driver is being stopped, it will destory all
|
|
// the IP child it opens.
|
|
//
|
|
if (IpInstance->State == IP6_STATE_DESTROY) {
|
|
gBS->RestoreTPL (OldTpl);
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
State = IpInstance->State;
|
|
IpInstance->State = IP6_STATE_DESTROY;
|
|
|
|
//
|
|
// Close the Managed Network protocol.
|
|
//
|
|
gBS->CloseProtocol (
|
|
IpSb->MnpChildHandle,
|
|
&gEfiManagedNetworkProtocolGuid,
|
|
gIp6DriverBinding.DriverBindingHandle,
|
|
ChildHandle
|
|
);
|
|
|
|
//
|
|
// Uninstall the IP6 protocol first. Many thing happens during
|
|
// this:
|
|
// 1. The consumer of the IP6 protocol will be stopped if it
|
|
// opens the protocol BY_DRIVER. For eaxmple, if MNP driver is
|
|
// stopped, IP driver's stop function will be called, and uninstall
|
|
// EFI_IP6_PROTOCOL will trigger the UDP's stop function. This
|
|
// makes it possible to create the network stack bottom up, and
|
|
// stop it top down.
|
|
// 2. the upper layer will recycle the received packet. The recycle
|
|
// event's TPL is higher than this function. The recycle events
|
|
// will be called back before preceeding. If any packets not recycled,
|
|
// that means there is a resource leak.
|
|
//
|
|
Status = gBS->UninstallProtocolInterface (
|
|
ChildHandle,
|
|
&gEfiIp6ProtocolGuid,
|
|
&IpInstance->Ip6Proto
|
|
);
|
|
|
|
if (EFI_ERROR (Status)) {
|
|
goto ON_ERROR;
|
|
}
|
|
|
|
Status = Ip6CleanProtocol (IpInstance);
|
|
|
|
Ip6SetVariableData (IpSb);
|
|
|
|
if (EFI_ERROR (Status)) {
|
|
gBS->InstallMultipleProtocolInterfaces (
|
|
&ChildHandle,
|
|
&gEfiIp6ProtocolGuid,
|
|
Ip6,
|
|
NULL
|
|
);
|
|
|
|
goto ON_ERROR;
|
|
}
|
|
|
|
RemoveEntryList (&IpInstance->Link);
|
|
ASSERT (IpSb->NumChildren > 0);
|
|
IpSb->NumChildren--;
|
|
|
|
gBS->RestoreTPL (OldTpl);
|
|
|
|
FreePool (IpInstance);
|
|
return EFI_SUCCESS;
|
|
|
|
ON_ERROR:
|
|
IpInstance->State = State;
|
|
gBS->RestoreTPL (OldTpl);
|
|
|
|
return Status;
|
|
}
|