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