summaryrefslogtreecommitdiffstats
path: root/NetworkPkg/Ip6Dxe/Ip6Driver.c
diff options
context:
space:
mode:
authorhhtian <hhtian@6f19259b-4bc3-4df7-8a09-765794883524>2010-11-01 06:13:54 +0000
committerhhtian <hhtian@6f19259b-4bc3-4df7-8a09-765794883524>2010-11-01 06:13:54 +0000
commita3bcde70e6dc69000f85cc5deee98101d2ae200a (patch)
tree693709a5293f80b320931693b34b2d6983cfcf4b /NetworkPkg/Ip6Dxe/Ip6Driver.c
parent12873d57666d0beff41959a1fb8f9062016f0983 (diff)
downloadedk2-a3bcde70e6dc69000f85cc5deee98101d2ae200a.tar.gz
edk2-a3bcde70e6dc69000f85cc5deee98101d2ae200a.tar.bz2
edk2-a3bcde70e6dc69000f85cc5deee98101d2ae200a.zip
Add NetworkPkg (P.UDK2010.UP3.Network.P1)
git-svn-id: https://edk2.svn.sourceforge.net/svnroot/edk2/trunk/edk2@10986 6f19259b-4bc3-4df7-8a09-765794883524
Diffstat (limited to 'NetworkPkg/Ip6Dxe/Ip6Driver.c')
-rw-r--r--NetworkPkg/Ip6Dxe/Ip6Driver.c930
1 files changed, 930 insertions, 0 deletions
diff --git a/NetworkPkg/Ip6Dxe/Ip6Driver.c b/NetworkPkg/Ip6Dxe/Ip6Driver.c
new file mode 100644
index 0000000000..388dadef8d
--- /dev/null
+++ b/NetworkPkg/Ip6Dxe/Ip6Driver.c
@@ -0,0 +1,930 @@
+/** @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;
+}