summaryrefslogtreecommitdiffstats
path: root/SecurityPkg/Tcg/Opal/OpalPasswordSmm/OpalPasswordSmm.c
diff options
context:
space:
mode:
Diffstat (limited to 'SecurityPkg/Tcg/Opal/OpalPasswordSmm/OpalPasswordSmm.c')
-rw-r--r--SecurityPkg/Tcg/Opal/OpalPasswordSmm/OpalPasswordSmm.c1036
1 files changed, 1036 insertions, 0 deletions
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.<BR>
+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
+ );
+
+}
+