1 /** @file
2 
3 Copyright (c) 2006 - 2016, Intel Corporation. All rights reserved.<BR>
4 This program and the accompanying materials
5 are licensed and made available under the terms and conditions of the BSD License
6 which accompanies this distribution.  The full text of the license may be found at
7 http://opensource.org/licenses/bsd-license.php
8 
9 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
10 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
11 
12 **/
13 
14 #include "Dhcp4Impl.h"
15 #include "Dhcp4Driver.h"
16 
17 EFI_DRIVER_BINDING_PROTOCOL gDhcp4DriverBinding = {
18   Dhcp4DriverBindingSupported,
19   Dhcp4DriverBindingStart,
20   Dhcp4DriverBindingStop,
21   0xa,
22   NULL,
23   NULL
24 };
25 
26 EFI_SERVICE_BINDING_PROTOCOL mDhcp4ServiceBindingTemplate = {
27   Dhcp4ServiceBindingCreateChild,
28   Dhcp4ServiceBindingDestroyChild
29 };
30 
31 /**
32   This is the declaration of an EFI image entry point. This entry point is
33   the same for UEFI Applications, UEFI OS Loaders, and UEFI Drivers including
34   both device drivers and bus drivers.
35 
36   Entry point of the DHCP driver to install various protocols.
37 
38   @param[in]  ImageHandle           The firmware allocated handle for the UEFI image.
39   @param[in]  SystemTable           A pointer to the EFI System Table.
40 
41   @retval EFI_SUCCESS           The operation completed successfully.
42   @retval EFI_OUT_OF_RESOURCES  The request could not be completed due to a lack of resources.
43 
44 **/
45 EFI_STATUS
46 EFIAPI
Dhcp4DriverEntryPoint(IN EFI_HANDLE ImageHandle,IN EFI_SYSTEM_TABLE * SystemTable)47 Dhcp4DriverEntryPoint (
48   IN EFI_HANDLE             ImageHandle,
49   IN EFI_SYSTEM_TABLE       *SystemTable
50   )
51 {
52   return EfiLibInstallDriverBindingComponentName2 (
53            ImageHandle,
54            SystemTable,
55            &gDhcp4DriverBinding,
56            ImageHandle,
57            &gDhcp4ComponentName,
58            &gDhcp4ComponentName2
59            );
60 }
61 
62 
63 /**
64   Test to see if this driver supports ControllerHandle. This service
65   is called by the EFI boot service ConnectController(). In
66   order to make drivers as small as possible, there are a few calling
67   restrictions for this service. ConnectController() must
68   follow these calling restrictions. If any other agent wishes to call
69   Supported() it must also follow these calling restrictions.
70 
71   @param[in]  This                Protocol instance pointer.
72   @param[in]  ControllerHandle    Handle of device to test
73   @param[in]  RemainingDevicePath Optional parameter use to pick a specific child
74                                   device to start.
75 
76   @retval EFI_SUCCESS         This driver supports this device
77   @retval EFI_ALREADY_STARTED This driver is already running on this device
78   @retval other               This driver does not support this device
79 
80 **/
81 EFI_STATUS
82 EFIAPI
Dhcp4DriverBindingSupported(IN EFI_DRIVER_BINDING_PROTOCOL * This,IN EFI_HANDLE ControllerHandle,IN EFI_DEVICE_PATH_PROTOCOL * RemainingDevicePath OPTIONAL)83 Dhcp4DriverBindingSupported (
84   IN EFI_DRIVER_BINDING_PROTOCOL  *This,
85   IN EFI_HANDLE                   ControllerHandle,
86   IN EFI_DEVICE_PATH_PROTOCOL     *RemainingDevicePath OPTIONAL
87   )
88 {
89   EFI_STATUS  Status;
90 
91   Status = gBS->OpenProtocol (
92                   ControllerHandle,
93                   &gEfiUdp4ServiceBindingProtocolGuid,
94                   NULL,
95                   This->DriverBindingHandle,
96                   ControllerHandle,
97                   EFI_OPEN_PROTOCOL_TEST_PROTOCOL
98                   );
99 
100   return Status;
101 }
102 
103 
104 
105 /**
106   Configure the default UDP child to receive all the DHCP traffics
107   on this network interface.
108 
109   @param[in]  UdpIo                  The UDP IO to configure
110   @param[in]  Context                The context to the function
111 
112   @retval EFI_SUCCESS            The UDP IO is successfully configured.
113   @retval Others                 Failed to configure the UDP child.
114 
115 **/
116 EFI_STATUS
117 EFIAPI
DhcpConfigUdpIo(IN UDP_IO * UdpIo,IN VOID * Context)118 DhcpConfigUdpIo (
119   IN UDP_IO                 *UdpIo,
120   IN VOID                   *Context
121   )
122 {
123   EFI_UDP4_CONFIG_DATA      UdpConfigData;
124 
125   UdpConfigData.AcceptBroadcast           = TRUE;
126   UdpConfigData.AcceptPromiscuous         = FALSE;
127   UdpConfigData.AcceptAnyPort             = FALSE;
128   UdpConfigData.AllowDuplicatePort        = TRUE;
129   UdpConfigData.TypeOfService             = 0;
130   UdpConfigData.TimeToLive                = 64;
131   UdpConfigData.DoNotFragment             = FALSE;
132   UdpConfigData.ReceiveTimeout            = 0;
133   UdpConfigData.TransmitTimeout           = 0;
134 
135   UdpConfigData.UseDefaultAddress         = FALSE;
136   UdpConfigData.StationPort               = DHCP_CLIENT_PORT;
137   UdpConfigData.RemotePort                = DHCP_SERVER_PORT;
138 
139   ZeroMem (&UdpConfigData.StationAddress, sizeof (EFI_IPv4_ADDRESS));
140   ZeroMem (&UdpConfigData.SubnetMask, sizeof (EFI_IPv4_ADDRESS));
141   ZeroMem (&UdpConfigData.RemoteAddress, sizeof (EFI_IPv4_ADDRESS));
142 
143   return UdpIo->Protocol.Udp4->Configure (UdpIo->Protocol.Udp4, &UdpConfigData);;
144 }
145 
146 
147 
148 /**
149   Destroy the DHCP service. The Dhcp4 service may be partly initialized,
150   or partly destroyed. If a resource is destroyed, it is marked as so in
151   case the destroy failed and being called again later.
152 
153   @param[in]  DhcpSb                 The DHCP service instance to destroy.
154 
155   @retval EFI_SUCCESS            Always return success.
156 
157 **/
158 EFI_STATUS
Dhcp4CloseService(IN DHCP_SERVICE * DhcpSb)159 Dhcp4CloseService (
160   IN DHCP_SERVICE           *DhcpSb
161   )
162 {
163   DhcpCleanLease (DhcpSb);
164 
165   if (DhcpSb->UdpIo != NULL) {
166     UdpIoFreeIo (DhcpSb->UdpIo);
167     DhcpSb->UdpIo = NULL;
168   }
169 
170   if (DhcpSb->Timer != NULL) {
171     gBS->SetTimer (DhcpSb->Timer, TimerCancel, 0);
172     gBS->CloseEvent (DhcpSb->Timer);
173 
174     DhcpSb->Timer = NULL;
175   }
176 
177   return EFI_SUCCESS;
178 }
179 
180 
181 
182 /**
183   Create a new DHCP service binding instance for the controller.
184 
185   @param[in]  Controller             The controller to install DHCP service binding
186                                      protocol onto
187   @param[in]  ImageHandle            The driver's image handle
188   @param[out] Service                The variable to receive the created DHCP service
189                                      instance.
190 
191   @retval EFI_OUT_OF_RESOURCES   Failed to allocate resource .
192   @retval EFI_SUCCESS            The DHCP service instance is created.
193   @retval other                  Other error occurs.
194 
195 **/
196 EFI_STATUS
Dhcp4CreateService(IN EFI_HANDLE Controller,IN EFI_HANDLE ImageHandle,OUT DHCP_SERVICE ** Service)197 Dhcp4CreateService (
198   IN  EFI_HANDLE            Controller,
199   IN  EFI_HANDLE            ImageHandle,
200   OUT DHCP_SERVICE          **Service
201   )
202 {
203   DHCP_SERVICE              *DhcpSb;
204   EFI_STATUS                Status;
205 
206   *Service  = NULL;
207   DhcpSb    = AllocateZeroPool (sizeof (DHCP_SERVICE));
208 
209   if (DhcpSb == NULL) {
210     return EFI_OUT_OF_RESOURCES;
211   }
212 
213   DhcpSb->Signature       = DHCP_SERVICE_SIGNATURE;
214   DhcpSb->ServiceState    = DHCP_UNCONFIGED;
215   DhcpSb->Controller      = Controller;
216   DhcpSb->Image           = ImageHandle;
217   InitializeListHead (&DhcpSb->Children);
218   DhcpSb->DhcpState       = Dhcp4Stopped;
219   DhcpSb->Xid             = NET_RANDOM (NetRandomInitSeed ());
220   CopyMem (
221     &DhcpSb->ServiceBinding,
222     &mDhcp4ServiceBindingTemplate,
223     sizeof (EFI_SERVICE_BINDING_PROTOCOL)
224     );
225   //
226   // Create various resources, UdpIo, Timer, and get Mac address
227   //
228   Status = gBS->CreateEvent (
229                   EVT_NOTIFY_SIGNAL | EVT_TIMER,
230                   TPL_CALLBACK,
231                   DhcpOnTimerTick,
232                   DhcpSb,
233                   &DhcpSb->Timer
234                   );
235 
236   if (EFI_ERROR (Status)) {
237     goto ON_ERROR;
238   }
239 
240   DhcpSb->UdpIo = UdpIoCreateIo (
241                     Controller,
242                     ImageHandle,
243                     DhcpConfigUdpIo,
244                     UDP_IO_UDP4_VERSION,
245                     NULL
246                     );
247 
248   if (DhcpSb->UdpIo == NULL) {
249     Status = EFI_OUT_OF_RESOURCES;
250     goto ON_ERROR;
251   }
252 
253   DhcpSb->HwLen  = (UINT8) DhcpSb->UdpIo->SnpMode.HwAddressSize;
254   DhcpSb->HwType = DhcpSb->UdpIo->SnpMode.IfType;
255   CopyMem (&DhcpSb->Mac, &DhcpSb->UdpIo->SnpMode.CurrentAddress, sizeof (DhcpSb->Mac));
256 
257   *Service       = DhcpSb;
258   return EFI_SUCCESS;
259 
260 ON_ERROR:
261   Dhcp4CloseService (DhcpSb);
262   FreePool (DhcpSb);
263 
264   return Status;
265 }
266 
267 
268 /**
269   Start this driver on ControllerHandle. This service is called by the
270   EFI boot service ConnectController(). In order to make
271   drivers as small as possible, there are a few calling restrictions for
272   this service. ConnectController() must follow these
273   calling restrictions. If any other agent wishes to call Start() it
274   must also follow these calling restrictions.
275 
276   @param[in]  This                 Protocol instance pointer.
277   @param[in]  ControllerHandle     Handle of device to bind driver to
278   @param[in]  RemainingDevicePath  Optional parameter use to pick a specific child
279                                    device to start.
280 
281   @retval EFI_SUCCESS          This driver is added to ControllerHandle
282   @retval EFI_ALREADY_STARTED  This driver is already running on ControllerHandle
283   @retval other                This driver does not support this device
284 
285 **/
286 EFI_STATUS
287 EFIAPI
Dhcp4DriverBindingStart(IN EFI_DRIVER_BINDING_PROTOCOL * This,IN EFI_HANDLE ControllerHandle,IN EFI_DEVICE_PATH_PROTOCOL * RemainingDevicePath OPTIONAL)288 Dhcp4DriverBindingStart (
289   IN EFI_DRIVER_BINDING_PROTOCOL  *This,
290   IN EFI_HANDLE                   ControllerHandle,
291   IN EFI_DEVICE_PATH_PROTOCOL     *RemainingDevicePath OPTIONAL
292   )
293 {
294   DHCP_SERVICE              *DhcpSb;
295   EFI_STATUS                Status;
296 
297   //
298   // First: test for the DHCP4 Protocol
299   //
300   Status = gBS->OpenProtocol (
301                   ControllerHandle,
302                   &gEfiDhcp4ServiceBindingProtocolGuid,
303                   NULL,
304                   This->DriverBindingHandle,
305                   ControllerHandle,
306                   EFI_OPEN_PROTOCOL_TEST_PROTOCOL
307                   );
308 
309   if (Status == EFI_SUCCESS) {
310     return EFI_ALREADY_STARTED;
311   }
312 
313   Status = Dhcp4CreateService (ControllerHandle, This->DriverBindingHandle, &DhcpSb);
314 
315   if (EFI_ERROR (Status)) {
316     return Status;
317   }
318   ASSERT (DhcpSb != NULL);
319 
320   //
321   // Start the receiving
322   //
323   Status = UdpIoRecvDatagram (DhcpSb->UdpIo, DhcpInput, DhcpSb, 0);
324 
325   if (EFI_ERROR (Status)) {
326     goto ON_ERROR;
327   }
328   Status = gBS->SetTimer (DhcpSb->Timer, TimerPeriodic, TICKS_PER_SECOND);
329 
330   if (EFI_ERROR (Status)) {
331     goto ON_ERROR;
332   }
333 
334   //
335   // Install the Dhcp4ServiceBinding Protocol onto ControlerHandle
336   //
337   Status = gBS->InstallMultipleProtocolInterfaces (
338                   &ControllerHandle,
339                   &gEfiDhcp4ServiceBindingProtocolGuid,
340                   &DhcpSb->ServiceBinding,
341                   NULL
342                   );
343 
344   if (EFI_ERROR (Status)) {
345     goto ON_ERROR;
346   }
347 
348   return Status;
349 
350 ON_ERROR:
351   Dhcp4CloseService (DhcpSb);
352   FreePool (DhcpSb);
353   return Status;
354 }
355 
356 /**
357   Callback function which provided by user to remove one node in NetDestroyLinkList process.
358 
359   @param[in]    Entry           The entry to be removed.
360   @param[in]    Context         Pointer to the callback context corresponds to the Context in NetDestroyLinkList.
361 
362   @retval EFI_SUCCESS           The entry has been removed successfully.
363   @retval Others                Fail to remove the entry.
364 
365 **/
366 EFI_STATUS
367 EFIAPI
Dhcp4DestroyChildEntry(IN LIST_ENTRY * Entry,IN VOID * Context)368 Dhcp4DestroyChildEntry (
369   IN LIST_ENTRY         *Entry,
370   IN VOID               *Context
371   )
372 {
373   DHCP_PROTOCOL                    *Instance;
374   EFI_SERVICE_BINDING_PROTOCOL     *ServiceBinding;
375 
376   if (Entry == NULL || Context == NULL) {
377     return EFI_INVALID_PARAMETER;
378   }
379 
380   Instance = NET_LIST_USER_STRUCT_S (Entry, DHCP_PROTOCOL, Link, DHCP_PROTOCOL_SIGNATURE);
381   ServiceBinding = (EFI_SERVICE_BINDING_PROTOCOL *) Context;
382 
383   return ServiceBinding->DestroyChild (ServiceBinding, Instance->Handle);
384 }
385 
386 
387 /**
388   Stop this driver on ControllerHandle. This service is called by the
389   EFI boot service DisconnectController(). In order to
390   make drivers as small as possible, there are a few calling
391   restrictions for this service. DisconnectController()
392   must follow these calling restrictions. If any other agent wishes
393   to call Stop() it must also follow these calling restrictions.
394 
395   @param[in]  This              Protocol instance pointer.
396   @param[in]  ControllerHandle  Handle of device to stop driver on
397   @param[in]  NumberOfChildren  Number of Handles in ChildHandleBuffer. If number of
398                                 children is zero stop the entire bus driver.
399   @param[in]  ChildHandleBuffer List of Child Handles to Stop.
400 
401   @retval EFI_SUCCESS       This driver is removed ControllerHandle
402   @retval other             This driver was not removed from this device
403 
404 **/
405 EFI_STATUS
406 EFIAPI
Dhcp4DriverBindingStop(IN EFI_DRIVER_BINDING_PROTOCOL * This,IN EFI_HANDLE ControllerHandle,IN UINTN NumberOfChildren,IN EFI_HANDLE * ChildHandleBuffer)407 Dhcp4DriverBindingStop (
408   IN  EFI_DRIVER_BINDING_PROTOCOL  *This,
409   IN  EFI_HANDLE                   ControllerHandle,
410   IN  UINTN                        NumberOfChildren,
411   IN  EFI_HANDLE                   *ChildHandleBuffer
412   )
413 {
414   EFI_SERVICE_BINDING_PROTOCOL  *ServiceBinding;
415   DHCP_SERVICE                  *DhcpSb;
416   EFI_HANDLE                    NicHandle;
417   EFI_STATUS                    Status;
418   LIST_ENTRY                    *List;
419   UINTN                         ListLength;
420 
421   //
422   // DHCP driver opens UDP child, So, the ControllerHandle is the
423   // UDP child handle. locate the Nic handle first.
424   //
425   NicHandle = NetLibGetNicHandle (ControllerHandle, &gEfiUdp4ProtocolGuid);
426 
427   if (NicHandle == NULL) {
428     return EFI_SUCCESS;
429   }
430 
431    Status = gBS->OpenProtocol (
432                   NicHandle,
433                   &gEfiDhcp4ServiceBindingProtocolGuid,
434                   (VOID **) &ServiceBinding,
435                   This->DriverBindingHandle,
436                   NicHandle,
437                   EFI_OPEN_PROTOCOL_GET_PROTOCOL
438                   );
439 
440   if (EFI_ERROR (Status)) {
441     return EFI_DEVICE_ERROR;
442   }
443 
444   DhcpSb = DHCP_SERVICE_FROM_THIS (ServiceBinding);
445   if (!IsListEmpty (&DhcpSb->Children)) {
446     //
447     // Destroy all the children instances before destory the service.
448     //
449     List = &DhcpSb->Children;
450     Status = NetDestroyLinkList (
451                List,
452                Dhcp4DestroyChildEntry,
453                ServiceBinding,
454                &ListLength
455                );
456     if (EFI_ERROR (Status) || ListLength != 0) {
457       Status = EFI_DEVICE_ERROR;
458     }
459   }
460 
461   if (NumberOfChildren == 0 && !IsListEmpty (&DhcpSb->Children)) {
462     Status = EFI_DEVICE_ERROR;
463   }
464 
465   if (NumberOfChildren == 0 && IsListEmpty (&DhcpSb->Children)) {
466     //
467     // Destroy the service itself if no child instance left.
468     //
469     DhcpSb->ServiceState = DHCP_DESTROY;
470 
471     gBS->UninstallProtocolInterface (
472            NicHandle,
473            &gEfiDhcp4ServiceBindingProtocolGuid,
474            ServiceBinding
475            );
476 
477     Dhcp4CloseService (DhcpSb);
478 
479     if (gDhcpControllerNameTable != NULL) {
480       FreeUnicodeStringTable (gDhcpControllerNameTable);
481       gDhcpControllerNameTable = NULL;
482     }
483     FreePool (DhcpSb);
484 
485     Status = EFI_SUCCESS;
486   }
487 
488   return Status;
489 }
490 
491 
492 /**
493   Initialize a new DHCP instance.
494 
495   @param  DhcpSb                 The dhcp service instance
496   @param  Instance               The dhcp instance to initialize
497 
498 **/
499 VOID
DhcpInitProtocol(IN DHCP_SERVICE * DhcpSb,IN OUT DHCP_PROTOCOL * Instance)500 DhcpInitProtocol (
501   IN     DHCP_SERVICE           *DhcpSb,
502   IN OUT DHCP_PROTOCOL          *Instance
503   )
504 {
505   Instance->Signature         = DHCP_PROTOCOL_SIGNATURE;
506   CopyMem (&Instance->Dhcp4Protocol, &mDhcp4ProtocolTemplate, sizeof (Instance->Dhcp4Protocol));
507   InitializeListHead (&Instance->Link);
508   Instance->Handle            = NULL;
509   Instance->Service           = DhcpSb;
510   Instance->InDestroy         = FALSE;
511   Instance->CompletionEvent   = NULL;
512   Instance->RenewRebindEvent  = NULL;
513   Instance->Token             = NULL;
514   Instance->UdpIo             = NULL;
515   Instance->ElaspedTime       = 0;
516   NetbufQueInit (&Instance->ResponseQueue);
517 }
518 
519 
520 /**
521   Creates a child handle and installs a protocol.
522 
523   The CreateChild() function installs a protocol on ChildHandle.
524   If ChildHandle is a pointer to NULL, then a new handle is created and returned in ChildHandle.
525   If ChildHandle is not a pointer to NULL, then the protocol installs on the existing ChildHandle.
526 
527   @param  This        Pointer to the EFI_SERVICE_BINDING_PROTOCOL instance.
528   @param  ChildHandle Pointer to the handle of the child to create. If it is NULL,
529                       then a new handle is created. If it is a pointer to an existing UEFI handle,
530                       then the protocol is added to the existing UEFI handle.
531 
532   @retval EFI_SUCCES            The protocol was added to ChildHandle.
533   @retval EFI_INVALID_PARAMETER ChildHandle is NULL.
534   @retval EFI_OUT_OF_RESOURCES  There are not enough resources available to create
535                                 the child
536   @retval other                 The child handle was not created
537 
538 **/
539 EFI_STATUS
540 EFIAPI
Dhcp4ServiceBindingCreateChild(IN EFI_SERVICE_BINDING_PROTOCOL * This,IN EFI_HANDLE * ChildHandle)541 Dhcp4ServiceBindingCreateChild (
542   IN EFI_SERVICE_BINDING_PROTOCOL  *This,
543   IN EFI_HANDLE                    *ChildHandle
544   )
545 {
546   DHCP_SERVICE              *DhcpSb;
547   DHCP_PROTOCOL             *Instance;
548   EFI_STATUS                Status;
549   EFI_TPL                   OldTpl;
550   VOID                      *Udp4;
551 
552   if ((This == NULL) || (ChildHandle == NULL)) {
553     return EFI_INVALID_PARAMETER;
554   }
555 
556   Instance = AllocatePool (sizeof (*Instance));
557 
558   if (Instance == NULL) {
559     return EFI_OUT_OF_RESOURCES;
560   }
561 
562   DhcpSb = DHCP_SERVICE_FROM_THIS (This);
563   DhcpInitProtocol (DhcpSb, Instance);
564 
565   //
566   // Install DHCP4 onto ChildHandle
567   //
568   Status = gBS->InstallMultipleProtocolInterfaces (
569                   ChildHandle,
570                   &gEfiDhcp4ProtocolGuid,
571                   &Instance->Dhcp4Protocol,
572                   NULL
573                   );
574 
575   if (EFI_ERROR (Status)) {
576     FreePool (Instance);
577     return Status;
578   }
579 
580   Instance->Handle  = *ChildHandle;
581 
582   //
583   // Open the Udp4 protocol BY_CHILD.
584   //
585   Status = gBS->OpenProtocol (
586                   DhcpSb->UdpIo->UdpHandle,
587                   &gEfiUdp4ProtocolGuid,
588                   (VOID **) &Udp4,
589                   gDhcp4DriverBinding.DriverBindingHandle,
590                   Instance->Handle,
591                   EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER
592                   );
593   if (EFI_ERROR (Status)) {
594     gBS->UninstallMultipleProtocolInterfaces (
595            Instance->Handle,
596            &gEfiDhcp4ProtocolGuid,
597            &Instance->Dhcp4Protocol,
598            NULL
599            );
600 
601     FreePool (Instance);
602     return Status;
603   }
604 
605   OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
606 
607   InsertTailList (&DhcpSb->Children, &Instance->Link);
608   DhcpSb->NumChildren++;
609 
610   gBS->RestoreTPL (OldTpl);
611 
612   return EFI_SUCCESS;
613 }
614 
615 
616 /**
617   Destroys a child handle with a protocol installed on it.
618 
619   The DestroyChild() function does the opposite of CreateChild(). It removes a protocol
620   that was installed by CreateChild() from ChildHandle. If the removed protocol is the
621   last protocol on ChildHandle, then ChildHandle is destroyed.
622 
623   @param  This        Pointer to the EFI_SERVICE_BINDING_PROTOCOL instance.
624   @param  ChildHandle Handle of the child to destroy
625 
626   @retval EFI_SUCCES            The protocol was removed from ChildHandle.
627   @retval EFI_UNSUPPORTED       ChildHandle does not support the protocol that is being removed.
628   @retval EFI_INVALID_PARAMETER Child handle is NULL.
629   @retval EFI_ACCESS_DENIED     The protocol could not be removed from the ChildHandle
630                                 because its services are being used.
631   @retval other                 The child handle was not destroyed
632 
633 **/
634 EFI_STATUS
635 EFIAPI
Dhcp4ServiceBindingDestroyChild(IN EFI_SERVICE_BINDING_PROTOCOL * This,IN EFI_HANDLE ChildHandle)636 Dhcp4ServiceBindingDestroyChild (
637   IN EFI_SERVICE_BINDING_PROTOCOL  *This,
638   IN EFI_HANDLE                    ChildHandle
639   )
640 {
641   DHCP_SERVICE              *DhcpSb;
642   DHCP_PROTOCOL             *Instance;
643   EFI_DHCP4_PROTOCOL        *Dhcp;
644   EFI_TPL                   OldTpl;
645   EFI_STATUS                Status;
646 
647   if ((This == NULL) || (ChildHandle == NULL)) {
648     return EFI_INVALID_PARAMETER;
649   }
650 
651   //
652   // Retrieve the private context data structures
653   //
654   Status = gBS->OpenProtocol (
655                   ChildHandle,
656                   &gEfiDhcp4ProtocolGuid,
657                   (VOID **) &Dhcp,
658                   gDhcp4DriverBinding.DriverBindingHandle,
659                   ChildHandle,
660                   EFI_OPEN_PROTOCOL_GET_PROTOCOL
661                   );
662 
663   if (EFI_ERROR (Status)) {
664     return EFI_UNSUPPORTED;
665   }
666 
667   Instance  = DHCP_INSTANCE_FROM_THIS (Dhcp);
668   DhcpSb    = DHCP_SERVICE_FROM_THIS (This);
669 
670   if (Instance->Service != DhcpSb) {
671     return EFI_INVALID_PARAMETER;
672   }
673 
674   //
675   // A child can be destroyed more than once. For example,
676   // Dhcp4DriverBindingStop will destroy all of its children.
677   // when caller driver is being stopped, it will destroy the
678   // dhcp child it opens.
679   //
680   if (Instance->InDestroy) {
681     return EFI_SUCCESS;
682   }
683 
684   OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
685   Instance->InDestroy = TRUE;
686 
687   //
688   // Close the Udp4 protocol.
689   //
690   gBS->CloseProtocol (
691          DhcpSb->UdpIo->UdpHandle,
692          &gEfiUdp4ProtocolGuid,
693          gDhcp4DriverBinding.DriverBindingHandle,
694          ChildHandle
695          );
696 
697   //
698   // Uninstall the DHCP4 protocol first to enable a top down destruction.
699   //
700   gBS->RestoreTPL (OldTpl);
701   Status = gBS->UninstallProtocolInterface (
702                   ChildHandle,
703                   &gEfiDhcp4ProtocolGuid,
704                   Dhcp
705                   );
706   OldTpl = gBS->RaiseTPL (TPL_CALLBACK);
707   if (EFI_ERROR (Status)) {
708     Instance->InDestroy = FALSE;
709 
710     gBS->RestoreTPL (OldTpl);
711     return Status;
712   }
713 
714   if (DhcpSb->ActiveChild == Instance) {
715     DhcpYieldControl (DhcpSb);
716   }
717 
718   RemoveEntryList (&Instance->Link);
719   DhcpSb->NumChildren--;
720 
721   if (Instance->UdpIo != NULL) {
722     UdpIoCleanIo (Instance->UdpIo);
723     gBS->CloseProtocol (
724            Instance->UdpIo->UdpHandle,
725            &gEfiUdp4ProtocolGuid,
726            Instance->Service->Image,
727            Instance->Handle
728            );
729     UdpIoFreeIo (Instance->UdpIo);
730     Instance->UdpIo = NULL;
731     Instance->Token = NULL;
732   }
733 
734   gBS->RestoreTPL (OldTpl);
735 
736   FreePool (Instance);
737   return EFI_SUCCESS;
738 }
739