/** @file OpalSecurity.c
* OpalSecurity DXE driver -- TCG Opal storage security for Lenovo HR650X.
*
* Source: e:\hs\AmiModulePkg\OpalSecurity\OpalSecurity.c
*
* This driver provides Opal Storage Security Protocol support for
* self-encrypting drives (SEDs) conforming to the TCG Storage Security
* Subsystem Class (Opal SSC).
*
* It follows the UEFI driver binding model:
* 1. OpalSecurityDriverInit - Entry point (install protocol)
* 2. OpalSecurityBindingStart - Start binding (connect to device)
* 3. OpalSecurityBindingStop - Stop binding (disconnect from device)
*
* The module communicates with storage devices using the TCG Storage
* IF-SEND/IF-RECV protocol at Level 0 (security protocol discovery)
* to identify Opal-capable devices and perform security operations.
*
* Copyright (C) 2025 Lenovo. All rights reserved.
*/
#include "OpalSecurity.h"
//
// External global variables set by the UEFI Boot Services Table Library
//
extern EFI_HANDLE gImageHandle;
extern EFI_SYSTEM_TABLE *gST;
extern EFI_BOOT_SERVICES *gBS;
//
// Module-level global state
//
STATIC EFI_BOOT_SERVICES *mBootServices = NULL;
STATIC EFI_RUNTIME_SERVICES *mRuntimeServices = NULL;
STATIC EFI_SYSTEM_TABLE *mSystemTable = NULL;
STATIC UINT8 mOpalSecurityEnabled = 0;
STATIC OPAL_STORAGE_PROTOCOL *mStorageProtocol = NULL;
STATIC OPAL_STORAGE_PROTOCOL *mChildProtocol = NULL;
//
// Forward declarations
//
STATIC
UINTN
OpalReadCmosDebugLevel (
VOID
);
STATIC
EFI_STATUS
OpalGetHobList (
VOID
);
//------------------------------------------------------------------------------
// Module Entry Point
//------------------------------------------------------------------------------
EFI_STATUS
EFIAPI
OpalSecurityDriverInit (
IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable
)
{
EFI_BOOT_SERVICES *BootServices;
EFI_RUNTIME_SERVICES *RuntimeServices;
UINT64 Status;
//
// Cache UEFI service table pointers locally
//
if (mSystemTable == NULL) {
mSystemTable = SystemTable;
BootServices = SystemTable->BootServices;
mBootServices = BootServices;
RuntimeServices = SystemTable->RuntimeServices;
mRuntimeServices = RuntimeServices;
}
//
// Allocate a protocol instance structure
//
Status = mBootServices->AllocatePool (
EfiBootServicesData,
sizeof (OPAL_STORAGE_PROTOCOL),
(VOID **)&mStorageProtocol
);
if (EFI_ERROR (Status)) {
goto EXIT;
}
//
// Populate the protocol dispatch table with binding Start/Stop
//
mStorageProtocol->Send = (OPAL_STORAGE_SEND)OpalSecurityBindingStart;
mStorageProtocol->Recv = (OPAL_STORAGE_RECV)OpalSecurityBindingStop;
//
// Install the Opal Security Protocol onto the image handle
//
{
EFI_GUID OpalProtocolGuid = OPAL_SECURITY_PROTOCOL_GUID;
Status = BootServices->InstallMultipleProtocolInterfaces (
&OpalProtocolGuid,
&OpalProtocolGuid,
NULL
);
}
if (EFI_ERROR (Status)) {
OPAL_ASSERT (!EFI_ERROR (Status));
goto EXIT;
}
//
// Set the MemoryOverwriteRequestControl variable per UEFI spec
//
{
EFI_GUID MorGuid = MEMORY_OVERWRITE_REQUEST_CONTROL_GUID;
UINT64 MorAttributes = 1;
UINT8 MorValue = 0;
RuntimeServices->SetVariable (
L"MemoryOverwriteRequestControl",
&MorGuid,
0,
&MorAttributes,
&MorValue
);
}
EXIT:
return Status;
}
//------------------------------------------------------------------------------
// Driver Binding Start
//------------------------------------------------------------------------------
EFI_STATUS
EFIAPI
OpalSecurityBindingStart (
IN EFI_DRIVER_BINDING_PROTOCOL *This,
IN EFI_HANDLE ControllerHandle,
IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL
)
{
EFI_STATUS Status;
UINT8 Buffer[510];
UINT64 StorageIoGuid;
//
// Zero the buffer used for TCG command data
//
OpalMemset (Buffer, 0, sizeof (Buffer));
StorageIoGuid = 0;
//
// Check if the controller supports TCG Storage IO (type == 1)
//
if (StorageIoType == 1)
{
//
// Copy the device context from the controller handle
//
CopyMem (Buffer, (VOID *)((UINTN)ControllerHandle + 35), 4 * sizeof (UINT128));
StorageIoGuid = *((UINT64 *)This);
}
//
// Verify TCG SSC feature bit in Level 0 discovery data (bit 94)
//
if ((Buffer[94] & 1) == 0) {
return OPAL_STATUS_NOT_READY;
}
//
// Check if the protocol already exists on this controller
//
{
EFI_GUID StorageGuid = OPAL_STORAGE_PROTOCOL_GUID;
Status = mBootServices->LocateProtocol (
&StorageGuid,
NULL,
NULL
);
if (!EFI_ERROR (Status)) {
return OPAL_STATUS_ALREADY_STARTED;
}
}
//
// Allocate the child protocol instance (32 bytes)
//
Status = mBootServices->AllocatePool (
EfiBootServicesData,
32,
(VOID **)&mChildProtocol
);
if (EFI_ERROR (Status)) {
return Status;
}
if (StorageIoType == 1)
{
//
// Initialize child protocol with TCG send/recv handlers
//
mChildProtocol->Send = (OPAL_STORAGE_SEND)TcgStorageIoSend;
mChildProtocol->Recv = (OPAL_STORAGE_RECV)TcgStorageIoRecv;
mChildProtocol->Context = (VOID *)ControllerHandle;
((OPAL_STORAGE_PROTOCOL *)ControllerHandle)->Context = mChildProtocol;
}
//
// Install the child protocol
//
{
EFI_GUID StorageGuid = OPAL_STORAGE_PROTOCOL_GUID;
Status = mBootServices->InstallMultipleProtocolInterfaces (
&StorageGuid,
mChildProtocol,
NULL
);
}
if (EFI_ERROR (Status)) {
OPAL_ASSERT (!EFI_ERROR (Status));
return Status;
}
//
// If Opal Security is enabled, perform initial security exchange
//
if ((mOpalSecurityEnabled & 1) != 0 &&
OpalSecurityAllocBuffer () >= EFI_SUCCESS)
{
OpalSecurityReceiveResponse (
mChildProtocol,
This,
StorageIoType
);
}
return Status;
}
//------------------------------------------------------------------------------
// Driver Binding Stop
//------------------------------------------------------------------------------
EFI_STATUS
EFIAPI
OpalSecurityBindingStop (
IN EFI_DRIVER_BINDING_PROTOCOL *This,
IN EFI_HANDLE ControllerHandle,
IN UINTN NumberOfChildren,
IN EFI_HANDLE *ChildHandleBuffer OPTIONAL
)
{
EFI_STATUS Status;
UINT64 StorageIoGuid;
VOID *ChildProtocol;
StorageIoGuid = 0;
ChildProtocol = NULL;
if (StorageIoType == 1)
{
StorageIoGuid = *((UINT64 *)This);
ChildProtocol = ((OPAL_STORAGE_PROTOCOL *)ControllerHandle)->Context;
}
Status = mBootServices->UninstallMultipleProtocolInterfaces (
ControllerHandle,
&ChildProtocol,
NULL
);
if (Status == EFI_SUCCESS && ChildProtocol != NULL)
{
mBootServices->FreePool (ChildProtocol);
}
return Status;
}
//------------------------------------------------------------------------------
// Buffer Allocation for TCG Security Protocol
//------------------------------------------------------------------------------
EFI_STATUS
OpalSecurityAllocBuffer (
VOID
)
{
EFI_STATUS Status;
OPAL_STORAGE_PROTOCOL *Protocol;
VOID *Buffer;
UINT64 BufferSize;
Protocol = mChildProtocol;
if (Protocol == NULL) {
return OPAL_STATUS_NOT_READY;
}
Buffer = NULL;
BufferSize = 512;
Status = mBootServices->AllocatePool (
EfiBootServicesData,
512,
&Buffer
);
if (EFI_ERROR (Status)) {
return OPAL_STATUS_DEVICE_ERROR;
}
//
// Issue IF-RECV (Level 0 Discovery) to read TCG capabilities
//
Protocol->Send (
Protocol,
0,
0,
0,
0,
512,
Buffer
);
Protocol->SecurityProtocolBuffer = Buffer;
return EFI_SUCCESS;
}
//------------------------------------------------------------------------------
// Process TCG Security Response (IF-RECV data parsing)
//------------------------------------------------------------------------------
EFI_STATUS
OpalSecurityReceiveResponse (
IN OPAL_STORAGE_PROTOCOL *Protocol,
IN EFI_DRIVER_BINDING_PROTOCOL *Binding,
IN UINT32 StorageIoType
)
{
EFI_STATUS Status;
UINT8 Buffer[510];
UINT8 *DiscoveryBuffer;
UINT16 NumDescriptors;
UINT16 Index;
UINT64 TmpBuffer;
DiscoveryBuffer = (UINT8 *)Protocol->SecurityProtocolBuffer;
OpalMemset (Buffer, 0, sizeof (Buffer));
if (StorageIoType == 1)
{
CopyMem (Buffer, (VOID *)((UINTN)Binding + 35), 4 * sizeof (UINT128));
}
//
// Read descriptor count from Level 0 Discovery response
//
NumDescriptors = _byteswap_ushort (*(UINT16 *)(DiscoveryBuffer + 6));
if (NumDescriptors == 0) {
return OPAL_STATUS_NOT_READY;
}
//
// Parse TCG Level 0 Discovery descriptors
//
for (Index = 0; Index < NumDescriptors; Index++)
{
UINT8 FeatureCode = *(UINT8 *)(DiscoveryBuffer + 8 + Index);
if (FeatureCode == 0x02)
{
//
// TCG Storage SSC feature found -- send IF-SEND command
//
TmpBuffer = 0;
Status = mBootServices->AllocatePool (
EfiBootServicesData,
512,
&TmpBuffer
);
if (EFI_ERROR (Status)) {
return Status;
}
mBootServices->SetMem ((VOID *)TmpBuffer, 512, 0);
Protocol->Send (
Protocol,
0,
0,
2,
0x0400,
512,
TmpBuffer
);
mBootServices->FreePool ((VOID *)TmpBuffer);
return EFI_SUCCESS;
}
if (FeatureCode == 0xEE && (Buffer[0x8A] & 0x80) == 0)
{
//
// Opal SSC v2 (Pyrite) feature -- issue IF-RECV
//
Status = OpalSecuritySendCommand (Protocol);
if (EFI_ERROR (Status)) {
OPAL_ASSERT (!EFI_ERROR (Status));
}
return EFI_SUCCESS;
}
}
return EFI_SUCCESS;
}
//------------------------------------------------------------------------------
// TCG Storage IF-SEND Command
//------------------------------------------------------------------------------
EFI_STATUS
OpalSecuritySendCommand (
IN OPAL_STORAGE_PROTOCOL *Protocol
)
{
EFI_STATUS Status;
VOID *CmdResponseBuffer;
VOID *SmallBuffer;
UINT64 ParameterLength;
UINT16 CommandIteration;
CmdResponseBuffer = NULL;
CmdParamBuffer = NULL;
ParameterLength = 4;
Status = mBootServices->AllocatePool (
EfiBootServicesData,
4,
&CmdResponseBuffer
);
if (EFI_ERROR (Status)) {
return OPAL_STATUS_DEVICE_ERROR;
}
//
// IF-RECV with Protocol = 0xEE, ComId = 256
//
CommandIteration = 256;
Status = ((OPAL_STORAGE_RECV_PTR)Protocol->Context)->Recv (
Protocol,
0,
0,
0xEE,
CommandIteration,
512,
CmdResponseBuffer
);
if (EFI_ERROR (Status)) {
goto CLEANUP;
}
//
// Verify the COM ID iteration matches
//
while (CommandIteration != _byteswap_ushort (*(UINT16 *)(CmdResponseBuffer + 2)))
{
CommandIteration++;
if (CommandIteration >= 0x200) {
goto CLEANUP;
}
}
//
// Final IF-RECV with Level 1 discovery ProtocolId
//
SmallBuffer = NULL;
Status = mBootServices->AllocatePool (
EfiBootServicesData,
4,
&SmallBuffer
);
if (EFI_ERROR (Status)) {
return OPAL_STATUS_DEVICE_ERROR;
}
Status = ((OPAL_STORAGE_RECV_PTR)Protocol->Context)->Recv (
Protocol,
0,
0,
0xEE,
0x0500,
512,
SmallBuffer
);
mBootServices->FreePool (SmallBuffer);
CLEANUP:
mBootServices->FreePool (CmdResponseBuffer);
return Status;
}
//------------------------------------------------------------------------------
// TCG IF-RECV (receive security data without transfer length)
//------------------------------------------------------------------------------
EFI_STATUS
EFIAPI
TcgStorageIoRecv (
IN OPAL_STORAGE_PROTOCOL *This,
IN UINT32 StorageIoType,
IN UINT64 Timeout,
IN UINT8 Command,
IN UINT16 ProtocolId,
IN UINT64 BufferLength,
IN VOID *Buffer
)
{
OPAL_STORAGE_PROTOCOL *Parent;
UINT64 BlockIo;
UINT32 *TcgParams;
UINT64 Lba;
UINT8 TcgPacket[49];
Parent = (OPAL_STORAGE_PROTOCOL *)This->Context;
BlockIo = *(UINT64 *)((UINTN)Parent + 848);
TcgParams = *(UINT32 **)(BlockIo + 864);
if (!TcgParams || !BlockIo) {
return OPAL_STATUS_NOT_READY;
}
if (*TcgParams != StorageIoType) {
return OPAL_STATUS_INVALID_PARAMETER;
}
Lba = (BufferLength / TcgParams[3]) >> 8;
OpalMemset (TcgPacket, 0, sizeof (TcgPacket));
//
// Build TCG IF-RECV packet
//
TcgPacket[12] = Command;
TcgPacket[18] = HIBYTE (ProtocolId);
TcgPacket[20] = ProtocolId;
if (BufferLength)
{
TcgPacket[16] = (UINT8)Lba;
TcgPacket[23] = 0x5E; // IF-RECV opcode
if (Timeout) {
TcgPacket[25] = (UINT8)(Timeout / 0x2710);
} else {
*(UINT64 *)&TcgPacket[17] = -1;
}
*(UINT64 *)&TcgPacket[0] = (UINT64)Buffer;
*(UINT32 *)&TcgPacket[4] = (UINT32)BufferLength;
if (Buffer) {
return ((UINT64 (*)(VOID *, VOID *, UINT8))BlockIo)(
Parent,
TcgPacket,
1
);
}
}
else
{
TcgPacket[23] = 0x5B; // IF-RECV (no data)
if (Timeout) {
TcgPacket[25] = (UINT8)(Timeout / 0x2710);
} else {
*(UINT64 *)&TcgPacket[17] = -1;
}
*(UINT32 *)&TcgPacket[4] = 0;
*(UINT64 *)&TcgPacket[0] = (UINT64)Buffer;
return ((UINT64 (*)(VOID *, VOID *))BlockIo)(
Parent,
TcgPacket
);
}
return OPAL_STATUS_INVALID_PARAMETER;
}
//------------------------------------------------------------------------------
// TCG IF-SEND (send security data with transfer length)
//------------------------------------------------------------------------------
EFI_STATUS
EFIAPI
TcgStorageIoSend (
IN OPAL_STORAGE_PROTOCOL *This,
IN UINT32 StorageIoType,
IN UINT64 Timeout,
IN UINT8 Command,
IN UINT16 ProtocolId,
IN UINT64 BufferLength,
IN VOID *Buffer,
OUT UINT64 *TransferLength OPTIONAL
)
{
OPAL_STORAGE_PROTOCOL *Parent;
UINT64 BlockIo;
UINT32 *TcgParams;
UINT64 Lba;
UINT64 AlignedLba;
UINT8 TcgPacket[49];
EFI_STATUS Status;
Parent = (OPAL_STORAGE_PROTOCOL *)This->Context;
BlockIo = *(UINT64 *)((UINTN)Parent + 864);
TcgParams = *(UINT32 **)(BlockIo + 8);
if (!BlockIo || !TcgParams) {
return OPAL_STATUS_NOT_READY;
}
if (*TcgParams != StorageIoType) {
return OPAL_STATUS_INVALID_PARAMETER;
}
Lba = (BufferLength / TcgParams[3]) >> 8;
AlignedLba = BufferLength / TcgParams[3];
OpalMemset (TcgPacket, 0, sizeof (TcgPacket));
if (BufferLength)
{
if (!Buffer || !TransferLength) {
return OPAL_STATUS_INVALID_PARAMETER;
}
TcgPacket[14] = (UINT8)AlignedLba;
TcgPacket[16] = (UINT8)Lba;
TcgPacket[18] = HIBYTE (ProtocolId);
TcgPacket[20] = ProtocolId;
TcgPacket[23] = 0x5C; // IF-SEND opcode
TcgPacket[12] = Command;
if (Timeout) {
TcgPacket[25] = (UINT8)(Timeout / 0x2710);
} else {
*(UINT64 *)&TcgPacket[17] = -1;
}
*(UINT64 *)&TcgPacket[0] = (UINT64)Buffer;
*(UINT32 *)&TcgPacket[4] = (UINT32)BufferLength;
Status = ((UINT64 (*)(VOID *, VOID *, UINT64))BlockIo)(
Parent,
TcgPacket,
0
);
}
else
{
TcgPacket[18] = HIBYTE (ProtocolId);
TcgPacket[20] = ProtocolId;
TcgPacket[17] = 1;
TcgPacket[23] = 0x5B; // IF-SEND (no data)
TcgPacket[12] = Command;
if (Timeout) {
TcgPacket[25] = (UINT8)(Timeout / 0x2710);
} else {
*(UINT64 *)&TcgPacket[17] = -1;
}
*(UINT32 *)&TcgPacket[4] = 0;
*(UINT64 *)&TcgPacket[0] = (UINT64)Buffer;
Status = ((UINT64 (*)(VOID *, VOID *))BlockIo)(
Parent,
TcgPacket
);
}
if (EFI_ERROR (Status))
{
if (TransferLength != NULL) {
*TransferLength = 0;
}
}
else
{
if (TransferLength != NULL) {
*TransferLength = *(UINT32 *)&TcgPacket[4];
}
}
return Status;
}
//------------------------------------------------------------------------------
// Debug Support Functions
//------------------------------------------------------------------------------
STATIC
VOID *
OpalGetDebugContext (
VOID
)
{
STATIC VOID *mDebugContext = NULL;
if (mDebugContext == NULL)
{
UINTN RngValue;
RngValue = gBS->GetTimerValue (31);
gBS->Stall (RngValue);
if (RngValue <= 0x10)
{
EFI_STATUS Status;
EFI_GUID Guid = OPAL_STORAGE_PROTOCOL_GUID;
Status = gBS->LocateHandleBuffer (
ByProtocol,
&Guid,
NULL,
&mDebugContext
);
if (EFI_ERROR (Status)) {
mDebugContext = NULL;
}
}
}
return mDebugContext;
}
STATIC
VOID
OpalDebugAssertPrint (
IN EFI_STATUS Status,
IN CHAR8 *Format,
...
)
{
VOID *DebugContext;
VA_LIST Args;
VA_START (Args, Format);
DebugContext = OpalGetDebugContext ();
if (DebugContext != NULL)
{
UINTN DebugLevel;
DebugLevel = OpalReadCmosDebugLevel ();
if ((DebugLevel & Status) != 0)
{
((VOID (*)(EFI_STATUS, CHAR8 *, VA_LIST))DebugContext)(
Status,
Format,
Args
);
}
}
VA_END (Args);
}
STATIC
VOID
OpalAssertHandler (
IN CHAR8 *File,
IN UINTN Line,
IN CHAR8 *Condition
)
{
VOID *DebugContext;
DebugContext = OpalGetDebugContext ();
if (DebugContext != NULL)
{
((VOID (*)(CHAR8 *, UINTN, CHAR8 *))DebugContext)(
File,
Line,
Condition
);
}
}
//------------------------------------------------------------------------------
// HOB List Retrieval
//------------------------------------------------------------------------------
STATIC
VOID *
OpalGetHobList (
VOID
)
{
STATIC VOID *mHobList = NULL;
if (mHobList == NULL)
{
EFI_CONFIGURATION_TABLE *ConfigTable;
UINTN TableIndex;
EFI_GUID HobListGuid = gEfiHobListGuid;
ConfigTable = (EFI_CONFIGURATION_TABLE *)gST->ConfigurationTable;
mHobList = NULL;
if (gST->NumberOfTableEntries > 0)
{
for (TableIndex = 0; TableIndex < gST->NumberOfTableEntries; TableIndex++)
{
if (OpalCompareGuid (
&HobListGuid,
&ConfigTable[TableIndex].VendorGuid
))
{
mHobList = ConfigTable[TableIndex].VendorTable;
break;
}
}
}
if (mHobList == NULL)
{
OpalDebugAssertPrint (
OPAL_STATUS_NOT_FOUND,
"\nASSERT_EFI_ERROR (Status = %r)\n",
OPAL_STATUS_NOT_FOUND
);
OpalAssertHandler (
"e:\\hs\\MdePkg\\Library\\DxeHobLib\\HobLib.c",
54,
"!EFI_ERROR (Status)"
);
}
}
if (mHobList == NULL)
{
OpalAssertHandler (
"e:\\hs\\MdePkg\\Library\\DxeHobLib\\HobLib.c",
55,
"mHobList != ((void *) 0)"
);
}
return mHobList;
}
//------------------------------------------------------------------------------
// Utility Functions
//------------------------------------------------------------------------------
STATIC
UINTN
OpalReadCmosDebugLevel (
VOID
)
{
UINT8 CmosValue;
STATIC UINT8 sCachedValue = 0;
__outbyte (0x70, (__inbyte (0x70) & 0x80) | RTC_CMOS_DEBUG_LEVEL_REGISTER);
CmosValue = __inbyte (0x71);
sCachedValue = CmosValue;
if (CmosValue > 3)
{
if (CmosValue == 0)
{
sCachedValue = (*(UINT8 *)0xFDAF0490 & 2) | 1;
}
}
if (sCachedValue - 1 > 0xFD)
{
return 0;
}
if (sCachedValue == 1)
{
return OPAL_STATUS_NOT_READY;
}
return OPAL_STATUS_NOT_FOUND;
}
STATIC
BOOLEAN
OpalCompareGuid (
IN EFI_GUID *Guid1,
IN EFI_GUID *Guid2
)
{
EFI_GUID CandidateGuid = *Guid2;
return (OpalReadUnaligned64 (&Guid1->Data1) == OpalReadUnaligned64 (&CandidateGuid.Data1) &&
OpalReadUnaligned64 (&Guid1->Data4) == OpalReadUnaligned64 (&Guid2->Data4));
}
STATIC
UINT64
OpalReadUnaligned64 (
IN CONST VOID *Buffer
)
{
if (Buffer == NULL) {
OpalAssertHandler (
"e:\\hs\\MdePkg\\Library\\BaseLib\\Unaligned.c",
192,
"Buffer != ((void *) 0)"
);
}
return *(UINT64 *)Buffer;
}
STATIC
VOID *
OpalMemset (
OUT VOID *Buffer,
IN INT32 Value,
IN UINTN Length
)
{
return memset (Buffer, Value, Length);
}
STATIC
VOID *
OpalMemmove (
OUT VOID *Destination,
IN CONST VOID *Source,
IN UINTN Length
)
{
return memmove (Destination, Source, Length);
}