/** @file
  EDKII Device Security library for SPDM device.
  It follows the SPDM Specification.

Copyright (c) 2024, Intel Corporation. All rights reserved.<BR>
SPDX-License-Identifier: BSD-2-Clause-Patent

**/

#include "SpdmSecurityLibInternal.h"

/**
  This function returns the SPDM device type for TCG SPDM event.

  @param[in]  SpdmDeviceContext           The SPDM context for the device.

  @return TCG SPDM device type
**/
UINT32
EFIAPI
GetSpdmDeviceType (
  IN  SPDM_DEVICE_CONTEXT  *SpdmDeviceContext
  )
{
  if (CompareGuid (&SpdmDeviceContext->DeviceId.DeviceType, &gEdkiiDeviceIdentifierTypePciGuid)) {
    return TCG_DEVICE_SECURITY_EVENT_DATA_DEVICE_TYPE_PCI;
  }

  if (CompareGuid (&SpdmDeviceContext->DeviceId.DeviceType, &gEdkiiDeviceIdentifierTypeUsbGuid)) {
    return TCG_DEVICE_SECURITY_EVENT_DATA_DEVICE_TYPE_USB;
  }

  return TCG_DEVICE_SECURITY_EVENT_DATA_DEVICE_TYPE_NULL;
}

/**
  This function returns the SPDM device measurement context size for TCG SPDM event.

  @param[in]  SpdmDeviceContext          The SPDM context for the device.

  @return TCG SPDM device measurement context size
**/
UINTN
EFIAPI
GetDeviceMeasurementContextSize (
  IN  SPDM_DEVICE_CONTEXT  *SpdmDeviceContext
  )
{
  if (CompareGuid (&SpdmDeviceContext->DeviceId.DeviceType, &gEdkiiDeviceIdentifierTypePciGuid)) {
    return sizeof (TCG_DEVICE_SECURITY_EVENT_DATA_PCI_CONTEXT);
  }

  if (CompareGuid (&SpdmDeviceContext->DeviceId.DeviceType, &gEdkiiDeviceIdentifierTypeUsbGuid)) {
    // TBD - usb context
    return 0;
  }

  return 0;
}

/**
  This function creates the SPDM PCI device measurement context for TCG SPDM event.

  @param[in]       SpdmDeviceContext       The SPDM context for the device.
  @param[in, out]  DeviceContext           The TCG SPDM PCI device measurement context.
  @param[in]       DeviceContextSize       The size of TCG SPDM PCI device measurement context.

  @retval EFI_SUCCESS      The TCG SPDM PCI device measurement context is returned.
**/
EFI_STATUS
CreatePciDeviceMeasurementContext (
  IN  SPDM_DEVICE_CONTEXT  *SpdmDeviceContext,
  IN OUT VOID              *DeviceContext,
  IN UINTN                 DeviceContextSize
  )
{
  TCG_DEVICE_SECURITY_EVENT_DATA_PCI_CONTEXT  *PciContext;
  PCI_TYPE00                                  PciData;
  EFI_PCI_IO_PROTOCOL                         *PciIo;
  EFI_STATUS                                  Status;

  if (DeviceContextSize != sizeof (*PciContext)) {
    return EFI_BUFFER_TOO_SMALL;
  }

  PciIo  = SpdmDeviceContext->DeviceIo;
  Status = PciIo->Pci.Read (PciIo, EfiPciIoWidthUint8, 0, sizeof (PciData), &PciData);
  ASSERT_EFI_ERROR (Status);

  PciContext               = DeviceContext;
  PciContext->Version      = TCG_DEVICE_SECURITY_EVENT_DATA_PCI_CONTEXT_VERSION;
  PciContext->Length       = sizeof (*PciContext);
  PciContext->VendorId     = PciData.Hdr.VendorId;
  PciContext->DeviceId     = PciData.Hdr.DeviceId;
  PciContext->RevisionID   = PciData.Hdr.RevisionID;
  PciContext->ClassCode[0] = PciData.Hdr.ClassCode[0];
  PciContext->ClassCode[1] = PciData.Hdr.ClassCode[1];
  PciContext->ClassCode[2] = PciData.Hdr.ClassCode[2];
  if ((PciData.Hdr.HeaderType & HEADER_LAYOUT_CODE) == HEADER_TYPE_DEVICE) {
    PciContext->SubsystemVendorID = PciData.Device.SubsystemVendorID;
    PciContext->SubsystemID       = PciData.Device.SubsystemID;
  } else {
    PciContext->SubsystemVendorID = 0;
    PciContext->SubsystemID       = 0;
  }

  return EFI_SUCCESS;
}

