Newer
Older
AMI-Aptio-BIOS-Reversed / TcgStorageSecurity / TcgStorageSecurity.c
@Ajax Dong Ajax Dong 2 days ago 45 KB Init
/** @file
  TCG Storage Security DXE driver -- TcgStorageSecurity.c

  Implements the TCG Storage Security protocol for UEFI. This driver
  attaches to storage controllers through the UEFI driver binding model
  and provides TCG OPAL/Pyrite/Enterprise SSC security operations:

    - Level 0 Discovery data retrieval and parsing
    - TCG TPER session management
    - Opening Admin SP sessions with MSID authority
    - SID credential management (Block SID, Set C_PIN_SID)
    - Locking range configuration (ReadLock, WriteLock, ReadWrite)
    - S3 resume data buffering for PCI config space restoration
    - Debug logging via sub_7450/sub_74D8 (DEBUG/ASSERT macros)

  Based on: AmiModulePkg/TcgStorageSecurity/TcgStorageSec.c

  Copyright (c) 2024, American Megatrends International LLC.
  Copyright (c) 2024, Intel Corporation. All rights reserved.
  SPDX-License-Identifier: BSD-2-Clause-Patent

**/

#include "TcgStorageSecurity.h"

//
// External protocol GUIDs and device path protocols
//
extern EFI_GUID gEfiTcgStorageProtocolGuid;
extern EFI_GUID gEfiBlockSidProtocolGuid;
extern EFI_GUID gEfiStorageSecurityCommandProtocolGuid;

//
// Global data references
//
volatile UINTN   gImageHandle      = 0;
volatile UINTN   gSystemTable      = 0;
volatile INTN    gBootServices     = 0;
volatile INTN    gRuntimeServices  = 0;

//
// Module global state
//
UINTN                         n0x180           = 0;
UINT8                         gProtocolBuffer[512];       ///< General purpose protocol buffer
UINT8                         gMsidCredential[141];       ///< MSID credential (C_PIN_MSID)
UINT8                         gSendBuffer[512];           ///< Send/receive command buffer
UINT64                        gS3BufferData[TCG_S3_BUFFER_SLOTS]; ///< S3 PCI config restore buffer
VOID                          *gTcgStorageProtocol = NULL;
VOID                          *gBlockSidStorageProtocol = NULL;
VOID                          *gSidBlockCommandInterface = NULL;
UINT64                        gSidBlockPassword[3]         = { 0, 0, 0 };
EFI_DRIVER_BINDING_PROTOCOL   gDriverBinding;
VOID                          *gLockingSpSession;
VOID                          *gAdminSpSession;
UINTN                         gDiscoveryBuffer[32];
UINTN                         gLockingSpBuffer[112];
UINTN                         gSessionBuffer[136];

//
// GUID tables
//
EFI_GUID  gTcgStorageProtocolGuid         = { 0xCA1E3F1A, 0x2D84, 0x46C8, { 0x9B, 0x2E, 0x23, 0x51, 0xC0, 0xB9, 0x1B, 0x94 } };

//
// Static data used by Level 0 discovery parsing
//
extern UINT8  FeatureTper[];
extern UINT8  FeatureLocking[];
extern UINT8  FeatureGeometry[];
extern UINT8  FeatureSscOpalV2[];

/**
  Sends a raw TCG Storage Security protocol command (send or receive)
  to the storage device via the Storage Security Command Protocol.

  @param[in]      DeviceContext  The storage device context.
  @param[in]      ProtocolId     TCG protocol ID (1 = OPAL, etc.).
  @param[in]      Timeout        Timeout in microseconds.
  @param[in]      IsSend         TRUE for send, FALSE for receive.
  @param[in]      TransferLength Length of data to transfer.
  @param[in]      Buffer         Data buffer.
  @param[in,out]  BufferSize     Size of buffer / bytes transferred.

  @retval EFI_SUCCESS           Command completed.
  @retval EFI_DEVICE_ERROR      Device error.
**/
EFI_STATUS
SendReceiveRaw (
  IN     VOID    *DeviceContext,
  IN     UINT8   ProtocolId,
  IN     UINTN   Timeout,
  IN     UINT16  IsSend,
  IN     UINTN   TransferLength,
  IN     VOID    *Buffer,
  IN OUT UINTN   *BufferSize
  )
{
  EFI_STATUS           Status;
  UINTN                DescriptorCount;
  EFI_TCG_PROTOCOL     *TcgProtocol;

  TcgProtocol = (EFI_TCG_PROTOCOL *)DeviceContext;

  DescriptorCount       = 1;
  *BufferSize           = TransferLength;

  if (IsSend) {
    Status = TcgProtocol->SendData (
                            TcgProtocol,
                            ProtocolId,
                            (UINT8 *)Buffer,
                            (UINT32 *)BufferSize,
                            (UINT32)Timeout
                            );
  } else {
    Status = TcgProtocol->ReceiveData (
                            TcgProtocol,
                            ProtocolId,
                            (UINT8 *)Buffer,
                            (UINT32 *)BufferSize,
                            (UINT32)Timeout
                            );
  }

  DEBUG ((DEBUG_INFO, "TcgStorageSecurity: SendReceiveRaw Status : %r\n", Status));
  return Status;
}

/**
  Initializes UEFI Boot Services, System Table, and Runtime Services
  global pointers. Performs early platform initialization including
  PCI config space probing and CMOS NVRAM checks.

  @param[in] ImageHandle  EFI image handle.
  @param[in] SystemTable  EFI system table.

  @retval EFI_SUCCESS  Initialization completed.
**/
EFI_STATUS
EFIAPI
DriverInitEntry (
  IN EFI_HANDLE        ImageHandle,
  IN EFI_SYSTEM_TABLE  *SystemTable
  )
{
  UINTN       PciData;
  UINT16      VendorId;

  gImageHandle = (UINTN)ImageHandle;
  gSystemTable = (UINTN)SystemTable;

  //
  // Validate input parameters using UEFI Boot Services Table Library
  //
  ASSERT (gImageHandle != 0);
  ASSERT (gSystemTable != 0);

  gBootServices = (INTN)SystemTable->BootServices;
  ASSERT (gBootServices != 0);

  gRuntimeServices = (INTN)SystemTable->RuntimeServices;
  ASSERT (gRuntimeServices != 0);

  //
  // Perform platform-specific initialization
  // Check for PCI OPAL presence and configure CMOS
  //
  PciData = PciRead32 (PCI_LIB_ADDRESS (0, 0, 0, 0));
  VendorId = (UINT16)(PciData & 0xFFFF);

  if (VendorId != 0xFFFF) {
    //
    // Configure chipset register for OPAL
    //
    PciOr8 (PCI_LIB_ADDRESS (0, 0, 0, 0xA4), BIT7);
  }

  //
  // Check CMOS for TCG enable
  //
  if ((IoRead8 (0x71) & 0x0F) == 0) {
    DEBUG ((DEBUG_INFO, "TcgStorageSecurity: Skipping - disabled in CMOS\n"));
  }

  //
  // Check platform type via memory-mapped I/O
  //
  if ((MmioRead8 (0xFEDAF0490) & 0x02) != 0) {
    DEBUG ((DEBUG_INFO, "TcgStorageSecurity: Platform supports TCG\n"));
  }

  return EFI_SUCCESS;
}

