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