/** @file Implementation for iSCSI Boot Firmware Table publication. Copyright (c) 2004 - 2018, Intel Corporation. All rights reserved.
SPDX-License-Identifier: BSD-2-Clause-Patent **/ #include "IScsiImpl.h" BOOLEAN mIbftInstalled = FALSE; UINTN mTableKey; /** Initialize the header of the iSCSI Boot Firmware Table. @param[out] Header The header of the iSCSI Boot Firmware Table. @param[in] OemId The OEM ID. @param[in] OemTableId The OEM table ID for the iBFT. **/ VOID IScsiInitIbfTableHeader ( OUT EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_HEADER *Header, IN UINT8 *OemId, IN UINT64 *OemTableId ) { Header->Signature = EFI_ACPI_3_0_ISCSI_BOOT_FIRMWARE_TABLE_SIGNATURE; Header->Length = IBFT_HEAP_OFFSET; Header->Revision = EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_REVISION; Header->Checksum = 0; CopyMem (Header->OemId, OemId, sizeof (Header->OemId)); CopyMem (&Header->OemTableId, OemTableId, sizeof (UINT64)); } /** Initialize the control section of the iSCSI Boot Firmware Table. @param[in] Table The ACPI table. **/ VOID IScsiInitControlSection ( IN EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_HEADER *Table ) { EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_CONTROL_STRUCTURE *Control; UINTN NumOffset; Control = (EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_CONTROL_STRUCTURE *) (Table + 1); Control->Header.StructureId = EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_CONTROL_STRUCTURE_ID; Control->Header.Version = EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_CONTROL_STRUCTURE_VERSION; Control->Header.Length = (UINT16) sizeof (EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_CONTROL_STRUCTURE); // // If in multipathing mode, enable the Boot Failover Flag. // If in single path mode, disable it. Mix-model is not allowed. // // BUGBUG: if Boot Failover Flag is set to 1, the OS installer cannot // find the iSCSI mapped disk. So still keep not set for single path mode. // if (mPrivate->EnableMpio) { Control->Header.Flags = 0; NumOffset = 2 * (mPrivate->MpioCount - mPrivate->Krb5MpioCount); } else { NumOffset = 2 * mPrivate->ValidSinglePathCount; } // // Each attempt occupies two offsets: one for the NIC section; // the other for the Target section. // if (NumOffset > 4) { // // Need expand the control section if more than 2 NIC/Target attempts // exist. // Control->Header.Length = (UINT16) (Control->Header.Length + (NumOffset - 4) * sizeof (UINT16)); } } /** Add one item into the heap. @param[in, out] Heap On input, the current address of the heap. On output, the address of the heap after the item is added. @param[in] Data The data to add into the heap. @param[in] Len Length of the Data in byte. **/ VOID IScsiAddHeapItem ( IN OUT UINT8 **Heap, IN VOID *Data, IN UINTN Len ) { // // Add one byte for the NULL delimiter. // *Heap -= Len + 1; CopyMem (*Heap, Data, Len); *(*Heap + Len) = 0; } /** Fill the Initiator section of the iSCSI Boot Firmware Table. @param[in] Table The ACPI table. @param[in, out] Heap The heap. **/ VOID IScsiFillInitiatorSection ( IN EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_HEADER *Table, IN OUT UINT8 **Heap ) { EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_CONTROL_STRUCTURE *Control; EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_INITIATOR_STRUCTURE *Initiator; Control = (EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_CONTROL_STRUCTURE *) (Table + 1); // // Initiator section immediately follows the control section. // Initiator = (EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_INITIATOR_STRUCTURE *) ((UINT8 *) Control + IBFT_ROUNDUP (Control->Header.Length)); Control->InitiatorOffset = (UINT16) ((UINTN) Initiator - (UINTN) Table); Initiator->Header.StructureId = EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_INITIATOR_STRUCTURE_ID; Initiator->Header.Version = EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_INITIATOR_STRUCTURE_VERSION; Initiator->Header.Length = (UINT16) sizeof (EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_INITIATOR_STRUCTURE); Initiator->Header.Flags = EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_INITIATOR_STRUCTURE_FLAG_BLOCK_VALID | EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_INITIATOR_STRUCTURE_FLAG_BOOT_SELECTED; // // Fill the iSCSI Initiator Name into the heap. // IScsiAddHeapItem (Heap, mPrivate->InitiatorName, mPrivate->InitiatorNameLength - 1); Initiator->IScsiNameLength = (UINT16) (mPrivate->InitiatorNameLength - 1); Initiator->IScsiNameOffset = (UINT16) ((UINTN) *Heap - (UINTN) Table); } /** Map the v4 IP address into v6 IP address. @param[in] V4 The v4 IP address. @param[out] V6 The v6 IP address. **/ VOID IScsiMapV4ToV6Addr ( IN EFI_IPv4_ADDRESS *V4, OUT EFI_IPv6_ADDRESS *V6 ) { UINTN Index; ZeroMem (V6, sizeof (EFI_IPv6_ADDRESS)); V6->Addr[10] = 0xff; V6->Addr[11] = 0xff; for (Index = 0; Index < 4; Index++) { V6->Addr[12 + Index] = V4->Addr[Index]; } } /** Fill the NIC and target sections in iSCSI Boot Firmware Table. @param[in] Table The buffer of the ACPI table. @param[in, out] Heap The heap buffer used to store the variable length parameters such as iSCSI name. **/ VOID IScsiFillNICAndTargetSections ( IN EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_HEADER *Table, IN OUT UINT8 **Heap ) { EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_CONTROL_STRUCTURE *Control; EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_NIC_STRUCTURE *Nic; EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_TARGET_STRUCTURE *Target; ISCSI_SESSION_CONFIG_NVDATA *NvData; ISCSI_CHAP_AUTH_CONFIG_NVDATA *AuthConfig; UINT16 *SectionOffset; UINTN Index; UINT16 Length; LIST_ENTRY *Entry; ISCSI_ATTEMPT_CONFIG_NVDATA *Attempt; ISCSI_NIC_INFO *NicInfo; BOOLEAN Flag; // // Get the offset of the first Nic and Target section. // Control = (EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_CONTROL_STRUCTURE *) (Table + 1); Nic = (EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_NIC_STRUCTURE *) ((UINTN) Table + Control->InitiatorOffset + IBFT_ROUNDUP (sizeof (EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_INITIATOR_STRUCTURE))); Target = (EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_TARGET_STRUCTURE *) ((UINTN) Nic + IBFT_ROUNDUP (sizeof (EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_NIC_STRUCTURE))); SectionOffset = &Control->NIC0Offset; Index = 0; Flag = TRUE; NET_LIST_FOR_EACH (Entry, &mPrivate->AttemptConfigs) { if (Index == 0) { // // First entry should be boot selected entry. // Attempt = IScsiConfigGetAttemptByConfigIndex (mPrivate->BootSelectedIndex); if (Attempt == NULL) { // // First boot selected entry can not be found. // break; } ASSERT (Attempt->SessionConfigData.Enabled != ISCSI_DISABLED); } else { if (Index == 1 && Flag) { Entry = mPrivate->AttemptConfigs.ForwardLink; Flag = FALSE; } Attempt = NET_LIST_USER_STRUCT (Entry, ISCSI_ATTEMPT_CONFIG_NVDATA, Link); if (Attempt->AttemptConfigIndex == mPrivate->BootSelectedIndex) { continue; } } if (Attempt->SessionConfigData.Enabled == ISCSI_DISABLED) { continue; } // // Krb5 attempt will not be recorded in iBFT. // if (Attempt->AuthenticationType == ISCSI_AUTH_TYPE_KRB) { continue; } // // If multipath mode is enabled, only the attempts in MPIO will be recorded in iBFT. // if (mPrivate->EnableMpio && Attempt->SessionConfigData.Enabled != ISCSI_ENABLED_FOR_MPIO) { continue; } // // Only the valid attempts will be recorded. // if (!Attempt->ValidiBFTPath) { continue; } NvData = &Attempt->SessionConfigData; AuthConfig = &Attempt->AuthConfigData.CHAP; // // Fill the Nic section. // Nic->Header.StructureId = EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_NIC_STRUCTURE_ID; Nic->Header.Version = EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_NIC_STRUCTURE_VERSION; Nic->Header.Length = (UINT16) sizeof (EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_NIC_STRUCTURE); Nic->Header.Index = (UINT8) Index; Nic->Header.Flags = EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_NIC_STRUCTURE_FLAG_BLOCK_VALID | EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_NIC_STRUCTURE_FLAG_GLOBAL; if (Index == 0) { Nic->Header.Flags |= EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_NIC_STRUCTURE_FLAG_BOOT_SELECTED; } if (NvData->InitiatorInfoFromDhcp) { Nic->Origin = IpPrefixOriginDhcp; } else { Nic->Origin = IpPrefixOriginManual; } if (NvData->IpMode == IP_MODE_IP4 || NvData->IpMode == IP_MODE_AUTOCONFIG) { // // Get the subnet mask prefix length. // Nic->SubnetMaskPrefixLength = IScsiGetSubnetMaskPrefixLength (&NvData->SubnetMask); // // Map the various v4 addresses into v6 addresses. // IScsiMapV4ToV6Addr (&NvData->LocalIp.v4, &Nic->Ip); IScsiMapV4ToV6Addr (&NvData->Gateway.v4, &Nic->Gateway); IScsiMapV4ToV6Addr (&Attempt->PrimaryDns.v4, &Nic->PrimaryDns); IScsiMapV4ToV6Addr (&Attempt->SecondaryDns.v4, &Nic->SecondaryDns); IScsiMapV4ToV6Addr (&Attempt->DhcpServer.v4, &Nic->DhcpServer); } else if (NvData->IpMode == IP_MODE_IP6 || NvData->IpMode == IP_MODE_AUTOCONFIG) { Nic->SubnetMaskPrefixLength = NvData->PrefixLength; CopyMem (&Nic->Ip, &NvData->LocalIp, sizeof (EFI_IPv6_ADDRESS)); CopyMem (&Nic->Gateway, &NvData->Gateway, sizeof (EFI_IPv6_ADDRESS)); CopyMem (&Nic->PrimaryDns, &Attempt->PrimaryDns, sizeof (EFI_IPv6_ADDRESS)); CopyMem (&Nic->SecondaryDns, &Attempt->SecondaryDns, sizeof (EFI_IPv6_ADDRESS)); CopyMem (&Nic->DhcpServer, &Attempt->DhcpServer, sizeof (EFI_IPv6_ADDRESS)); } else { ASSERT (FALSE); } // // Get Nic Info: VLAN tag, Mac address, PCI location. // NicInfo = IScsiGetNicInfoByIndex (Attempt->NicIndex); ASSERT (NicInfo != NULL); Nic->VLanTag = NicInfo->VlanId; CopyMem (Nic->Mac, &NicInfo->PermanentAddress, sizeof (Nic->Mac)); Nic->PciLocation = (UINT16) ((NicInfo->BusNumber << 8) | (NicInfo->DeviceNumber << 3) | NicInfo->FunctionNumber); *SectionOffset = (UINT16) ((UINTN) Nic - (UINTN) Table); SectionOffset++; // // Fill the Target section. // Target->Header.StructureId = EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_TARGET_STRUCTURE_ID; Target->Header.Version = EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_TARGET_STRUCTURE_VERSION; Target->Header.Length = (UINT16) sizeof (EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_TARGET_STRUCTURE); Target->Header.Index = (UINT8) Index; Target->Header.Flags = EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_TARGET_STRUCTURE_FLAG_BLOCK_VALID; if (Index == 0) { Target->Header.Flags |= EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_TARGET_STRUCTURE_FLAG_BOOT_SELECTED; } Target->Port = NvData->TargetPort; if (Attempt->AuthenticationType == ISCSI_AUTH_TYPE_CHAP) { if (AuthConfig->CHAPType == ISCSI_CHAP_UNI) { Target->CHAPType = EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_TARGET_STRUCTURE_CHAP_TYPE_CHAP; } else if (AuthConfig->CHAPType == ISCSI_CHAP_MUTUAL) { Target->CHAPType = EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_TARGET_STRUCTURE_CHAP_TYPE_MUTUAL_CHAP; } } else if (Attempt->AuthenticationType == ISCSI_AUTH_TYPE_NONE) { Target->CHAPType = EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_TARGET_STRUCTURE_CHAP_TYPE_NO_CHAP; } Target->NicIndex = (UINT8) Index; if (NvData->IpMode == IP_MODE_IP4 || NvData->IpMode == IP_MODE_AUTOCONFIG) { IScsiMapV4ToV6Addr (&NvData->TargetIp.v4, &Target->Ip); } else if (NvData->IpMode == IP_MODE_IP6 || NvData->IpMode == IP_MODE_AUTOCONFIG) { CopyMem (&Target->Ip, &NvData->TargetIp, sizeof (EFI_IPv6_ADDRESS)); } else { ASSERT (FALSE); } CopyMem (Target->BootLun, NvData->BootLun, sizeof (Target->BootLun)); // // Target iSCSI Name, CHAP name/secret, reverse CHAP name/secret. // Length = (UINT16) AsciiStrLen (NvData->TargetName); IScsiAddHeapItem (Heap, NvData->TargetName, Length); Target->IScsiNameLength = Length; Target->IScsiNameOffset = (UINT16) ((UINTN) *Heap - (UINTN) Table); if (Attempt->AuthenticationType == ISCSI_AUTH_TYPE_CHAP) { // // CHAP Name // Length = (UINT16) AsciiStrLen (AuthConfig->CHAPName); IScsiAddHeapItem (Heap, AuthConfig->CHAPName, Length); Target->CHAPNameLength = Length; Target->CHAPNameOffset = (UINT16) ((UINTN) *Heap - (UINTN) Table); // // CHAP Secret // Length = (UINT16) AsciiStrLen (AuthConfig->CHAPSecret); IScsiAddHeapItem (Heap, AuthConfig->CHAPSecret, Length); Target->CHAPSecretLength = Length; Target->CHAPSecretOffset = (UINT16) ((UINTN) *Heap - (UINTN) Table); if (Target->CHAPType == EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_TARGET_STRUCTURE_CHAP_TYPE_MUTUAL_CHAP) { // // Reverse CHAP Name. // Length = (UINT16) AsciiStrLen (AuthConfig->ReverseCHAPName); IScsiAddHeapItem (Heap, AuthConfig->ReverseCHAPName, Length); Target->ReverseCHAPNameLength = Length; Target->ReverseCHAPNameOffset = (UINT16) ((UINTN) *Heap - (UINTN) Table); // // Reverse CHAP Secret. // Length = (UINT16) AsciiStrLen (AuthConfig->ReverseCHAPSecret); IScsiAddHeapItem (Heap, AuthConfig->ReverseCHAPSecret, Length); Target->ReverseCHAPSecretLength = Length; Target->ReverseCHAPSecretOffset = (UINT16) ((UINTN) *Heap - (UINTN) Table); } } *SectionOffset = (UINT16) ((UINTN) Target - (UINTN) Table); SectionOffset++; // // Advance to the next NIC/Target pair. // Nic = (EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_NIC_STRUCTURE *) ((UINTN) Target + IBFT_ROUNDUP (sizeof (EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_TARGET_STRUCTURE))); Target = (EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_TARGET_STRUCTURE *) ((UINTN) Nic + IBFT_ROUNDUP (sizeof (EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_NIC_STRUCTURE))); Index++; } } /** Publish and remove the iSCSI Boot Firmware Table according to the iSCSI session status. **/ VOID IScsiPublishIbft ( IN VOID ) { EFI_STATUS Status; EFI_ACPI_TABLE_PROTOCOL *AcpiTableProtocol; EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_HEADER *Table; EFI_ACPI_3_0_ROOT_SYSTEM_DESCRIPTION_POINTER *Rsdp; EFI_ACPI_DESCRIPTION_HEADER *Rsdt; EFI_ACPI_DESCRIPTION_HEADER *Xsdt; UINT8 *Heap; UINT8 Checksum; Rsdt = NULL; Xsdt = NULL; Status = gBS->LocateProtocol (&gEfiAcpiTableProtocolGuid, NULL, (VOID **) &AcpiTableProtocol); if (EFI_ERROR (Status)) { return ; } // // Find ACPI table RSD_PTR from the system table. // Status = EfiGetSystemConfigurationTable (&gEfiAcpiTableGuid, (VOID **) &Rsdp); if (EFI_ERROR (Status)) { Status = EfiGetSystemConfigurationTable (&gEfiAcpi10TableGuid, (VOID **) &Rsdp); } if (EFI_ERROR (Status) || (Rsdp == NULL)) { return ; } else if (Rsdp->Revision >= EFI_ACPI_2_0_ROOT_SYSTEM_DESCRIPTION_POINTER_REVISION && Rsdp->XsdtAddress != 0) { Xsdt = (EFI_ACPI_DESCRIPTION_HEADER *) (UINTN) Rsdp->XsdtAddress; } else if (Rsdp->RsdtAddress != 0) { Rsdt = (EFI_ACPI_DESCRIPTION_HEADER *) (UINTN) Rsdp->RsdtAddress; } if ((Xsdt == NULL) && (Rsdt == NULL)) { return ; } if (mIbftInstalled) { Status = AcpiTableProtocol->UninstallAcpiTable ( AcpiTableProtocol, mTableKey ); if (EFI_ERROR (Status)) { return ; } mIbftInstalled = FALSE; } // // If there is no valid attempt configuration, just return. // if ((!mPrivate->EnableMpio && mPrivate->ValidSinglePathCount == 0) || (mPrivate->EnableMpio && mPrivate->MpioCount <= mPrivate->Krb5MpioCount)) { return ; } // // Allocate 4k bytes to hold the ACPI table. // Table = AllocateZeroPool (IBFT_MAX_SIZE); if (Table == NULL) { return ; } Heap = (UINT8 *) Table + IBFT_HEAP_OFFSET; // // Fill in the various section of the iSCSI Boot Firmware Table. // if (Rsdp->Revision >= EFI_ACPI_2_0_ROOT_SYSTEM_DESCRIPTION_POINTER_REVISION) { IScsiInitIbfTableHeader (Table, Xsdt->OemId, &Xsdt->OemTableId); } else { IScsiInitIbfTableHeader (Table, Rsdt->OemId, &Rsdt->OemTableId); } IScsiInitControlSection (Table); IScsiFillInitiatorSection (Table, &Heap); IScsiFillNICAndTargetSections (Table, &Heap); Checksum = CalculateCheckSum8((UINT8 *)Table, Table->Length); Table->Checksum = Checksum; // // Install or update the iBFT table. // Status = AcpiTableProtocol->InstallAcpiTable ( AcpiTableProtocol, Table, Table->Length, &mTableKey ); if (EFI_ERROR(Status)) { return; } mIbftInstalled = TRUE; FreePool (Table); }