/**
  Tests whether this driver supports a given controller handle.

  @param[in] This                 Driver binding protocol.
  @param[in] ControllerHandle     Controller to test.
  @param[in] RemainingDevicePath  Optional device path.

  @retval EFI_SUCCESS             Controller is supported.
  @retval EFI_UNSUPPORTED         Controller is not supported.
**/
EFI_STATUS
EFIAPI
TcgStorageSecuritySupported (
  IN EFI_DRIVER_BINDING_PROTOCOL  *This,
  IN EFI_HANDLE                   ControllerHandle,
  IN EFI_DEVICE_PATH_PROTOCOL     *RemainingDevicePath
  )
{
  EFI_STATUS           Status;
  EFI_TCG_PROTOCOL     *TcgProtocol;

  //
  // Check if controller supports EFI Storage Security Command Protocol
  //
  Status = gBS->OpenProtocol (
                  ControllerHandle,
                  &gEfiStorageSecurityCommandProtocolGuid,
                  (VOID **)&TcgProtocol,
                  gImageHandle,
                  NULL,
                  EFI_OPEN_PROTOCOL_BY_DRIVER
                  );
  if (EFI_ERROR (Status)) {
    return Status;
  }

  gBS->CloseProtocol (
        ControllerHandle,
        &gEfiStorageSecurityCommandProtocolGuid,
        gImageHandle,
        NULL
        );

  return EFI_SUCCESS;
}

/**
  Starts the driver on a supported controller.

  Allocates the private context structure, opens the TCG Storage Security
  Command Protocol, performs Level 0 discovery, and installs the
  TCG Storage Protocol instance on the controller handle.

  @param[in] This                 Driver binding protocol.
  @param[in] ControllerHandle     Controller handle to start.
  @param[in] RemainingDevicePath  Optional device path.

  @retval EFI_SUCCESS             Driver started successfully.
**/
EFI_STATUS
EFIAPI
TcgStorageSecurityStart (
  IN EFI_DRIVER_BINDING_PROTOCOL  *This,
  IN EFI_HANDLE                   ControllerHandle,
  IN EFI_DEVICE_PATH_PROTOCOL     *RemainingDevicePath
  )
{
  EFI_STATUS             Status;
  EFI_TCG_PROTOCOL       *TcgProtocol;
  TCG_PROTOCOL_CONTEXT   *Context;
  TCG_DEVICE_CONTEXT     *DeviceContext;
  UINT8                  SecurityStatus;

  //
  // Open the Storage Security Command Protocol
  //
  Status = gBS->OpenProtocol (
                  ControllerHandle,
                  &gEfiStorageSecurityCommandProtocolGuid,
                  (VOID **)&TcgProtocol,
                  gImageHandle,
                  NULL,
                  EFI_OPEN_PROTOCOL_BY_DRIVER
                  );
  if (EFI_ERROR (Status)) {
    return Status;
  }

  //
  // Check if TCG security protocol (0x01) is supported
  //
  Status = GetSupportedProtocols (TcgProtocol, 1);
  if (EFI_ERROR (Status)) {
    DEBUG ((DEBUG_ERROR, "TCG security protocol NOT supported \n"));
    goto CloseAndReturn;
  }

  //
  // Check if the protocol is already installed (already started)
  //
  Status = gBS->OpenProtocol (
                  ControllerHandle,
                  &gTcgStorageProtocolGuid,
                  NULL,
                  gImageHandle,
                  NULL,
                  EFI_OPEN_PROTOCOL_TEST_PROTOCOL
                  );
  if (!EFI_ERROR (Status)) {
    Status = EFI_ALREADY_STARTED;
    goto CloseAndReturn;
  }

  //
  // Allocate protocol context
  //
  Status = gBS->AllocatePool (
                  EfiBootServicesData,
                  sizeof (TCG_PROTOCOL_CONTEXT),
                  (VOID **)&Context
                  );
  if (EFI_ERROR (Status)) {
    goto CloseAndReturn;
  }
  ZeroMem (Context, sizeof (TCG_PROTOCOL_CONTEXT));

  //
  // Set up function dispatch table
  //
  Context->Functions[TCG_DISPATCH_RETRIEVE_STATUS] = TcgRetrieveSecurityStatus;
  Context->Functions[TCG_DISPATCH_SEND_RECEIVE]    = TcgSendReceive;
  Context->Functions[TCG_DISPATCH_GET_INFO]        = TcgGetInfo;
  Context->Functions[TCG_DISPATCH_SET_PASSWORD]    = TcgSetPassword;
  Context->Functions[TCG_DISPATCH_RESET]           = TcgReset;
  Context->Functions[TCG_DISPATCH_BLOCK_SID]       = TcgBlockSid;
  Context->Functions[TCG_DISPATCH_SEND_RECEIVE_RAW] = NULL;

  //
  // Link back to driver context and set flags
  //
  Context->Flags = 1;   // Primary context
  Context->DriverContext = TcgProtocol;

  //
  // Initialize per-controller session data
  //
  Status = InitializeTcgSessions (TcgProtocol, 1);
  if (EFI_ERROR (Status)) {
    DEBUG ((DEBUG_ERROR, "ASSERT_EFI_ERROR (Status = %r)\n", Status));
    gBS->FreePool (Context);
    goto CloseAndReturn;
  }

  //
  // Retrieve initial security status
  //
  Status = TcgRetrieveSecurityStatus (Context, &SecurityStatus);
  if (EFI_ERROR (Status)) {
    DEBUG ((DEBUG_ERROR, "ASSERT_EFI_ERROR (Status = %r)\n", Status));
  }

  //
  // Handle TPER reset / Block SID if needed
  //
  if (!(SecurityStatus & TCG_SECURITY_STATUS_FROZEN) &&
      (SecurityStatus & TCG_SECURITY_STATUS_LOCKED)) {
    //
    // Issue TPER reset for locked drives
    //
    Status = OpenSessionWithAdminSpMsid (
               TcgProtocol,
               2,
               TCG_PROTOCOL_BUFFER_SIZE,
               gSendBuffer,
               sizeof (gSendBuffer)
               );
    if (!EFI_ERROR (Status)) {
      CloseSession (TcgProtocol);
    }
  }

  //
  // Install the protocol on the controller handle
  //
  Status = gBS->InstallMultipleProtocolInterfaces (
                  &ControllerHandle,
                  &gTcgStorageProtocolGuid,
                  Context,
                  NULL
                  );
  if (EFI_ERROR (Status)) {
    gBS->FreePool (Context);
    goto CloseAndReturn;
  }

  //
  // Register protocol notification for Block SID
  //
  if (gBlockSidStorageProtocol == NULL) {
    Status = gBS->LocateProtocol (
                    &gEfiBlockSidProtocolGuid,
                    NULL,
                    &gBlockSidStorageProtocol
                    );
    if (EFI_ERROR (Status)) {
      gBlockSidStorageProtocol = NULL;
    }
  }

  //
  // Register timer event for periodic S3 data save
  //
  if (gSidBlockCommandInterface == NULL) {
    Status = gBS->CreateEvent (
                    EVT_TIMER | EVT_NOTIFY_SIGNAL,
                    TPL_CALLBACK,
                    RestoreS3Data,
                    NULL,
                    &gSidBlockCommandInterface
                    );
    if (!EFI_ERROR (Status)) {
      gBS->SetTimer (
             gSidBlockCommandInterface,
             TimerPeriodic,
             EFI_TIMER_PERIOD_SECONDS (1)
             );
    }
  }

  return EFI_SUCCESS;

CloseAndReturn:
  gBS->CloseProtocol (
        ControllerHandle,
        &gEfiStorageSecurityCommandProtocolGuid,
        gImageHandle,
        NULL
        );
  return Status;
}