/**
  This function creates the SPDM device measurement context for TCG SPDM event.

  @param[in]       SpdmDeviceContext       The SPDM context for the device.
  @param[in, out]  DeviceContext           The TCG SPDM device measurement context.
  @param[in]       DeviceContextSize       The size of TCG SPDM device measurement context.

  @retval EFI_SUCCESS      The TCG SPDM device measurement context is returned.
  @retval EFI_UNSUPPORTED  The TCG SPDM device measurement context is unsupported.
**/
EFI_STATUS
EFIAPI
CreateDeviceMeasurementContext (
  IN  SPDM_DEVICE_CONTEXT  *SpdmDeviceContext,
  IN OUT VOID              *DeviceContext,
  IN UINTN                 DeviceContextSize
  )
{
  if (CompareGuid (&SpdmDeviceContext->DeviceId.DeviceType, &gEdkiiDeviceIdentifierTypePciGuid)) {
    return CreatePciDeviceMeasurementContext (SpdmDeviceContext, DeviceContext, DeviceContextSize);
  }

  if (CompareGuid (&SpdmDeviceContext->DeviceId.DeviceType, &gEdkiiDeviceIdentifierTypeUsbGuid)) {
    return EFI_UNSUPPORTED;
  }

  return EFI_UNSUPPORTED;
}

/**
  This function dumps data.

  @param[in]  Data             A pointer to Data.
  @param[in]  Size             The size of Data.

**/
VOID
EFIAPI
InternalDumpData (
  CONST UINT8  *Data,
  UINTN        Size
  )
{
  UINTN  Index;

  for (Index = 0; Index < Size; Index++) {
    DEBUG ((DEBUG_INFO, "%02x ", (UINTN)Data[Index]));
  }
}

