IScsiDriver.c revision e5eed7d3641d71d7ea539e5379ea9c6a5cd97004
1/** @file
2  The entry point of IScsi driver.
3
4Copyright (c) 2004 - 2009, 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
26EFI_GUID                    gIScsiPrivateGuid   = ISCSI_PRIVATE_GUID;
27
28
29/**
30  Tests to see if this driver supports a given controller. If a child device is provided,
31  it further tests to see if this driver supports creating a handle for the specified child device.
32
33  @param[in]  This                 A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance.
34  @param[in]  ControllerHandle     The handle of the controller to test. This handle
35                                   must support a protocol interface that supplies
36                                   an I/O abstraction to the driver.
37  @param[in]  RemainingDevicePath  A pointer to the remaining portion of a device path.
38                                   This parameter is ignored by device drivers, and is optional for bus drivers.
39
40
41  @retval EFI_SUCCESS              The device specified by ControllerHandle and
42                                   RemainingDevicePath is supported by the driver specified by This.
43  @retval EFI_ALREADY_STARTED      The device specified by ControllerHandle and
44                                   RemainingDevicePath is already being managed by the driver
45                                   specified by This.
46  @retval EFI_ACCESS_DENIED        The device specified by ControllerHandle and
47                                   RemainingDevicePath is already being managed by a different
48                                   driver or an application that requires exclusive acces.
49                                   Currently not implemented.
50  @retval EFI_UNSUPPORTED          The device specified by ControllerHandle and
51                                   RemainingDevicePath is not supported by the driver specified by This.
52**/
53EFI_STATUS
54EFIAPI
55IScsiDriverBindingSupported (
56  IN EFI_DRIVER_BINDING_PROTOCOL  *This,
57  IN EFI_HANDLE                   ControllerHandle,
58  IN EFI_DEVICE_PATH_PROTOCOL     *RemainingDevicePath OPTIONAL
59  )
60{
61  EFI_STATUS                Status;
62  EFI_DEVICE_PATH_PROTOCOL  *CurrentDevicePath;
63
64  Status = gBS->OpenProtocol (
65                  ControllerHandle,
66                  &gIScsiPrivateGuid,
67                  NULL,
68                  This->DriverBindingHandle,
69                  ControllerHandle,
70                  EFI_OPEN_PROTOCOL_TEST_PROTOCOL
71                  );
72  if (!EFI_ERROR (Status)) {
73    return EFI_ALREADY_STARTED;
74  }
75
76  Status = gBS->OpenProtocol (
77                  ControllerHandle,
78                  &gEfiTcp4ServiceBindingProtocolGuid,
79                  NULL,
80                  This->DriverBindingHandle,
81                  ControllerHandle,
82                  EFI_OPEN_PROTOCOL_TEST_PROTOCOL
83                  );
84  if (EFI_ERROR (Status)) {
85    return EFI_UNSUPPORTED;
86  }
87
88  CurrentDevicePath = RemainingDevicePath;
89  if (CurrentDevicePath != NULL) {
90    while (!IsDevicePathEnd (CurrentDevicePath)) {
91      if ((CurrentDevicePath->Type == MESSAGING_DEVICE_PATH) && (CurrentDevicePath->SubType == MSG_ISCSI_DP)) {
92        return EFI_SUCCESS;
93      }
94
95      CurrentDevicePath = NextDevicePathNode (CurrentDevicePath);
96    }
97
98    return EFI_UNSUPPORTED;
99  }
100
101  return EFI_SUCCESS;
102}
103
104/**
105  Start this driver on ControllerHandle.
106
107  The Start() function is designed to be invoked from the EFI boot service ConnectController().
108  As a result, much of the error checking on the parameters to Start() has been moved into this
109  common boot service. It is legal to call Start() from other locations, but the following calling
110  restrictions must be followed or the system behavior will not be deterministic.
111  1. ControllerHandle must be a valid EFI_HANDLE.
112  2. If RemainingDevicePath is not NULL, then it must be a pointer to a naturally aligned
113     EFI_DEVICE_PATH_PROTOCOL.
114  3. Prior to calling Start(), the Supported() function for the driver specified by This must
115     have been called with the same calling parameters, and Supported() must have returned EFI_SUCCESS.
116
117  @param[in]  This                 A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance.
118  @param[in]  ControllerHandle     The handle of the controller to start. This handle
119                                   must support a protocol interface that supplies
120                                   an I/O abstraction to the driver.
121  @param[in]  RemainingDevicePath  A pointer to the remaining portion of a device path.
122                                   This parameter is ignored by device drivers, and is optional for bus drivers.
123
124  @retval EFI_SUCCESS              The device was started.
125  @retval EFI_DEVICE_ERROR         The device could not be started due to a device error.
126                                   Currently not implemented.
127  @retval EFI_OUT_OF_RESOURCES     The request could not be completed due to a lack of resources.
128  @retval Others                   The driver failded to start the device.
129**/
130EFI_STATUS
131EFIAPI
132IScsiDriverBindingStart (
133  IN EFI_DRIVER_BINDING_PROTOCOL  *This,
134  IN EFI_HANDLE                   ControllerHandle,
135  IN EFI_DEVICE_PATH_PROTOCOL     *RemainingDevicePath OPTIONAL
136  )
137{
138  EFI_STATUS        Status;
139  ISCSI_DRIVER_DATA *Private;
140
141  //
142  // Try to add a port configuration page for this controller.
143  //
144  IScsiConfigUpdateForm (This->DriverBindingHandle, ControllerHandle, TRUE);
145
146  Private = IScsiCreateDriverData (This->DriverBindingHandle, ControllerHandle);
147  if (Private == NULL) {
148    return EFI_OUT_OF_RESOURCES;
149  }
150  //
151  // Get the iSCSI configuration data of this controller.
152  //
153  Status = IScsiGetConfigData (Private);
154  if (EFI_ERROR (Status)) {
155    goto ON_ERROR;
156  }
157  //
158  // Try to login and create an iSCSI session according to the configuration.
159  //
160  Status = IScsiSessionLogin (Private);
161  if (Status == EFI_MEDIA_CHANGED) {
162    //
163    // The specified target is not available and the redirection information is
164    // got, login the session again with the updated target address.
165    //
166    Status = IScsiSessionLogin (Private);
167  }
168
169  if (EFI_ERROR (Status)) {
170    goto ON_ERROR;
171  }
172  //
173  // Duplicate the Session's tcp connection device path. The source port field
174  // will be set to zero as one iSCSI session is comprised of several iSCSI
175  // connections.
176  //
177  Private->DevicePath = IScsiGetTcpConnDevicePath (Private);
178  if (Private->DevicePath == NULL) {
179    goto ON_ERROR;
180  }
181  //
182  // Install the updated device path onto the ExtScsiPassThruHandle.
183  //
184  Status = gBS->InstallProtocolInterface (
185                  &Private->ExtScsiPassThruHandle,
186                  &gEfiDevicePathProtocolGuid,
187                  EFI_NATIVE_INTERFACE,
188                  Private->DevicePath
189                  );
190  if (EFI_ERROR (Status)) {
191    goto ON_ERROR;
192  }
193  //
194  // Install the iSCSI private stuff as a flag to indicate this controller
195  // is already controlled by iSCSI driver.
196  //
197  Status = gBS->InstallProtocolInterface (
198                  &ControllerHandle,
199                  &gIScsiPrivateGuid,
200                  EFI_NATIVE_INTERFACE,
201                  &Private->IScsiIdentifier
202                  );
203  if (EFI_ERROR (Status)) {
204    goto ON_ERROR;
205  }
206  //
207  // Update/Publish the iSCSI Boot Firmware Table.
208  //
209  IScsiPublishIbft ();
210
211  return EFI_SUCCESS;
212
213ON_ERROR:
214
215  IScsiSessionAbort (&Private->Session);
216  IScsiCleanDriverData (Private);
217
218  return Status;
219}
220
221/**
222  Stop this driver on ControllerHandle.
223
224  Release the control of this controller and remove the IScsi functions. The Stop()
225  function is designed to be invoked from the EFI boot service DisconnectController().
226  As a result, much of the error checking on the parameters to Stop() has been moved
227  into this common boot service. It is legal to call Stop() from other locations,
228  but the following calling restrictions must be followed or the system behavior will not be deterministic.
229  1. ControllerHandle must be a valid EFI_HANDLE that was used on a previous call to this
230     same driver's Start() function.
231  2. The first NumberOfChildren handles of ChildHandleBuffer must all be a valid
232     EFI_HANDLE. In addition, all of these handles must have been created in this driver's
233     Start() function, and the Start() function must have called OpenProtocol() on
234     ControllerHandle with an Attribute of EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER.
235
236  @param[in]  This              A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance.
237  @param[in]  ControllerHandle  A handle to the device being stopped. The handle must
238                                support a bus specific I/O protocol for the driver
239                                to use to stop the device.
240  @param[in]  NumberOfChildren  The number of child device handles in ChildHandleBuffer.Not used.
241  @param[in]  ChildHandleBuffer An array of child handles to be freed. May be NULL
242                                if NumberOfChildren is 0.Not used.
243
244  @retval EFI_SUCCESS           The device was stopped.
245  @retval EFI_DEVICE_ERROR      The device could not be stopped due to a device error.
246**/
247EFI_STATUS
248EFIAPI
249IScsiDriverBindingStop (
250  IN EFI_DRIVER_BINDING_PROTOCOL  *This,
251  IN EFI_HANDLE                   ControllerHandle,
252  IN UINTN                        NumberOfChildren,
253  IN EFI_HANDLE                   *ChildHandleBuffer OPTIONAL
254  )
255{
256  EFI_HANDLE                      IScsiController;
257  EFI_STATUS                      Status;
258  ISCSI_PRIVATE_PROTOCOL          *IScsiIdentifier;
259  ISCSI_DRIVER_DATA               *Private;
260  EFI_EXT_SCSI_PASS_THRU_PROTOCOL *PassThru;
261  ISCSI_CONNECTION                *Conn;
262
263  if (NumberOfChildren != 0) {
264    //
265    // We should have only one child.
266    //
267    Status = gBS->OpenProtocol (
268                    ChildHandleBuffer[0],
269                    &gEfiExtScsiPassThruProtocolGuid,
270                    (VOID **) &PassThru,
271                    This->DriverBindingHandle,
272                    ControllerHandle,
273                    EFI_OPEN_PROTOCOL_GET_PROTOCOL
274                    );
275    if (EFI_ERROR (Status)) {
276      return EFI_DEVICE_ERROR;
277    }
278
279    Private = ISCSI_DRIVER_DATA_FROM_EXT_SCSI_PASS_THRU (PassThru);
280    Conn    = NET_LIST_HEAD (&Private->Session.Conns, ISCSI_CONNECTION, Link);
281
282    //
283    // Previously the TCP4 protocol is opened BY_CHILD_CONTROLLER. Just close
284    // the protocol here but not uninstall the device path protocol and
285    // EXT SCSI PASS THRU protocol installed on ExtScsiPassThruHandle.
286    //
287    gBS->CloseProtocol (
288          Conn->Tcp4Io.Handle,
289          &gEfiTcp4ProtocolGuid,
290          Private->Image,
291          Private->ExtScsiPassThruHandle
292          );
293
294    return EFI_SUCCESS;
295  }
296  //
297  // Get the handle of the controller we are controling.
298  //
299  IScsiController = NetLibGetNicHandle (ControllerHandle, &gEfiTcp4ProtocolGuid);
300
301  Status = gBS->OpenProtocol (
302                  IScsiController,
303                  &gIScsiPrivateGuid,
304                  (VOID **)&IScsiIdentifier,
305                  This->DriverBindingHandle,
306                  ControllerHandle,
307                  EFI_OPEN_PROTOCOL_GET_PROTOCOL
308                  );
309  if (EFI_ERROR (Status)) {
310    return EFI_DEVICE_ERROR;
311  }
312
313  Private = ISCSI_DRIVER_DATA_FROM_IDENTIFIER (IScsiIdentifier);
314
315  //
316  // Uninstall the private protocol.
317  //
318  gBS->UninstallProtocolInterface (
319        IScsiController,
320        &gIScsiPrivateGuid,
321        &Private->IScsiIdentifier
322        );
323
324  //
325  // Update the iSCSI Boot Firware Table.
326  //
327  IScsiPublishIbft ();
328
329  IScsiSessionAbort (&Private->Session);
330  IScsiCleanDriverData (Private);
331
332  return EFI_SUCCESS;
333}
334
335/**
336  Unloads an image(the iSCSI driver).
337
338  @param[in]  ImageHandle       Handle that identifies the image to be unloaded.
339
340  @retval EFI_SUCCESS           The image has been unloaded.
341  @retval Others                Other errors as indicated.
342**/
343EFI_STATUS
344EFIAPI
345EfiIScsiUnload (
346  IN EFI_HANDLE  ImageHandle
347  )
348{
349  EFI_STATUS  Status;
350  UINTN       DeviceHandleCount;
351  EFI_HANDLE  *DeviceHandleBuffer;
352  UINTN       Index;
353
354  //
355  // Try to disonnect the driver from the devices it's controlling.
356  //
357  Status = gBS->LocateHandleBuffer (
358                  AllHandles,
359                  NULL,
360                  NULL,
361                  &DeviceHandleCount,
362                  &DeviceHandleBuffer
363                  );
364  if (!EFI_ERROR (Status)) {
365    for (Index = 0; Index < DeviceHandleCount; Index++) {
366      Status = gBS->DisconnectController (
367                      DeviceHandleBuffer[Index],
368                      ImageHandle,
369                      NULL
370                      );
371    }
372
373    if (DeviceHandleBuffer != NULL) {
374      FreePool (DeviceHandleBuffer);
375    }
376  }
377  //
378  // Unload the iSCSI configuration form.
379  //
380  IScsiConfigFormUnload (gIScsiDriverBinding.DriverBindingHandle);
381
382  //
383  // Uninstall the protocols installed by iSCSI driver.
384  //
385  Status = gBS->UninstallMultipleProtocolInterfaces (
386                  ImageHandle,
387                  &gEfiDriverBindingProtocolGuid,
388                  &gIScsiDriverBinding,
389                  &gEfiComponentName2ProtocolGuid,
390                  &gIScsiComponentName2,
391                  &gEfiComponentNameProtocolGuid,
392                  &gIScsiComponentName,
393                  &gEfiIScsiInitiatorNameProtocolGuid,
394                  &gIScsiInitiatorName,
395                  NULL
396                  );
397
398  return Status;
399}
400
401/**
402  This is the declaration of an EFI image entry point. This entry point is
403  the same for UEFI Applications, UEFI OS Loaders, and UEFI Drivers including
404  both device drivers and bus drivers. It initialize the global variables and
405  publish the driver binding protocol.
406
407  @param[in]   ImageHandle      The firmware allocated handle for the UEFI image.
408  @param[in]   SystemTable      A pointer to the EFI System Table.
409
410  @retval EFI_SUCCESS           The operation completed successfully.
411  @retval EFI_ACCESS_DENIED     EFI_ISCSI_INITIATOR_NAME_PROTOCOL was installed unexpectedly.
412  @retval Others                Other errors as indicated.
413**/
414EFI_STATUS
415EFIAPI
416IScsiDriverEntryPoint (
417  IN EFI_HANDLE         ImageHandle,
418  IN EFI_SYSTEM_TABLE   *SystemTable
419  )
420{
421  EFI_STATUS                         Status;
422  EFI_ISCSI_INITIATOR_NAME_PROTOCOL  *IScsiInitiatorName;
423
424  //
425  // There should be only one EFI_ISCSI_INITIATOR_NAME_PROTOCOL.
426  //
427  Status = gBS->LocateProtocol (
428                   &gEfiIScsiInitiatorNameProtocolGuid,
429                   NULL,
430                   (VOID**) &IScsiInitiatorName
431                   );
432
433  if (!EFI_ERROR (Status)) {
434    return EFI_ACCESS_DENIED;
435  }
436
437  //
438  // Initialize the EFI Driver Library
439  //
440  Status = EfiLibInstallDriverBindingComponentName2 (
441             ImageHandle,
442             SystemTable,
443             &gIScsiDriverBinding,
444             ImageHandle,
445             &gIScsiComponentName,
446             &gIScsiComponentName2
447           );
448
449  if (!EFI_ERROR (Status)) {
450    //
451    // Install the iSCSI Initiator Name Protocol.
452    //
453    Status = gBS->InstallProtocolInterface (
454                    &ImageHandle,
455                    &gEfiIScsiInitiatorNameProtocolGuid,
456                    EFI_NATIVE_INTERFACE,
457                    &gIScsiInitiatorName
458                    );
459    if (EFI_ERROR (Status)) {
460      gBS->UninstallMultipleProtocolInterfaces (
461            ImageHandle,
462            &gEfiDriverBindingProtocolGuid,
463            &gIScsiDriverBinding,
464            &gEfiComponentName2ProtocolGuid,
465            &gIScsiComponentName2,
466            &gEfiComponentNameProtocolGuid,
467            &gIScsiComponentName,
468            NULL
469            );
470      return Status;
471    }
472
473    //
474    // Initialize the configuration form of iSCSI.
475    //
476    Status = IScsiConfigFormInit ();
477    if (EFI_ERROR (Status)) {
478      gBS->UninstallMultipleProtocolInterfaces (
479            ImageHandle,
480            &gEfiDriverBindingProtocolGuid,
481            &gIScsiDriverBinding,
482            &gEfiComponentName2ProtocolGuid,
483            &gIScsiComponentName2,
484            &gEfiComponentNameProtocolGuid,
485            &gIScsiComponentName,
486            &gEfiIScsiInitiatorNameProtocolGuid,
487            &gIScsiInitiatorName,
488            NULL
489            );
490    }
491  }
492  return Status;
493}
494
495