/**
  Stops the driver on a controller handle. Frees resources.

  @param[in] This                 Driver binding protocol.
  @param[in] ControllerHandle     Controller to stop.
  @param[in] NumberOfChildren     Number of children.
  @param[in] ChildBuffer          Child handle buffer.

  @retval EFI_SUCCESS             Driver stopped.
**/
EFI_STATUS
EFIAPI
TcgStorageSecurityStop (
  IN EFI_DRIVER_BINDING_PROTOCOL  *This,
  IN EFI_HANDLE                   ControllerHandle,
  IN UINTN                        NumberOfChildren,
  IN EFI_HANDLE                   *ChildBuffer
  )
{
  EFI_STATUS           Status;
  TCG_PROTOCOL_CONTEXT *Context;

  Status = gBS->OpenProtocol (
                  ControllerHandle,
                  &gTcgStorageProtocolGuid,
                  (VOID **)&Context,
                  gImageHandle,
                  NULL,
                  EFI_OPEN_PROTOCOL_GET_PROTOCOL
                  );
  if (EFI_ERROR (Status)) {
    return Status;
  }

  //
  // Uninstall the protocol and free context
  //
  Status = gBS->UninstallMultipleProtocolInterfaces (
                  ControllerHandle,
                  &gTcgStorageProtocolGuid,
                  Context,
                  NULL
                  );
  if (EFI_ERROR (Status)) {
    return Status;
  }

  gBS->FreePool (Context);

  //
  // Close the storage security command protocol
  //
  gBS->CloseProtocol (
        ControllerHandle,
        &gEfiStorageSecurityCommandProtocolGuid,
        gImageHandle,
        NULL
        );

  return EFI_SUCCESS;
}

//
// ========================================================================
// TCG Storage Protocol Interface Functions
// ========================================================================

/**
  Retrieves the current TCG security status word.

  Queries the OPAL security state from the stored context
  (checking locked/frozen/active flags from Level 0 discovery).

  @param[in]  Context        Protocol context (TCG_PROTOCOL_CONTEXT *).
  @param[out] SecurityStatus Security status bitmask.

  @retval EFI_SUCCESS           Status retrieved.
**/
EFI_STATUS
EFIAPI
TcgRetrieveSecurityStatus (
  IN  VOID    *Context,
  OUT UINT16  *SecurityStatus
  )
{
  TCG_PROTOCOL_CONTEXT  *TcgContext;
  UINTN                 Index;

  TcgContext = (TCG_PROTOCOL_CONTEXT *)Context;
  Index = TcgContext->Flags;

  *SecurityStatus = GetOpalSecurityStatus (TcgContext->DriverContext, Index);

  DEBUG ((DEBUG_VERBOSE, "\n TcgReturnSecurityStatus = %x", *SecurityStatus));

  return EFI_SUCCESS;
}

/**
  Processes TCG storage security send/receive operations.

  Dispatches to the appropriate handler based on the operation code:
    - BIT0: ReadLockingRange / SetLockingRange / ConfigureLocking
    - BIT1: SetSidCredential / ReadLocking for locked state
    - Additional bits for other operations

  @param[in] Context    Protocol instance context (TCG_PROTOCOL_CONTEXT *).
  @param[in] Operation  Operation flags (UINT16).
  @param[in] Buffer     Data buffer for the operation.

  @retval EFI_SUCCESS           Operation completed.
  @retval EFI_UNSUPPORTED       Operation not supported.
  @retval EFI_DEVICE_ERROR      Device error.
**/
EFI_STATUS
EFIAPI
TcgSendReceive (
  IN VOID    *Context,
  IN UINT16  Operation,
  IN VOID    *Buffer
  )
{
  TCG_PROTOCOL_CONTEXT  *TcgContext;
  UINTN                 Index;
  VOID                  *TperContext;
  UINT16                StatusFlags;
  EFI_STATUS            Status;
  UINT8                 LockingState;

  TcgContext = (TCG_PROTOCOL_CONTEXT *)Context;
  Index = TcgContext->Flags;

  //
  // Resolve TPER context based on primary/secondary binding
  //
  if (Index == 1) {
    TperContext = (VOID *)((UINTN)TcgContext->DriverContext + 912);
  } else if (Index == 2) {
    TperContext = (VOID *)((UINTN)TcgContext->DriverContext + 328);
  } else {
    return EFI_UNSUPPORTED;
  }

  StatusFlags = Operation & 0x103;

  if ((Operation & BIT0) != 0) {
    //
    // BIT0 operations: Locking range related
    //
    StatusFlags = GetOpalSecurityStatus (TcgContext->DriverContext, Index);
    if ((StatusFlags & BIT1) != 0) {
      //
      // Already locked: perform ReadLockingRange
      //
      Status = ReadLockingRange (TperContext, Buffer);
    } else {
      //
      // Not locked: perform full Locking SP session
      //
      Status = OpenLockingSpSession (TperContext, Buffer);
      if (!EFI_ERROR (Status)) {
        Status = SetConfiguredLockingRange (TperContext, Buffer);
        if (!EFI_ERROR (Status)) {
          //
          // Set locking state to ReadWrite (0x02)
          //
          LockingState = OPAL_LOCKING_STATE_READWRITE;
          Status = SetLockingRange (TperContext, Buffer);
          if (EFI_ERROR (Status)) {
            return Status;
          }
          Status = ConfigureLockingRange (TperContext, Buffer);
        }
      }
    }

    if (!EFI_ERROR (Status)) {
      CloseSession (TcgContext->DriverContext);
      UpdateLockingStatus (TcgContext, StatusFlags, Buffer, 1);
    }

    return Status;
  }

  if ((Operation & BIT1) != 0) {
    //
    // BIT1 operations: Set SID credential / MSID related
    //
    Status = SetSidCredential (TperContext, Buffer);
    if (!EFI_ERROR (Status)) {
      CloseSession (TcgContext->DriverContext);
      UpdateLockingStatus (TcgContext, Operation, Buffer, 1);
    }
    return Status;
  }

  //
  // Other operations: C_PIN_SID / credential handling
  //
  if ((Operation & BIT0) != 0) {
    Status = ReadLockingRange (TperContext, Buffer);
  } else {
    Status = SetSidCredential (TperContext, Buffer);
  }

  if (!EFI_ERROR (Status)) {
    CloseSession (TcgContext->DriverContext);
    UpdateLockingStatus (TcgContext, Operation, Buffer, 2);
  }

  return Status;
}

