/** @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); | |
// 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 | |
@param[out] BlockSidSupported Whether device support BlockSid feature. | |
**/ | |
BOOLEAN | |
IsOpalDeviceLocked( | |
OPAL_SMM_DEVICE *OpalDev, | |
BOOLEAN *BlockSidSupported | |
) | |
{ | |
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; | |
*BlockSidSupported = SupportedAttributes.BlockSid == 1 ? TRUE : FALSE; | |
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; | |
BOOLEAN BlockSidSupport; | |
UINT32 PpStorageFlag; | |
BOOLEAN BlockSIDEnabled; | |
ZeroMem (StorePcieConfDataList, sizeof (StorePcieConfDataList)); | |
Status = EFI_DEVICE_ERROR; | |
PpStorageFlag = Tcg2PhysicalPresenceLibGetManagementFlags (); | |
if ((PpStorageFlag & TCG2_BIOS_STORAGE_MANAGEMENT_FLAG_ENABLE_BLOCK_SID) != 0) { | |
BlockSIDEnabled = TRUE; | |
} else { | |
BlockSIDEnabled = FALSE; | |
} | |
// | |
// 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; | |
BlockSidSupport = FALSE; | |
if (IsOpalDeviceLocked (OpalDev, &BlockSidSupport)) { | |
ZeroMem(&Session, sizeof(Session)); | |
Session.Sscp = &OpalDev->Sscp; | |
Session.MediaId = 0; | |
Session.OpalBaseComId = OpalDev->OpalBaseComId; | |
Result = OpalSupportUnlock (&Session, OpalDev->Password, OpalDev->PasswordLength, NULL); | |
if (Result == TcgResultSuccess) { | |
Status = EFI_SUCCESS; | |
} | |
} | |
if (BlockSIDEnabled && BlockSidSupport) { | |
Result = OpalBlockSid (&Session, TRUE); | |
if (Result != TcgResultSuccess) { | |
break; | |
} | |
} | |
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; | |
} | |
/** | |
The function extracts device information from OpalDeviceList and creat SmmDeviceList used for S3. | |
@param[in] OpalDeviceList Opal device list created at POST which contains the information of OPAL_DISK_AND_PASSWORD_INFO | |
@param[in,out] SmmDeviceList Opal Smm device list to be created and used for unlocking devices at S3 resume. | |
@retval EFI_SUCCESS Create SmmDeviceList successfully. | |
@retval Others Other execution results. | |
**/ | |
EFI_STATUS | |
CreateSmmDeviceList ( | |
IN LIST_ENTRY *OpalDeviceList, | |
IN OUT LIST_ENTRY *SmmDeviceList | |
) | |
{ | |
LIST_ENTRY *Entry; | |
OPAL_DISK_AND_PASSWORD_INFO *PciDev; | |
OPAL_SMM_DEVICE *SmmDev; | |
for (Entry = OpalDeviceList->ForwardLink; Entry != OpalDeviceList; 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; | |
DEBUG ((DEBUG_INFO, "Opal SMM: Insert device node to SmmDeviceList:\n")); | |
DEBUG ((DEBUG_INFO, "DeviceType:%x, Bus:%d, Dev:%d, Fun:%d\n", \ | |
SmmDev->DeviceType, SmmDev->BusNum, SmmDev->DevNum, SmmDev->FuncNum)); | |
DEBUG ((DEBUG_INFO, "SataPort:%x, MultiplierPort:%x, NvmeNamespaceId:%x\n", \ | |
SmmDev->SataPort, SmmDev->SataPortMultiplierPort, SmmDev->NvmeNamespaceId)); | |
InsertHeadList (SmmDeviceList, &SmmDev->Link); | |
} | |
return EFI_SUCCESS; | |
} | |
/** | |
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_HC_PCI_REGISTER_SAVE *HcRegisterSaveListPtr; | |
UINTN Count; | |
OPAL_SMM_DEVICE *SmmDev; | |
Data = 0; | |
Status = EFI_SUCCESS; | |
mOpalDeviceList = OpalSupportGetOpalDeviceList(); | |
if (IsListEmpty (mOpalDeviceList)) { | |
// | |
// No Opal enabled device. Do nothing. | |
// | |
return EFI_SUCCESS; | |
} | |
if (IsListEmpty (&mSmmDeviceList)) { | |
// | |
// mSmmDeviceList for S3 is empty, creat it by mOpalDeviceList. | |
// | |
Status = CreateSmmDeviceList (mOpalDeviceList, &mSmmDeviceList); | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
} | |
// | |
// Go through SmmDeviceList to save register data for S3 | |
// | |
for (Entry = mSmmDeviceList.ForwardLink; Entry != &mSmmDeviceList; Entry = Entry->ForwardLink) { | |
SmmDev = BASE_CR (Entry, OPAL_SMM_DEVICE, 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; | |
} | |
} | |
} | |
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_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; | |
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 | |
); | |
} | |