1/** @file
2  The entry point of IScsi driver.
3
4Copyright (c) 2004 - 2015, Intel Corporation. All rights reserved.<BR>
5This program and the accompanying materials
6are licensed and made available under the terms and conditions of the BSD License
7which accompanies this distribution.  The full text of the license may be found at
8http://opensource.org/licenses/bsd-license.php
9
10THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
11WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
12
13**/
14
15#include "IScsiImpl.h"
16
17EFI_DRIVER_BINDING_PROTOCOL gIScsiDriverBinding = {
18  IScsiDriverBindingSupported,
19  IScsiDriverBindingStart,
20  IScsiDriverBindingStop,
21  0xa,
22  NULL,
23  NULL
24};
25
26/**
27  Tests to see if this driver supports the RemainingDevicePath.
28
29  @param[in]  RemainingDevicePath  A pointer to the remaining portion of a device path.  This
30                                   parameter is ignored by device drivers, and is optional for bus
31                                   drivers. For bus drivers, if this parameter is not NULL, then
32                                   the bus driver must determine if the bus controller specified
33                                   by ControllerHandle and the child controller specified
34                                   by RemainingDevicePath are both supported by this
35                                   bus driver.
36
37  @retval EFI_SUCCESS              The RemainingDevicePath is supported or NULL.
38  @retval EFI_UNSUPPORTED          The device specified by ControllerHandle and
39                                   RemainingDevicePath is not supported by the driver specified by This.
40**/
41EFI_STATUS
42IScsiIsDevicePathSupported (
43  IN EFI_DEVICE_PATH_PROTOCOL     *RemainingDevicePath OPTIONAL
44  )
45{
46  EFI_DEVICE_PATH_PROTOCOL  *CurrentDevicePath;
47
48  CurrentDevicePath = RemainingDevicePath;
49  if (CurrentDevicePath != NULL) {
50    while (!IsDevicePathEnd (CurrentDevicePath)) {
51      if ((CurrentDevicePath->Type == MESSAGING_DEVICE_PATH) && (CurrentDevicePath->SubType == MSG_ISCSI_DP)) {
52        return EFI_SUCCESS;
53      }
54
55      CurrentDevicePath = NextDevicePathNode (CurrentDevicePath);
56    }
57
58    return EFI_UNSUPPORTED;
59  }
60
61  return EFI_SUCCESS;
62}
63
64/**
65  Tests to see if this driver supports a given controller. If a child device is provided,
66  it further tests to see if this driver supports creating a handle for the specified child device.
67
68  @param[in]  This                 A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance.
69  @param[in]  ControllerHandle     The handle of the controller to test. This handle
70                                   must support a protocol interface that supplies
71                                   an I/O abstraction to the driver.
72  @param[in]  RemainingDevicePath  A pointer to the remaining portion of a device path.
73                                   This parameter is ignored by device drivers, and is optional for bus drivers.
74
75
76  @retval EFI_SUCCESS              The device specified by ControllerHandle and
77                                   RemainingDevicePath is supported by the driver specified by This.
78  @retval EFI_ALREADY_STARTED      The device specified by ControllerHandle and
79                                   RemainingDevicePath is already being managed by the driver
80                                   specified by This.
81  @retval EFI_ACCESS_DENIED        The device specified by ControllerHandle and
82                                   RemainingDevicePath is already being managed by a different
83                                   driver or an application that requires exclusive acces.
84                                   Currently not implemented.
85  @retval EFI_UNSUPPORTED          The device specified by ControllerHandle and
86                                   RemainingDevicePath is not supported by the driver specified by This.
87**/
88EFI_STATUS
89EFIAPI
90IScsiDriverBindingSupported (
91  IN EFI_DRIVER_BINDING_PROTOCOL  *This,
92  IN EFI_HANDLE                   ControllerHandle,
93  IN EFI_DEVICE_PATH_PROTOCOL     *RemainingDevicePath OPTIONAL
94  )
95{
96  EFI_STATUS                Status;
97
98  Status = gBS->OpenProtocol (
99                  ControllerHandle,
100                  &gEfiCallerIdGuid,
101                  NULL,
102                  This->DriverBindingHandle,
103                  ControllerHandle,
104                  EFI_OPEN_PROTOCOL_TEST_PROTOCOL
105                  );
106  if (!EFI_ERROR (Status)) {
107    return EFI_ALREADY_STARTED;
108  }
109
110  Status = gBS->OpenProtocol (
111                  ControllerHandle,
112                  &gEfiTcp4ServiceBindingProtocolGuid,
113                  NULL,
114                  This->DriverBindingHandle,
115                  ControllerHandle,
116                  EFI_OPEN_PROTOCOL_TEST_PROTOCOL
117                  );
118  if (EFI_ERROR (Status)) {
119    return EFI_UNSUPPORTED;
120  }
121
122  Status = IScsiIsDevicePathSupported (RemainingDevicePath);
123  if (EFI_ERROR (Status)) {
124    return EFI_UNSUPPORTED;
125  }
126
127  if (IScsiDhcpIsConfigured (ControllerHandle)) {
128    Status = gBS->OpenProtocol (
129                    ControllerHandle,
130                    &gEfiDhcp4ServiceBindingProtocolGuid,
131                    NULL,
132                    This->DriverBindingHandle,
133                    ControllerHandle,
134                    EFI_OPEN_PROTOCOL_TEST_PROTOCOL
135                    );
136    if (EFI_ERROR (Status)) {
137      return EFI_UNSUPPORTED;
138    }
139  }
140
141  return EFI_SUCCESS;
142}
143
144/**
145  Start this driver on ControllerHandle.
146
147  The Start() function is designed to be invoked from the EFI boot service ConnectController().
148  As a result, much of the error checking on the parameters to Start() has been moved into this
149  common boot service. It is legal to call Start() from other locations, but the following calling
150  restrictions must be followed or the system behavior will not be deterministic.
151  1. ControllerHandle must be a valid EFI_HANDLE.
152  2. If RemainingDevicePath is not NULL, then it must be a pointer to a naturally aligned
153     EFI_DEVICE_PATH_PROTOCOL.
154  3. Prior to calling Start(), the Supported() function for the driver specified by This must
155     have been called with the same calling parameters, and Supported() must have returned EFI_SUCCESS.
156
157  @param[in]  This                 A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance.
158  @param[in]  ControllerHandle     The handle of the controller to start. This handle
159                                   must support a protocol interface that supplies
160                                   an I/O abstraction to the driver.
161  @param[in]  RemainingDevicePath  A pointer to the remaining portion of a device path.
162                                   This parameter is ignored by device drivers, and is optional for bus drivers.
163
164  @retval EFI_SUCCESS              The device was started.
165  @retval EFI_DEVICE_ERROR         The device could not be started due to a device error.
166                                   Currently not implemented.
167  @retval EFI_OUT_OF_RESOURCES     The request could not be completed due to a lack of resources.
168  @retval Others                   The driver failded to start the device.
169**/
170EFI_STATUS
171EFIAPI
172IScsiDriverBindingStart (
173  IN EFI_DRIVER_BINDING_PROTOCOL  *This,
174  IN EFI_HANDLE                   ControllerHandle,
175  IN EFI_DEVICE_PATH_PROTOCOL     *RemainingDevicePath OPTIONAL
176  )
177{
178  EFI_STATUS        Status;
179  ISCSI_DRIVER_DATA *Private;
180  VOID              *Interface;
181
182  Private = IScsiCreateDriverData (This->DriverBindingHandle, ControllerHandle);
183  if (Private == NULL) {
184    return EFI_OUT_OF_RESOURCES;
185  }
186
187  //
188  // Create a underlayer child instance, but not need to configure it. Just open ChildHandle
189  // via BY_DRIVER. That is, establishing the relationship between ControllerHandle and ChildHandle.
190  // Therefore, when DisconnectController(), especially VLAN virtual controller handle,
191  // IScsiDriverBindingStop() will be called.
192  //
193  Status = NetLibCreateServiceChild (
194             ControllerHandle,
195             This->DriverBindingHandle,
196             &gEfiTcp4ServiceBindingProtocolGuid,
197             &Private->ChildHandle
198             );
199
200  if (EFI_ERROR (Status)) {
201    goto ON_ERROR;
202  }
203
204  Status = gBS->OpenProtocol (
205                  Private->ChildHandle,
206                  &gEfiTcp4ProtocolGuid,
207                  &Interface,
208                  This->DriverBindingHandle,
209                  ControllerHandle,
210                  EFI_OPEN_PROTOCOL_BY_DRIVER
211                  );
212  if (EFI_ERROR (Status)) {
213    goto ON_ERROR;
214  }
215
216  //
217  // Always install private protocol no matter what happens later. We need to
218  // keep the relationship between ControllerHandle and ChildHandle.
219  //
220  Status = gBS->InstallProtocolInterface (
221                  &ControllerHandle,
222                  &gEfiCallerIdGuid,
223                  EFI_NATIVE_INTERFACE,
224                  &Private->IScsiIdentifier
225                  );
226  if (EFI_ERROR (Status)) {
227    goto ON_ERROR;
228  }
229
230  //
231  // Try to add a port configuration page for this controller.
232  //
233  IScsiConfigUpdateForm (This->DriverBindingHandle, ControllerHandle, TRUE);
234
235  //
236  // Get the iSCSI configuration data of this controller.
237  //
238  Status = IScsiGetConfigData (Private);
239  if (EFI_ERROR (Status)) {
240    goto ON_ERROR;
241  }
242  //
243  // Try to login and create an iSCSI session according to the configuration.
244  //
245  Status = IScsiSessionLogin (Private);
246  if (Status == EFI_MEDIA_CHANGED) {
247    //
248    // The specified target is not available and the redirection information is
249    // got, login the session again with the updated target address.
250    //
251    Status = IScsiSessionLogin (Private);
252  }
253
254  if (EFI_ERROR (Status)) {
255    goto ON_ERROR;
256  }
257  //
258  // Duplicate the Session's tcp connection device path. The source port field
259  // will be set to zero as one iSCSI session is comprised of several iSCSI
260  // connections.
261  //
262  Private->DevicePath = IScsiGetTcpConnDevicePath (Private);
263  if (Private->DevicePath == NULL) {
264    goto ON_ERROR;
265  }
266  //
267  // Install the updated device path onto the ExtScsiPassThruHandle.
268  //
269  Status = gBS->InstallProtocolInterface (
270                  &Private->ExtScsiPassThruHandle,
271                  &gEfiDevicePathProtocolGuid,
272                  EFI_NATIVE_INTERFACE,
273                  Private->DevicePath
274                  );
275  if (EFI_ERROR (Status)) {
276    goto ON_ERROR;
277  }
278
279  //
280  // ISCSI children should share the default Tcp child, just open the default Tcp child via BY_CHILD_CONTROLLER.
281  //
282  Status = gBS->OpenProtocol (
283                  Private->ChildHandle, /// Default Tcp child
284                  &gEfiTcp4ProtocolGuid,
285                  &Interface,
286                  This->DriverBindingHandle,
287                  Private->ExtScsiPassThruHandle,
288                  EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER
289                  );
290  if (EFI_ERROR (Status)) {
291    gBS->UninstallMultipleProtocolInterfaces (
292           Private->ExtScsiPassThruHandle,
293           &gEfiExtScsiPassThruProtocolGuid,
294           &Private->IScsiExtScsiPassThru,
295           &gEfiDevicePathProtocolGuid,
296           Private->DevicePath,
297           NULL
298           );
299
300    goto ON_ERROR;
301  }
302
303  //
304  // Update/Publish the iSCSI Boot Firmware Table.
305  //
306  IScsiPublishIbft ();
307
308  return EFI_SUCCESS;
309
310ON_ERROR:
311
312  IScsiSessionAbort (&Private->Session);
313
314  return Status;
315}
316
317/**
318  Stop this driver on ControllerHandle.
319
320  Release the control of this controller and remove the IScsi functions. The Stop()
321  function is designed to be invoked from the EFI boot service DisconnectController().
322  As a result, much of the error checking on the parameters to Stop() has been moved
323  into this common boot service. It is legal to call Stop() from other locations,
324  but the following calling restrictions must be followed or the system behavior will not be deterministic.
325  1. ControllerHandle must be a valid EFI_HANDLE that was used on a previous call to this
326     same driver's Start() function.
327  2. The first NumberOfChildren handles of ChildHandleBuffer must all be a valid
328     EFI_HANDLE. In addition, all of these handles must have been created in this driver's
329     Start() function, and the Start() function must have called OpenProtocol() on
330     ControllerHandle with an Attribute of EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER.
331
332  @param[in]  This              A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance.
333  @param[in]  ControllerHandle  A handle to the device being stopped. The handle must
334                                support a bus specific I/O protocol for the driver
335                                to use to stop the device.
336  @param[in]  NumberOfChildren  The number of child device handles in ChildHandleBuffer.Not used.
337  @param[in]  ChildHandleBuffer An array of child handles to be freed. May be NULL
338                                if NumberOfChildren is 0.Not used.
339
340  @retval EFI_SUCCESS           The device was stopped.
341  @retval EFI_DEVICE_ERROR      The device could not be stopped due to a device error.
342**/
343EFI_STATUS
344EFIAPI
345IScsiDriverBindingStop (
346  IN EFI_DRIVER_BINDING_PROTOCOL  *This,
347  IN EFI_HANDLE                   ControllerHandle,
348  IN UINTN                        NumberOfChildren,
349  IN EFI_HANDLE                   *ChildHandleBuffer OPTIONAL
350  )
351{
352  EFI_HANDLE                      IScsiController;
353  EFI_STATUS                      Status;
354  ISCSI_PRIVATE_PROTOCOL          *IScsiIdentifier;
355  ISCSI_DRIVER_DATA               *Private;
356  EFI_EXT_SCSI_PASS_THRU_PROTOCOL *PassThru;
357  ISCSI_CONNECTION                *Conn;
358
359  if (NumberOfChildren != 0) {
360    //
361    // We should have only one child.
362    //
363    Status = gBS->OpenProtocol (
364                    ChildHandleBuffer[0],
365                    &gEfiExtScsiPassThruProtocolGuid,
366                    (VOID **) &PassThru,
367                    This->DriverBindingHandle,
368                    ControllerHandle,
369                    EFI_OPEN_PROTOCOL_GET_PROTOCOL
370                    );
371    if (EFI_ERROR (Status)) {
372      return EFI_DEVICE_ERROR;
373    }
374
375    Private = ISCSI_DRIVER_DATA_FROM_EXT_SCSI_PASS_THRU (PassThru);
376    Conn    = NET_LIST_HEAD (&Private->Session.Conns, ISCSI_CONNECTION, Link);
377
378    //
379    // Previously the TCP4 protocol is opened BY_CHILD_CONTROLLER. Just close
380    // the protocol here but not uninstall the device path protocol and
381    // EXT SCSI PASS THRU protocol installed on ExtScsiPassThruHandle.
382    //
383    gBS->CloseProtocol (
384           Private->ChildHandle,
385           &gEfiTcp4ProtocolGuid,
386           Private->Image,
387           Private->ExtScsiPassThruHandle
388           );
389
390    gBS->CloseProtocol (
391          Conn->Tcp4Io.Handle,
392          &gEfiTcp4ProtocolGuid,
393          Private->Image,
394          Private->ExtScsiPassThruHandle
395          );
396
397    return EFI_SUCCESS;
398  }
399  //
400  // Get the handle of the controller we are controling.
401  //
402  IScsiController = NetLibGetNicHandle (ControllerHandle, &gEfiTcp4ProtocolGuid);
403
404  Status = gBS->OpenProtocol (
405                  IScsiController,
406                  &gEfiCallerIdGuid,
407                  (VOID **)&IScsiIdentifier,
408                  This->DriverBindingHandle,
409                  ControllerHandle,
410                  EFI_OPEN_PROTOCOL_GET_PROTOCOL
411                  );
412  if (EFI_ERROR (Status)) {
413    return EFI_DEVICE_ERROR;
414  }
415
416  Private = ISCSI_DRIVER_DATA_FROM_IDENTIFIER (IScsiIdentifier);
417
418  if (Private->ChildHandle != NULL) {
419    Status = gBS->CloseProtocol (
420                    Private->ChildHandle,
421                    &gEfiTcp4ProtocolGuid,
422                    This->DriverBindingHandle,
423                    IScsiController
424                    );
425
426    ASSERT (!EFI_ERROR (Status));
427
428    Status = NetLibDestroyServiceChild (
429               IScsiController,
430               This->DriverBindingHandle,
431               &gEfiTcp4ServiceBindingProtocolGuid,
432               Private->ChildHandle
433               );
434    ASSERT (!EFI_ERROR (Status));
435  }
436
437  IScsiConfigUpdateForm (This->DriverBindingHandle, IScsiController, FALSE);
438
439  //
440  // Uninstall the private protocol.
441  //
442  gBS->UninstallProtocolInterface (
443        IScsiController,
444        &gEfiCallerIdGuid,
445        &Private->IScsiIdentifier
446        );
447
448  //
449  // Update the iSCSI Boot Firware Table.
450  //
451  IScsiPublishIbft ();
452
453  IScsiSessionAbort (&Private->Session);
454  IScsiCleanDriverData (Private);
455
456  return EFI_SUCCESS;
457}
458
459/**
460  Unloads an image(the iSCSI driver).
461
462  @param[in]  ImageHandle       Handle that identifies the image to be unloaded.
463
464  @retval EFI_SUCCESS           The image has been unloaded.
465  @retval Others                Other errors as indicated.
466**/
467EFI_STATUS
468EFIAPI
469EfiIScsiUnload (
470  IN EFI_HANDLE  ImageHandle
471  )
472{
473  EFI_STATUS                        Status;
474  UINTN                             DeviceHandleCount;
475  EFI_HANDLE                        *DeviceHandleBuffer;
476  UINTN                             Index;
477  EFI_COMPONENT_NAME_PROTOCOL       *ComponentName;
478  EFI_COMPONENT_NAME2_PROTOCOL      *ComponentName2;
479
480  //
481  // Try to disonnect the driver from the devices it's controlling.
482  //
483  Status = gBS->LocateHandleBuffer (
484                  AllHandles,
485                  NULL,
486                  NULL,
487                  &DeviceHandleCount,
488                  &DeviceHandleBuffer
489                  );
490  if (EFI_ERROR (Status)) {
491    return Status;
492  }
493
494  for (Index = 0; Index < DeviceHandleCount; Index++) {
495    Status = IScsiTestManagedDevice (
496               DeviceHandleBuffer[Index],
497               gIScsiDriverBinding.DriverBindingHandle,
498               &gEfiTcp4ProtocolGuid
499               );
500    if (EFI_ERROR (Status)) {
501      continue;
502    }
503    Status = gBS->DisconnectController (
504                    DeviceHandleBuffer[Index],
505                    gIScsiDriverBinding.DriverBindingHandle,
506                    NULL
507                    );
508    if (EFI_ERROR (Status)) {
509      goto ON_EXIT;
510    }
511  }
512
513  //
514  // Unload the iSCSI configuration form.
515  //
516  Status = IScsiConfigFormUnload (gIScsiDriverBinding.DriverBindingHandle);
517  if (EFI_ERROR (Status)) {
518    goto ON_EXIT;
519  }
520
521  //
522  // Uninstall the ComponentName and ComponentName2 protocol from iSCSI4 driver binding handle
523  // if it has been installed.
524  //
525  Status = gBS->HandleProtocol (
526                  gIScsiDriverBinding.DriverBindingHandle,
527                  &gEfiComponentNameProtocolGuid,
528                  (VOID **) &ComponentName
529                  );
530  if (!EFI_ERROR (Status)) {
531    Status = gBS->UninstallMultipleProtocolInterfaces (
532           gIScsiDriverBinding.DriverBindingHandle,
533           &gEfiComponentNameProtocolGuid,
534           ComponentName,
535           NULL
536           );
537    if (EFI_ERROR (Status)) {
538      goto ON_EXIT;
539    }
540  }
541
542  Status = gBS->HandleProtocol (
543                  gIScsiDriverBinding.DriverBindingHandle,
544                  &gEfiComponentName2ProtocolGuid,
545                  (VOID **) &ComponentName2
546                  );
547  if (!EFI_ERROR (Status)) {
548    gBS->UninstallMultipleProtocolInterfaces (
549           gIScsiDriverBinding.DriverBindingHandle,
550           &gEfiComponentName2ProtocolGuid,
551           ComponentName2,
552           NULL
553           );
554    if (EFI_ERROR (Status)) {
555      goto ON_EXIT;
556    }
557  }
558
559  Status = gBS->UninstallMultipleProtocolInterfaces (
560                  ImageHandle,
561                  &gEfiDriverBindingProtocolGuid,
562                  &gIScsiDriverBinding,
563                  &gEfiIScsiInitiatorNameProtocolGuid,
564                  &gIScsiInitiatorName,
565                  NULL
566                  );
567ON_EXIT:
568
569  if (DeviceHandleBuffer != NULL) {
570    FreePool (DeviceHandleBuffer);
571  }
572
573  return Status;
574}
575
576/**
577  This is the declaration of an EFI image entry point. This entry point is
578  the same for UEFI Applications, UEFI OS Loaders, and UEFI Drivers including
579  both device drivers and bus drivers. It initialize the global variables and
580  publish the driver binding protocol.
581
582  @param[in]   ImageHandle      The firmware allocated handle for the UEFI image.
583  @param[in]   SystemTable      A pointer to the EFI System Table.
584
585  @retval EFI_SUCCESS           The operation completed successfully.
586  @retval EFI_ACCESS_DENIED     EFI_ISCSI_INITIATOR_NAME_PROTOCOL was installed unexpectedly.
587  @retval Others                Other errors as indicated.
588**/
589EFI_STATUS
590EFIAPI
591IScsiDriverEntryPoint (
592  IN EFI_HANDLE         ImageHandle,
593  IN EFI_SYSTEM_TABLE   *SystemTable
594  )
595{
596  EFI_STATUS                         Status;
597  EFI_ISCSI_INITIATOR_NAME_PROTOCOL  *IScsiInitiatorName;
598
599  //
600  // There should be only one EFI_ISCSI_INITIATOR_NAME_PROTOCOL.
601  //
602  Status = gBS->LocateProtocol (
603                   &gEfiIScsiInitiatorNameProtocolGuid,
604                   NULL,
605                   (VOID**) &IScsiInitiatorName
606                   );
607
608  if (!EFI_ERROR (Status)) {
609    return EFI_ACCESS_DENIED;
610  }
611
612  //
613  // Initialize the EFI Driver Library
614  //
615  Status = EfiLibInstallDriverBindingComponentName2 (
616             ImageHandle,
617             SystemTable,
618             &gIScsiDriverBinding,
619             ImageHandle,
620             &gIScsiComponentName,
621             &gIScsiComponentName2
622           );
623
624  if (!EFI_ERROR (Status)) {
625    //
626    // Install the iSCSI Initiator Name Protocol.
627    //
628    Status = gBS->InstallProtocolInterface (
629                    &ImageHandle,
630                    &gEfiIScsiInitiatorNameProtocolGuid,
631                    EFI_NATIVE_INTERFACE,
632                    &gIScsiInitiatorName
633                    );
634    if (EFI_ERROR (Status)) {
635      gBS->UninstallMultipleProtocolInterfaces (
636            ImageHandle,
637            &gEfiDriverBindingProtocolGuid,
638            &gIScsiDriverBinding,
639            &gEfiComponentName2ProtocolGuid,
640            &gIScsiComponentName2,
641            &gEfiComponentNameProtocolGuid,
642            &gIScsiComponentName,
643            NULL
644            );
645      return Status;
646    }
647
648    //
649    // Initialize the configuration form of iSCSI.
650    //
651    Status = IScsiConfigFormInit ();
652    if (EFI_ERROR (Status)) {
653      gBS->UninstallMultipleProtocolInterfaces (
654            ImageHandle,
655            &gEfiDriverBindingProtocolGuid,
656            &gIScsiDriverBinding,
657            &gEfiComponentName2ProtocolGuid,
658            &gIScsiComponentName2,
659            &gEfiComponentNameProtocolGuid,
660            &gIScsiComponentName,
661            &gEfiIScsiInitiatorNameProtocolGuid,
662            &gIScsiInitiatorName,
663            NULL
664            );
665    }
666  }
667  return Status;
668}
669
670