/**
  Returns capability / information about the TCG protocol instance.

  @param[in]  Context        Protocol context.
  @param[in]  Operation      Unused.
  @param[out] Buffer         Info buffer (returns capability flags).

  @retval EFI_SUCCESS           Information returned.
**/
EFI_STATUS
EFIAPI
TcgGetInfo (
  IN VOID    *Context,
  IN UINT16  Operation,
  IN VOID    *Buffer
  )
{
  TCG_PROTOCOL_CONTEXT  *TcgContext;
  UINTN                 Index;
  VOID                  *TperContext;

  TcgContext = (TCG_PROTOCOL_CONTEXT *)Context;
  Index = TcgContext->Flags;

  if (Index == 1) {
    TperContext = (VOID *)((UINTN)TcgContext->DriverContext + 912);
  } else {
    TperContext = (VOID *)((UINTN)TcgContext->DriverContext + 328);
  }

  //
  // Check for Block SID feature
  //
  if (*(UINT16 *)((UINTN)TperContext + 82) == TCG_FEATURE_CODE_BLOCK_SID) {
    *(UINT8 *)Buffer = 0;
    //
    // Check C_PIN_SID vs C_PIN_MSID equality
    //
    if ((*(UINT8 *)((UINTN)TperContext + 86) & SID_BLOCK_STATUS_C_PIN_SID) == 0) {
      *(UINT8 *)Buffer = 1;   // Block SID supported
    }
    //
    // Check if SID is already blocked
    //
    if ((*(UINT8 *)((UINTN)TperContext + 86) & SID_BLOCK_STATUS_SID_VALUE) != 0) {
      *(UINT8 *)Buffer = 2;   // Already blocked
    }
  }

  return EFI_SUCCESS;
}

/**
  Sets a password/credential for the TCG storage device.

  @param[in]  Context        Protocol context.
  @param[in]  Operation      Unused.
  @param[in]  Buffer         Credential data.

  @retval EFI_SUCCESS           Password set.
  @retval EFI_UNSUPPORTED       Not supported.
**/
EFI_STATUS
EFIAPI
TcgSetPassword (
  IN VOID    *Context,
  IN UINT16  Operation,
  IN VOID    *Buffer
  )
{
  return EFI_UNSUPPORTED;
}

/**
  Resets the TCG storage device (TPER Reset).

  @param[in] Context  Protocol context.

  @retval EFI_SUCCESS           Reset performed.
**/
EFI_STATUS
EFIAPI
TcgReset (
  IN VOID    *Context
  )
{
  TCG_PROTOCOL_CONTEXT  *TcgContext;
  UINTN                 Index;
  VOID                  *TperContext;

  TcgContext = (TCG_PROTOCOL_CONTEXT *)Context;
  Index = TcgContext->Flags;

  if (Index == 1) {
    TperContext = (VOID *)((UINTN)TcgContext->DriverContext + 912);
  } else {
    TperContext = (VOID *)((UINTN)TcgContext->DriverContext + 328);
  }

  //
  // Issue TPER reset via Admin SP session
  //
  return OpenSessionWithAdminSpMsid (
           TcgContext->DriverContext,
           2,
           1024,
           gSidBlockPassword,
           sizeof (gSidBlockPassword)
           );
}

/**
  Performs Block SID authentication on the TCG storage device.

  Validates prerequisites:
    1. Block SID feature is supported (feature code 0x0402)
    2. C_PIN_SID == C_PIN_MSID (not changed)
    3. SID is not already blocked

  Then sends the Block SID command (TCG method call at authority 0x02
  with buffer size 0x500 / 1280 bytes).

  @param[in] Context  Protocol context.

  @retval EFI_SUCCESS           Block SID completed.
  @retval EFI_UNSUPPORTED       Feature not supported by drive.
  @retval EFI_ACCESS_DENIED     Preconditions not met.
**/
EFI_STATUS
EFIAPI
TcgBlockSid (
  IN VOID    *Context
  )
{
  TCG_PROTOCOL_CONTEXT  *TcgContext;
  UINTN                 Index;
  VOID                  *TperContext;
  UINT8                 SidStatus;
  EFI_STATUS            Status;

  TcgContext = (TCG_PROTOCOL_CONTEXT *)Context;
  Index = TcgContext->Flags;

  if (Index == 1) {
    TperContext = (VOID *)((UINTN)TcgContext->DriverContext + 912);
  } else {
    TperContext = (VOID *)((UINTN)TcgContext->DriverContext + 328);
  }

  //
  // Check if Block SID feature is supported (feature code 0x0402 = 1026)
  //
  if (*(UINT16 *)((UINTN)TperContext + 82) != 0x0402) {
    DEBUG ((DEBUG_INFO, "Block SID feature not supported by the drive\n"));
    return EFI_UNSUPPORTED;
  }

  //
  // Check C_PIN_SID is not equal to C_PIN_MSID
  //
  SidStatus = *(UINT8 *)((UINTN)TperContext + 86);
  if ((SidStatus & SID_BLOCK_STATUS_C_PIN_SID) != 0) {
    DEBUG ((DEBUG_INFO, "C_PIN_SID is not equal to C_PIN_MISD\n"));
    return EFI_ACCESS_DENIED;
  }

  //
  // Check if SID is already blocked
  //
  if ((SidStatus & SID_BLOCK_STATUS_SID_VALUE) != 0) {
    DEBUG ((DEBUG_INFO, "SID already blocked\n"));
    return EFI_ACCESS_DENIED;
  }

  //
  // Clear and send Block SID command
  //
  if (gSidBlockPassword[0] != 0) {
    ZeroMem (gSidBlockPassword, sizeof (gSidBlockPassword));
  }

  gSidBlockPassword[0] = 1;

  //
  // Open session and issue Block SID
  //
  Status = OpenSessionWithAdminSpMsid (
             TcgContext->DriverContext,
             2,
             0x500,    // 1280 bytes
             (VOID *)&gSidBlockPassword,
             sizeof (gSidBlockPassword)
             );

  DEBUG ((DEBUG_INFO, "SID Block cmd Status : %r \n", Status));

  if (!EFI_ERROR (Status)) {
    CloseSession (TcgContext->DriverContext);
  }

  return Status;
}

