summaryrefslogtreecommitdiffstats
path: root/src/VBox/Devices/EFI/Firmware/NetworkPkg/IScsiDxe/IScsiDriver.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/VBox/Devices/EFI/Firmware/NetworkPkg/IScsiDxe/IScsiDriver.c')
-rw-r--r--src/VBox/Devices/EFI/Firmware/NetworkPkg/IScsiDxe/IScsiDriver.c1874
1 files changed, 1874 insertions, 0 deletions
diff --git a/src/VBox/Devices/EFI/Firmware/NetworkPkg/IScsiDxe/IScsiDriver.c b/src/VBox/Devices/EFI/Firmware/NetworkPkg/IScsiDxe/IScsiDriver.c
new file mode 100644
index 00000000..bbbf9f79
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/NetworkPkg/IScsiDxe/IScsiDriver.c
@@ -0,0 +1,1874 @@
+/** @file
+ The entry point of IScsi driver.
+
+Copyright (c) 2019, NVIDIA Corporation. All rights reserved.
+Copyright (c) 2004 - 2018, Intel Corporation. All rights reserved.<BR>
+(C) Copyright 2017 Hewlett Packard Enterprise Development LP<BR>
+
+SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include "IScsiImpl.h"
+
+EFI_DRIVER_BINDING_PROTOCOL gIScsiIp4DriverBinding = {
+ IScsiIp4DriverBindingSupported,
+ IScsiIp4DriverBindingStart,
+ IScsiIp4DriverBindingStop,
+ 0xa,
+ NULL,
+ NULL
+};
+
+EFI_DRIVER_BINDING_PROTOCOL gIScsiIp6DriverBinding = {
+ IScsiIp6DriverBindingSupported,
+ IScsiIp6DriverBindingStart,
+ IScsiIp6DriverBindingStop,
+ 0xa,
+ NULL,
+ NULL
+};
+
+EFI_GUID gIScsiV4PrivateGuid = ISCSI_V4_PRIVATE_GUID;
+EFI_GUID gIScsiV6PrivateGuid = ISCSI_V6_PRIVATE_GUID;
+ISCSI_PRIVATE_DATA *mPrivate = NULL;
+
+/**
+ Tests to see if this driver supports the RemainingDevicePath.
+
+ @param[in] RemainingDevicePath A pointer to the remaining portion of a device path. This
+ parameter is ignored by device drivers, and is optional for bus
+ drivers. For bus drivers, if this parameter is not NULL, then
+ the bus driver must determine if the bus controller specified
+ by ControllerHandle and the child controller specified
+ by RemainingDevicePath are both supported by this
+ bus driver.
+
+ @retval EFI_SUCCESS The RemainingDevicePath is supported or NULL.
+ @retval EFI_UNSUPPORTED The device specified by ControllerHandle and
+ RemainingDevicePath is not supported by the driver specified by This.
+**/
+EFI_STATUS
+IScsiIsDevicePathSupported (
+ IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL
+ )
+{
+ EFI_DEVICE_PATH_PROTOCOL *CurrentDevicePath;
+
+ CurrentDevicePath = RemainingDevicePath;
+ if (CurrentDevicePath != NULL) {
+ while (!IsDevicePathEnd (CurrentDevicePath)) {
+ if ((CurrentDevicePath->Type == MESSAGING_DEVICE_PATH) && (CurrentDevicePath->SubType == MSG_ISCSI_DP)) {
+ return EFI_SUCCESS;
+ }
+
+ CurrentDevicePath = NextDevicePathNode (CurrentDevicePath);
+ }
+
+ return EFI_UNSUPPORTED;
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Check whether an iSCSI HBA adapter already installs an AIP instance with
+ network boot policy matching the value specified in PcdIScsiAIPNetworkBootPolicy.
+ If yes, return EFI_SUCCESS.
+
+ @retval EFI_SUCCESS Found an AIP with matching network boot policy.
+ @retval EFI_NOT_FOUND AIP is unavailable or the network boot policy
+ not matched.
+**/
+EFI_STATUS
+IScsiCheckAip (
+ VOID
+ )
+{
+ UINTN AipHandleCount;
+ EFI_HANDLE *AipHandleBuffer;
+ UINTN AipIndex;
+ EFI_ADAPTER_INFORMATION_PROTOCOL *Aip;
+ EFI_EXT_SCSI_PASS_THRU_PROTOCOL *ExtScsiPassThru;
+ EFI_GUID *InfoTypesBuffer;
+ UINTN InfoTypeBufferCount;
+ UINTN TypeIndex;
+ VOID *InfoBlock;
+ UINTN InfoBlockSize;
+ BOOLEAN Supported;
+ EFI_ADAPTER_INFO_NETWORK_BOOT *NetworkBoot;
+ EFI_STATUS Status;
+ UINT8 NetworkBootPolicy;
+
+ //
+ // Check any AIP instances exist in system.
+ //
+ AipHandleCount = 0;
+ AipHandleBuffer = NULL;
+ Status = gBS->LocateHandleBuffer (
+ ByProtocol,
+ &gEfiAdapterInformationProtocolGuid,
+ NULL,
+ &AipHandleCount,
+ &AipHandleBuffer
+ );
+ if (EFI_ERROR (Status) || AipHandleCount == 0) {
+ return EFI_NOT_FOUND;
+ }
+
+ ASSERT (AipHandleBuffer != NULL);
+
+ InfoBlock = NULL;
+
+ for (AipIndex = 0; AipIndex < AipHandleCount; AipIndex++) {
+ Status = gBS->HandleProtocol (
+ AipHandleBuffer[AipIndex],
+ &gEfiAdapterInformationProtocolGuid,
+ (VOID *) &Aip
+ );
+ ASSERT_EFI_ERROR (Status);
+ ASSERT (Aip != NULL);
+
+ Status = gBS->HandleProtocol (
+ AipHandleBuffer[AipIndex],
+ &gEfiExtScsiPassThruProtocolGuid,
+ (VOID *) &ExtScsiPassThru
+ );
+ if (EFI_ERROR (Status) || ExtScsiPassThru == NULL) {
+ continue;
+ }
+
+ InfoTypesBuffer = NULL;
+ InfoTypeBufferCount = 0;
+ Status = Aip->GetSupportedTypes (Aip, &InfoTypesBuffer, &InfoTypeBufferCount);
+ if (EFI_ERROR (Status) || InfoTypesBuffer == NULL) {
+ continue;
+ }
+ //
+ // Check whether the AIP instance has Network boot information block.
+ //
+ Supported = FALSE;
+ for (TypeIndex = 0; TypeIndex < InfoTypeBufferCount; TypeIndex++) {
+ if (CompareGuid (&InfoTypesBuffer[TypeIndex], &gEfiAdapterInfoNetworkBootGuid)) {
+ Supported = TRUE;
+ break;
+ }
+ }
+
+ FreePool (InfoTypesBuffer);
+ if (!Supported) {
+ continue;
+ }
+
+ //
+ // We now have network boot information block.
+ //
+ InfoBlock = NULL;
+ InfoBlockSize = 0;
+ Status = Aip->GetInformation (Aip, &gEfiAdapterInfoNetworkBootGuid, &InfoBlock, &InfoBlockSize);
+ if (EFI_ERROR (Status) || InfoBlock == NULL) {
+ continue;
+ }
+
+ //
+ // Check whether the network boot policy matches.
+ //
+ NetworkBoot = (EFI_ADAPTER_INFO_NETWORK_BOOT *) InfoBlock;
+ NetworkBootPolicy = PcdGet8 (PcdIScsiAIPNetworkBootPolicy);
+
+ if (NetworkBootPolicy == STOP_UEFI_ISCSI_IF_HBA_INSTALL_AIP) {
+ Status = EFI_SUCCESS;
+ goto Exit;
+ }
+ if (((NetworkBootPolicy & STOP_UEFI_ISCSI_IF_AIP_SUPPORT_IP4) != 0 &&
+ !NetworkBoot->iScsiIpv4BootCapablity) ||
+ ((NetworkBootPolicy & STOP_UEFI_ISCSI_IF_AIP_SUPPORT_IP6) != 0 &&
+ !NetworkBoot->iScsiIpv6BootCapablity) ||
+ ((NetworkBootPolicy & STOP_UEFI_ISCSI_IF_AIP_SUPPORT_OFFLOAD) != 0 &&
+ !NetworkBoot->OffloadCapability) ||
+ ((NetworkBootPolicy & STOP_UEFI_ISCSI_IF_AIP_SUPPORT_MPIO) != 0 &&
+ !NetworkBoot->iScsiMpioCapability) ||
+ ((NetworkBootPolicy & STOP_UEFI_ISCSI_IF_AIP_CONFIGURED_IP4) != 0 &&
+ !NetworkBoot->iScsiIpv4Boot) ||
+ ((NetworkBootPolicy & STOP_UEFI_ISCSI_IF_AIP_CONFIGURED_IP6) != 0 &&
+ !NetworkBoot->iScsiIpv6Boot)) {
+ FreePool (InfoBlock);
+ continue;
+ }
+
+ Status = EFI_SUCCESS;
+ goto Exit;
+ }
+
+ Status = EFI_NOT_FOUND;
+
+Exit:
+ if (InfoBlock != NULL) {
+ FreePool (InfoBlock);
+ }
+ if (AipHandleBuffer != NULL) {
+ FreePool (AipHandleBuffer);
+ }
+ return Status;
+}
+
+/**
+ Tests to see if this driver supports a given controller. This is the worker function for
+ IScsiIp4(6)DriverBindingSupported.
+
+ @param[in] This A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance.
+ @param[in] ControllerHandle The handle of the controller to test. This handle
+ must support a protocol interface that supplies
+ an I/O abstraction to the driver.
+ @param[in] RemainingDevicePath A pointer to the remaining portion of a device path. This
+ parameter is ignored by device drivers, and is optional for bus
+ drivers. For bus drivers, if this parameter is not NULL, then
+ the bus driver must determine if the bus controller specified
+ by ControllerHandle and the child controller specified
+ by RemainingDevicePath are both supported by this
+ bus driver.
+ @param[in] IpVersion IP_VERSION_4 or IP_VERSION_6.
+
+ @retval EFI_SUCCESS The device specified by ControllerHandle and
+ RemainingDevicePath is supported by the driver specified by This.
+ @retval EFI_ALREADY_STARTED The device specified by ControllerHandle and
+ RemainingDevicePath is already being managed by the driver
+ specified by This.
+ @retval EFI_UNSUPPORTED The device specified by ControllerHandle and
+ RemainingDevicePath is not supported by the driver specified by This.
+**/
+EFI_STATUS
+EFIAPI
+IScsiSupported (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL,
+ IN UINT8 IpVersion
+ )
+{
+ EFI_STATUS Status;
+ EFI_GUID *IScsiServiceBindingGuid;
+ EFI_GUID *TcpServiceBindingGuid;
+ EFI_GUID *DhcpServiceBindingGuid;
+ EFI_GUID *DnsServiceBindingGuid;
+
+ if (IpVersion == IP_VERSION_4) {
+ IScsiServiceBindingGuid = &gIScsiV4PrivateGuid;
+ TcpServiceBindingGuid = &gEfiTcp4ServiceBindingProtocolGuid;
+ DhcpServiceBindingGuid = &gEfiDhcp4ServiceBindingProtocolGuid;
+ DnsServiceBindingGuid = &gEfiDns4ServiceBindingProtocolGuid;
+
+ } else {
+ IScsiServiceBindingGuid = &gIScsiV6PrivateGuid;
+ TcpServiceBindingGuid = &gEfiTcp6ServiceBindingProtocolGuid;
+ DhcpServiceBindingGuid = &gEfiDhcp6ServiceBindingProtocolGuid;
+ DnsServiceBindingGuid = &gEfiDns6ServiceBindingProtocolGuid;
+ }
+
+ Status = gBS->OpenProtocol (
+ ControllerHandle,
+ IScsiServiceBindingGuid,
+ NULL,
+ This->DriverBindingHandle,
+ ControllerHandle,
+ EFI_OPEN_PROTOCOL_TEST_PROTOCOL
+ );
+ if (!EFI_ERROR (Status)) {
+ return EFI_ALREADY_STARTED;
+ }
+
+ Status = gBS->OpenProtocol (
+ ControllerHandle,
+ TcpServiceBindingGuid,
+ NULL,
+ This->DriverBindingHandle,
+ ControllerHandle,
+ EFI_OPEN_PROTOCOL_TEST_PROTOCOL
+ );
+ if (EFI_ERROR (Status)) {
+ return EFI_UNSUPPORTED;
+ }
+
+ Status = IScsiIsDevicePathSupported (RemainingDevicePath);
+ if (EFI_ERROR (Status)) {
+ return EFI_UNSUPPORTED;
+ }
+
+ if (IScsiDhcpIsConfigured (ControllerHandle, IpVersion)) {
+ Status = gBS->OpenProtocol (
+ ControllerHandle,
+ DhcpServiceBindingGuid,
+ NULL,
+ This->DriverBindingHandle,
+ ControllerHandle,
+ EFI_OPEN_PROTOCOL_TEST_PROTOCOL
+ );
+ if (EFI_ERROR (Status)) {
+ return EFI_UNSUPPORTED;
+ }
+ }
+
+ if (IScsiDnsIsConfigured (ControllerHandle)) {
+ Status = gBS->OpenProtocol (
+ ControllerHandle,
+ DnsServiceBindingGuid,
+ NULL,
+ This->DriverBindingHandle,
+ ControllerHandle,
+ EFI_OPEN_PROTOCOL_TEST_PROTOCOL
+ );
+ if (EFI_ERROR (Status)) {
+ return EFI_UNSUPPORTED;
+ }
+ }
+
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Start to manage the controller. This is the worker function for
+ IScsiIp4(6)DriverBindingStart.
+
+ @param[in] Image Handle of the image.
+ @param[in] ControllerHandle Handle of the controller.
+ @param[in] IpVersion IP_VERSION_4 or IP_VERSION_6.
+
+ @retval EFI_SUCCESS This driver was started.
+ @retval EFI_ALREADY_STARTED This driver is already running on this device.
+ @retval EFI_INVALID_PARAMETER Any input parameter is invalid.
+ @retval EFI_NOT_FOUND There is no sufficient information to establish
+ the iScsi session.
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
+ @retval EFI_DEVICE_ERROR Failed to get TCP connection device path.
+ @retval EFI_ACCESS_DENIED The protocol could not be removed from the Handle
+ because its interfaces are being used.
+
+**/
+EFI_STATUS
+IScsiStart (
+ IN EFI_HANDLE Image,
+ IN EFI_HANDLE ControllerHandle,
+ IN UINT8 IpVersion
+ )
+{
+ EFI_STATUS Status;
+ ISCSI_DRIVER_DATA *Private;
+ LIST_ENTRY *Entry;
+ LIST_ENTRY *NextEntry;
+ ISCSI_ATTEMPT_CONFIG_NVDATA *AttemptConfigData;
+ ISCSI_SESSION *Session;
+ UINT8 Index;
+ EFI_EXT_SCSI_PASS_THRU_PROTOCOL *ExistIScsiExtScsiPassThru;
+ ISCSI_DRIVER_DATA *ExistPrivate;
+ UINT8 *AttemptConfigOrder;
+ UINTN AttemptConfigOrderSize;
+ UINT8 BootSelected;
+ EFI_HANDLE *HandleBuffer;
+ UINTN NumberOfHandles;
+ EFI_DEVICE_PATH_PROTOCOL *DevicePath;
+ EFI_GUID *IScsiPrivateGuid;
+ EFI_GUID *TcpServiceBindingGuid;
+ BOOLEAN NeedUpdate;
+ VOID *Interface;
+ EFI_GUID *ProtocolGuid;
+ UINT8 NetworkBootPolicy;
+ ISCSI_SESSION_CONFIG_NVDATA *NvData;
+
+ //
+ // Test to see if iSCSI driver supports the given controller.
+ //
+
+ if (IpVersion == IP_VERSION_4) {
+ IScsiPrivateGuid = &gIScsiV4PrivateGuid;
+ TcpServiceBindingGuid = &gEfiTcp4ServiceBindingProtocolGuid;
+ ProtocolGuid = &gEfiTcp4ProtocolGuid;
+ } else if (IpVersion == IP_VERSION_6) {
+ IScsiPrivateGuid = &gIScsiV6PrivateGuid;
+ TcpServiceBindingGuid = &gEfiTcp6ServiceBindingProtocolGuid;
+ ProtocolGuid = &gEfiTcp6ProtocolGuid;
+ } else {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Status = gBS->OpenProtocol (
+ ControllerHandle,
+ IScsiPrivateGuid,
+ NULL,
+ Image,
+ ControllerHandle,
+ EFI_OPEN_PROTOCOL_TEST_PROTOCOL
+ );
+ if (!EFI_ERROR (Status)) {
+ return EFI_ALREADY_STARTED;
+ }
+
+ Status = gBS->OpenProtocol (
+ ControllerHandle,
+ TcpServiceBindingGuid,
+ NULL,
+ Image,
+ ControllerHandle,
+ EFI_OPEN_PROTOCOL_TEST_PROTOCOL
+ );
+ if (EFI_ERROR (Status)) {
+ return EFI_UNSUPPORTED;
+ }
+
+ NetworkBootPolicy = PcdGet8 (PcdIScsiAIPNetworkBootPolicy);
+ if (NetworkBootPolicy == ALWAYS_USE_ISCSI_HBA_AND_IGNORE_UEFI_ISCSI) {
+ return EFI_ABORTED;
+ }
+
+ if (NetworkBootPolicy != ALWAYS_USE_UEFI_ISCSI_AND_IGNORE_ISCSI_HBA) {
+ //
+ // Check existing iSCSI AIP.
+ //
+ Status = IScsiCheckAip ();
+ if (!EFI_ERROR (Status)) {
+ //
+ // Find iSCSI AIP with specified network boot policy. return EFI_ABORTED.
+ //
+ return EFI_ABORTED;
+ }
+ }
+
+ //
+ // Record the incoming NIC info.
+ //
+ Status = IScsiAddNic (ControllerHandle, Image);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ //
+ // Create the instance private data.
+ //
+ Private = IScsiCreateDriverData (Image, ControllerHandle);
+ if (Private == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ //
+ // Create a underlayer child instance, but not need to configure it. Just open ChildHandle
+ // via BY_DRIVER. That is, establishing the relationship between ControllerHandle and ChildHandle.
+ // Therefore, when DisconnectController(), especially VLAN virtual controller handle,
+ // IScsiDriverBindingStop() will be called.
+ //
+ Status = NetLibCreateServiceChild (
+ ControllerHandle,
+ Image,
+ TcpServiceBindingGuid,
+ &Private->ChildHandle
+ );
+
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+
+ Status = gBS->OpenProtocol (
+ Private->ChildHandle, /// Default Tcp child
+ ProtocolGuid,
+ &Interface,
+ Image,
+ ControllerHandle,
+ EFI_OPEN_PROTOCOL_BY_DRIVER
+ );
+
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+
+ //
+ // Always install private protocol no matter what happens later. We need to
+ // keep the relationship between ControllerHandle and ChildHandle.
+ //
+ Status = gBS->InstallProtocolInterface (
+ &ControllerHandle,
+ IScsiPrivateGuid,
+ EFI_NATIVE_INTERFACE,
+ &Private->IScsiIdentifier
+ );
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+
+ if (IpVersion == IP_VERSION_4) {
+ mPrivate->Ipv6Flag = FALSE;
+ } else {
+ mPrivate->Ipv6Flag = TRUE;
+ }
+
+ //
+ // Get the current iSCSI configuration data.
+ //
+ Status = IScsiGetConfigData (Private);
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+
+ //
+ // If there is already a successul attempt, check whether this attempt is the
+ // first "enabled for MPIO" attempt. If not, still try the first attempt.
+ // In single path mode, try all attempts.
+ //
+ ExistPrivate = NULL;
+ Status = EFI_NOT_FOUND;
+
+ if (mPrivate->OneSessionEstablished && mPrivate->EnableMpio) {
+ AttemptConfigData = NULL;
+ NET_LIST_FOR_EACH (Entry, &mPrivate->AttemptConfigs) {
+ AttemptConfigData = NET_LIST_USER_STRUCT (Entry, ISCSI_ATTEMPT_CONFIG_NVDATA, Link);
+ if (AttemptConfigData->SessionConfigData.Enabled == ISCSI_ENABLED_FOR_MPIO) {
+ break;
+ }
+ }
+
+ if (AttemptConfigData == NULL) {
+ goto ON_ERROR;
+ }
+
+ if (AttemptConfigData->AttemptConfigIndex == mPrivate->BootSelectedIndex) {
+ goto ON_EXIT;
+ }
+
+ //
+ // Uninstall the original ExtScsiPassThru first.
+ //
+
+ //
+ // Locate all ExtScsiPassThru protocol instances.
+ //
+ Status = gBS->LocateHandleBuffer (
+ ByProtocol,
+ &gEfiExtScsiPassThruProtocolGuid,
+ NULL,
+ &NumberOfHandles,
+ &HandleBuffer
+ );
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+
+ //
+ // Find ExtScsiPassThru protocol instance produced by this driver.
+ //
+ ExistIScsiExtScsiPassThru = NULL;
+ for (Index = 0; Index < NumberOfHandles && ExistIScsiExtScsiPassThru == NULL; Index++) {
+ Status = gBS->HandleProtocol (
+ HandleBuffer[Index],
+ &gEfiDevicePathProtocolGuid,
+ (VOID **) &DevicePath
+ );
+ if (EFI_ERROR (Status)) {
+ continue;
+ }
+
+ while (!IsDevicePathEnd (DevicePath)) {
+ if ((DevicePath->Type == MESSAGING_DEVICE_PATH) && (DevicePath->SubType == MSG_MAC_ADDR_DP)) {
+ //
+ // Get the ExtScsiPassThru protocol instance.
+ //
+ Status = gBS->HandleProtocol (
+ HandleBuffer[Index],
+ &gEfiExtScsiPassThruProtocolGuid,
+ (VOID **) &ExistIScsiExtScsiPassThru
+ );
+ ASSERT_EFI_ERROR (Status);
+ break;
+ }
+
+ DevicePath = NextDevicePathNode (DevicePath);
+ }
+ }
+
+ FreePool (HandleBuffer);
+
+ if (ExistIScsiExtScsiPassThru == NULL) {
+ Status = EFI_NOT_FOUND;
+ goto ON_ERROR;
+ }
+
+ ExistPrivate = ISCSI_DRIVER_DATA_FROM_EXT_SCSI_PASS_THRU (ExistIScsiExtScsiPassThru);
+
+ Status = gBS->UninstallProtocolInterface (
+ ExistPrivate->ExtScsiPassThruHandle,
+ &gEfiExtScsiPassThruProtocolGuid,
+ &ExistPrivate->IScsiExtScsiPassThru
+ );
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+ }
+
+ //
+ // Install the Ext SCSI PASS THRU protocol.
+ //
+ Status = gBS->InstallProtocolInterface (
+ &Private->ExtScsiPassThruHandle,
+ &gEfiExtScsiPassThruProtocolGuid,
+ EFI_NATIVE_INTERFACE,
+ &Private->IScsiExtScsiPassThru
+ );
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+
+ BootSelected = 0;
+
+ NET_LIST_FOR_EACH_SAFE (Entry, NextEntry, &mPrivate->AttemptConfigs) {
+ AttemptConfigData = NET_LIST_USER_STRUCT (Entry, ISCSI_ATTEMPT_CONFIG_NVDATA, Link);
+ //
+ // Don't process the attempt that does not associate with the current NIC or
+ // this attempt is disabled or established.
+ //
+ if (AttemptConfigData->NicIndex != mPrivate->CurrentNic ||
+ AttemptConfigData->SessionConfigData.Enabled == ISCSI_DISABLED ||
+ AttemptConfigData->ValidPath) {
+ continue;
+ }
+
+ //
+ // In multipath mode, don't process attempts configured for single path.
+ // In default single path mode, don't process attempts configured for multipath.
+ //
+ if ((mPrivate->EnableMpio &&
+ AttemptConfigData->SessionConfigData.Enabled != ISCSI_ENABLED_FOR_MPIO) ||
+ (!mPrivate->EnableMpio &&
+ AttemptConfigData->SessionConfigData.Enabled != ISCSI_ENABLED)) {
+ continue;
+ }
+
+ //
+ // Don't process the attempt that fails to get the init/target information from DHCP.
+ //
+ if (AttemptConfigData->SessionConfigData.InitiatorInfoFromDhcp &&
+ !AttemptConfigData->DhcpSuccess) {
+ if (!mPrivate->EnableMpio && mPrivate->ValidSinglePathCount > 0) {
+ mPrivate->ValidSinglePathCount--;
+ }
+ continue;
+ }
+
+ //
+ // Don't process the autoconfigure path if it is already established.
+ //
+ if (AttemptConfigData->SessionConfigData.IpMode == IP_MODE_AUTOCONFIG &&
+ AttemptConfigData->AutoConfigureSuccess) {
+ continue;
+ }
+
+ //
+ // Don't process the attempt if its IP mode is not in the current IP version.
+ //
+ if (!mPrivate->Ipv6Flag) {
+ if (AttemptConfigData->SessionConfigData.IpMode == IP_MODE_IP6) {
+ continue;
+ }
+ if (AttemptConfigData->SessionConfigData.IpMode == IP_MODE_AUTOCONFIG &&
+ AttemptConfigData->AutoConfigureMode == IP_MODE_AUTOCONFIG_IP6) {
+ continue;
+ }
+ } else {
+ if (AttemptConfigData->SessionConfigData.IpMode == IP_MODE_IP4) {
+ continue;
+ }
+ if (AttemptConfigData->SessionConfigData.IpMode == IP_MODE_AUTOCONFIG &&
+ AttemptConfigData->AutoConfigureMode == IP_MODE_AUTOCONFIG_IP4) {
+ continue;
+ }
+ }
+
+ //
+ // Fill in the Session and init it.
+ //
+ Session = (ISCSI_SESSION *) AllocateZeroPool (sizeof (ISCSI_SESSION));
+ if (Session == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ goto ON_ERROR;
+ }
+
+ Session->Private = Private;
+ Session->ConfigData = AttemptConfigData;
+ Session->AuthType = AttemptConfigData->AuthenticationType;
+
+ UnicodeSPrint (
+ mPrivate->PortString,
+ (UINTN) ISCSI_NAME_IFR_MAX_SIZE,
+ L"Attempt %d",
+ (UINTN) AttemptConfigData->AttemptConfigIndex
+ );
+
+ if (Session->AuthType == ISCSI_AUTH_TYPE_CHAP) {
+ Session->AuthData.CHAP.AuthConfig = &AttemptConfigData->AuthConfigData.CHAP;
+ }
+
+ IScsiSessionInit (Session, FALSE);
+
+ //
+ // Try to login and create an iSCSI session according to the configuration.
+ //
+ Status = IScsiSessionLogin (Session);
+ if (Status == EFI_MEDIA_CHANGED) {
+ //
+ // The specified target is not available, and the redirection information is
+ // received. Login the session again with the updated target address.
+ //
+ Status = IScsiSessionLogin (Session);
+ } else if (Status == EFI_NOT_READY) {
+ Status = IScsiSessionReLogin (Session);
+ }
+
+ //
+ // Restore the original user setting which specifies the proxy/virtual iSCSI target to NV region.
+ //
+ NvData = &AttemptConfigData->SessionConfigData;
+ if (NvData->RedirectFlag) {
+ NvData->TargetPort = NvData->OriginalTargetPort;
+ CopyMem (&NvData->TargetIp, &NvData->OriginalTargetIp, sizeof (EFI_IP_ADDRESS));
+ NvData->RedirectFlag = FALSE;
+
+ gRT->SetVariable (
+ mPrivate->PortString,
+ &gEfiIScsiInitiatorNameProtocolGuid,
+ ISCSI_CONFIG_VAR_ATTR,
+ sizeof (ISCSI_ATTEMPT_CONFIG_NVDATA),
+ AttemptConfigData
+ );
+ }
+
+ if (EFI_ERROR (Status)) {
+ //
+ // In Single path mode, only the successful attempt will be recorded in iBFT;
+ // in multi-path mode, all the attempt entries in MPIO will be recorded in iBFT.
+ //
+ if (!mPrivate->EnableMpio && mPrivate->ValidSinglePathCount > 0) {
+ mPrivate->ValidSinglePathCount--;
+ }
+
+ FreePool (Session);
+
+ } else {
+ AttemptConfigData->ValidPath = TRUE;
+
+ //
+ // Do not record the attempt in iBFT if it login with KRB5.
+ // TODO: record KRB5 attempt information in the iSCSI device path.
+ //
+ if (Session->AuthType == ISCSI_AUTH_TYPE_KRB) {
+ if (!mPrivate->EnableMpio && mPrivate->ValidSinglePathCount > 0) {
+ mPrivate->ValidSinglePathCount--;
+ }
+
+ AttemptConfigData->ValidiBFTPath = FALSE;
+ } else {
+ AttemptConfigData->ValidiBFTPath = TRUE;
+ }
+
+ //
+ // IScsi session success. Update the attempt state to NVR.
+ //
+ if (AttemptConfigData->SessionConfigData.IpMode == IP_MODE_AUTOCONFIG) {
+ AttemptConfigData->AutoConfigureSuccess = TRUE;
+ }
+
+ gRT->SetVariable (
+ mPrivate->PortString,
+ &gEfiIScsiInitiatorNameProtocolGuid,
+ ISCSI_CONFIG_VAR_ATTR,
+ sizeof (ISCSI_ATTEMPT_CONFIG_NVDATA),
+ AttemptConfigData
+ );
+
+ //
+ // Select the first login session. Abort others.
+ //
+ if (Private->Session == NULL) {
+ Private->Session = Session;
+ BootSelected = AttemptConfigData->AttemptConfigIndex;
+ //
+ // Don't validate other attempt in multipath mode if one is success.
+ //
+ if (mPrivate->EnableMpio) {
+ break;
+ }
+ } else {
+ IScsiSessionAbort (Session);
+ FreePool (Session);
+ }
+ }
+ }
+
+ //
+ // All attempts configured for this driver instance are not valid.
+ //
+ if (Private->Session == NULL) {
+ Status = gBS->UninstallProtocolInterface (
+ Private->ExtScsiPassThruHandle,
+ &gEfiExtScsiPassThruProtocolGuid,
+ &Private->IScsiExtScsiPassThru
+ );
+ ASSERT_EFI_ERROR (Status);
+ Private->ExtScsiPassThruHandle = NULL;
+
+ //
+ // Reinstall the original ExtScsiPassThru back.
+ //
+ if (mPrivate->OneSessionEstablished && ExistPrivate != NULL) {
+ Status = gBS->InstallProtocolInterface (
+ &ExistPrivate->ExtScsiPassThruHandle,
+ &gEfiExtScsiPassThruProtocolGuid,
+ EFI_NATIVE_INTERFACE,
+ &ExistPrivate->IScsiExtScsiPassThru
+ );
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+
+ goto ON_EXIT;
+ }
+
+ Status = EFI_NOT_FOUND;
+
+ goto ON_ERROR;
+ }
+
+ NeedUpdate = TRUE;
+ //
+ // More than one attempt successes.
+ //
+ if (Private->Session != NULL && mPrivate->OneSessionEstablished) {
+
+ AttemptConfigOrder = IScsiGetVariableAndSize (
+ L"AttemptOrder",
+ &gIScsiConfigGuid,
+ &AttemptConfigOrderSize
+ );
+ if (AttemptConfigOrder == NULL) {
+ goto ON_ERROR;
+ }
+ for (Index = 0; Index < AttemptConfigOrderSize / sizeof (UINT8); Index++) {
+ if (AttemptConfigOrder[Index] == mPrivate->BootSelectedIndex ||
+ AttemptConfigOrder[Index] == BootSelected) {
+ break;
+ }
+ }
+
+ if (mPrivate->EnableMpio) {
+ //
+ // Use the attempt in earlier order. Abort the later one in MPIO.
+ //
+ if (AttemptConfigOrder[Index] == mPrivate->BootSelectedIndex) {
+ IScsiSessionAbort (Private->Session);
+ FreePool (Private->Session);
+ Private->Session = NULL;
+ gBS->UninstallProtocolInterface (
+ Private->ExtScsiPassThruHandle,
+ &gEfiExtScsiPassThruProtocolGuid,
+ &Private->IScsiExtScsiPassThru
+ );
+ Private->ExtScsiPassThruHandle = NULL;
+
+ //
+ // Reinstall the original ExtScsiPassThru back.
+ //
+ Status = gBS->InstallProtocolInterface (
+ &ExistPrivate->ExtScsiPassThruHandle,
+ &gEfiExtScsiPassThruProtocolGuid,
+ EFI_NATIVE_INTERFACE,
+ &ExistPrivate->IScsiExtScsiPassThru
+ );
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+
+ goto ON_EXIT;
+ } else {
+ if (AttemptConfigOrder[Index] != BootSelected) {
+ goto ON_ERROR;
+ }
+ mPrivate->BootSelectedIndex = BootSelected;
+ //
+ // Clear the resource in ExistPrivate.
+ //
+ gBS->UninstallProtocolInterface (
+ ExistPrivate->Controller,
+ IScsiPrivateGuid,
+ &ExistPrivate->IScsiIdentifier
+ );
+
+ IScsiRemoveNic (ExistPrivate->Controller);
+ if (ExistPrivate->Session != NULL) {
+ IScsiSessionAbort (ExistPrivate->Session);
+ }
+
+ if (ExistPrivate->DevicePath != NULL) {
+ Status = gBS->UninstallProtocolInterface (
+ ExistPrivate->ExtScsiPassThruHandle,
+ &gEfiDevicePathProtocolGuid,
+ ExistPrivate->DevicePath
+ );
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+
+ FreePool (ExistPrivate->DevicePath);
+ }
+
+ gBS->CloseEvent (ExistPrivate->ExitBootServiceEvent);
+ FreePool (ExistPrivate);
+
+ }
+ } else {
+ //
+ // Use the attempt in earlier order as boot selected in single path mode.
+ //
+ if (AttemptConfigOrder[Index] == mPrivate->BootSelectedIndex) {
+ NeedUpdate = FALSE;
+ }
+ }
+
+ }
+
+ if (NeedUpdate) {
+ mPrivate->OneSessionEstablished = TRUE;
+ mPrivate->BootSelectedIndex = BootSelected;
+ }
+
+ //
+ // Duplicate the Session's tcp connection device path. The source port field
+ // will be set to zero as one iSCSI session is comprised of several iSCSI
+ // connections.
+ //
+ Private->DevicePath = IScsiGetTcpConnDevicePath (Private->Session);
+ if (Private->DevicePath == NULL) {
+ Status = EFI_DEVICE_ERROR;
+ goto ON_ERROR;
+ }
+ //
+ // Install the updated device path onto the ExtScsiPassThruHandle.
+ //
+ Status = gBS->InstallProtocolInterface (
+ &Private->ExtScsiPassThruHandle,
+ &gEfiDevicePathProtocolGuid,
+ EFI_NATIVE_INTERFACE,
+ Private->DevicePath
+ );
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+
+ //
+ // ISCSI children should share the default Tcp child, just open the default Tcp child via BY_CHILD_CONTROLLER.
+ //
+ Status = gBS->OpenProtocol (
+ Private->ChildHandle, /// Default Tcp child
+ ProtocolGuid,
+ &Interface,
+ Image,
+ Private->ExtScsiPassThruHandle,
+ EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER
+ );
+ if (EFI_ERROR (Status)) {
+ gBS->UninstallMultipleProtocolInterfaces (
+ Private->ExtScsiPassThruHandle,
+ &gEfiExtScsiPassThruProtocolGuid,
+ &Private->IScsiExtScsiPassThru,
+ &gEfiDevicePathProtocolGuid,
+ Private->DevicePath,
+ NULL
+ );
+
+ goto ON_ERROR;
+ }
+
+ON_EXIT:
+
+ //
+ // Update/Publish the iSCSI Boot Firmware Table.
+ //
+ if (mPrivate->BootSelectedIndex != 0) {
+ IScsiPublishIbft ();
+ }
+
+ return EFI_SUCCESS;
+
+ON_ERROR:
+
+ if (Private->Session != NULL) {
+ IScsiSessionAbort (Private->Session);
+ }
+
+ return Status;
+}
+
+/**
+ Stops a device controller or a bus controller. This is the worker function for
+ IScsiIp4(6)DriverBindingStop.
+
+ @param[in] This A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance.
+ @param[in] ControllerHandle A handle to the device being stopped. The handle must
+ support a bus specific I/O protocol for the driver
+ to use to stop the device.
+ @param[in] NumberOfChildren The number of child device handles in ChildHandleBuffer.
+ @param[in] ChildHandleBuffer An array of child handles to be freed. May be NULL
+ if NumberOfChildren is 0.
+ @param[in] IpVersion IP_VERSION_4 or IP_VERSION_6.
+
+ @retval EFI_SUCCESS The device was stopped.
+ @retval EFI_DEVICE_ERROR The device could not be stopped due to a device error.
+ @retval EFI_INVALID_PARAMETER Child handle is NULL.
+ @retval EFI_ACCESS_DENIED The protocol could not be removed from the Handle
+ because its interfaces are being used.
+
+**/
+EFI_STATUS
+EFIAPI
+IScsiStop (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN UINTN NumberOfChildren,
+ IN EFI_HANDLE *ChildHandleBuffer OPTIONAL,
+ IN UINT8 IpVersion
+ )
+{
+ EFI_HANDLE IScsiController;
+ EFI_STATUS Status;
+ ISCSI_PRIVATE_PROTOCOL *IScsiIdentifier;
+ ISCSI_DRIVER_DATA *Private;
+ EFI_EXT_SCSI_PASS_THRU_PROTOCOL *PassThru;
+ ISCSI_CONNECTION *Conn;
+ EFI_GUID *ProtocolGuid;
+ EFI_GUID *TcpServiceBindingGuid;
+ EFI_GUID *TcpProtocolGuid;
+
+
+ if (NumberOfChildren != 0) {
+ //
+ // We should have only one child.
+ //
+ Status = gBS->OpenProtocol (
+ ChildHandleBuffer[0],
+ &gEfiExtScsiPassThruProtocolGuid,
+ (VOID **) &PassThru,
+ This->DriverBindingHandle,
+ ControllerHandle,
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL
+ );
+ if (EFI_ERROR (Status)) {
+ return EFI_DEVICE_ERROR;
+ }
+
+ Private = ISCSI_DRIVER_DATA_FROM_EXT_SCSI_PASS_THRU (PassThru);
+ Conn = NET_LIST_HEAD (&Private->Session->Conns, ISCSI_CONNECTION, Link);
+
+ //
+ // Previously the TCP protocol is opened BY_CHILD_CONTROLLER. Just close
+ // the protocol here, but do not uninstall the device path protocol and
+ // EXT SCSI PASS THRU protocol installed on ExtScsiPassThruHandle.
+ //
+ if (IpVersion == IP_VERSION_4) {
+ ProtocolGuid = &gEfiTcp4ProtocolGuid;
+ } else {
+ ProtocolGuid = &gEfiTcp6ProtocolGuid;
+ }
+
+ gBS->CloseProtocol (
+ Private->ChildHandle,
+ ProtocolGuid,
+ Private->Image,
+ Private->ExtScsiPassThruHandle
+ );
+
+ gBS->CloseProtocol (
+ Conn->TcpIo.Handle,
+ ProtocolGuid,
+ Private->Image,
+ Private->ExtScsiPassThruHandle
+ );
+
+ return EFI_SUCCESS;
+ }
+
+ //
+ // Get the handle of the controller we are controlling.
+ //
+ if (IpVersion == IP_VERSION_4) {
+ ProtocolGuid = &gIScsiV4PrivateGuid;
+ TcpProtocolGuid = &gEfiTcp4ProtocolGuid;
+ TcpServiceBindingGuid = &gEfiTcp4ServiceBindingProtocolGuid;
+ } else {
+ ProtocolGuid = &gIScsiV6PrivateGuid;
+ TcpProtocolGuid = &gEfiTcp6ProtocolGuid;
+ TcpServiceBindingGuid = &gEfiTcp6ServiceBindingProtocolGuid;
+ }
+ IScsiController = NetLibGetNicHandle (ControllerHandle, TcpProtocolGuid);
+ if (IScsiController == NULL) {
+ return EFI_SUCCESS;
+ }
+
+ Status = gBS->OpenProtocol (
+ IScsiController,
+ ProtocolGuid,
+ (VOID **) &IScsiIdentifier,
+ This->DriverBindingHandle,
+ ControllerHandle,
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL
+ );
+ if (EFI_ERROR (Status)) {
+ return EFI_DEVICE_ERROR;
+ }
+
+ Private = ISCSI_DRIVER_DATA_FROM_IDENTIFIER (IScsiIdentifier);
+ ASSERT (Private != NULL);
+
+ if (Private->ChildHandle != NULL) {
+ Status = gBS->CloseProtocol (
+ Private->ChildHandle,
+ TcpProtocolGuid,
+ This->DriverBindingHandle,
+ IScsiController
+ );
+
+ ASSERT (!EFI_ERROR (Status));
+
+ Status = NetLibDestroyServiceChild (
+ IScsiController,
+ This->DriverBindingHandle,
+ TcpServiceBindingGuid,
+ Private->ChildHandle
+ );
+
+ ASSERT (!EFI_ERROR (Status));
+ }
+
+ gBS->UninstallProtocolInterface (
+ IScsiController,
+ ProtocolGuid,
+ &Private->IScsiIdentifier
+ );
+
+ //
+ // Remove this NIC.
+ //
+ IScsiRemoveNic (IScsiController);
+
+ //
+ // Update the iSCSI Boot Firmware Table.
+ //
+ IScsiPublishIbft ();
+
+ if (Private->Session != NULL) {
+ IScsiSessionAbort (Private->Session);
+ }
+
+ Status = IScsiCleanDriverData (Private);
+
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Tests to see if this driver supports a given controller. If a child device is provided,
+ it tests to see if this driver supports creating a handle for the specified child device.
+
+ This function checks to see if the driver specified by This supports the device specified by
+ ControllerHandle. Drivers typically use the device path attached to
+ ControllerHandle and/or the services from the bus I/O abstraction attached to
+ ControllerHandle to determine if the driver supports ControllerHandle. This function
+ may be called many times during platform initialization. In order to reduce boot times, the tests
+ performed by this function must be very small and take as little time as possible to execute. This
+ function must not change the state of any hardware devices, and this function must be aware that the
+ device specified by ControllerHandle may already be managed by the same driver or a
+ different driver. This function must match its calls to AllocatePages() with FreePages(),
+ AllocatePool() with FreePool(), and OpenProtocol() with CloseProtocol().
+ Since ControllerHandle may have been previously started by the same driver, if a protocol is
+ already in the opened state, then it must not be closed with CloseProtocol(). This is required
+ to guarantee the state of ControllerHandle is not modified by this function.
+
+ @param[in] This A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance.
+ @param[in] ControllerHandle The handle of the controller to test. This handle
+ must support a protocol interface that supplies
+ an I/O abstraction to the driver.
+ @param[in] RemainingDevicePath A pointer to the remaining portion of a device path. This
+ parameter is ignored by device drivers, and is optional for bus
+ drivers. For bus drivers, if this parameter is not NULL, then
+ the bus driver must determine if the bus controller specified
+ by ControllerHandle and the child controller specified
+ by RemainingDevicePath are both supported by this
+ bus driver.
+
+ @retval EFI_SUCCESS The device specified by ControllerHandle and
+ RemainingDevicePath is supported by the driver specified by This.
+ @retval EFI_ALREADY_STARTED The device specified by ControllerHandle and
+ RemainingDevicePath is already managed by the driver
+ specified by This.
+ @retval EFI_ACCESS_DENIED The device specified by ControllerHandle and
+ RemainingDevicePath is already managed by a different
+ driver or an application that requires exclusive access.
+ Currently not implemented.
+ @retval EFI_UNSUPPORTED The device specified by ControllerHandle and
+ RemainingDevicePath is not supported by the driver specified by This.
+**/
+EFI_STATUS
+EFIAPI
+IScsiIp4DriverBindingSupported (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL
+ )
+{
+ return IScsiSupported (
+ This,
+ ControllerHandle,
+ RemainingDevicePath,
+ IP_VERSION_4
+ );
+}
+
+/**
+ Starts a device controller or a bus controller.
+
+ The Start() function is designed to be invoked from the EFI boot service ConnectController().
+ As a result, much of the error checking on the parameters to Start() has been moved into this
+ common boot service. It is legal to call Start() from other locations,
+ but the following calling restrictions must be followed or the system behavior will not be deterministic.
+ 1. ControllerHandle must be a valid EFI_HANDLE.
+ 2. If RemainingDevicePath is not NULL, then it must be a pointer to a naturally aligned
+ EFI_DEVICE_PATH_PROTOCOL.
+ 3. Prior to calling Start(), the Supported() function for the driver specified by This must
+ have been called with the same calling parameters, and Supported() must have returned EFI_SUCCESS.
+
+ @param[in] This A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance.
+ @param[in] ControllerHandle The handle of the controller to start. This handle
+ must support a protocol interface that supplies
+ an I/O abstraction to the driver.
+ @param[in] RemainingDevicePath A pointer to the remaining portion of a device path. This
+ parameter is ignored by device drivers, and is optional for bus
+ drivers. For a bus driver, if this parameter is NULL, then handles
+ for all the children of Controller are created by this driver.
+ If this parameter is not NULL and the first Device Path Node is
+ not the End of Device Path Node, then only the handle for the
+ child device specified by the first Device Path Node of
+ RemainingDevicePath is created by this driver.
+ If the first Device Path Node of RemainingDevicePath is
+ the End of Device Path Node, no child handle is created by this
+ driver.
+
+ @retval EFI_SUCCESS The device was started.
+ @retval EFI_DEVICE_ERROR The device could not be started due to a device error. Currently not implemented.
+ @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a lack of resources.
+ @retval Others The driver failed to start the device.
+
+**/
+EFI_STATUS
+EFIAPI
+IScsiIp4DriverBindingStart (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL
+ )
+{
+ EFI_STATUS Status;
+
+ Status = IScsiStart (This->DriverBindingHandle, ControllerHandle, IP_VERSION_4);
+ if (Status == EFI_ALREADY_STARTED) {
+ Status = EFI_SUCCESS;
+ }
+
+ return Status;
+}
+
+/**
+ Stops a device controller or a bus controller.
+
+ The Stop() function is designed to be invoked from the EFI boot service DisconnectController().
+ As a result, much of the error checking on the parameters to Stop() has been moved
+ into this common boot service. It is legal to call Stop() from other locations,
+ but the following calling restrictions must be followed or the system behavior will not be deterministic.
+ 1. ControllerHandle must be a valid EFI_HANDLE that was used on a previous call to this
+ same driver's Start() function.
+ 2. The first NumberOfChildren handles of ChildHandleBuffer must all be a valid
+ EFI_HANDLE. In addition, all of these handles must have been created in this driver's
+ Start() function, and the Start() function must have called OpenProtocol() on
+ ControllerHandle with an Attribute of EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER.
+
+ @param[in] This A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance.
+ @param[in] ControllerHandle A handle to the device being stopped. The handle must
+ support a bus specific I/O protocol for the driver
+ to use to stop the device.
+ @param[in] NumberOfChildren The number of child device handles in ChildHandleBuffer.
+ @param[in] ChildHandleBuffer An array of child handles to be freed. May be NULL
+ if NumberOfChildren is 0.
+
+ @retval EFI_SUCCESS The device was stopped.
+ @retval EFI_DEVICE_ERROR The device could not be stopped due to a device error.
+
+**/
+EFI_STATUS
+EFIAPI
+IScsiIp4DriverBindingStop (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN UINTN NumberOfChildren,
+ IN EFI_HANDLE *ChildHandleBuffer OPTIONAL
+ )
+{
+ return IScsiStop (
+ This,
+ ControllerHandle,
+ NumberOfChildren,
+ ChildHandleBuffer,
+ IP_VERSION_4
+ );
+}
+
+/**
+ Tests to see if this driver supports a given controller. If a child device is provided,
+ it tests to see if this driver supports creating a handle for the specified child device.
+
+ This function checks to see if the driver specified by This supports the device specified by
+ ControllerHandle. Drivers typically use the device path attached to
+ ControllerHandle and/or the services from the bus I/O abstraction attached to
+ ControllerHandle to determine if the driver supports ControllerHandle. This function
+ may be called many times during platform initialization. In order to reduce boot times, the tests
+ performed by this function must be very small and take as little time as possible to execute. This
+ function must not change the state of any hardware devices, and this function must be aware that the
+ device specified by ControllerHandle may already be managed by the same driver or a
+ different driver. This function must match its calls to AllocatePages() with FreePages(),
+ AllocatePool() with FreePool(), and OpenProtocol() with CloseProtocol().
+ Since ControllerHandle may have been previously started by the same driver, if a protocol is
+ already in the opened state, then it must not be closed with CloseProtocol(). This is required
+ to guarantee the state of ControllerHandle is not modified by this function.
+
+ @param[in] This A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance.
+ @param[in] ControllerHandle The handle of the controller to test. This handle
+ must support a protocol interface that supplies
+ an I/O abstraction to the driver.
+ @param[in] RemainingDevicePath A pointer to the remaining portion of a device path. This
+ parameter is ignored by device drivers, and is optional for bus
+ drivers. For bus drivers, if this parameter is not NULL, then
+ the bus driver must determine if the bus controller specified
+ by ControllerHandle and the child controller specified
+ by RemainingDevicePath are both supported by this
+ bus driver.
+
+ @retval EFI_SUCCESS The device specified by ControllerHandle and
+ RemainingDevicePath is supported by the driver specified by This.
+ @retval EFI_ALREADY_STARTED The device specified by ControllerHandle and
+ RemainingDevicePath is already managed by the driver
+ specified by This.
+ @retval EFI_ACCESS_DENIED The device specified by ControllerHandle and
+ RemainingDevicePath is already managed by a different
+ driver or an application that requires exclusive access.
+ Currently not implemented.
+ @retval EFI_UNSUPPORTED The device specified by ControllerHandle and
+ RemainingDevicePath is not supported by the driver specified by This.
+**/
+EFI_STATUS
+EFIAPI
+IScsiIp6DriverBindingSupported (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL
+ )
+{
+ return IScsiSupported (
+ This,
+ ControllerHandle,
+ RemainingDevicePath,
+ IP_VERSION_6
+ );
+}
+
+/**
+ Starts a device controller or a bus controller.
+
+ The Start() function is designed to be invoked from the EFI boot service ConnectController().
+ As a result, much of the error checking on the parameters to Start() has been moved into this
+ common boot service. It is legal to call Start() from other locations,
+ but the following calling restrictions must be followed or the system behavior will not be deterministic.
+ 1. ControllerHandle must be a valid EFI_HANDLE.
+ 2. If RemainingDevicePath is not NULL, then it must be a pointer to a naturally aligned
+ EFI_DEVICE_PATH_PROTOCOL.
+ 3. Prior to calling Start(), the Supported() function for the driver specified by This must
+ have been called with the same calling parameters, and Supported() must have returned EFI_SUCCESS.
+
+ @param[in] This A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance.
+ @param[in] ControllerHandle The handle of the controller to start. This handle
+ must support a protocol interface that supplies
+ an I/O abstraction to the driver.
+ @param[in] RemainingDevicePath A pointer to the remaining portion of a device path. This
+ parameter is ignored by device drivers, and is optional for bus
+ drivers. For a bus driver, if this parameter is NULL, then handles
+ for all the children of Controller are created by this driver.
+ If this parameter is not NULL and the first Device Path Node is
+ not the End of Device Path Node, then only the handle for the
+ child device specified by the first Device Path Node of
+ RemainingDevicePath is created by this driver.
+ If the first Device Path Node of RemainingDevicePath is
+ the End of Device Path Node, no child handle is created by this
+ driver.
+
+ @retval EFI_SUCCESS The device was started.
+ @retval EFI_DEVICE_ERROR The device could not be started due to a device error. Currently not implemented.
+ @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a lack of resources.
+ @retval Others The driver failed to start the device.
+
+**/
+EFI_STATUS
+EFIAPI
+IScsiIp6DriverBindingStart (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL
+ )
+{
+ EFI_STATUS Status;
+
+ Status = IScsiStart (This->DriverBindingHandle, ControllerHandle, IP_VERSION_6);
+ if (Status == EFI_ALREADY_STARTED) {
+ Status = EFI_SUCCESS;
+ }
+
+ return Status;
+}
+
+/**
+ Stops a device controller or a bus controller.
+
+ The Stop() function is designed to be invoked from the EFI boot service DisconnectController().
+ As a result, much of the error checking on the parameters to Stop() has been moved
+ into this common boot service. It is legal to call Stop() from other locations,
+ but the following calling restrictions must be followed or the system behavior will not be deterministic.
+ 1. ControllerHandle must be a valid EFI_HANDLE that was used on a previous call to this
+ same driver's Start() function.
+ 2. The first NumberOfChildren handles of ChildHandleBuffer must all be a valid
+ EFI_HANDLE. In addition, all of these handles must have been created in this driver's
+ Start() function, and the Start() function must have called OpenProtocol() on
+ ControllerHandle with an Attribute of EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER.
+
+ @param[in] This A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance.
+ @param[in] ControllerHandle A handle to the device being stopped. The handle must
+ support a bus specific I/O protocol for the driver
+ to use to stop the device.
+ @param[in] NumberOfChildren The number of child device handles in ChildHandleBuffer.
+ @param[in] ChildHandleBuffer An array of child handles to be freed. May be NULL
+ if NumberOfChildren is 0.
+
+ @retval EFI_SUCCESS The device was stopped.
+ @retval EFI_DEVICE_ERROR The device could not be stopped due to a device error.
+
+**/
+EFI_STATUS
+EFIAPI
+IScsiIp6DriverBindingStop (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN UINTN NumberOfChildren,
+ IN EFI_HANDLE *ChildHandleBuffer OPTIONAL
+ )
+{
+ return IScsiStop (
+ This,
+ ControllerHandle,
+ NumberOfChildren,
+ ChildHandleBuffer,
+ IP_VERSION_6
+ );
+}
+
+/**
+ Unload the iSCSI driver.
+
+ @param[in] ImageHandle The handle of the driver image.
+
+ @retval EFI_SUCCESS The driver is unloaded.
+ @retval EFI_DEVICE_ERROR An unexpected error occurred.
+
+**/
+EFI_STATUS
+EFIAPI
+IScsiUnload (
+ IN EFI_HANDLE ImageHandle
+ )
+{
+ EFI_STATUS Status;
+ UINTN DeviceHandleCount;
+ EFI_HANDLE *DeviceHandleBuffer;
+ UINTN Index;
+ EFI_COMPONENT_NAME_PROTOCOL *ComponentName;
+ EFI_COMPONENT_NAME2_PROTOCOL *ComponentName2;
+
+ //
+ // Try to disconnect the driver from the devices it's controlling.
+ //
+ Status = gBS->LocateHandleBuffer (
+ AllHandles,
+ NULL,
+ NULL,
+ &DeviceHandleCount,
+ &DeviceHandleBuffer
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ //
+ // Disconnect the iSCSI4 driver from the controlled device.
+ //
+ for (Index = 0; Index < DeviceHandleCount; Index++) {
+ Status = IScsiTestManagedDevice (
+ DeviceHandleBuffer[Index],
+ gIScsiIp4DriverBinding.DriverBindingHandle,
+ &gEfiTcp4ProtocolGuid)
+ ;
+ if (EFI_ERROR (Status)) {
+ continue;
+ }
+ Status = gBS->DisconnectController (
+ DeviceHandleBuffer[Index],
+ gIScsiIp4DriverBinding.DriverBindingHandle,
+ NULL
+ );
+ if (EFI_ERROR (Status)) {
+ goto ON_EXIT;
+ }
+ }
+
+ //
+ // Disconnect the iSCSI6 driver from the controlled device.
+ //
+ for (Index = 0; Index < DeviceHandleCount; Index++) {
+ Status = IScsiTestManagedDevice (
+ DeviceHandleBuffer[Index],
+ gIScsiIp6DriverBinding.DriverBindingHandle,
+ &gEfiTcp6ProtocolGuid
+ );
+ if (EFI_ERROR (Status)) {
+ continue;
+ }
+ Status = gBS->DisconnectController (
+ DeviceHandleBuffer[Index],
+ gIScsiIp6DriverBinding.DriverBindingHandle,
+ NULL
+ );
+ if (EFI_ERROR (Status)) {
+ goto ON_EXIT;
+ }
+ }
+
+ //
+ // Unload the iSCSI configuration form.
+ //
+ Status = IScsiConfigFormUnload (gIScsiIp4DriverBinding.DriverBindingHandle);
+ if (EFI_ERROR (Status)) {
+ goto ON_EXIT;
+ }
+
+ //
+ // Uninstall the protocols installed by iSCSI driver.
+ //
+ Status = gBS->UninstallMultipleProtocolInterfaces (
+ ImageHandle,
+ &gEfiAuthenticationInfoProtocolGuid,
+ &gIScsiAuthenticationInfo,
+ NULL
+ );
+ if (EFI_ERROR (Status)) {
+ goto ON_EXIT;
+ }
+
+ if (gIScsiControllerNameTable!= NULL) {
+ Status = FreeUnicodeStringTable (gIScsiControllerNameTable);
+ if (EFI_ERROR (Status)) {
+ goto ON_EXIT;
+ }
+ gIScsiControllerNameTable = NULL;
+ }
+
+ //
+ // Uninstall the ComponentName and ComponentName2 protocol from iSCSI4 driver binding handle
+ // if it has been installed.
+ //
+ Status = gBS->HandleProtocol (
+ gIScsiIp4DriverBinding.DriverBindingHandle,
+ &gEfiComponentNameProtocolGuid,
+ (VOID **) &ComponentName
+ );
+ if (!EFI_ERROR (Status)) {
+ Status = gBS->UninstallMultipleProtocolInterfaces (
+ gIScsiIp4DriverBinding.DriverBindingHandle,
+ &gEfiComponentNameProtocolGuid,
+ ComponentName,
+ NULL
+ );
+ if (EFI_ERROR (Status)) {
+ goto ON_EXIT;
+ }
+ }
+
+ Status = gBS->HandleProtocol (
+ gIScsiIp4DriverBinding.DriverBindingHandle,
+ &gEfiComponentName2ProtocolGuid,
+ (VOID **) &ComponentName2
+ );
+ if (!EFI_ERROR (Status)) {
+ gBS->UninstallMultipleProtocolInterfaces (
+ gIScsiIp4DriverBinding.DriverBindingHandle,
+ &gEfiComponentName2ProtocolGuid,
+ ComponentName2,
+ NULL
+ );
+ if (EFI_ERROR (Status)) {
+ goto ON_EXIT;
+ }
+ }
+
+ //
+ // Uninstall the ComponentName and ComponentName2 protocol from iSCSI6 driver binding handle
+ // if it has been installed.
+ //
+ Status = gBS->HandleProtocol (
+ gIScsiIp6DriverBinding.DriverBindingHandle,
+ &gEfiComponentNameProtocolGuid,
+ (VOID **) &ComponentName
+ );
+ if (!EFI_ERROR (Status)) {
+ Status = gBS->UninstallMultipleProtocolInterfaces (
+ gIScsiIp6DriverBinding.DriverBindingHandle,
+ &gEfiComponentNameProtocolGuid,
+ ComponentName,
+ NULL
+ );
+ if (EFI_ERROR (Status)) {
+ goto ON_EXIT;
+ }
+ }
+
+ Status = gBS->HandleProtocol (
+ gIScsiIp6DriverBinding.DriverBindingHandle,
+ &gEfiComponentName2ProtocolGuid,
+ (VOID **) &ComponentName2
+ );
+ if (!EFI_ERROR (Status)) {
+ gBS->UninstallMultipleProtocolInterfaces (
+ gIScsiIp6DriverBinding.DriverBindingHandle,
+ &gEfiComponentName2ProtocolGuid,
+ ComponentName2,
+ NULL
+ );
+ if (EFI_ERROR (Status)) {
+ goto ON_EXIT;
+ }
+ }
+
+ //
+ // Uninstall the IScsiInitiatorNameProtocol and all the driver binding protocols.
+ //
+ Status = gBS->UninstallMultipleProtocolInterfaces (
+ gIScsiIp4DriverBinding.DriverBindingHandle,
+ &gEfiDriverBindingProtocolGuid,
+ &gIScsiIp4DriverBinding,
+ &gEfiIScsiInitiatorNameProtocolGuid,
+ &gIScsiInitiatorName,
+ NULL
+ );
+ if (EFI_ERROR (Status)) {
+ goto ON_EXIT;
+ }
+
+ Status = gBS->UninstallMultipleProtocolInterfaces (
+ gIScsiIp6DriverBinding.DriverBindingHandle,
+ &gEfiDriverBindingProtocolGuid,
+ &gIScsiIp6DriverBinding,
+ NULL
+ );
+
+ON_EXIT:
+
+ if (DeviceHandleBuffer != NULL) {
+ FreePool (DeviceHandleBuffer);
+ }
+
+ return Status;
+}
+
+/**
+ This is the declaration of an EFI image entry point. This entry point is
+ the same for UEFI Applications, UEFI OS Loaders, and UEFI Drivers including
+ both device drivers and bus drivers.
+
+ The entry point for iSCSI driver which initializes the global variables and
+ installs the driver binding, component name protocol, iSCSI initiator name
+ protocol and Authentication Info protocol on its image.
+
+ @param[in] ImageHandle The firmware allocated handle for the UEFI image.
+ @param[in] SystemTable A pointer to the EFI System Table.
+
+ @retval EFI_SUCCESS The operation completed successfully.
+ @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a lack of resources.
+
+**/
+EFI_STATUS
+EFIAPI
+IScsiDriverEntryPoint (
+ IN EFI_HANDLE ImageHandle,
+ IN EFI_SYSTEM_TABLE *SystemTable
+ )
+{
+ EFI_STATUS Status;
+ EFI_ISCSI_INITIATOR_NAME_PROTOCOL *IScsiInitiatorName;
+ EFI_AUTHENTICATION_INFO_PROTOCOL *AuthenticationInfo;
+
+ //
+ // There should be only one EFI_ISCSI_INITIATOR_NAME_PROTOCOL.
+ //
+ Status = gBS->LocateProtocol (
+ &gEfiIScsiInitiatorNameProtocolGuid,
+ NULL,
+ (VOID **) &IScsiInitiatorName
+ );
+ if (!EFI_ERROR (Status)) {
+ return EFI_ACCESS_DENIED;
+ }
+
+ //
+ // Initialize the EFI Driver Library.
+ //
+ Status = EfiLibInstallDriverBindingComponentName2 (
+ ImageHandle,
+ SystemTable,
+ &gIScsiIp4DriverBinding,
+ ImageHandle,
+ &gIScsiComponentName,
+ &gIScsiComponentName2
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ Status = EfiLibInstallDriverBindingComponentName2 (
+ ImageHandle,
+ SystemTable,
+ &gIScsiIp6DriverBinding,
+ NULL,
+ &gIScsiComponentName,
+ &gIScsiComponentName2
+ );
+ if (EFI_ERROR (Status)) {
+ goto Error1;
+ }
+
+ //
+ // Install the iSCSI Initiator Name Protocol.
+ //
+ Status = gBS->InstallProtocolInterface (
+ &ImageHandle,
+ &gEfiIScsiInitiatorNameProtocolGuid,
+ EFI_NATIVE_INTERFACE,
+ &gIScsiInitiatorName
+ );
+ if (EFI_ERROR (Status)) {
+ goto Error2;
+ }
+
+ //
+ // Create the private data structures.
+ //
+ mPrivate = AllocateZeroPool (sizeof (ISCSI_PRIVATE_DATA));
+ if (mPrivate == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ goto Error3;
+ }
+
+ InitializeListHead (&mPrivate->NicInfoList);
+ InitializeListHead (&mPrivate->AttemptConfigs);
+
+ //
+ // Initialize the configuration form of iSCSI.
+ //
+ Status = IScsiConfigFormInit (gIScsiIp4DriverBinding.DriverBindingHandle);
+ if (EFI_ERROR (Status)) {
+ goto Error4;
+ }
+
+ //
+ // Create the Maximum Attempts.
+ //
+ Status = IScsiCreateAttempts (PcdGet8 (PcdMaxIScsiAttemptNumber));
+ if (EFI_ERROR (Status)) {
+ goto Error5;
+ }
+
+ //
+ // Create Keywords for all the Attempts.
+ //
+ Status = IScsiCreateKeywords (PcdGet8 (PcdMaxIScsiAttemptNumber));
+ if (EFI_ERROR (Status)) {
+ goto Error6;
+ }
+
+ //
+ // There should be only one EFI_AUTHENTICATION_INFO_PROTOCOL. If already exists,
+ // do not produce the protocol instance.
+ //
+ Status = gBS->LocateProtocol (
+ &gEfiAuthenticationInfoProtocolGuid,
+ NULL,
+ (VOID **) &AuthenticationInfo
+ );
+ if (Status == EFI_NOT_FOUND) {
+ Status = gBS->InstallProtocolInterface (
+ &ImageHandle,
+ &gEfiAuthenticationInfoProtocolGuid,
+ EFI_NATIVE_INTERFACE,
+ &gIScsiAuthenticationInfo
+ );
+ if (EFI_ERROR (Status)) {
+ goto Error6;
+ }
+ }
+
+ return EFI_SUCCESS;
+
+Error6:
+ IScsiCleanAttemptVariable ();
+
+Error5:
+ IScsiConfigFormUnload (gIScsiIp4DriverBinding.DriverBindingHandle);
+
+Error4:
+ if (mPrivate != NULL) {
+ FreePool (mPrivate);
+ mPrivate = NULL;
+ }
+
+Error3:
+ gBS->UninstallMultipleProtocolInterfaces (
+ ImageHandle,
+ &gEfiIScsiInitiatorNameProtocolGuid,
+ &gIScsiInitiatorName,
+ NULL
+ );
+
+Error2:
+ EfiLibUninstallDriverBindingComponentName2 (
+ &gIScsiIp6DriverBinding,
+ &gIScsiComponentName,
+ &gIScsiComponentName2
+ );
+
+Error1:
+ EfiLibUninstallDriverBindingComponentName2 (
+ &gIScsiIp4DriverBinding,
+ &gIScsiComponentName,
+ &gIScsiComponentName2
+ );
+
+ return Status;
+}
+