diff options
Diffstat (limited to 'src/VBox/Devices/EFI/Firmware/NetworkPkg/IScsiDxe/IScsiDriver.c')
-rw-r--r-- | src/VBox/Devices/EFI/Firmware/NetworkPkg/IScsiDxe/IScsiDriver.c | 1874 |
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; +} + |