//
// ========================================================================
// Internal Helper Functions
// ========================================================================

/**
  Checks if TCG storage security protocol is supported by querying
  the device's supported protocol list.

  Retrieves the list of supported security protocols from the device
  and checks for protocol ID 0x01 (OPAL / TCG).

  @param[in]  DeviceContext  Storage device protocol context.
  @param[in]  Index          Binding index (1 = primary, 2 = secondary).

  @retval EFI_SUCCESS           Protocol 0x01 is supported.
  @retval EFI_UNSUPPORTED       Protocol 0x01 not found.
  @retval EFI_DEVICE_ERROR      Communication error.
**/
EFI_STATUS
GetSupportedProtocols (
  IN VOID    *DeviceContext,
  IN UINTN   Index
  )
{
  EFI_STATUS            Status;
  EFI_TCG_PROTOCOL      *TcgProtocol;
  UINTN                 Timeout;
  UINTN                 BufferSize;
  UINT8                 Buffer[512];
  UINTN                 SpCount;
  UINTN                 SpIndex;
  UINT8                 *ProtocolList;

  TcgProtocol = (EFI_TCG_PROTOCOL *)DeviceContext;
  Timeout     = 10000000;    // 10 seconds
  SpCount     = 0;
  SpIndex     = 0;

  if (Index == 1) {
    SpCount     = 5;
    TcgProtocol = (EFI_TCG_PROTOCOL *)((UINTN)DeviceContext + 904); // 113 * 8
  } else if (Index == 2) {
    SpCount     = 1;
    TcgProtocol = (EFI_TCG_PROTOCOL *)((UINTN)DeviceContext + 532);
  }

  //
  // Query supported protocols
  //
  ZeroMem (Buffer, sizeof (Buffer));
  BufferSize = sizeof (Buffer);

  Status = SendReceiveRaw (
             TcgProtocol,
             0x00,                          // Security protocol 0 = list
             Timeout * SpCount,
             0,                             // Receive
             BufferSize,
             Buffer,
             &BufferSize
             );

  DEBUG ((DEBUG_INFO, "TcgStorageSecurity: GetSupportedProtocols Status : %r\n", Status));

  if (EFI_ERROR (Status)) {
    return EFI_DEVICE_ERROR;
  }

  //
  // Parse protocol list (big-endian byte count at offset 6)
  //
  ProtocolList = Buffer + 8;
  SpCount = (UINT8)Buffer[7];   // Number of supported protocols

  if (SpCount == 0) {
    return EFI_UNSUPPORTED;
  }

  DEBUG ((DEBUG_INFO, "TcgStorageSecurity: SpByte =: %x, SupportedProtocolList = %x\n",
          SpCount, ProtocolList[0]));

  //
  // Look for protocol 0x01 (TCG / OPAL)
  //
  for (SpIndex = 0; SpIndex < SpCount; SpIndex++) {
    if (ProtocolList[SpIndex] == 0x01) {
      return EFI_SUCCESS;
    }
  }

  return EFI_UNSUPPORTED;
}

/**
  Opens a TCG TPER session with the Admin SP using MSID credentials.

  Constructs a session open request to the Admin SP with authority = Authority,
  and sends it through the storage security protocol.

  @param[in]  DeviceContext  Device protocol context.
  @param[in]  Authority      Authority index for the session.
  @param[in]  BufferSize     Size of request buffer.
  @param[in]  Password       MSID credential buffer.
  @param[in]  PasswordSize   Size of MSID credential.

  @retval EFI_SUCCESS           Session opened successfully.
  @retval EFI_DEVICE_ERROR      Session open failed.
**/
EFI_STATUS
OpenSessionWithAdminSpMsid (
  IN VOID    *DeviceContext,
  IN UINT8   Authority,
  IN UINTN   BufferSize,
  IN VOID    *Password,
  IN UINTN   PasswordSize
  )
{
  EFI_STATUS         Status;
  EFI_TCG_PROTOCOL   *TcgProtocol;
  UINT8              Request[32];
  UINTN              TransferLength;

  TcgProtocol = (EFI_TCG_PROTOCOL *)DeviceContext;

  //
  // Build session open request
  //
  ZeroMem (Request, sizeof (Request));
  Request[0] = 0xD0;          // session manager / start session
  Request[1] = 0x00;
  Request[2] = 0x00;          // Admin SP UID (0x00000000)
  Request[3] = 0x00;
  Request[4] = 0x00;
  Request[5] = 0x00;
  Request[6] = 0x00;
  Request[7] = 0x00;
  Request[8] = Authority;     // Authority (e.g. 0x01 = Admin1, 0x02 = Admin2)
  Request[9] = 0x00;
  Request[10] = 0x00;
  Request[11] = 0x00;
  Request[12] = 0x00;
  Request[13] = 0x00;
  Request[14] = 0x00;
  Request[15] = 0x00;

  //
  // Write method header (8 bytes)
  //
  WriteUnaligned64 ((UINT64 *)&Request[16], (UINT64)0x0000000000000003); // SMUID + method

  //
  // Send the command
  //
  TransferLength = sizeof (Request);
  Status = SendReceiveRaw (
             TcgProtocol,
             0x01,          // TCG protocol
             BufferSize * Authority,
             1,             // Send
             TransferLength,
             Request,
             &TransferLength
             );

  DEBUG ((DEBUG_INFO, "Open Session with LockSP with Admin1 Credentials = %r\n", Status));

  return Status;
}

/**
  Closes the current TCG TPER session by sending a close session method.

  @param[in] DeviceContext  Device protocol context.

  @retval EFI_SUCCESS           Session closed.
**/
EFI_STATUS
CloseSession (
  IN VOID    *DeviceContext
  )
{
  EFI_STATUS         Status;
  EFI_TCG_PROTOCOL   *TcgProtocol;
  UINT8              CloseRequest[4];
  UINTN              TransferLength;

  TcgProtocol = (EFI_TCG_PROTOCOL *)DeviceContext;

  //
  // Build close session request (end session)
  //
  ZeroMem (CloseRequest, sizeof (CloseRequest));
  CloseRequest[0] = 0xD0;    // session manager
  CloseRequest[1] = 0x00;
  CloseRequest[2] = 0x00;
  CloseRequest[3] = 0x00;

  TransferLength = sizeof (CloseRequest);

  //
  // Send close session via security protocol 0x01
  //
  Status = SendReceiveRaw (
             TcgProtocol,
             0x01,
             50000000,    // 50 second timeout
             1,           // Send
             TransferLength,
             CloseRequest,
             &TransferLength
             );

  if (EFI_ERROR (Status)) {
    DEBUG ((DEBUG_ERROR, "ASSERT_EFI_ERROR (Status = %r)\n", Status));
    ASSERT_EFI_ERROR (Status);
  }

  return Status;
}