/**
  This function extend the PCI digest from the DvSec register.

  @param[in]  SpdmDeviceContext       The SPDM context for the device.
  @param[in]  AuthState               The auth state of the device.
  @param[in]  MeasurementRecordLength The length of the SPDM measurement record
  @param[in]  MeasurementRecord       The SPDM measurement record
  @param[in]  RequesterNonce           A buffer to hold the requester nonce (32 bytes), if not NULL.
  @param[in]  ResponderNonce           A buffer to hold the responder nonce (32 bytes), if not NULL.
  @param[out] SecurityState            The Device Security state associated with the device.

  @retval EFI_SUCCESS                 Operation completed successfully.
  @retval EFI_OUT_OF_RESOURCES          Out of memory.
  @retval EFI_DEVICE_ERROR              The operation was unsuccessful.

**/
EFI_STATUS
ExtendMeasurement (
  IN  SPDM_DEVICE_CONTEXT          *SpdmDeviceContext,
  IN UINT8                         AuthState,
  IN UINT32                        MeasurementRecordLength,
  IN UINT8                         *MeasurementRecord,
  IN UINT8                         *RequesterNonce,
  IN UINT8                         *ResponderNonce,
  OUT EDKII_DEVICE_SECURITY_STATE  *SecurityState
  )
{
  UINT32  PcrIndex;
  UINT32  EventType;
  VOID    *EventLog;
  UINT32  EventLogSize;
  UINT8   *EventLogPtr;

  TCG_DEVICE_SECURITY_EVENT_DATA_HEADER2                            *EventData2;
  TCG_DEVICE_SECURITY_EVENT_DATA_SUB_HEADER_SPDM_MEASUREMENT_BLOCK  *TcgSpdmMeasurementBlock;
  VOID                                                              *DeviceContext;
  UINTN                                                             DeviceContextSize;
  EFI_STATUS                                                        Status;
  SPDM_MEASUREMENT_BLOCK_COMMON_HEADER                              *SpdmMeasurementBlockCommonHeader;
  SPDM_MEASUREMENT_BLOCK_DMTF_HEADER                                *SpdmMeasurementBlockDmtfHeader;
  VOID                                                              *Digest;
  UINTN                                                             DigestSize;
  UINTN                                                             DevicePathSize;
  UINT32                                                            MeasurementHashAlgo;
  UINTN                                                             DataSize;
  VOID                                                              *SpdmContext;
  SPDM_DATA_PARAMETER                                               Parameter;

  SpdmContext = SpdmDeviceContext->SpdmContext;

  EventLog = NULL;
  ZeroMem (&Parameter, sizeof (Parameter));
  Parameter.location = (libspdm_data_location_t)SpdmDataLocationConnection;
  DataSize           = sizeof (MeasurementHashAlgo);
  Status             = SpdmGetData (SpdmContext, (libspdm_data_type_t)SpdmDataMeasurementHashAlgo, &Parameter, &MeasurementHashAlgo, &DataSize);
  ASSERT_EFI_ERROR (Status);

  if (MeasurementRecord != NULL) {
    SpdmMeasurementBlockCommonHeader = (VOID *)MeasurementRecord;
    SpdmMeasurementBlockDmtfHeader   = (VOID *)(SpdmMeasurementBlockCommonHeader + 1);
    Digest                           = (SpdmMeasurementBlockDmtfHeader + 1);
    DigestSize                       = MeasurementRecordLength - sizeof (SPDM_MEASUREMENT_BLOCK_DMTF);

    DEBUG ((DEBUG_INFO, "SpdmMeasurementBlockCommonHeader\n"));
    DEBUG ((DEBUG_INFO, "  Index                        - 0x%02x\n", SpdmMeasurementBlockCommonHeader->Index));
    DEBUG ((DEBUG_INFO, "  MeasurementSpecification     - 0x%02x\n", SpdmMeasurementBlockCommonHeader->MeasurementSpecification));
    DEBUG ((DEBUG_INFO, "  MeasurementSize              - 0x%04x\n", SpdmMeasurementBlockCommonHeader->MeasurementSize));
    DEBUG ((DEBUG_INFO, "SpdmMeasurementBlockDmtfHeader\n"));
    DEBUG ((DEBUG_INFO, "  DMTFSpecMeasurementValueType - 0x%02x\n", SpdmMeasurementBlockDmtfHeader->DMTFSpecMeasurementValueType));
    DEBUG ((DEBUG_INFO, "  DMTFSpecMeasurementValueSize - 0x%04x\n", SpdmMeasurementBlockDmtfHeader->DMTFSpecMeasurementValueSize));
    DEBUG ((DEBUG_INFO, "Measurement - "));
    InternalDumpData (Digest, DigestSize);
    DEBUG ((DEBUG_INFO, "\n"));
    if (MeasurementRecordLength <= sizeof (SPDM_MEASUREMENT_BLOCK_COMMON_HEADER) + sizeof (SPDM_MEASUREMENT_BLOCK_DMTF_HEADER)) {
      SecurityState->MeasurementState = EDKII_DEVICE_SECURITY_STATE_ERROR_MEASUREMENT_AUTH_FAILURE;
      return EFI_SECURITY_VIOLATION;
    }

    if ((SpdmMeasurementBlockCommonHeader->MeasurementSpecification & SPDM_MEASUREMENT_SPECIFICATION_DMTF) == 0) {
      SecurityState->MeasurementState = EDKII_DEVICE_SECURITY_STATE_ERROR_MEASUREMENT_AUTH_FAILURE;
      return EFI_SECURITY_VIOLATION;
    }

    if (SpdmMeasurementBlockCommonHeader->MeasurementSize != MeasurementRecordLength - sizeof (SPDM_MEASUREMENT_BLOCK_COMMON_HEADER)) {
      SecurityState->MeasurementState = EDKII_DEVICE_SECURITY_STATE_ERROR_MEASUREMENT_AUTH_FAILURE;
      return EFI_SECURITY_VIOLATION;
    }

    if (SpdmMeasurementBlockDmtfHeader->DMTFSpecMeasurementValueSize != SpdmMeasurementBlockCommonHeader->MeasurementSize - sizeof (SPDM_MEASUREMENT_BLOCK_DMTF_HEADER)) {
      SecurityState->MeasurementState = EDKII_DEVICE_SECURITY_STATE_ERROR_MEASUREMENT_AUTH_FAILURE;
      return EFI_SECURITY_VIOLATION;
    }

    //
    // Use PCR 2 for Firmware Blob code.
    //
    switch (SpdmMeasurementBlockDmtfHeader->DMTFSpecMeasurementValueType & 0x7F) {
      case SPDM_MEASUREMENT_BLOCK_MEASUREMENT_TYPE_IMMUTABLE_ROM:
      case SPDM_MEASUREMENT_BLOCK_MEASUREMENT_TYPE_MUTABLE_FIRMWARE:
      case SPDM_MEASUREMENT_BLOCK_MEASUREMENT_TYPE_VERSION:
      case SPDM_MEASUREMENT_BLOCK_MEASUREMENT_TYPE_SECURE_VERSION_NUMBER:
        if (SpdmDeviceContext->IsEmbeddedDevice) {
          PcrIndex = 0;
        } else {
          PcrIndex = 2;
        }

        EventType = EV_EFI_SPDM_FIRMWARE_BLOB;
        break;
      case SPDM_MEASUREMENT_BLOCK_MEASUREMENT_TYPE_HARDWARE_CONFIGURATION:
      case SPDM_MEASUREMENT_BLOCK_MEASUREMENT_TYPE_FIRMWARE_CONFIGURATION:
      case SPDM_MEASUREMENT_BLOCK_MEASUREMENT_TYPE_DEVICE_MODE:
        if (SpdmDeviceContext->IsEmbeddedDevice) {
          PcrIndex = 1;
        } else {
          PcrIndex = 3;
        }

        EventType = EV_EFI_SPDM_FIRMWARE_CONFIG;
        break;
      case SPDM_MEASUREMENT_BLOCK_MEASUREMENT_TYPE_MEASUREMENT_MANIFEST:
      // skip manifest, because manifest doesn't belong to the EV_EFI_SPDM_FIRMWARE_BLOB and EV_EFI_SPDM_FIRMWARE_CONFIG
      default:
        return EFI_SUCCESS;
    }
  } else {
    if (SpdmDeviceContext->IsEmbeddedDevice) {
      PcrIndex = 0;
    } else {
      PcrIndex = 2;
    }

    EventType = EV_EFI_SPDM_FIRMWARE_BLOB;
  }

  DeviceContextSize = GetDeviceMeasurementContextSize (SpdmDeviceContext);
  DevicePathSize    = GetDevicePathSize (SpdmDeviceContext->DevicePath);

  switch (AuthState) {
    case TCG_DEVICE_SECURITY_EVENT_DATA_DEVICE_AUTH_STATE_SUCCESS:
      EventLogSize = (UINT32)(sizeof (TCG_DEVICE_SECURITY_EVENT_DATA_HEADER2) +
                              sizeof (UINT64) + DevicePathSize +
                              sizeof (TCG_DEVICE_SECURITY_EVENT_DATA_SUB_HEADER_SPDM_MEASUREMENT_BLOCK) +
                              MeasurementRecordLength +
                              DeviceContextSize);
      EventLog = AllocatePool (EventLogSize);
      if (EventLog == NULL) {
        SecurityState->MeasurementState = EDKII_DEVICE_SECURITY_STATE_ERROR_UEFI_OUT_OF_RESOURCE;
        return EFI_OUT_OF_RESOURCES;
      }

      EventLogPtr = EventLog;

      EventData2 = (VOID *)EventLogPtr;
      CopyMem (EventData2->Signature, TCG_DEVICE_SECURITY_EVENT_DATA_SIGNATURE_2, sizeof (EventData2->Signature));
      EventData2->Version    = TCG_DEVICE_SECURITY_EVENT_DATA_VERSION_2;
      EventData2->AuthState  = AuthState;
      EventData2->Reserved   = 0;
      EventData2->Length     = (UINT32)EventLogSize;
      EventData2->DeviceType = GetSpdmDeviceType (SpdmDeviceContext);

      EventData2->SubHeaderType   = TCG_DEVICE_SECURITY_EVENT_DATA_DEVICE_SUB_HEADER_TYPE_SPDM_MEASUREMENT_BLOCK;
      EventData2->SubHeaderLength = sizeof (TCG_DEVICE_SECURITY_EVENT_DATA_SUB_HEADER_SPDM_MEASUREMENT_BLOCK) + MeasurementRecordLength;
      EventData2->SubHeaderUID    = SpdmDeviceContext->DeviceUID;

      EventLogPtr = (VOID *)(EventData2 + 1);

      *(UINT64 *)EventLogPtr = (UINT64)DevicePathSize;
      EventLogPtr           += sizeof (UINT64);
      CopyMem (EventLogPtr, SpdmDeviceContext->DevicePath, DevicePathSize);
      EventLogPtr += DevicePathSize;

      TcgSpdmMeasurementBlock                            = (VOID *)EventLogPtr;
      TcgSpdmMeasurementBlock->SpdmVersion               = SpdmDeviceContext->SpdmVersion;
      TcgSpdmMeasurementBlock->SpdmMeasurementBlockCount = 1;
      TcgSpdmMeasurementBlock->Reserved                  = 0;
      TcgSpdmMeasurementBlock->SpdmMeasurementHashAlgo   = MeasurementHashAlgo;
      EventLogPtr                                       += sizeof (TCG_DEVICE_SECURITY_EVENT_DATA_SUB_HEADER_SPDM_MEASUREMENT_BLOCK);

      if ((MeasurementRecord != NULL) && (MeasurementRecordLength != 0)) {
        CopyMem (EventLogPtr, MeasurementRecord, MeasurementRecordLength);
        EventLogPtr += MeasurementRecordLength;
      }

      if (DeviceContextSize != 0) {
        DeviceContext = (VOID *)EventLogPtr;
        Status        = CreateDeviceMeasurementContext (SpdmDeviceContext, DeviceContext, DeviceContextSize);
        if (Status != EFI_SUCCESS) {
          SecurityState->MeasurementState = EDKII_DEVICE_SECURITY_STATE_ERROR_DEVICE_ERROR;
          Status                          = EFI_DEVICE_ERROR;
          goto Exit;
        }
      }

      Status = TpmMeasureAndLogData (
                 PcrIndex,
                 EventType,
                 EventLog,
                 EventLogSize,
                 EventLog,
                 EventLogSize
                 );
      if (EFI_ERROR (Status)) {
        SecurityState->MeasurementState = EDKII_DEVICE_SECURITY_STATE_ERROR_TCG_EXTEND_TPM_PCR;
      }

      DEBUG ((DEBUG_INFO, "TpmMeasureAndLogData (Measurement) - %r\n", Status));
      break;
    case TCG_DEVICE_SECURITY_EVENT_DATA_DEVICE_AUTH_STATE_FAIL_INVALID:
      EventLogSize = (UINT32)(sizeof (TCG_DEVICE_SECURITY_EVENT_DATA_HEADER2) +
                              sizeof (UINT64) + DevicePathSize +
                              DeviceContextSize);
      EventLog = AllocatePool (EventLogSize);
      if (EventLog == NULL) {
        SecurityState->MeasurementState = EDKII_DEVICE_SECURITY_STATE_ERROR_UEFI_OUT_OF_RESOURCE;
        return EFI_OUT_OF_RESOURCES;
      }

      EventLogPtr = EventLog;

      EventData2 = (VOID *)EventLogPtr;
      CopyMem (EventData2->Signature, TCG_DEVICE_SECURITY_EVENT_DATA_SIGNATURE_2, sizeof (EventData2->Signature));
      EventData2->Version    = TCG_DEVICE_SECURITY_EVENT_DATA_VERSION_2;
      EventData2->AuthState  = AuthState;
      EventData2->Reserved   = 0;
      EventData2->Length     = (UINT32)EventLogSize;
      EventData2->DeviceType = GetSpdmDeviceType (SpdmDeviceContext);

      EventData2->SubHeaderType   = TCG_DEVICE_SECURITY_EVENT_DATA_DEVICE_SUB_HEADER_TYPE_SPDM_MEASUREMENT_BLOCK;
      EventData2->SubHeaderLength = 0;
      EventData2->SubHeaderUID    = SpdmDeviceContext->DeviceUID;

      EventLogPtr = (VOID *)(EventData2 + 1);

      *(UINT64 *)EventLogPtr = (UINT64)DevicePathSize;
      EventLogPtr           += sizeof (UINT64);
      CopyMem (EventLogPtr, SpdmDeviceContext->DevicePath, DevicePathSize);
      EventLogPtr += DevicePathSize;

      if (DeviceContextSize != 0) {
        DeviceContext = (VOID *)EventLogPtr;
        Status        = CreateDeviceMeasurementContext (SpdmDeviceContext, DeviceContext, DeviceContextSize);
        if (Status != EFI_SUCCESS) {
          SecurityState->MeasurementState = EDKII_DEVICE_SECURITY_STATE_ERROR_DEVICE_ERROR;
          Status                          = EFI_DEVICE_ERROR;
          goto Exit;
        }
      }

      Status = TpmMeasureAndLogData (
                 PcrIndex,
                 EventType,
                 EventLog,
                 EventLogSize,
                 EventLog,
                 EventLogSize
                 );
      if (EFI_ERROR (Status)) {
        SecurityState->MeasurementState = EDKII_DEVICE_SECURITY_STATE_ERROR_TCG_EXTEND_TPM_PCR;
      }

      DEBUG ((DEBUG_INFO, "TpmMeasureAndLogData (Measurement) - %r\n", Status));
      goto Exit;
    default:
      SecurityState->MeasurementState = EDKII_DEVICE_SECURITY_STATE_ERROR_UEFI_UNSUPPORTED;
      return EFI_UNSUPPORTED;
  }

  if (RequesterNonce != NULL) {
    TCG_NV_INDEX_DYNAMIC_EVENT_LOG_STRUCT_SPDM_GET_MEASUREMENTS  DynamicEventLogSpdmGetMeasurementsEvent;

    CopyMem (DynamicEventLogSpdmGetMeasurementsEvent.Header.Signature, TCG_NV_EXTEND_INDEX_FOR_DYNAMIC_SIGNATURE, sizeof (TCG_NV_EXTEND_INDEX_FOR_DYNAMIC_SIGNATURE));
    DynamicEventLogSpdmGetMeasurementsEvent.Header.Version = TCG_NV_INDEX_DYNAMIC_EVENT_LOG_STRUCT_VERSION;
    ZeroMem (DynamicEventLogSpdmGetMeasurementsEvent.Header.Reserved, sizeof (DynamicEventLogSpdmGetMeasurementsEvent.Header.Reserved));
    DynamicEventLogSpdmGetMeasurementsEvent.Header.Uid      = SpdmDeviceContext->DeviceUID;
    DynamicEventLogSpdmGetMeasurementsEvent.DescriptionSize = sizeof (TCG_SPDM_GET_MEASUREMENTS_DESCRIPTION);
    CopyMem (DynamicEventLogSpdmGetMeasurementsEvent.Description, TCG_SPDM_GET_MEASUREMENTS_DESCRIPTION, sizeof (TCG_SPDM_GET_MEASUREMENTS_DESCRIPTION));
    DynamicEventLogSpdmGetMeasurementsEvent.DataSize = SPDM_NONCE_SIZE;
    CopyMem (DynamicEventLogSpdmGetMeasurementsEvent.Data, RequesterNonce, SPDM_NONCE_SIZE);

    Status = TpmMeasureAndLogData (
               TCG_NV_EXTEND_INDEX_FOR_DYNAMIC,
               EV_NO_ACTION,
               &DynamicEventLogSpdmGetMeasurementsEvent,
               sizeof (DynamicEventLogSpdmGetMeasurementsEvent),
               &DynamicEventLogSpdmGetMeasurementsEvent,
               sizeof (DynamicEventLogSpdmGetMeasurementsEvent)
               );
    if (EFI_ERROR (Status)) {
      SecurityState->MeasurementState = EDKII_DEVICE_SECURITY_STATE_ERROR_TCG_EXTEND_TPM_PCR;
    }

    DEBUG ((DEBUG_INFO, "TpmMeasureAndLogData (Dynamic) - %r\n", Status));
  }

  if (ResponderNonce != NULL) {
    TCG_NV_INDEX_DYNAMIC_EVENT_LOG_STRUCT_SPDM_MEASUREMENTS  DynamicEventLogSpdmMeasurementsEvent;

    CopyMem (DynamicEventLogSpdmMeasurementsEvent.Header.Signature, TCG_NV_EXTEND_INDEX_FOR_DYNAMIC_SIGNATURE, sizeof (TCG_NV_EXTEND_INDEX_FOR_DYNAMIC_SIGNATURE));
    DynamicEventLogSpdmMeasurementsEvent.Header.Version = TCG_NV_INDEX_DYNAMIC_EVENT_LOG_STRUCT_VERSION;
    ZeroMem (DynamicEventLogSpdmMeasurementsEvent.Header.Reserved, sizeof (DynamicEventLogSpdmMeasurementsEvent.Header.Reserved));
    DynamicEventLogSpdmMeasurementsEvent.Header.Uid      = SpdmDeviceContext->DeviceUID;
    DynamicEventLogSpdmMeasurementsEvent.DescriptionSize = sizeof (TCG_SPDM_MEASUREMENTS_DESCRIPTION);
    CopyMem (DynamicEventLogSpdmMeasurementsEvent.Description, TCG_SPDM_MEASUREMENTS_DESCRIPTION, sizeof (TCG_SPDM_MEASUREMENTS_DESCRIPTION));
    DynamicEventLogSpdmMeasurementsEvent.DataSize = SPDM_NONCE_SIZE;
    CopyMem (DynamicEventLogSpdmMeasurementsEvent.Data, ResponderNonce, SPDM_NONCE_SIZE);

    Status = TpmMeasureAndLogData (
               TCG_NV_EXTEND_INDEX_FOR_DYNAMIC,
               EV_NO_ACTION,
               &DynamicEventLogSpdmMeasurementsEvent,
               sizeof (DynamicEventLogSpdmMeasurementsEvent),
               &DynamicEventLogSpdmMeasurementsEvent,
               sizeof (DynamicEventLogSpdmMeasurementsEvent)
               );
    if (EFI_ERROR (Status)) {
      SecurityState->MeasurementState = EDKII_DEVICE_SECURITY_STATE_ERROR_TCG_EXTEND_TPM_PCR;
    }

    DEBUG ((DEBUG_INFO, "TpmMeasureAndLogData (Dynamic) - %r\n", Status));
  }

Exit:
  if (EventLog != NULL) {
    FreePool (EventLog);
  }

  return Status;
}

