1 /** @file
2   Driver Binding functions and Service Binding functions
3   implementationfor for Dhcp6 Driver.
4 
5   Copyright (c) 2009 - 2012, Intel Corporation. All rights reserved.<BR>
6 
7   This program and the accompanying materials
8   are licensed and made available under the terms and conditions of the BSD License
9   which accompanies this distribution.  The full text of the license may be found at
10   http://opensource.org/licenses/bsd-license.php.
11 
12   THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
13   WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
14 
15 **/
16 
17 #include "Dhcp6Impl.h"
18 
19 
20 EFI_DRIVER_BINDING_PROTOCOL gDhcp6DriverBinding = {
21   Dhcp6DriverBindingSupported,
22   Dhcp6DriverBindingStart,
23   Dhcp6DriverBindingStop,
24   0xa,
25   NULL,
26   NULL
27 };
28 
29 EFI_SERVICE_BINDING_PROTOCOL gDhcp6ServiceBindingTemplate = {
30   Dhcp6ServiceBindingCreateChild,
31   Dhcp6ServiceBindingDestroyChild
32 };
33 
34 /**
35   Configure the default Udp6Io to receive all the DHCP6 traffic
36   on this network interface.
37 
38   @param[in]  UdpIo                  The pointer to Udp6Io to be configured.
39   @param[in]  Context                The pointer to the context.
40 
41   @retval EFI_SUCCESS            The Udp6Io is successfully configured.
42   @retval Others                 Failed to configure the Udp6Io.
43 
44 **/
45 EFI_STATUS
46 EFIAPI
Dhcp6ConfigureUdpIo(IN UDP_IO * UdpIo,IN VOID * Context)47 Dhcp6ConfigureUdpIo (
48   IN UDP_IO                 *UdpIo,
49   IN VOID                   *Context
50   )
51 {
52   EFI_UDP6_PROTOCOL         *Udp6;
53   EFI_UDP6_CONFIG_DATA      *Config;
54 
55   Udp6   = UdpIo->Protocol.Udp6;
56   Config = &(UdpIo->Config.Udp6);
57 
58   ZeroMem (Config, sizeof (EFI_UDP6_CONFIG_DATA));
59 
60   //
61   // Set Udp6 configure data for the Dhcp6 instance.
62   //
63   Config->AcceptPromiscuous  = FALSE;
64   Config->AcceptAnyPort      = FALSE;
65   Config->AllowDuplicatePort = FALSE;
66   Config->TrafficClass       = 0;
67   Config->HopLimit           = 128;
68   Config->ReceiveTimeout     = 0;
69   Config->TransmitTimeout    = 0;
70 
71   //
72   // Configure an endpoint of client(0, 546), server(0, 0), the addresses
73   // will be overridden later. Note that we MUST not limit RemotePort.
74   // More details, refer to RFC 3315 section 5.2.
75   //
76   Config->StationPort        = DHCP6_PORT_CLIENT;
77   Config->RemotePort         = 0;
78 
79   return Udp6->Configure (Udp6, Config);;
80 }
81 
82 
83 /**
84   Destroy the Dhcp6 service. The Dhcp6 service may be partly initialized,
85   or partly destroyed. If a resource is destroyed, it is marked as such in
86   case the destroy failed and being called again later.
87 
88   @param[in, out]  Service       The pointer to Dhcp6 service to be destroyed.
89 
90 **/
91 VOID
Dhcp6DestroyService(IN OUT DHCP6_SERVICE * Service)92 Dhcp6DestroyService (
93   IN OUT DHCP6_SERVICE          *Service
94   )
95 {
96   //
97   // All children instances should have been already destroyed here.
98   //
99   ASSERT (Service->NumOfChild == 0);
100 
101   if (Service->ClientId != NULL) {
102     FreePool (Service->ClientId);
103   }
104 
105   if (Service->UdpIo != NULL) {
106     UdpIoFreeIo (Service->UdpIo);
107   }
108 
109   FreePool (Service);
110 }
111 
112 
113 /**
114   Create a new Dhcp6 service for the Nic controller.
115 
116   @param[in]  Controller             The controller to be installed DHCP6 service
117                                      binding protocol.
118   @param[in]  ImageHandle            The image handle of the Dhcp6 driver.
119   @param[out] Service                The return pointer of the new Dhcp6 service.
120 
121   @retval EFI_SUCCESS            The Dhcp6 service is created successfully.
122   @retval EFI_DEVICE_ERROR       An unexpected system or network error occurred.
123   @retval EFI_OUT_OF_RESOURCES   Failed to allocate resource.
124 
125 **/
126 EFI_STATUS
Dhcp6CreateService(IN EFI_HANDLE Controller,IN EFI_HANDLE ImageHandle,OUT DHCP6_SERVICE ** Service)127 Dhcp6CreateService (
128   IN  EFI_HANDLE            Controller,
129   IN  EFI_HANDLE            ImageHandle,
130   OUT DHCP6_SERVICE         **Service
131   )
132 {
133   DHCP6_SERVICE             *Dhcp6Srv;
134   EFI_STATUS                Status;
135 
136   *Service = NULL;
137   Dhcp6Srv = AllocateZeroPool (sizeof (DHCP6_SERVICE));
138 
139   if (Dhcp6Srv == NULL) {
140     return EFI_OUT_OF_RESOURCES;
141   }
142 
143   //
144   // Open the SNP protocol to get mode data later.
145   //
146   Dhcp6Srv->Snp = NULL;
147   NetLibGetSnpHandle (Controller, &Dhcp6Srv->Snp);
148   if (Dhcp6Srv->Snp == NULL) {
149     FreePool (Dhcp6Srv);
150     return EFI_DEVICE_ERROR;
151   }
152 
153   //
154   // Initialize the fields of the new Dhcp6 service.
155   //
156   Dhcp6Srv->Signature       = DHCP6_SERVICE_SIGNATURE;
157   Dhcp6Srv->Controller      = Controller;
158   Dhcp6Srv->Image           = ImageHandle;
159   Dhcp6Srv->Xid             = (0xffffff & NET_RANDOM (NetRandomInitSeed ()));
160 
161   CopyMem (
162     &Dhcp6Srv->ServiceBinding,
163     &gDhcp6ServiceBindingTemplate,
164     sizeof (EFI_SERVICE_BINDING_PROTOCOL)
165     );
166 
167   //
168   // Locate Ip6->Ip6Config and store it for get IP6 Duplicate Address Detection transmits.
169   //
170   Status = gBS->HandleProtocol (
171                   Controller,
172                   &gEfiIp6ConfigProtocolGuid,
173                   (VOID **) &Dhcp6Srv->Ip6Cfg
174                   );
175   if (EFI_ERROR (Status)) {
176     FreePool (Dhcp6Srv);
177     return Status;
178   }
179 
180   //
181   // Generate client Duid: If SMBIOS system UUID is located, generate DUID in DUID-UUID format.
182   // Otherwise, in DUID-LLT format.
183   //
184   Dhcp6Srv->ClientId        = Dhcp6GenerateClientId (Dhcp6Srv->Snp->Mode);
185 
186   if (Dhcp6Srv->ClientId == NULL) {
187     FreePool (Dhcp6Srv);
188     return EFI_DEVICE_ERROR;
189   }
190 
191   //
192   // Create an Udp6Io for stateful transmit/receive of each Dhcp6 instance.
193   //
194   Dhcp6Srv->UdpIo = UdpIoCreateIo (
195                       Controller,
196                       ImageHandle,
197                       Dhcp6ConfigureUdpIo,
198                       UDP_IO_UDP6_VERSION,
199                       NULL
200                       );
201 
202   if (Dhcp6Srv->UdpIo == NULL) {
203     FreePool (Dhcp6Srv->ClientId);
204     FreePool (Dhcp6Srv);
205     return EFI_DEVICE_ERROR;
206   }
207 
208   InitializeListHead (&Dhcp6Srv->Child);
209 
210   *Service = Dhcp6Srv;
211 
212   return EFI_SUCCESS;
213 }
214 
215 
216 /**
217   Destroy the Dhcp6 instance and recycle the resources.
218 
219   @param[in, out]  Instance        The pointer to the Dhcp6 instance.
220 
221 **/
222 VOID
Dhcp6DestroyInstance(IN OUT DHCP6_INSTANCE * Instance)223 Dhcp6DestroyInstance (
224   IN OUT DHCP6_INSTANCE         *Instance
225   )
226 {
227   //
228   // Clean up the retry list first.
229   //
230   Dhcp6CleanupRetry (Instance, DHCP6_PACKET_ALL);
231   gBS->CloseEvent (Instance->Timer);
232 
233   //
234   // Clean up the current configure data.
235   //
236   if (Instance->Config != NULL) {
237     Dhcp6CleanupConfigData (Instance->Config);
238     FreePool (Instance->Config);
239   }
240 
241   //
242   // Clean up the current Ia.
243   //
244   if (Instance->IaCb.Ia != NULL) {
245     if (Instance->IaCb.Ia->ReplyPacket != NULL) {
246       FreePool (Instance->IaCb.Ia->ReplyPacket);
247     }
248     FreePool (Instance->IaCb.Ia);
249   }
250 
251   if (Instance->Unicast != NULL) {
252     FreePool (Instance->Unicast);
253   }
254 
255   if (Instance->AdSelect != NULL) {
256     FreePool (Instance->AdSelect);
257   }
258 
259   FreePool (Instance);
260 }
261 
262 
263 /**
264   Create the Dhcp6 instance and initialize it.
265 
266   @param[in]  Service              The pointer to the Dhcp6 service.
267   @param[out] Instance             The pointer to the Dhcp6 instance.
268 
269   @retval EFI_SUCCESS            The Dhcp6 instance is created.
270   @retval EFI_OUT_OF_RESOURCES   Failed to allocate resources.
271 
272 **/
273 EFI_STATUS
Dhcp6CreateInstance(IN DHCP6_SERVICE * Service,OUT DHCP6_INSTANCE ** Instance)274 Dhcp6CreateInstance (
275   IN  DHCP6_SERVICE         *Service,
276   OUT DHCP6_INSTANCE        **Instance
277   )
278 {
279   EFI_STATUS                Status;
280   DHCP6_INSTANCE            *Dhcp6Ins;
281 
282   *Instance = NULL;
283   Dhcp6Ins  = AllocateZeroPool (sizeof (DHCP6_INSTANCE));
284 
285   if (Dhcp6Ins == NULL) {
286     return EFI_OUT_OF_RESOURCES;
287   }
288 
289   //
290   // Initialize the fields of the new Dhcp6 instance.
291   //
292   Dhcp6Ins->Signature       = DHCP6_INSTANCE_SIGNATURE;
293   Dhcp6Ins->UdpSts          = EFI_ALREADY_STARTED;
294   Dhcp6Ins->Service         = Service;
295   Dhcp6Ins->InDestroy       = FALSE;
296   Dhcp6Ins->MediaPresent    = TRUE;
297 
298   CopyMem (
299     &Dhcp6Ins->Dhcp6,
300     &gDhcp6ProtocolTemplate,
301     sizeof (EFI_DHCP6_PROTOCOL)
302     );
303 
304   InitializeListHead (&Dhcp6Ins->TxList);
305   InitializeListHead (&Dhcp6Ins->InfList);
306 
307   //
308   // There is a timer for each Dhcp6 instance, which is used to track the
309   // lease time of Ia and the retransmisson time of all sent packets.
310   //
311   Status = gBS->CreateEvent (
312                   EVT_NOTIFY_SIGNAL | EVT_TIMER,
313                   TPL_CALLBACK,
314                   Dhcp6OnTimerTick,
315                   Dhcp6Ins,
316                   &Dhcp6Ins->Timer
317                   );
318 
319   if (EFI_ERROR (Status)) {
320     FreePool (Dhcp6Ins);
321     return Status;
322   }
323 
324   *Instance = Dhcp6Ins;
325 
326   return EFI_SUCCESS;
327 }
328 
329 /**
330   Callback function which provided by user to remove one node in NetDestroyLinkList process.
331 
332   @param[in]    Entry           The entry to be removed.
333   @param[in]    Context         Pointer to the callback context corresponds to the Context in NetDestroyLinkList.
334 
335   @retval EFI_SUCCESS           The entry has been removed successfully.
336   @retval Others                Fail to remove the entry.
337 
338 **/
339 EFI_STATUS
340 EFIAPI
Dhcp6DestroyChildEntry(IN LIST_ENTRY * Entry,IN VOID * Context)341 Dhcp6DestroyChildEntry (
342   IN LIST_ENTRY         *Entry,
343   IN VOID               *Context
344   )
345 {
346   DHCP6_INSTANCE                   *Instance;
347   EFI_SERVICE_BINDING_PROTOCOL     *ServiceBinding;
348 
349   if (Entry == NULL || Context == NULL) {
350     return EFI_INVALID_PARAMETER;
351   }
352 
353   Instance = NET_LIST_USER_STRUCT_S (Entry, DHCP6_INSTANCE, Link, DHCP6_INSTANCE_SIGNATURE);
354   ServiceBinding = (EFI_SERVICE_BINDING_PROTOCOL *) Context;
355 
356   return ServiceBinding->DestroyChild (ServiceBinding, Instance->Handle);
357 }
358 
359 
360 /**
361   Entry point of the DHCP6 driver to install various protocols.
362 
363   @param[in]  ImageHandle           The handle of the UEFI image file.
364   @param[in]  SystemTable           The pointer to the EFI System Table.
365 
366   @retval EFI_SUCCESS           The operation completed successfully.
367   @retval Others                Unexpected error occurs.
368 
369 **/
370 EFI_STATUS
371 EFIAPI
Dhcp6DriverEntryPoint(IN EFI_HANDLE ImageHandle,IN EFI_SYSTEM_TABLE * SystemTable)372 Dhcp6DriverEntryPoint (
373   IN EFI_HANDLE                   ImageHandle,
374   IN EFI_SYSTEM_TABLE             *SystemTable
375   )
376 {
377   return EfiLibInstallDriverBindingComponentName2 (
378            ImageHandle,
379            SystemTable,
380            &gDhcp6DriverBinding,
381            ImageHandle,
382            &gDhcp6ComponentName,
383            &gDhcp6ComponentName2
384            );
385 }
386 
387 
388 /**
389   Test to see if this driver supports ControllerHandle. This service
390   is called by the EFI boot service ConnectController(). In
391   order to make drivers as small as possible, there are a few calling
392   restrictions for this service. ConnectController() must
393   follow these calling restrictions. If any other agent wishes to call
394   Supported() it must also follow these calling restrictions.
395 
396   @param[in]  This                The pointer to the driver binding protocol.
397   @param[in]  ControllerHandle    The handle of device to be tested.
398   @param[in]  RemainingDevicePath Optional parameter use to pick a specific child
399                                   device to be started.
400 
401   @retval EFI_SUCCESS         This driver supports this device.
402   @retval Others              This driver does not support this device.
403 
404 **/
405 EFI_STATUS
406 EFIAPI
Dhcp6DriverBindingSupported(IN EFI_DRIVER_BINDING_PROTOCOL * This,IN EFI_HANDLE ControllerHandle,IN EFI_DEVICE_PATH_PROTOCOL * RemainingDevicePath OPTIONAL)407 Dhcp6DriverBindingSupported (
408   IN EFI_DRIVER_BINDING_PROTOCOL  *This,
409   IN EFI_HANDLE                   ControllerHandle,
410   IN EFI_DEVICE_PATH_PROTOCOL     *RemainingDevicePath OPTIONAL
411   )
412 {
413   return gBS->OpenProtocol (
414                 ControllerHandle,
415                 &gEfiUdp6ServiceBindingProtocolGuid,
416                 NULL,
417                 This->DriverBindingHandle,
418                 ControllerHandle,
419                 EFI_OPEN_PROTOCOL_TEST_PROTOCOL
420                 );
421 }
422 
423 
424 /**
425   Start this driver on ControllerHandle. This service is called by the
426   EFI boot service ConnectController(). In order to make
427   drivers as small as possible, there are a few calling restrictions for
428   this service. ConnectController() must follow these
429   calling restrictions. If any other agent wishes to call Start() it
430   must also follow these calling restrictions.
431 
432   @param[in]  This                 The pointer to the driver binding protocol.
433   @param[in]  ControllerHandle     The handle of device to be started.
434   @param[in]  RemainingDevicePath  Optional parameter use to pick a specific child
435                                    device to be started.
436 
437   @retval EFI_SUCCESS          This driver is installed to ControllerHandle.
438   @retval EFI_ALREADY_STARTED  This driver is already running on ControllerHandle.
439   @retval other                This driver does not support this device.
440 
441 **/
442 EFI_STATUS
443 EFIAPI
Dhcp6DriverBindingStart(IN EFI_DRIVER_BINDING_PROTOCOL * This,IN EFI_HANDLE ControllerHandle,IN EFI_DEVICE_PATH_PROTOCOL * RemainingDevicePath OPTIONAL)444 Dhcp6DriverBindingStart (
445   IN EFI_DRIVER_BINDING_PROTOCOL  *This,
446   IN EFI_HANDLE                   ControllerHandle,
447   IN EFI_DEVICE_PATH_PROTOCOL     *RemainingDevicePath OPTIONAL
448   )
449 {
450   EFI_STATUS                      Status;
451   DHCP6_SERVICE                   *Service;
452 
453   //
454   // Check the Dhcp6 serivce whether already started.
455   //
456   Status = gBS->OpenProtocol (
457                   ControllerHandle,
458                   &gEfiDhcp6ServiceBindingProtocolGuid,
459                   NULL,
460                   This->DriverBindingHandle,
461                   ControllerHandle,
462                   EFI_OPEN_PROTOCOL_TEST_PROTOCOL
463                   );
464 
465   if (!EFI_ERROR (Status)) {
466     return EFI_ALREADY_STARTED;
467   }
468 
469   //
470   // Create and initialize the Dhcp6 service.
471   //
472   Status = Dhcp6CreateService (
473              ControllerHandle,
474              This->DriverBindingHandle,
475              &Service
476              );
477 
478   if (EFI_ERROR (Status)) {
479     return Status;
480   }
481 
482   ASSERT (Service != NULL);
483 
484   Status = gBS->InstallMultipleProtocolInterfaces (
485                   &ControllerHandle,
486                   &gEfiDhcp6ServiceBindingProtocolGuid,
487                   &Service->ServiceBinding,
488                   NULL
489                   );
490 
491   if (EFI_ERROR (Status)) {
492     Dhcp6DestroyService (Service);
493     return Status;
494   }
495 
496   return EFI_SUCCESS;
497 }
498 
499 
500 /**
501   Stop this driver on ControllerHandle. This service is called by the
502   EFI boot service DisconnectController(). In order to
503   make drivers as small as possible, there are a few calling
504   restrictions for this service. DisconnectController()
505   must follow these calling restrictions. If any other agent wishes
506   to call Stop() it must also follow these calling restrictions.
507 
508   @param[in]  This              Protocol instance pointer.
509   @param[in]  ControllerHandle  Handle of device to stop driver on
510   @param[in]  NumberOfChildren  Number of Handles in ChildHandleBuffer. If number of
511                                 children is zero stop the entire bus driver.
512   @param[in]  ChildHandleBuffer List of Child Handles to Stop.
513 
514   @retval EFI_SUCCESS           This driver is removed ControllerHandle
515   @retval EFI_DEVICE_ERROR      An unexpected system or network error occurred.
516   @retval other                 This driver was not removed from this device
517 
518 **/
519 EFI_STATUS
520 EFIAPI
Dhcp6DriverBindingStop(IN EFI_DRIVER_BINDING_PROTOCOL * This,IN EFI_HANDLE ControllerHandle,IN UINTN NumberOfChildren,IN EFI_HANDLE * ChildHandleBuffer OPTIONAL)521 Dhcp6DriverBindingStop (
522   IN  EFI_DRIVER_BINDING_PROTOCOL  *This,
523   IN  EFI_HANDLE                   ControllerHandle,
524   IN  UINTN                        NumberOfChildren,
525   IN  EFI_HANDLE                   *ChildHandleBuffer   OPTIONAL
526   )
527 {
528   EFI_STATUS                       Status;
529   EFI_HANDLE                       NicHandle;
530   EFI_SERVICE_BINDING_PROTOCOL     *ServiceBinding;
531   DHCP6_SERVICE                    *Service;
532   LIST_ENTRY                       *List;
533   UINTN                            ListLength;
534 
535   //
536   // Find and check the Nic handle by the controller handle.
537   //
538   NicHandle = NetLibGetNicHandle (ControllerHandle, &gEfiUdp6ProtocolGuid);
539 
540   if (NicHandle == NULL) {
541     return EFI_SUCCESS;
542   }
543 
544   Status = gBS->OpenProtocol (
545                   NicHandle,
546                   &gEfiDhcp6ServiceBindingProtocolGuid,
547                   (VOID **) &ServiceBinding,
548                   This->DriverBindingHandle,
549                   NicHandle,
550                   EFI_OPEN_PROTOCOL_GET_PROTOCOL
551                   );
552 
553   if (EFI_ERROR (Status)) {
554     return Status;
555   }
556 
557   Service = DHCP6_SERVICE_FROM_THIS (ServiceBinding);
558   if (!IsListEmpty (&Service->Child)) {
559     //
560     // Destroy all the children instances before destory the service.
561     //
562     List = &Service->Child;
563     Status = NetDestroyLinkList (
564                List,
565                Dhcp6DestroyChildEntry,
566                ServiceBinding,
567                &ListLength
568                );
569     if (EFI_ERROR (Status) || ListLength != 0) {
570       Status = EFI_DEVICE_ERROR;
571     }
572   }
573 
574   if (NumberOfChildren == 0 && !IsListEmpty (&Service->Child)) {
575     Status = EFI_DEVICE_ERROR;
576   }
577 
578   if (NumberOfChildren == 0 && IsListEmpty (&Service->Child)) {
579     //
580     // Destroy the service itself if no child instance left.
581     //
582     Status = gBS->UninstallProtocolInterface (
583                     NicHandle,
584                     &gEfiDhcp6ServiceBindingProtocolGuid,
585                     ServiceBinding
586                     );
587     if (EFI_ERROR (Status)) {
588       goto ON_EXIT;
589     }
590 
591     Dhcp6DestroyService (Service);
592     Status = EFI_SUCCESS;
593   }
594 
595 ON_EXIT:
596   return Status;
597 }
598 
599 
600 /**
601   Creates a child handle and installs a protocol.
602 
603   The CreateChild() function installs a protocol on ChildHandle.
604   If ChildHandle is a pointer to NULL, then a new handle is created and returned in ChildHandle.
605   If ChildHandle is not a pointer to NULL, then the protocol installs on the existing ChildHandle.
606 
607   @param[in]      This        Pointer to the EFI_SERVICE_BINDING_PROTOCOL instance.
608   @param[in, out] ChildHandle Pointer to the handle of the child to create. If it is NULL,
609                               then a new handle is created. If it is a pointer to an existing
610                               UEFI handle, then the protocol is added to the existing UEFI handle.
611 
612   @retval EFI_SUCCES            The protocol was added to ChildHandle.
613   @retval EFI_INVALID_PARAMETER ChildHandle is NULL.
614   @retval other                 The child handle was not created.
615 
616 **/
617 EFI_STATUS
618 EFIAPI
Dhcp6ServiceBindingCreateChild(IN EFI_SERVICE_BINDING_PROTOCOL * This,IN OUT EFI_HANDLE * ChildHandle)619 Dhcp6ServiceBindingCreateChild (
620   IN     EFI_SERVICE_BINDING_PROTOCOL  *This,
621   IN OUT EFI_HANDLE                    *ChildHandle
622   )
623 {
624   EFI_STATUS                       Status;
625   EFI_TPL                          OldTpl;
626   DHCP6_SERVICE                    *Service;
627   DHCP6_INSTANCE                   *Instance;
628   VOID                             *Udp6;
629 
630   if (This == NULL || ChildHandle == NULL) {
631     return EFI_INVALID_PARAMETER;
632   }
633 
634   Service = DHCP6_SERVICE_FROM_THIS (This);
635 
636   Status  = Dhcp6CreateInstance (Service, &Instance);
637 
638   if (EFI_ERROR (Status)) {
639     return Status;
640   }
641 
642   ASSERT (Instance != NULL);
643 
644   //
645   // Start the timer when the instance is ready to use.
646   //
647   Status = gBS->SetTimer (
648                   Instance->Timer,
649                   TimerPeriodic,
650                   TICKS_PER_SECOND
651                   );
652 
653   if (EFI_ERROR (Status)) {
654     goto ON_ERROR;
655   }
656 
657   //
658   // Install the DHCP6 protocol onto ChildHandle.
659   //
660   Status = gBS->InstallMultipleProtocolInterfaces (
661                   ChildHandle,
662                   &gEfiDhcp6ProtocolGuid,
663                   &Instance->Dhcp6,
664                   NULL
665                   );
666 
667   if (EFI_ERROR (Status)) {
668     goto ON_ERROR;
669   }
670 
671   Instance->Handle = *ChildHandle;
672 
673   //
674   // Open the UDP6 protocol BY_CHILD.
675   //
676   Status = gBS->OpenProtocol (
677                   Service->UdpIo->UdpHandle,
678                   &gEfiUdp6ProtocolGuid,
679                   (VOID **) &Udp6,
680                   gDhcp6DriverBinding.DriverBindingHandle,
681                   Instance->Handle,
682                   EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER
683                   );
684 
685   if (EFI_ERROR (Status)) {
686 
687     gBS->UninstallMultipleProtocolInterfaces (
688            Instance->Handle,
689            &gEfiDhcp6ProtocolGuid,
690            &Instance->Dhcp6,
691            NULL
692            );
693     goto ON_ERROR;
694   }
695 
696   //
697   // Add into the children list of its parent service.
698   //
699   OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
700 
701   InsertTailList (&Service->Child, &Instance->Link);
702   Service->NumOfChild++;
703 
704   gBS->RestoreTPL (OldTpl);
705   return EFI_SUCCESS;
706 
707 ON_ERROR:
708 
709   Dhcp6DestroyInstance (Instance);
710   return Status;
711 }
712 
713 
714 /**
715   Destroys a child handle with a protocol installed on it.
716 
717   The DestroyChild() function does the opposite of CreateChild(). It removes a protocol
718   that was installed by CreateChild() from ChildHandle. If the removed protocol is the
719   last protocol on ChildHandle, then ChildHandle is destroyed.
720 
721   @param[in]  This        Pointer to the EFI_SERVICE_BINDING_PROTOCOL instance.
722   @param[in]  ChildHandle Handle of the child to destroy
723 
724   @retval EFI_SUCCES            The protocol was removed from ChildHandle.
725   @retval EFI_UNSUPPORTED       ChildHandle does not support the protocol that is being removed.
726   @retval EFI_INVALID_PARAMETER Child handle is NULL.
727   @retval EFI_ACCESS_DENIED     The protocol could not be removed from the ChildHandle
728                                 because its services are being used.
729   @retval other                 The child handle was not destroyed
730 
731 **/
732 EFI_STATUS
733 EFIAPI
Dhcp6ServiceBindingDestroyChild(IN EFI_SERVICE_BINDING_PROTOCOL * This,IN EFI_HANDLE ChildHandle)734 Dhcp6ServiceBindingDestroyChild (
735   IN EFI_SERVICE_BINDING_PROTOCOL  *This,
736   IN EFI_HANDLE                    ChildHandle
737   )
738 {
739   EFI_STATUS                       Status;
740   EFI_TPL                          OldTpl;
741   EFI_DHCP6_PROTOCOL               *Dhcp6;
742   DHCP6_SERVICE                    *Service;
743   DHCP6_INSTANCE                   *Instance;
744 
745   if (This == NULL || ChildHandle == NULL) {
746     return EFI_INVALID_PARAMETER;
747   }
748 
749   //
750   // Retrieve the private context data structures
751   //
752   Status = gBS->OpenProtocol (
753                   ChildHandle,
754                   &gEfiDhcp6ProtocolGuid,
755                   (VOID **) &Dhcp6,
756                   gDhcp6DriverBinding.DriverBindingHandle,
757                   ChildHandle,
758                   EFI_OPEN_PROTOCOL_GET_PROTOCOL
759                   );
760 
761   if (EFI_ERROR (Status)) {
762     return EFI_UNSUPPORTED;
763   }
764 
765   Instance = DHCP6_INSTANCE_FROM_THIS (Dhcp6);
766   Service  = DHCP6_SERVICE_FROM_THIS (This);
767 
768   if (Instance->Service != Service) {
769     return EFI_INVALID_PARAMETER;
770   }
771 
772   if (Instance->InDestroy) {
773     return EFI_SUCCESS;
774   }
775 
776   OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
777 
778   Instance->InDestroy = TRUE;
779 
780   Status = gBS->CloseProtocol (
781                   Service->UdpIo->UdpHandle,
782                   &gEfiUdp6ProtocolGuid,
783                   gDhcp6DriverBinding.DriverBindingHandle,
784                   ChildHandle
785                   );
786 
787   if (EFI_ERROR (Status)) {
788     Instance->InDestroy = FALSE;
789     gBS->RestoreTPL (OldTpl);
790     return Status;
791   }
792 
793   //
794   // Uninstall the MTFTP6 protocol first to enable a top down destruction.
795   //
796   gBS->RestoreTPL (OldTpl);
797   Status = gBS->UninstallProtocolInterface (
798                   ChildHandle,
799                   &gEfiDhcp6ProtocolGuid,
800                   Dhcp6
801                   );
802   OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
803   if (EFI_ERROR (Status)) {
804     Instance->InDestroy = FALSE;
805     gBS->RestoreTPL (OldTpl);
806     return Status;
807   }
808 
809   //
810   // Remove it from the children list of its parent service.
811   //
812   RemoveEntryList (&Instance->Link);
813   Service->NumOfChild--;
814 
815   gBS->RestoreTPL (OldTpl);
816 
817   Dhcp6DestroyInstance (Instance);
818   return EFI_SUCCESS;
819 }
820