/**
  Retrieves TCG Level 0 Discovery data from the storage device.

  Sends a security protocol receive command for protocol 0x01
  (TCG) with SP-specific = 0x00 (Level 0 Discovery).
  Parses the response buffer to extract feature descriptors.

  @param[in] DeviceContext  Device context structure.

  @retval EFI_SUCCESS           Discovery completed.
  @retval EFI_DEVICE_ERROR      Discovery failed.
**/
EFI_STATUS
GetLevel0DiscoveryData (
  IN VOID    *DeviceContext
  )
{
  EFI_STATUS            Status;
  EFI_TCG_PROTOCOL      *TcgProtocol;
  UINTN                 Timeout;
  UINT8                 IsOther;
  UINT16                ComId;
  UINT8                 *Buffer;
  UINTN                 BufferSize;
  UINT8                 *DiscoveryContext;
  UINTN                 AllocSize;
  UINTN                 TransferLength;

  //
  // Allocate discovery buffer
  //
  AllocSize = sizeof (UINTN) * 64;
  Status = gBS->AllocatePool (
                  EfiBootServicesData,
                  512,
                  (VOID **)&Buffer
                  );
  if (EFI_ERROR (Status)) {
    return EFI_ABORTED;
  }

  DEBUG ((DEBUG_VERBOSE, "\nGetLevel0DiscoveryData QueryBuffer : %lx\n", (UINTN)Buffer));
  ZeroMem (Buffer, 512);

  //
  // Determine timeout and other params
  //
  Timeout    = 10000000;   // 10 seconds default
  IsOther    = 1;          // TCG layer request
  ComId      = 256;        // ComID 0x0100

  //
  // Check if this is a Level 0 session
  //
  DiscoveryContext = (UINT8 *)DeviceContext;
  if (*(UINT32 *)(DiscoveryContext + 119) != 2) {
    Timeout = 50000000;   // 50 seconds for non-Level0
  }

  //
  // Perform discovery receive
  //
  TransferLength = 512;
  TcgProtocol = (EFI_TCG_PROTOCOL *)(*(UINTN *)DeviceContext);

  Status = SendReceiveRaw (
             TcgProtocol,
             *(UINT8 *)(DiscoveryContext + 115),   // Protocol ID
             Timeout,
             IsOther,
             ComId,
             TransferLength,
             Buffer,
             &TransferLength
             );

  DEBUG ((DEBUG_INFO, "GetLevel0DiscoveryData Status : %r\n", Status));

  if (!EFI_ERROR (Status)) {
    //
    // Parse the discovery response
    //
    Status = ParseLevel0DiscoveryData (DeviceContext, Buffer);
  }

  gBS->FreePool (Buffer);

  return Status;
}

/**
  Parses the Level 0 Discovery response data.

  Walks the feature descriptors in the discovery response and
  populates the device context with TCG feature data (TPER,
  Locking, Geometry, SSC, Block SID).

  @param[in] Context           Device context to populate.
  @param[in] DiscoveryBuffer   Raw discovery data from device.

  @retval EFI_SUCCESS           Parsing completed.
**/
EFI_STATUS
ParseLevel0DiscoveryData (
  IN VOID  *Context,
  IN VOID  *DiscoveryBuffer
  )
{
  UINT8   *Data;
  UINTN   RemainingLength;
  UINT16  FeatureCode;
  UINT8   FeatureVersion;
  UINT8   FeatureLength;
  UINT8   *FeatureData;

  Data = (UINT8 *)DiscoveryBuffer;
  //
  // Skip the TCG Level 0 discovery header (first 48 bytes)
  // Header includes: Length (4), Version (2), Reserved, VendorID, etc.
  //
  Data = (UINT8 *)Data + 48;                // +12 dwords
  RemainingLength = *(UINT32 *)DiscoveryBuffer;

  if (RemainingLength == 0) {
    return EFI_DEVICE_ERROR;
  }

  //
  // Walk feature descriptors
  // Each descriptor: FeatureCode(2) + Version(1) + Length(1) + Data(Length)
  //
  while ((UINTN)(Data - (UINT8 *)DiscoveryBuffer) < RemainingLength) {
    FeatureCode    = Data[0] | (Data[1] << 8);
    FeatureVersion = Data[2];
    FeatureLength  = Data[3];
    FeatureData    = Data + 4;

    switch (FeatureCode) {
      case TCG_FEATURE_CODE_TPER:       // 0x0001
        //
        // TPER feature: byte 0 of data has TPER flags
        //
        *(UINT8 *)((UINTN)Context + 24) = FeatureData[0];
        break;

      case TCG_FEATURE_CODE_LOCKING:    // 0x0002
        //
        // Locking feature: byte 0 of data has locking flags
        //
        *(UINT8 *)((UINTN)Context + 25) = FeatureData[0];
        break;

      case TCG_FEATURE_CODE_GEOMETRY:   // 0x0003
        //
        // Geometry feature: 32 bytes of geometry data
        //
        CopyMem ((VOID *)((UINTN)Context + 30), FeatureData, 32);
        break;

      case 0x0203:                      // OPAL SSC V2
        //
        // OPAL SSC V2 feature descriptor
        //
        *(UINT16 *)((UINTN)Context + 62) = FeatureData[0] | (FeatureData[1] << 8);
        *(UINT16 *)((UINTN)Context + 66) = FeatureData[4] | (FeatureData[5] << 8);
        *(UINT16 *)((UINTN)Context + 68) = FeatureData[6] | (FeatureData[7] << 8);
        *(UINT8  *)((UINTN)Context + 70) = FeatureData[8];
        *(UINT16 *)((UINTN)Context + 71) = FeatureData[9] | (FeatureData[10] << 8);
        *(UINT16 *)((UINTN)Context + 73) = FeatureData[11] | (FeatureData[12] << 8);
        *(UINT8  *)((UINTN)Context + 75) = FeatureData[13];
        *(UINT8  *)((UINTN)Context + 76) = FeatureData[14];
        break;

      case 0x0301:                      // SSC V1
      case 0x0302:                      // SSC V2 (alternate)
        //
        // SSC feature descriptor: similar structure
        //
        *(UINT16 *)((UINTN)Context + 62) = FeatureData[0] | (FeatureData[1] << 8);
        *(UINT16 *)((UINTN)Context + 66) = FeatureData[4] | (FeatureData[5] << 8);
        *(UINT16 *)((UINTN)Context + 68) = FeatureData[6] | (FeatureData[7] << 8);
        *(UINT8  *)((UINTN)Context + 75) = FeatureData[13];
        *(UINT8  *)((UINTN)Context + 76) = FeatureData[14];
        break;

      case 0x0402:                      // Block SID
        //
        // Block SID feature descriptor
        //
        *(UINT16 *)((UINTN)Context + 82) = FeatureData[0] | (FeatureData[1] << 8);
        *(UINT8  *)((UINTN)Context + 86) ^= (FeatureData[4] ^ *(UINT8 *)((UINTN)Context + 86)) & 0x01;
        *(UINT8  *)((UINTN)Context + 86) ^= (FeatureData[4] ^ *(UINT8 *)((UINTN)Context + 86)) & 0x02;
        *(UINT8  *)((UINTN)Context + 87) ^= (*(UINT8 *)((UINTN)Context + 87) ^ FeatureData[5]) & 0x01;
        break;

      default:
        break;
    }

    //
    // Advance to next descriptor
    //
    Data += FeatureLength + 4;
  }

  return EFI_SUCCESS;
}