/**
  This function gets SPDM measurement and extend to TPM.

  @param[in]  SpdmDeviceContext            The SPDM context for the device.
  @param[in]  SlotId                       The number of slot id of the certificate.
  @param[out] SecurityState                A poniter to security state of the requester.

  @retval EFI_SUCCESS            Operation completed successfully.
  @retval EFI_OUT_OF_RESOURCES   Out of memory.
  @retval EFI_DEVICE_ERROR       The operation was unsuccessful.

**/
EFI_STATUS
EFIAPI
DoDeviceMeasurement (
  IN  SPDM_DEVICE_CONTEXT          *SpdmDeviceContext,
  IN  UINT8                        SlotId,
  OUT EDKII_DEVICE_SECURITY_STATE  *SecurityState
  )
{
  EFI_STATUS                   Status;
  SPDM_RETURN                  SpdmReturn;
  VOID                         *SpdmContext;
  UINT32                       CapabilityFlags;
  UINTN                        DataSize;
  SPDM_DATA_PARAMETER          Parameter;
  UINT8                        NumberOfBlocks;
  UINT32                       MeasurementRecordLength;
  UINT8                        MeasurementRecord[LIBSPDM_MAX_MEASUREMENT_RECORD_SIZE];
  UINT8                        Index;
  UINT8                        RequesterNonce[SPDM_NONCE_SIZE];
  UINT8                        ResponderNonce[SPDM_NONCE_SIZE];
  UINT8                        RequestAttribute;
  UINT32                       MeasurementsBlockSize;
  SPDM_MEASUREMENT_BLOCK_DMTF  *MeasurementBlock;
  UINT8                        NumberOfBlock;
  UINT8                        ReceivedNumberOfBlock;
  UINT8                        AuthState;
  UINT8                        ContentChanged;
  UINT8                        ContentChangedCount;

  SpdmContext = SpdmDeviceContext->SpdmContext;

  ZeroMem (&Parameter, sizeof (Parameter));
  Parameter.location = (libspdm_data_location_t)SpdmDataLocationConnection;
  DataSize           = sizeof (CapabilityFlags);
  SpdmGetData (SpdmContext, (libspdm_data_type_t)SpdmDataCapabilityFlags, &Parameter, &CapabilityFlags, &DataSize);

  if ((CapabilityFlags & SPDM_GET_CAPABILITIES_RESPONSE_FLAGS_MEAS_CAP_SIG) == 0) {
    AuthState                       = TCG_DEVICE_SECURITY_EVENT_DATA_DEVICE_AUTH_STATE_FAIL_NO_SIG;
    Status                          = ExtendCertificate (SpdmDeviceContext, AuthState, 0, NULL, NULL, 0, 0, SecurityState);
    SecurityState->MeasurementState = EDKII_DEVICE_SECURITY_STATE_ERROR_DEVICE_NO_CAPABILITIES;
    if (Status != EFI_SUCCESS) {
      return Status;
    } else {
      return EFI_UNSUPPORTED;
    }
  }

  RequestAttribute  = 0;
  RequestAttribute |= SPDM_GET_MEASUREMENTS_REQUEST_ATTRIBUTES_GENERATE_SIGNATURE;

  MeasurementRecordLength = sizeof (MeasurementRecord);
  ZeroMem (RequesterNonce, sizeof (RequesterNonce));
  ZeroMem (ResponderNonce, sizeof (ResponderNonce));

  //
  // get all measurement once, with signature.
  //
  SpdmReturn = SpdmGetMeasurementEx (
                 SpdmContext,
                 NULL,
                 RequestAttribute,
                 SPDM_GET_MEASUREMENTS_REQUEST_MEASUREMENT_OPERATION_ALL_MEASUREMENTS,
                 SlotId,
                 NULL,
                 &NumberOfBlocks,
                 &MeasurementRecordLength,
                 MeasurementRecord,
                 NULL,
                 RequesterNonce,
                 ResponderNonce,
                 NULL,
                 0
                 );
  if (LIBSPDM_STATUS_IS_SUCCESS (SpdmReturn)) {
    DEBUG ((DEBUG_INFO, "NumberOfBlocks %d\n", NumberOfBlocks));

    MeasurementBlock = (VOID *)MeasurementRecord;
    for (Index = 0; Index < NumberOfBlocks; Index++) {
      MeasurementsBlockSize =
        sizeof (SPDM_MEASUREMENT_BLOCK_DMTF) +
        MeasurementBlock
          ->MeasurementBlockDmtfHeader
          .DMTFSpecMeasurementValueSize;

      AuthState                       = TCG_DEVICE_SECURITY_EVENT_DATA_DEVICE_AUTH_STATE_SUCCESS;
      SecurityState->MeasurementState = EDKII_DEVICE_SECURITY_STATE_SUCCESS;
      if (Index == NumberOfBlocks - 1) {
        Status = ExtendMeasurement (SpdmDeviceContext, AuthState, MeasurementsBlockSize, (UINT8 *)MeasurementBlock, RequesterNonce, ResponderNonce, SecurityState);
      } else {
        Status = ExtendMeasurement (SpdmDeviceContext, AuthState, MeasurementsBlockSize, (UINT8 *)MeasurementBlock, NULL, NULL, SecurityState);
      }

      MeasurementBlock = (VOID *)((size_t)MeasurementBlock + MeasurementsBlockSize);
      if (Status != EFI_SUCCESS) {
        return Status;
      }
    }
  } else if (SpdmReturn == LIBSPDM_STATUS_VERIF_FAIL) {
    AuthState                       = TCG_DEVICE_SECURITY_EVENT_DATA_DEVICE_AUTH_STATE_FAIL_INVALID;
    SecurityState->MeasurementState = EDKII_DEVICE_SECURITY_STATE_ERROR_MEASUREMENT_AUTH_FAILURE;
    Status                          = ExtendMeasurement (SpdmDeviceContext, AuthState, 0, NULL, NULL, NULL, SecurityState);
    return Status;
  } else {
    ContentChangedCount = 0;
ContentChangedFlag:
    RequestAttribute      = 0;
    ContentChanged        = SPDM_MEASUREMENTS_RESPONSE_CONTENT_NO_CHANGE_DETECTED;
    ReceivedNumberOfBlock = 0;

    //
    // 1. Query the total number of measurements available.
    //
    SpdmReturn = SpdmGetMeasurement (
                   SpdmContext,
                   NULL,
                   RequestAttribute,
                   SPDM_GET_MEASUREMENTS_REQUEST_MEASUREMENT_OPERATION_TOTAL_NUMBER_OF_MEASUREMENTS,
                   SlotId,
                   NULL,
                   &NumberOfBlocks,
                   NULL,
                   NULL
                   );
    if (LIBSPDM_STATUS_IS_ERROR (SpdmReturn)) {
      SecurityState->MeasurementState = EDKII_DEVICE_SECURITY_STATE_ERROR_DEVICE_ERROR;
      return EFI_DEVICE_ERROR;
    }

    DEBUG ((DEBUG_INFO, "NumberOfBlocks - 0x%x\n", NumberOfBlocks));

    ReceivedNumberOfBlock = 0;
    for (Index = 1; Index <= 0xFE; Index++) {
      if (ReceivedNumberOfBlock == NumberOfBlocks) {
        break;
      }

      DEBUG ((DEBUG_INFO, "Index - 0x%x\n", Index));
      //
      // 2. query measurement one by one
      //    get signature in last message only.
      //
      if (ReceivedNumberOfBlock == NumberOfBlocks - 1) {
        RequestAttribute |= SPDM_GET_MEASUREMENTS_REQUEST_ATTRIBUTES_GENERATE_SIGNATURE;
      }

      MeasurementRecordLength = sizeof (MeasurementRecord);
      ZeroMem (RequesterNonce, sizeof (RequesterNonce));
      ZeroMem (ResponderNonce, sizeof (ResponderNonce));
      SpdmReturn = SpdmGetMeasurementEx (
                     SpdmContext,
                     NULL,
                     RequestAttribute,
                     Index,
                     SlotId,
                     &ContentChanged,
                     &NumberOfBlock,
                     &MeasurementRecordLength,
                     MeasurementRecord,
                     NULL,
                     RequesterNonce,
                     ResponderNonce,
                     NULL,
                     0
                     );
      if (LIBSPDM_STATUS_IS_ERROR (SpdmReturn)) {
        if (SpdmReturn == LIBSPDM_STATUS_VERIF_FAIL) {
          AuthState                       = TCG_DEVICE_SECURITY_EVENT_DATA_DEVICE_AUTH_STATE_FAIL_INVALID;
          SecurityState->MeasurementState = EDKII_DEVICE_SECURITY_STATE_ERROR_DEVICE_ERROR;
          Status                          = ExtendMeasurement (SpdmDeviceContext, AuthState, 0, NULL, NULL, NULL, SecurityState);
          return Status;
        } else {
          continue;
        }
      }

      if ((ReceivedNumberOfBlock == NumberOfBlocks - 1) &&
          (ContentChanged == SPDM_MEASUREMENTS_RESPONSE_CONTENT_CHANGE_DETECTED))
      {
        if (ContentChangedCount == 0) {
          ContentChangedCount++;
          goto ContentChangedFlag;
        } else {
          AuthState                       = TCG_DEVICE_SECURITY_EVENT_DATA_DEVICE_AUTH_STATE_FAIL_INVALID;
          SecurityState->MeasurementState = EDKII_DEVICE_SECURITY_STATE_ERROR_DEVICE_ERROR;
          Status                          = ExtendMeasurement (SpdmDeviceContext, AuthState, 0, NULL, NULL, NULL, SecurityState);
          return Status;
        }
      }

      DEBUG ((DEBUG_INFO, "ExtendMeasurement...\n"));
      AuthState                       = TCG_DEVICE_SECURITY_EVENT_DATA_DEVICE_AUTH_STATE_SUCCESS;
      SecurityState->MeasurementState = EDKII_DEVICE_SECURITY_STATE_SUCCESS;
      if (ReceivedNumberOfBlock == NumberOfBlocks - 1) {
        Status = ExtendMeasurement (SpdmDeviceContext, AuthState, MeasurementRecordLength, MeasurementRecord, RequesterNonce, ResponderNonce, SecurityState);
      } else {
        Status = ExtendMeasurement (SpdmDeviceContext, AuthState, MeasurementRecordLength, MeasurementRecord, NULL, ResponderNonce, SecurityState);
      }

      if (Status != EFI_SUCCESS) {
        return Status;
      }

      ReceivedNumberOfBlock += 1;
    }

    if (ReceivedNumberOfBlock != NumberOfBlocks) {
      SecurityState->MeasurementState = EDKII_DEVICE_SECURITY_STATE_ERROR_MEASUREMENT_AUTH_FAILURE;
      return EFI_DEVICE_ERROR;
    }
  }

  return EFI_SUCCESS;
}
