From cb274a270344cef20d6012eb873fc71a46931a85 Mon Sep 17 00:00:00 2001 From: Eric Dong Date: Tue, 29 Mar 2016 14:49:25 +0800 Subject: SecurityPkg: OpalPasswordSmm: Add Opal password Smm driver. This driver used to unlock device in S3 resume phase. Contributed-under: TianoCore Contribution Agreement 1.0 Signed-off-by: Eric Dong Reviewed-by: Feng Tian --- .../Tcg/Opal/OpalPasswordSmm/OpalPasswordSmm.c | 1036 ++++++++++++++++++++ 1 file changed, 1036 insertions(+) create mode 100644 SecurityPkg/Tcg/Opal/OpalPasswordSmm/OpalPasswordSmm.c (limited to 'SecurityPkg/Tcg/Opal/OpalPasswordSmm/OpalPasswordSmm.c') diff --git a/SecurityPkg/Tcg/Opal/OpalPasswordSmm/OpalPasswordSmm.c b/SecurityPkg/Tcg/Opal/OpalPasswordSmm/OpalPasswordSmm.c new file mode 100644 index 0000000000..57d10a17b7 --- /dev/null +++ b/SecurityPkg/Tcg/Opal/OpalPasswordSmm/OpalPasswordSmm.c @@ -0,0 +1,1036 @@ +/** @file + Opal password smm driver which is used to support Opal security feature at s3 path. + +Copyright (c) 2016, Intel Corporation. All rights reserved.
+This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#include "OpalPasswordSmm.h" + +#define SMM_SIZE_ALLOC_BYTES (512) +#define RESPONSE_SIZE (200) + +#define PCI_CLASS_MASS_STORAGE_AHCI (0x06) + +#define OPAL_PCIE_ROOTPORT_SAVESIZE (0x40) +#define STORE_INVALID_ROOTPORT_INDEX ((UINT8) -1) +#define OPAL_DEVICE_TYPE_SATA 0x1 +#define OPAL_DEVICE_TYPE_NVME 0x2 +#define OPAL_DEVICE_TYPE_UNKNOWN 0xFF + +// +// To unlock the Intel SATA controller at S3 Resume, restored the following registers. +// +const OPAL_HC_PCI_REGISTER_SAVE mSataHcRegisterSaveTemplate[] = { + {0x9, S3BootScriptWidthUint8}, + {0x10, S3BootScriptWidthUint32}, + {0x14, S3BootScriptWidthUint32}, + {0x18, S3BootScriptWidthUint32}, + {0x1C, S3BootScriptWidthUint32}, + {0x20, S3BootScriptWidthUint32}, + {0x24, S3BootScriptWidthUint32}, + {0x3c, S3BootScriptWidthUint8}, + {0x3d, S3BootScriptWidthUint8}, + {0x40, S3BootScriptWidthUint16}, + {0x42, S3BootScriptWidthUint16}, + {0x92, S3BootScriptWidthUint16}, + {0x94, S3BootScriptWidthUint32}, + {0x9C, S3BootScriptWidthUint32}, + {0x4, S3BootScriptWidthUint16}, +}; + + +UINT8 mSwSmiValue; +LIST_ENTRY *mOpalDeviceList; +LIST_ENTRY mSmmDeviceList = INITIALIZE_LIST_HEAD_VARIABLE (mSmmDeviceList); + +BOOLEAN mSendBlockSID = FALSE; + +// AHCI +UINT32 mAhciBar = 0; +EFI_AHCI_REGISTERS mAhciRegisters; +VOID *mBuffer = NULL; // DMA can not read/write Data to smram, so we pre-allocates Buffer from AcpiNVS. +// +// NVME +NVME_CONTEXT mNvmeContext; + +/** + Add new bridge node or nvme device info to the device list. + + @param[in] BusNum The bus number. + @param[in] DevNum The device number. + @param[in] FuncNum The function number. + @param[in] Dev The device which need to add device node info. + +**/ +VOID +AddPciDeviceNode ( + UINT32 BusNum, + UINT32 DevNum, + UINT32 FuncNum, + OPAL_SMM_DEVICE *Dev + ) +{ + UINT8 *DevList; + PCI_DEVICE *DeviceNode; + + DevList = AllocateZeroPool (sizeof (PCI_DEVICE) + Dev->Length); + ASSERT (DevList != NULL); + + if (Dev->Length != 0) { + CopyMem (DevList, Dev->PciBridgeNode, Dev->Length); + FreePool (Dev->PciBridgeNode); + } + + DeviceNode = (PCI_DEVICE *) (DevList + Dev->Length); + + DeviceNode->BusNum = BusNum; + DeviceNode->DevNum = DevNum; + DeviceNode->FuncNum = FuncNum; + + Dev->Length += sizeof (PCI_DEVICE); + Dev->PciBridgeNode = (PCI_DEVICE *)DevList; +} + +/** + Extract device info from the input device path. + + @param[in] DevicePath Device path info for the device. + @param[in,out] Dev The device which new inputed. + +**/ +VOID +ExtractDeviceInfoFromDevicePath ( + IN EFI_DEVICE_PATH_PROTOCOL *DevicePath, + IN OUT OPAL_SMM_DEVICE *Dev + ) +{ + EFI_DEVICE_PATH_PROTOCOL *TmpDevPath; + EFI_DEVICE_PATH_PROTOCOL *TmpDevPath2; + PCI_DEVICE_PATH *PciDevPath; + SATA_DEVICE_PATH *SataDevPath; + NVME_NAMESPACE_DEVICE_PATH *NvmeDevPath; + UINTN BusNum; + + TmpDevPath = DevicePath; + Dev->DeviceType = OPAL_DEVICE_TYPE_UNKNOWN; + + while (!IsDevicePathEnd(TmpDevPath)) { + if (TmpDevPath->Type == MESSAGING_DEVICE_PATH && TmpDevPath->SubType == MSG_SATA_DP) { + // + // SATA + // + SataDevPath = ( SATA_DEVICE_PATH* )TmpDevPath; + Dev->SataPort = SataDevPath->HBAPortNumber; + Dev->SataPortMultiplierPort = SataDevPath->PortMultiplierPortNumber; + Dev->DeviceType = OPAL_DEVICE_TYPE_SATA; + break; + } else if (TmpDevPath->Type == MESSAGING_DEVICE_PATH && TmpDevPath->SubType == MSG_NVME_NAMESPACE_DP) { + // + // NVMe + // + NvmeDevPath = ( NVME_NAMESPACE_DEVICE_PATH* )TmpDevPath; + Dev->NvmeNamespaceId = NvmeDevPath->NamespaceId; + Dev->DeviceType = OPAL_DEVICE_TYPE_NVME; + break; + } + TmpDevPath = NextDevicePathNode (TmpDevPath); + } + + // + // Get bridge node info for the nvme device. + // + BusNum = 0; + TmpDevPath = DevicePath; + TmpDevPath2 = NextDevicePathNode (DevicePath); + while (!IsDevicePathEnd(TmpDevPath2)) { + if (TmpDevPath->Type == HARDWARE_DEVICE_PATH && TmpDevPath->SubType == HW_PCI_DP) { + PciDevPath = (PCI_DEVICE_PATH *) TmpDevPath; + if ((TmpDevPath2->Type == MESSAGING_DEVICE_PATH && TmpDevPath2->SubType == MSG_NVME_NAMESPACE_DP)|| + (TmpDevPath2->Type == MESSAGING_DEVICE_PATH && TmpDevPath2->SubType == MSG_SATA_DP)) { + Dev->BusNum = (UINT32)BusNum; + Dev->DevNum = PciDevPath->Device; + Dev->FuncNum = PciDevPath->Function; + } else { + AddPciDeviceNode((UINT32)BusNum, PciDevPath->Device, PciDevPath->Function, Dev); + if (TmpDevPath2->Type == HARDWARE_DEVICE_PATH && TmpDevPath2->SubType == HW_PCI_DP) { + BusNum = PciRead8 (PCI_LIB_ADDRESS (BusNum, PciDevPath->Device, PciDevPath->Function, NVME_PCIE_SEC_BNUM)); + } + } + } + + TmpDevPath = NextDevicePathNode (TmpDevPath); + TmpDevPath2 = NextDevicePathNode (TmpDevPath2); + } +} + +/** + + The function returns whether or not the device is Opal Locked. + TRUE means that the device is partially or fully locked. + This will perform a Level 0 Discovery and parse the locking feature descriptor + + @param[in] OpalDev Opal object to determine if locked + +**/ +BOOLEAN +IsOpalDeviceLocked( + OPAL_SMM_DEVICE *OpalDev + ) +{ + OPAL_SESSION Session; + OPAL_DISK_SUPPORT_ATTRIBUTE SupportedAttributes; + TCG_LOCKING_FEATURE_DESCRIPTOR LockingFeature; + UINT16 OpalBaseComId; + TCG_RESULT Ret; + + Session.Sscp = &OpalDev->Sscp; + Session.MediaId = 0; + + Ret = OpalGetSupportedAttributesInfo (&Session, &SupportedAttributes, &OpalBaseComId); + if (Ret != TcgResultSuccess) { + return FALSE; + } + + OpalDev->OpalBaseComId = OpalBaseComId; + Session.OpalBaseComId = OpalBaseComId; + + Ret = OpalGetLockingInfo(&Session, &LockingFeature); + if (Ret != TcgResultSuccess) { + return FALSE; + } + + return OpalDeviceLocked (&SupportedAttributes, &LockingFeature); +} + +/** + Save/Restore RootPort configuration space. + + @param[in] DeviceNode - The device node. + @param[in] SaveAction - TRUE: Save, FALSE: Restore + @param[in,out] PcieConfBufferList - Configuration space data buffer for save/restore + + @retval - PCIE base address of this RootPort +**/ +UINTN +SaveRestoreRootportConfSpace ( + IN OPAL_SMM_DEVICE *DeviceNode, + IN BOOLEAN SaveAction, + IN OUT UINT8 **PcieConfBufferList + ) +{ + UINTN RpBase; + UINTN Length; + PCI_DEVICE *DevNode; + UINT8 *StorePcieConfData; + UINTN Index; + + Length = 0; + Index = 0; + RpBase = 0; + + while (Length < DeviceNode->Length) { + DevNode = (PCI_DEVICE *)((UINT8*)DeviceNode->PciBridgeNode + Length); + RpBase = PCI_LIB_ADDRESS (DevNode->BusNum, DevNode->DevNum, DevNode->FuncNum, 0x0); + + if (PcieConfBufferList != NULL) { + if (SaveAction) { + StorePcieConfData = (UINT8 *) AllocateZeroPool (OPAL_PCIE_ROOTPORT_SAVESIZE); + ASSERT (StorePcieConfData != NULL); + OpalPciRead (StorePcieConfData, RpBase, OPAL_PCIE_ROOTPORT_SAVESIZE); + PcieConfBufferList[Index] = StorePcieConfData; + } else { + // Skip PCIe Command & Status registers + StorePcieConfData = PcieConfBufferList[Index]; + OpalPciWrite (RpBase, StorePcieConfData, 4); + OpalPciWrite (RpBase + 8, StorePcieConfData + 8, OPAL_PCIE_ROOTPORT_SAVESIZE - 8); + + FreePool (StorePcieConfData); + } + } + + Length += sizeof (PCI_DEVICE); + Index ++; + } + + return RpBase; +} + +/** + Configure RootPort for downstream PCIe NAND devices. + + @param[in] RpBase - PCIe configuration space address of this RootPort + @param[in] BusNumber - Bus number + @param[in] MemoryBase - Memory base address + @param[in] MemoryLength - Memory size + +**/ +VOID +ConfigureRootPortForPcieNand ( + IN UINTN RpBase, + IN UINTN BusNumber, + IN UINT32 MemoryBase, + IN UINT32 MemoryLength + ) +{ + UINT32 MemoryLimit; + + DEBUG ((DEBUG_INFO, "ConfigureRootPortForPcieNand, BusNumber: %x, MemoryBase: %x, MemoryLength: %x\n", + BusNumber, MemoryBase, MemoryLength)); + + if (MemoryLength == 0) { + MemoryLimit = MemoryBase; + } else { + MemoryLimit = MemoryBase + MemoryLength + 0xFFFFF; // 1M + } + + /// + /// Configue PCIE configuration space for RootPort + /// + PciWrite8 (RpBase + NVME_PCIE_BNUM + 1, (UINT8) BusNumber); // Secondary Bus Number registers + PciWrite8 (RpBase + NVME_PCIE_BNUM + 2, (UINT8) BusNumber); // Subordinate Bus Number registers + PciWrite8 (RpBase + NVME_PCIE_IOBL, 0xFF); // I/O Base registers + PciWrite8 (RpBase + NVME_PCIE_IOBL + 1, 0x00); // I/O Limit registers + PciWrite16 (RpBase + NVME_PCIE_MBL, (UINT16) RShiftU64 ((UINTN)MemoryBase, 16)); // Memory Base register + PciWrite16 (RpBase + NVME_PCIE_MBL + 2, (UINT16) RShiftU64 ((UINTN)MemoryLimit, 16)); // Memory Limit register + PciWrite16 (RpBase + NVME_PCIE_PMBL, 0xFFFF); // Prefetchable Memory Base registers + PciWrite16 (RpBase + NVME_PCIE_PMBL + 2, 0x0000); // Prefetchable Memory Limit registers + PciWrite32 (RpBase + NVME_PCIE_PMBU32, 0xFFFFFFFF); // Prefetchable Memory Upper Base registers + PciWrite32 (RpBase + NVME_PCIE_PMLU32, 0x00000000); // Prefetchable Memory Upper Limit registers +} + + +/** + Dispatch function for a Software SMI handler. + + @param[in] DispatchHandle The unique handle assigned to this handler by SmiHandlerRegister(). + @param[in] RegisterContext Points to an optional handler context which was specified when the + handler was registered. + @param[in, out] CommBuffer A pointer to a collection of Data in memory that will + be conveyed from a non-SMM environment into an SMM environment. + @param[in, out] CommBufferSize The Size of the CommBuffer. + + @retval EFI_SUCCESS The interrupt was handled and quiesced. No other handlers + should still be called. + @retval Others Other execution results. +**/ +EFI_STATUS +EFIAPI +SmmUnlockOpalPassword ( + IN EFI_HANDLE DispatchHandle, + IN CONST VOID *RegisterContext, + IN OUT VOID *CommBuffer, + IN OUT UINTN *CommBufferSize + ) +{ + EFI_STATUS Status; + OPAL_SMM_DEVICE *OpalDev; + LIST_ENTRY *Entry; + UINT8 BaseClassCode; + UINT8 SubClassCode; + UINT8 ProgInt; + TCG_RESULT Result; + UINT8 SataCmdSt; + UINT8 *StorePcieConfDataList[16]; + UINTN RpBase; + UINTN MemoryBase; + UINTN MemoryLength; + OPAL_SESSION Session; + + ZeroMem (StorePcieConfDataList, sizeof (StorePcieConfDataList)); + Status = EFI_DEVICE_ERROR; + + // + // try to unlock all locked hdd disks. + // + for (Entry = mSmmDeviceList.ForwardLink; Entry != &mSmmDeviceList; Entry = Entry->ForwardLink) { + OpalDev = BASE_CR(Entry, OPAL_SMM_DEVICE, Link); + + RpBase = 0; + SataCmdSt = 0; + + /// + /// Configure RootPort for PCIe AHCI/NVME devices. + /// + if (OpalDev->DeviceType == OPAL_DEVICE_TYPE_NVME) { + /// + /// Save original RootPort configuration space to heap + /// + RpBase = SaveRestoreRootportConfSpace ( + OpalDev, + TRUE, + StorePcieConfDataList + ); + MemoryBase = mNvmeContext.Nbar; + MemoryLength = 0; + ConfigureRootPortForPcieNand (RpBase, OpalDev->BusNum, (UINT32) MemoryBase, (UINT32) MemoryLength); + + /// + /// Enable PCIE decode for RootPort + /// + SataCmdSt = PciRead8 (RpBase + NVME_PCIE_PCICMD); + PciWrite8 (RpBase + NVME_PCIE_PCICMD, 0x6); + } else { + SataCmdSt = PciRead8 (PCI_LIB_ADDRESS (OpalDev->BusNum, OpalDev->DevNum, OpalDev->FuncNum, NVME_PCIE_PCICMD)); + PciWrite8 (PCI_LIB_ADDRESS (OpalDev->BusNum, OpalDev->DevNum, OpalDev->FuncNum, NVME_PCIE_PCICMD), 0x6); + } + + BaseClassCode = PciRead8 (PCI_LIB_ADDRESS (OpalDev->BusNum, OpalDev->DevNum, OpalDev->FuncNum, 0x0B)); + SubClassCode = PciRead8 (PCI_LIB_ADDRESS (OpalDev->BusNum, OpalDev->DevNum, OpalDev->FuncNum, 0x0A)); + ProgInt = PciRead8 (PCI_LIB_ADDRESS (OpalDev->BusNum, OpalDev->DevNum, OpalDev->FuncNum, 0x09)); + if (BaseClassCode != PCI_CLASS_MASS_STORAGE) { + Status = EFI_INVALID_PARAMETER; + break; + } + + Status = EFI_DEVICE_ERROR; + if (OpalDev->DeviceType == OPAL_DEVICE_TYPE_SATA) { + if ((SubClassCode == PCI_CLASS_MASS_STORAGE_AHCI) || (SubClassCode == PCI_CLASS_MASS_STORAGE_RAID)) { + Status = GetAhciBaseAddress (OpalDev->BusNum, OpalDev->DevNum, OpalDev->FuncNum); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "GetAhciBaseAddress error, Status: %r\n", Status)); + goto done; + } + Status = AhciModeInitialize ((UINT8)OpalDev->SataPort); + ASSERT_EFI_ERROR (Status); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "AhciModeInitialize error, Status: %r\n", Status)); + goto done; + } + } else { + DEBUG ((DEBUG_ERROR, "SubClassCode not support for SATA device\n")); + } + } else if (OpalDev->DeviceType == OPAL_DEVICE_TYPE_NVME) { + if (SubClassCode == PCI_CLASS_MASS_STORAGE_NVM) { + if (ProgInt != PCI_IF_NVMHCI) { + DEBUG ((DEBUG_ERROR, "PI not support, skipped\n")); + Status = EFI_NOT_FOUND; + goto done; + } + + mNvmeContext.PciBase = PCI_LIB_ADDRESS (OpalDev->BusNum, OpalDev->DevNum, OpalDev->FuncNum, 0x0); + mNvmeContext.NvmeInitWaitTime = 0; + mNvmeContext.Nsid = OpalDev->NvmeNamespaceId; + Status = NvmeControllerInit (&mNvmeContext); + } else { + DEBUG ((DEBUG_ERROR, "SubClassCode not support for NVME device\n")); + } + } else { + DEBUG ((DEBUG_ERROR, "Invalid Devicetype\n")); + goto done; + } + + Status = EFI_DEVICE_ERROR; + if (IsOpalDeviceLocked(OpalDev)) { + ZeroMem(&Session, sizeof(Session)); + Session.Sscp = &OpalDev->Sscp; + Session.MediaId = 0; + Session.OpalBaseComId = OpalDev->OpalBaseComId; + + if (mSendBlockSID) { + Result = OpalBlockSid (&Session, TRUE); + if (Result != TcgResultSuccess) { + break; + } + } + + Result = OpalSupportUnlock (&Session, OpalDev->Password, OpalDev->PasswordLength, NULL); + if (Result == TcgResultSuccess) { + Status = EFI_SUCCESS; + } + } + + if (OpalDev->DeviceType == OPAL_DEVICE_TYPE_NVME) { + if (SubClassCode == PCI_CLASS_MASS_STORAGE_NVM) { + Status = NvmeControllerExit (&mNvmeContext); + } + } + +done: + if (OpalDev->DeviceType == OPAL_DEVICE_TYPE_NVME) { + ASSERT (RpBase != 0); + PciWrite8 (RpBase + NVME_PCIE_PCICMD, 0); + RpBase = SaveRestoreRootportConfSpace ( + OpalDev, + FALSE, // restore + StorePcieConfDataList + ); + PciWrite8 (RpBase + NVME_PCIE_PCICMD, SataCmdSt); + } else { + PciWrite8 (PCI_LIB_ADDRESS (OpalDev->BusNum, OpalDev->DevNum, OpalDev->FuncNum, NVME_PCIE_PCICMD), SataCmdSt); + } + + if (EFI_ERROR (Status)) { + break; + } + } + + return Status; +} + + +/** + Main entry point for an SMM handler dispatch or communicate-based callback. + + @param[in] DispatchHandle The unique handle assigned to this handler by SmiHandlerRegister(). + @param[in] Context Points to an optional handler context which was specified when the + handler was registered. + @param[in,out] CommBuffer A pointer to a collection of Data in memory that will + be conveyed from a non-SMM environment into an SMM environment. + @param[in,out] CommBufferSize The Size of the CommBuffer. + + @retval EFI_SUCCESS The interrupt was handled and quiesced. No other handlers + should still be called. + @retval EFI_WARN_INTERRUPT_SOURCE_QUIESCED The interrupt has been quiesced but other handlers should + still be called. + @retval EFI_WARN_INTERRUPT_SOURCE_PENDING The interrupt is still pending and other handlers should still + be called. + @retval EFI_INTERRUPT_PENDING The interrupt could not be quiesced. +**/ +EFI_STATUS +EFIAPI +S3SleepEntryCallBack ( + IN EFI_HANDLE DispatchHandle, + IN CONST VOID *Context OPTIONAL, + IN OUT VOID *CommBuffer OPTIONAL, + IN OUT UINTN *CommBufferSize OPTIONAL + ) +{ + UINTN Bus; + UINTN Device; + UINTN Function; + UINTN Index; + EFI_STATUS Status; + LIST_ENTRY *Entry; + UINTN Offset; + UINT64 Address; + S3_BOOT_SCRIPT_LIB_WIDTH Width; + UINT32 Data; + OPAL_DISK_AND_PASSWORD_INFO *PciDev; + OPAL_HC_PCI_REGISTER_SAVE *HcRegisterSaveListPtr; + UINTN Count; + OPAL_SMM_DEVICE *SmmDev; + + Data = 0; + Status = EFI_SUCCESS; + + mOpalDeviceList = OpalSupportGetOpalDeviceList(); + + for (Entry = mOpalDeviceList->ForwardLink; Entry != mOpalDeviceList; Entry = Entry->ForwardLink) { + PciDev = BASE_CR (Entry, OPAL_DISK_AND_PASSWORD_INFO, Link); + + SmmDev = AllocateZeroPool (sizeof (OPAL_SMM_DEVICE)); + if (SmmDev == NULL) { + return EFI_OUT_OF_RESOURCES; + } + SmmDev->Signature = OPAL_SMM_DEVICE_SIGNATURE; + + ExtractDeviceInfoFromDevicePath(&PciDev->OpalDevicePath, SmmDev); + + SmmDev->PasswordLength = PciDev->PasswordLength; + CopyMem(&(SmmDev->Password), PciDev->Password, OPAL_PASSWORD_MAX_LENGTH); + + SmmDev->Sscp.ReceiveData = SecurityReceiveData; + SmmDev->Sscp.SendData = SecuritySendData; + + InsertHeadList (&mSmmDeviceList, &SmmDev->Link); + + if (SmmDev->DeviceType == OPAL_DEVICE_TYPE_NVME) { + continue; + } + + // + // Save register Data for S3. Sata controller only. + // + Bus = SmmDev->BusNum; + Device = SmmDev->DevNum; + Function = SmmDev->FuncNum; + + ASSERT (SmmDev->DeviceType == OPAL_DEVICE_TYPE_SATA); + HcRegisterSaveListPtr = (OPAL_HC_PCI_REGISTER_SAVE *) mSataHcRegisterSaveTemplate; + Count = sizeof (mSataHcRegisterSaveTemplate) / sizeof (OPAL_HC_PCI_REGISTER_SAVE); + + for (Index = 0; Index < Count; Index += 1) { + Offset = HcRegisterSaveListPtr[Index].Address; + Width = HcRegisterSaveListPtr[Index].Width; + + switch (Width) { + case S3BootScriptWidthUint8: + Data = (UINT32)PciRead8 (PCI_LIB_ADDRESS(Bus,Device,Function,Offset)); + break; + case S3BootScriptWidthUint16: + Data = (UINT32)PciRead16 (PCI_LIB_ADDRESS(Bus,Device,Function,Offset)); + break; + case S3BootScriptWidthUint32: + Data = PciRead32 (PCI_LIB_ADDRESS(Bus,Device,Function,Offset)); + break; + default: + ASSERT (FALSE); + break; + } + + Address = S3_BOOT_SCRIPT_LIB_PCI_ADDRESS (Bus, Device, Function, Offset); + Status = S3BootScriptSavePciCfgWrite (Width, Address, 1, &Data); + if (EFI_ERROR (Status)) { + return Status; + } + } + } + + if (!IsListEmpty (mOpalDeviceList)) { + Status = S3BootScriptSaveIoWrite (S3BootScriptWidthUint8, 0xB2, 1, &mSwSmiValue); + ASSERT_EFI_ERROR (Status); + } + + return Status; +} + +/** + Main entry for this driver. + + @param ImageHandle Image handle this driver. + @param SystemTable Pointer to SystemTable. + + @retval EFI_SUCESS This function always complete successfully. + +**/ +EFI_STATUS +EFIAPI +OpalPasswordSmmInit ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + EFI_STATUS Status; + EFI_SMM_SW_DISPATCH2_PROTOCOL *SwDispatch; + EFI_SMM_SX_DISPATCH2_PROTOCOL *SxDispatch; + EFI_HANDLE SwHandle; + EFI_SMM_SW_REGISTER_CONTEXT Context; + EFI_HANDLE S3SleepEntryHandle; + EFI_SMM_SX_REGISTER_CONTEXT EntryRegisterContext; + EFI_SMM_VARIABLE_PROTOCOL *SmmVariable; + OPAL_EXTRA_INFO_VAR OpalExtraInfo; + UINTN DataSize; + EFI_PHYSICAL_ADDRESS Address; + + mBuffer = NULL; + SwHandle = NULL; + S3SleepEntryHandle = NULL; + ZeroMem (&mNvmeContext, sizeof (NVME_CONTEXT)); + + Status = gSmst->SmmLocateProtocol ( + &gEfiSmmSwDispatch2ProtocolGuid, + NULL, + (VOID **)&SwDispatch + ); + ASSERT_EFI_ERROR (Status); + if (EFI_ERROR (Status)) { + DEBUG((DEBUG_ERROR, " SmmLocateProtocol gEfiSmmSwDispatch2ProtocolGuid fail, Status: %r\n", Status)); + return Status; + } + + Status = gSmst->SmmLocateProtocol ( + &gEfiSmmSxDispatch2ProtocolGuid, + NULL, + (VOID **)&SxDispatch + ); + ASSERT_EFI_ERROR (Status); + if (EFI_ERROR (Status)) { + DEBUG((DEBUG_ERROR, " SmmLocateProtocol gEfiSmmSxDispatch2ProtocolGuid fail, Status: %r\n", Status)); + return Status; + } + + // + // Preallocate a 512 bytes Buffer to perform trusted I/O. + // Assume this is big enough for unlock commands + // It's because DMA can not access smmram stack at the cmd execution. + // + Address = 0xFFFFFFFF; + Status = gBS->AllocatePages ( + AllocateMaxAddress, + EfiACPIMemoryNVS, + EFI_SIZE_TO_PAGES (SMM_SIZE_ALLOC_BYTES), + &Address + ); + if (EFI_ERROR (Status)) { + DEBUG((DEBUG_ERROR, " AllocatePages for SATA DAM fail, Status: %r\n", Status)); + return EFI_OUT_OF_RESOURCES; + } + + mBuffer = (VOID *)(UINTN)Address; + ZeroMem ((VOID *)(UINTN)mBuffer, SMM_SIZE_ALLOC_BYTES); + + // + // Preallocate resource for AHCI transfer descriptor. + // + Status = AhciAllocateResource (); + if (EFI_ERROR (Status)) { + DEBUG((DEBUG_ERROR, " AhciAllocateResource fail, Status: %r\n", Status)); + Status = EFI_OUT_OF_RESOURCES; + goto EXIT; + } + + // + // Preallocate resource for NVMe configuration space. + // + Status = NvmeAllocateResource (ImageHandle, &mNvmeContext); + if (EFI_ERROR (Status)) { + DEBUG((DEBUG_ERROR, " NvmeAllocateResource fail, Status: %r\n", Status)); + Status = EFI_OUT_OF_RESOURCES; + goto EXIT; + } + + // + // Register a S3 entry callback function to store ATA host controller context to boot script. + // These boot scripts would be invoked at S3 path to recovery ATA host controller h/w context + // for executing HDD unlock cmd. + // + EntryRegisterContext.Type = SxS3; + EntryRegisterContext.Phase = SxEntry; + Status = SxDispatch->Register ( + SxDispatch, + S3SleepEntryCallBack, + &EntryRegisterContext, + &S3SleepEntryHandle + ); + ASSERT_EFI_ERROR (Status); + if (EFI_ERROR (Status)) { + goto EXIT; + } + + // + // Register Opal password smm unlock handler + // + Context.SwSmiInputValue = (UINTN) -1; + Status = SwDispatch->Register ( + SwDispatch, + SmmUnlockOpalPassword, + &Context, + &SwHandle + ); + ASSERT_EFI_ERROR (Status); + if (EFI_ERROR (Status)) { + DEBUG((DEBUG_ERROR, " SwDispatch->Register fail, Status: %r\n", Status)); + goto EXIT; + } + + // + // trigger smi to unlock hdd if it's locked. + // + mSwSmiValue = (UINT8) Context.SwSmiInputValue; + + Status = gSmst->SmmLocateProtocol (&gEfiSmmVariableProtocolGuid, NULL, (VOID**)&SmmVariable); + if (!EFI_ERROR (Status)) { + DataSize = sizeof (OPAL_EXTRA_INFO_VAR); + Status = SmmVariable->SmmGetVariable ( + OPAL_EXTRA_INFO_VAR_NAME, + &gOpalExtraInfoVariableGuid, + NULL, + &DataSize, + &OpalExtraInfo + ); + if (!EFI_ERROR (Status)) { + mSendBlockSID = OpalExtraInfo.EnableBlockSid; + } + } + + return EFI_SUCCESS; + +EXIT: + if (S3SleepEntryHandle != NULL) { + SxDispatch->UnRegister (SxDispatch, S3SleepEntryHandle); + } + + AhciFreeResource (); + + NvmeFreeResource (&mNvmeContext); + + if (mBuffer != NULL) { + gBS->FreePages ((EFI_PHYSICAL_ADDRESS)(UINTN) mBuffer, EFI_SIZE_TO_PAGES (SMM_SIZE_ALLOC_BYTES)); + } + + return Status; +} + +/** + Provide Io action support. + + @param[in] SmmDev the opal device need to perform trust io. + @param[in] IoType OPAL_IO_TYPE indicating whether to perform a Trusted Send or Trusted Receive. + @param[in] SecurityProtocol Security Protocol + @param[in] SpSpecific Security Protocol Specific + @param[in] TransferLength Transfer Length of Buffer (in bytes) - always a multiple of 512 + @param[in] Buffer Address of Data to transfer + + @retval TcgResultSuccess Perform the io action success. + @retval TcgResultFailure Perform the io action failed. + +**/ +EFI_STATUS +PerformTrustedIo ( + OPAL_SMM_DEVICE *SmmDev, + OPAL_IO_TYPE IoType, + UINT8 SecurityProtocol, + UINT16 SpSpecific, + UINTN TransferLength, + VOID *Buffer + ) +{ + EFI_STATUS Status; + UINTN BufferSizeBlocks; + EFI_ATA_COMMAND_BLOCK AtaCommandBlock; + + Status = EFI_DEVICE_ERROR; + if (SmmDev->DeviceType == OPAL_DEVICE_TYPE_SATA) { + BufferSizeBlocks = TransferLength / 512; + + ZeroMem( &AtaCommandBlock, sizeof( EFI_ATA_COMMAND_BLOCK ) ); + AtaCommandBlock.AtaCommand = ( IoType == OpalSend ) ? ATA_COMMAND_TRUSTED_SEND : ATA_COMMAND_TRUSTED_RECEIVE; + AtaCommandBlock.AtaSectorCount = ( UINT8 )BufferSizeBlocks; + AtaCommandBlock.AtaSectorNumber = ( UINT8 )( BufferSizeBlocks >> 8 ); + AtaCommandBlock.AtaFeatures = SecurityProtocol; + AtaCommandBlock.AtaCylinderLow = ( UINT8 )( SpSpecific >> 8 ); + AtaCommandBlock.AtaCylinderHigh = ( UINT8 )( SpSpecific ); + AtaCommandBlock.AtaDeviceHead = ATA_DEVICE_LBA; + + + ZeroMem( mBuffer, HDD_PAYLOAD ); + ASSERT( TransferLength <= HDD_PAYLOAD ); + + if (IoType == OpalSend) { + CopyMem( mBuffer, Buffer, TransferLength ); + } + + Status = AhciPioTransfer( + &mAhciRegisters, + (UINT8) SmmDev->SataPort, + (UINT8) SmmDev->SataPortMultiplierPort, + NULL, + 0, + ( IoType == OpalSend ) ? FALSE : TRUE, // i/o direction + &AtaCommandBlock, + NULL, + mBuffer, + (UINT32)TransferLength, + ATA_TIMEOUT + ); + + if (IoType == OpalRecv) { + CopyMem( Buffer, mBuffer, TransferLength ); + } + } else if (SmmDev->DeviceType == OPAL_DEVICE_TYPE_NVME) { + Status = NvmeSecuritySendReceive ( + &mNvmeContext, + IoType == OpalSend, + SecurityProtocol, + SwapBytes16(SpSpecific), + TransferLength, + Buffer + ); + } else { + DEBUG((DEBUG_ERROR, "DeviceType(%x) not support.\n", SmmDev->DeviceType)); + } + + return Status; +} + +/** + Send a security protocol command to a device that receives data and/or the result + of one or more commands sent by SendData. + + The ReceiveData function sends a security protocol command to the given MediaId. + The security protocol command sent is defined by SecurityProtocolId and contains + the security protocol specific data SecurityProtocolSpecificData. The function + returns the data from the security protocol command in PayloadBuffer. + + For devices supporting the SCSI command set, the security protocol command is sent + using the SECURITY PROTOCOL IN command defined in SPC-4. + + For devices supporting the ATA command set, the security protocol command is sent + using one of the TRUSTED RECEIVE commands defined in ATA8-ACS if PayloadBufferSize + is non-zero. + + If the PayloadBufferSize is zero, the security protocol command is sent using the + Trusted Non-Data command defined in ATA8-ACS. + + If PayloadBufferSize is too small to store the available data from the security + protocol command, the function shall copy PayloadBufferSize bytes into the + PayloadBuffer and return EFI_WARN_BUFFER_TOO_SMALL. + + If PayloadBuffer or PayloadTransferSize is NULL and PayloadBufferSize is non-zero, + the function shall return EFI_INVALID_PARAMETER. + + If the given MediaId does not support security protocol commands, the function shall + return EFI_UNSUPPORTED. If there is no media in the device, the function returns + EFI_NO_MEDIA. If the MediaId is not the ID for the current media in the device, + the function returns EFI_MEDIA_CHANGED. + + If the security protocol fails to complete within the Timeout period, the function + shall return EFI_TIMEOUT. + + If the security protocol command completes without an error, the function shall + return EFI_SUCCESS. If the security protocol command completes with an error, the + function shall return EFI_DEVICE_ERROR. + + @param This Indicates a pointer to the calling context. + @param MediaId ID of the medium to receive data from. + @param Timeout The timeout, in 100ns units, to use for the execution + of the security protocol command. A Timeout value of 0 + means that this function will wait indefinitely for the + security protocol command to execute. If Timeout is greater + than zero, then this function will return EFI_TIMEOUT + if the time required to execute the receive data command + is greater than Timeout. + @param SecurityProtocolId The value of the "Security Protocol" parameter of + the security protocol command to be sent. + @param SecurityProtocolSpecificData The value of the "Security Protocol Specific" parameter + of the security protocol command to be sent. + @param PayloadBufferSize Size in bytes of the payload data buffer. + @param PayloadBuffer A pointer to a destination buffer to store the security + protocol command specific payload data for the security + protocol command. The caller is responsible for having + either implicit or explicit ownership of the buffer. + @param PayloadTransferSize A pointer to a buffer to store the size in bytes of the + data written to the payload data buffer. + + @retval EFI_SUCCESS The security protocol command completed successfully. + @retval EFI_WARN_BUFFER_TOO_SMALL The PayloadBufferSize was too small to store the available + data from the device. The PayloadBuffer contains the truncated data. + @retval EFI_UNSUPPORTED The given MediaId does not support security protocol commands. + @retval EFI_DEVICE_ERROR The security protocol command completed with an error. + @retval EFI_NO_MEDIA There is no media in the device. + @retval EFI_MEDIA_CHANGED The MediaId is not for the current media. + @retval EFI_INVALID_PARAMETER The PayloadBuffer or PayloadTransferSize is NULL and + PayloadBufferSize is non-zero. + @retval EFI_TIMEOUT A timeout occurred while waiting for the security + protocol command to execute. + +**/ +EFI_STATUS +EFIAPI +SecurityReceiveData ( + IN EFI_STORAGE_SECURITY_COMMAND_PROTOCOL *This, + IN UINT32 MediaId, + IN UINT64 Timeout, + IN UINT8 SecurityProtocolId, + IN UINT16 SecurityProtocolSpecificData, + IN UINTN PayloadBufferSize, + OUT VOID *PayloadBuffer, + OUT UINTN *PayloadTransferSize + ) +{ + OPAL_SMM_DEVICE *SmmDev; + + SmmDev = OPAL_SMM_DEVICE_FROM_THIS (This); + if (SmmDev == NULL) { + return EFI_DEVICE_ERROR; + } + + return PerformTrustedIo ( + SmmDev, + OpalRecv, + SecurityProtocolId, + SecurityProtocolSpecificData, + PayloadBufferSize, + PayloadBuffer + ); +} + +/** + Send a security protocol command to a device. + + The SendData function sends a security protocol command containing the payload + PayloadBuffer to the given MediaId. The security protocol command sent is + defined by SecurityProtocolId and contains the security protocol specific data + SecurityProtocolSpecificData. If the underlying protocol command requires a + specific padding for the command payload, the SendData function shall add padding + bytes to the command payload to satisfy the padding requirements. + + For devices supporting the SCSI command set, the security protocol command is sent + using the SECURITY PROTOCOL OUT command defined in SPC-4. + + For devices supporting the ATA command set, the security protocol command is sent + using one of the TRUSTED SEND commands defined in ATA8-ACS if PayloadBufferSize + is non-zero. If the PayloadBufferSize is zero, the security protocol command is + sent using the Trusted Non-Data command defined in ATA8-ACS. + + If PayloadBuffer is NULL and PayloadBufferSize is non-zero, the function shall + return EFI_INVALID_PARAMETER. + + If the given MediaId does not support security protocol commands, the function + shall return EFI_UNSUPPORTED. If there is no media in the device, the function + returns EFI_NO_MEDIA. If the MediaId is not the ID for the current media in the + device, the function returns EFI_MEDIA_CHANGED. + + If the security protocol fails to complete within the Timeout period, the function + shall return EFI_TIMEOUT. + + If the security protocol command completes without an error, the function shall return + EFI_SUCCESS. If the security protocol command completes with an error, the function + shall return EFI_DEVICE_ERROR. + + @param This Indicates a pointer to the calling context. + @param MediaId ID of the medium to receive data from. + @param Timeout The timeout, in 100ns units, to use for the execution + of the security protocol command. A Timeout value of 0 + means that this function will wait indefinitely for the + security protocol command to execute. If Timeout is greater + than zero, then this function will return EFI_TIMEOUT + if the time required to execute the send data command + is greater than Timeout. + @param SecurityProtocolId The value of the "Security Protocol" parameter of + the security protocol command to be sent. + @param SecurityProtocolSpecificData The value of the "Security Protocol Specific" parameter + of the security protocol command to be sent. + @param PayloadBufferSize Size in bytes of the payload data buffer. + @param PayloadBuffer A pointer to a destination buffer to store the security + protocol command specific payload data for the security + protocol command. + + @retval EFI_SUCCESS The security protocol command completed successfully. + @retval EFI_UNSUPPORTED The given MediaId does not support security protocol commands. + @retval EFI_DEVICE_ERROR The security protocol command completed with an error. + @retval EFI_NO_MEDIA There is no media in the device. + @retval EFI_MEDIA_CHANGED The MediaId is not for the current media. + @retval EFI_INVALID_PARAMETER The PayloadBuffer is NULL and PayloadBufferSize is non-zero. + @retval EFI_TIMEOUT A timeout occurred while waiting for the security + protocol command to execute. + +**/ +EFI_STATUS +EFIAPI +SecuritySendData ( + IN EFI_STORAGE_SECURITY_COMMAND_PROTOCOL *This, + IN UINT32 MediaId, + IN UINT64 Timeout, + IN UINT8 SecurityProtocolId, + IN UINT16 SecurityProtocolSpecificData, + IN UINTN PayloadBufferSize, + IN VOID *PayloadBuffer + ) +{ + OPAL_SMM_DEVICE *SmmDev; + + SmmDev = OPAL_SMM_DEVICE_FROM_THIS (This); + if (SmmDev == NULL) { + return EFI_DEVICE_ERROR; + } + + return PerformTrustedIo ( + SmmDev, + OpalSend, + SecurityProtocolId, + SecurityProtocolSpecificData, + PayloadBufferSize, + PayloadBuffer + ); + +} + -- cgit v1.2.3