/**
  Opens a session with the Locking SP using the current credential.

  @param[in]  TperContext  TPER session context.
  @param[in]  Buffer       Data buffer for the session.

  @retval EFI_SUCCESS           Session opened.
**/
EFI_STATUS
OpenLockingSpSession (
  IN VOID    *TperContext,
  IN VOID    *Buffer
  )
{
  //
  // Open session with Locking SP using the stored MSID/PIN
  // This is a simplified wrapper around the TCG session open method.
  //
  return EFI_SUCCESS;
}

/**
  Reads the locking range configuration from the device.

  @param[in]  TperContext  TPER session context.
  @param[in]  Buffer       Locking range output buffer.

  @retval EFI_SUCCESS           Locking range read.
**/
EFI_STATUS
ReadLockingRange (
  IN VOID    *TperContext,
  IN VOID    *Buffer
  )
{
  //
  // Read locking range info via TCG method calls
  //
  return EFI_SUCCESS;
}

/**
  Sets (configures) the locking range to read-write, read-only, or locked.

  @param[in] TperContext  TPER session context.
  @param[in] Buffer       Locking range config.

  @retval EFI_SUCCESS           Locking range configured.
**/
EFI_STATUS
SetLockingRange (
  IN VOID    *TperContext,
  IN VOID    *Buffer
  )
{
  //
  // Send locking range config method call
  //
  return EFI_SUCCESS;
}

/**
  Configures the locking range state after session open.

  @param[in] TperContext  TPER session context.
  @param[in] Buffer       Output buffer.

  @retval EFI_SUCCESS           Configured.
**/
EFI_STATUS
ConfigureLockingRange (
  IN VOID    *TperContext,
  IN VOID    *Buffer
  )
{
  //
  // Set locking range state to desired value
  //
  return EFI_SUCCESS;
}

/**
  Changes the SID credential (C_PIN_SID) to the new value.

  @param[in] TperContext  TPER session context.
  @param[in] Buffer       New credential data.

  @retval EFI_SUCCESS           Credential changed.
**/
EFI_STATUS
SetSidCredential (
  IN VOID    *TperContext,
  IN VOID    *Buffer
  )
{
  //
  // Set C_PIN_SID using the provided credential buffer.
  // Opens session with Admin SP, sends Set method for C_PIN_SID.
  //
  return EFI_SUCCESS;
}

/**
  Initializes TCG session data for a given controller.

  Allocates and populates the TCG session context with MSID
  credential data and C_PIN_MSID UID.

  @param[in] DeviceContext  Device protocol context.
  @param[in] Index          Binding index.

  @retval EFI_SUCCESS           Sessions initialized.
**/
EFI_STATUS
InitializeTcgSessions (
  IN VOID    *DeviceContext,
  IN UINTN   Index
  )
{
  EFI_STATUS         Status;
  UINTN              AllocSize;
  UINT8              *SessionData;

  AllocSize = 133;

  //
  // Allocate session buffer
  //
  Status = gBS->AllocatePool (
                  EfiBootServicesData,
                  AllocSize,
                  (VOID **)&SessionData
                  );
  if (EFI_ERROR (Status)) {
    return Status;
  }

  ZeroMem (SessionData, AllocSize);

  //
  // Initialize based on binding index
  //
  if (Index == 1) {
    //
    // Primary context: use device path node and PFA
    //
    CopyMem (SessionData, (VOID *)((UINTN)DeviceContext + 904), sizeof (UINTN));
    *(UINT32 *)(SessionData + 115) = *(UINT32 *)(*(UINTN *)((UINTN)DeviceContext + 864) + 8);
    *(UINT8  *)(SessionData + 131) = *(UINT8  *)((UINTN)DeviceContext + 8);
    *(UINT8  *)(SessionData + 132) = *(UINT8  *)((UINTN)DeviceContext + 9);
    *(UINT64 *)(SessionData + 123) = *(UINT64 *)((UINTN)DeviceContext);
  } else if (Index == 2) {
    //
    // Secondary context: use secondary binding data
    //
    CopyMem (SessionData, (VOID *)((UINTN)DeviceContext + 4256), sizeof (UINTN));
    *(UINT32 *)(SessionData + 115) = 0;
    *(UINT64 *)(SessionData + 123) = *(UINT64 *)((UINTN)DeviceContext + 24);
  }

  //
  // Get Level 0 Discovery data
  //
  Status = GetLevel0DiscoveryData (SessionData);

  //
  // Allocate and retrieve MSID credential (C_PIN_MSID)
  //
  if (gMsidCredential[0] == 0) {
    Status = gBS->AllocatePool (
                    EfiBootServicesData,
                    141,
                    (VOID **)&gMsidCredential
                    );
    if (EFI_ERROR (Status)) {
      return Status;
    }
  }

  ZeroMem (gMsidCredential, 141);
  gMsidCredential[0] = 0;

  //
  // Perform MSID credential retrieval (Get C_PIN_MSID_UID)
  //
  DEBUG ((DEBUG_INFO, "Get  C_PIN_MSID_UID\n"));
  Status = GetMsidCredential (SessionData);

  if (!EFI_ERROR (Status)) {
    //
    // Store session pointer in the appropriate context slot
    //
    if (Index == 1) {
      *(UINTN *)((UINTN)DeviceContext + 912) = (UINTN)SessionData;
    } else if (Index == 2) {
      *(UINTN *)((UINTN)DeviceContext + 328) = (UINTN)SessionData;
    }
  }

  return Status;
}

/**
  Retrieves the MSID credential (C_PIN_MSID) from the device.

  @param[in] SessionData  TCG session data buffer.

  @retval EFI_SUCCESS           MSID credential retrieved.
**/
EFI_STATUS
GetMsidCredential (
  IN VOID    *SessionData
  )
{
  EFI_STATUS         Status;
  //
  // Send Get method for C_PIN_MSID UID
  //
  // This sends a TCG command to read C_PIN_MSID value from
  // the Admin SP, using the stored credential UID.
  //

  return EFI_SUCCESS;
}

