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