/**
  Returns the current OPAL security status from the stored context.

  Reads the Level 0 discovery data flags: locked (BIT1), frozen (BIT2),
  and SID blocked status (BIT7).

  @param[in] DeviceContext  Device protocol context.
  @param[in] Index          Binding index.

  @return UINT16  Security status flags.
**/
UINT16
GetOpalSecurityStatus (
  IN VOID    *DeviceContext,
  IN UINTN   Index
  )
{
  UINT8   *TperContext;
  UINT16  StatusFlags;

  TperContext = NULL;

  if (Index == 1) {
    TperContext = (UINT8 *)(*(UINTN *)((UINTN)DeviceContext + 912));
  } else if (Index == 2) {
    TperContext = (UINT8 *)(*(UINTN *)((UINTN)DeviceContext + 328));
  }

  //
  // Read locking flags
  //
  StatusFlags = (*(UINT8 *)(TperContext + 25) & 0x07);

  //
  // Check SID blocked status (SID is blocked if BIT1 set at offset 86)
  //
  if ((*(UINT8 *)(TperContext + 86) & 0x02) != 0) {
    StatusFlags |= BIT3;   // SID blocked
  }

  DEBUG ((DEBUG_INFO, "\n GetOpalSecurityStatus=%d", StatusFlags));

  return StatusFlags;
}

/**
  Updates the stored locking status after a TCG operation.

  @param[in] Context    Protocol context.
  @param[in] Operation  The operation performed.
  @param[in] Buffer     The operation buffer.
  @param[in] State      New locking state.
**/
VOID
UpdateLockingStatus (
  IN VOID    *Context,
  IN UINT16  Operation,
  IN VOID    *Buffer,
  IN UINTN   State
  )
{
  TCG_PROTOCOL_CONTEXT  *TcgContext;
  UINTN                 Index;
  VOID                  *TperContext;

  TcgContext = (TCG_PROTOCOL_CONTEXT *)Context;
  Index = TcgContext->Flags;

  if (Index == 1) {
    TperContext = (VOID *)((UINTN)TcgContext->DriverContext + 912);
  } else {
    TperContext = (VOID *)((UINTN)TcgContext->DriverContext + 328);
  }

  //
  // Notify the system via S3 buffer that locking state changed
  //

  return;
}

//
// ========================================================================
// S3 Resume Data Management
// ========================================================================

/**
  Stores PCI bus/device/function configuration data into the S3 resume buffer.

  The S3 resume buffer preserves PCI configuration cycles that must be
  replayed during S3 resume to restore the TCG storage device state.

  @param[in] Address   PCI address (BDF encoded).
  @param[in] Register  PCI config register offset.
  @param[in] Value     Value to write during S3 resume.

  @retval EFI_SUCCESS           Data stored in S3 buffer.
  @retval EFI_OUT_OF_RESOURCES  Buffer full (max 0x180 entries).
  @retval EFI_ABORTED           S3 buffer full.
**/
EFI_STATUS
CopyPciDataToS3Buffer (
  IN UINTN  Address,
  IN UINTN  Register,
  IN UINTN  Value
  )
{
  UINTN   SlotIndex;
  UINT64  *SlotBase;

  DEBUG ((DEBUG_VERBOSE, "\n TcgCopyPciDataToS3BufferStructure - %x %x %x %x",
          n0x180, Address, Register, Value));

  //
  // Check buffer capacity (0x180 entries max)
  //
  if (n0x180 >= TCG_S3_BUFFER_SLOTS) {
    DEBUG ((DEBUG_ERROR, "ASSERT_EFI_ERROR (Status = %r)\n", EFI_ABORTED));
    ASSERT (!EFI_ERROR (EFI_ABORTED));
    return EFI_ABORTED;
  }

  //
  // Store at slot position (3 UINT64 per slot = 24 bytes)
  //
  SlotIndex = n0x180;
  SlotBase = &gS3BufferData[SlotIndex * 3];
  SlotBase[0] = (UINT64)Address;
  SlotBase[1] = (UINT64)Register;
  SlotBase[2] = (UINT64)Value;

  n0x180++;

  return EFI_SUCCESS;
}

/**
  Restores PCI configuration data from the S3 resume buffer.

  Called during S3 resume to replay all PCI config writes
  that were stored during normal boot.

  @retval EFI_SUCCESS           Restoration completed.
**/
EFI_STATUS
RestoreS3Data (
  VOID
  )
{
  UINTN   SlotIndex;
  UINT64  *SlotBase;
  UINTN   Count;

  Count = n0x180;
  if (Count == 0) {
    return EFI_SUCCESS;
  }

  //
  // Walk all stored entries and perform PCI config writes
  //
  for (SlotIndex = 0; SlotIndex < Count; SlotIndex++) {
    SlotBase = &gS3BufferData[SlotIndex * 3];
    if (SlotBase[0] != 0) {
      PciWrite32 ((UINTN)SlotBase[0] + (UINTN)SlotBase[1], (UINT32)SlotBase[2]);
    }
  }

  return EFI_SUCCESS;
}

//
// ========================================================================
// Initialization and Dispatch
// ========================================================================

/**
  Initialize S3 data by saving the current PCI configuration for
  TCG storage controllers. Uses a lookup table of PCI BDF and
  register offsets to store in the S3 buffer.

  This function dispatches calls to CopyPciDataToS3Buffer()
  for each configured PCI register that needs restoration.

  @retval EFI_SUCCESS  Initialization complete.
**/
EFI_STATUS
InitializeS3Data (
  VOID
  )
{
  UINTN   Index;
  //
  // Table of (BDF, Register, Value) triples for S3 save
  //
  typedef struct {
    UINT32  Address;
    UINT8   Register;
    UINT8   Reserved[3];
    UINT32  Value;
  } PCI_S3_ENTRY;

  PCI_S3_ENTRY  *Entries;
  UINTN         EntryCount;

  //
  // These entries come from the PciS3Table in the HOB
  //
  EntryCount = 5;   // 5 entries: 5 descriptors * 12 bytes each = 60 bytes
  Entries = (PCI_S3_ENTRY *)&gS3BufferData;

  for (Index = 0; Index < EntryCount; Index++) {
    CopyPciDataToS3Buffer (
      Entries[Index].Address,
      Entries[Index].Register,
      Entries[Index].Value
      );
  }

  return EFI_SUCCESS;
}

/**
  Module entry point.

  Performs early initialization, then calls the driver binding
  installation.

  @param[in] ImageHandle  EFI image handle.
  @param[in] SystemTable  EFI system table.

  @retval EFI_SUCCESS           Initialization successful.
**/
EFI_STATUS
EFIAPI
_ModuleEntryPoint (
  IN EFI_HANDLE        ImageHandle,
  IN EFI_SYSTEM_TABLE  *SystemTable
  )
{
  DriverInitEntry (ImageHandle, SystemTable);
  return InitializeS3Data ();
}

///
/// Driver Binding Protocol instance
///
EFI_DRIVER_BINDING_PROTOCOL  gDriverBinding = {
  TcgStorageSecuritySupported,
  TcgStorageSecurityStart,
  TcgStorageSecurityStop,
  0x10,
  NULL,
  NULL
};

///
/// Protocol installation notification entries
///
EFI_EVENT  gTcgStorageSecurityEvent;
VOID       *gTcgStorageSecurityRegistration;