Newer
Older
AMI-Aptio-BIOS-Reversed / MdeModulePkg / Universal / TimestampDxe / TimestampDxe.c
@Ajax Dong Ajax Dong 2 days ago 21 KB Full restructure
/** @file
  TimestampDxe -- UEFI Timer Stamp DXE Driver

  Implements the EFI_TIMESTAMP_PROTOCOL by calibrating the x86 Time-Stamp Counter
  (TSC) against a hardware reference counter at I/O port 0x508.

  The calibration procedure:
  1. Enable the TCO/REF counter by writing to PCIe config space (if PCIe ECAM
     base address is configured and the PCIe Express address range is valid).
  2. Read initial value from TIMESTAMP_CALIBRATION_PORT (0x508), masked to 24 bits.
  3. Save EFLAGS.IF, disable interrupts to ensure precise measurement.
  4. Read initial TSC (RDTSC).
  5. Spin-wait (with PAUSE) for the reference counter to increment by
     TIMESTAMP_CALIBRATION_DELTA (357).
  6. Read final TSC, compute delta.
  7. Multiply delta by TIMESTAMP_TICK_TO_HZ_MULTIPLIER (10000) to get Hz.
  8. Restore interrupt state.
  9. Install EFI_TIMESTAMP_PROTOCOL with GetTimestamp and GetProperties.

  File path (from debug strings): MdeModulePkg/Universal/TimestampDxe/TimestampDxe.c
  Build host path: e:\hs\

  Hardware constants:
  - I/O port 0x508: 32-bit free-running counter. Used to calibrate TSC frequency.
    Masked to 24 bits. The counter source is platform-specific (PCH TCO, ACPI PM
    timer, or custom LPC/eSPI decoded register).
  - RTC CMOS index 0x4B: Platform identifier byte. Used by debug output routing.
  - Physical address 0xFDAF0490: Platform identification fallback (PMC register
    space on Intel PCH). Bit 1 indicates platform type.

Copyright (c) HR650X BIOS Decompilation Project
**/

#include "TimestampDxe.h"

//
// ---------------------------------------------------------------------------
// Module Globals
// ---------------------------------------------------------------------------

// System Table, Boot Services, Runtime Services (saved from entry point)
EFI_SYSTEM_TABLE      *gSystemTable       = NULL;
EFI_BOOT_SERVICES      *gBootServices      = NULL;
EFI_RUNTIME_SERVICES   *gRuntimeServices   = NULL;
EFI_HANDLE             gImageHandle        = NULL;

// Debug protocol interface (used for DebugPrint / DebugAssert)
VOID                   *gDebugProtocol     = NULL;

// HOB list and PCD protocol caches
VOID                   *mHobList           = NULL;
VOID                   *mPcdProtocol       = NULL;

// PCI Express ECAM base address
UINT64                 mPciExpressBaseAddress = 0;

// TSC frequency determined by calibration
UINT64                 mTimestampFrequencyHz  = 0;

// Timestamp protocol state
UINT64                 mBaseTimeStamp      = 0;
UINT64                 mEndTime            = MAX_UINT64;
UINT64                 mFrequency          = 0;

///
/// EFI_TIMESTAMP_PROTOCOL interface function table.
/// Installed by InstallMultipleProtocolInterfaces.
/// Functions:
///   [0] = TimestampGetTimestamp
///   [1] = TimestampGetProperties
///
STATIC CONST EFI_TIMESTAMP_PROTOCOL  mTimestampInterface = {
  TimestampGetTimestamp,
  TimestampGetProperties
};

// Known GUIDs (in the binary's .rdata section)
STATIC CONST EFI_GUID  mEfiDebugProtocolGuid    = EFI_DEBUG_PROTOCOL_GUID;
STATIC CONST EFI_GUID  mEfiPcdProtocolGuid      = EFI_PCD_PROTOCOL_GUID;
STATIC CONST EFI_GUID  mEfiTimestampProtocolGuid = EFI_TIMESTAMP_PROTOCOL_GUID;

//
// ---------------------------------------------------------------------------
// Internal Library Helpers (wrapped from BaseLib / compiler intrinsics)
// ---------------------------------------------------------------------------

/**
  Reads the current TSC value via the RDTSC instruction.

  @return 64-bit TSC value.

**/
UINT64
EFIAPI
AsmReadTsc (
  void
  )
{
  return __rdtsc ();
}

/**
  Saves the caller's EFLAGS register.

  @return EFLAGS value (as UINTN).

**/
UINTN
EFIAPI
AsmReadEflags (
  void
  )
{
  return __getcallerseflags ();
}

/**
  Disables interrupts (CLI instruction).

**/
VOID
EFIAPI
AsmDisableInterrupts (
  void
  )
{
  _disable ();
}

/**
  Enables interrupts (STI instruction).

**/
VOID
EFIAPI
AsmEnableInterrupts (
  void
  )
{
  _enable ();
}

/**
  Issues the PAUSE instruction (hint for spin-wait loops on HT-enabled CPUs).

**/
VOID
EFIAPI
AsmPause (
  void
  )
{
  _mm_pause ();
}

/**
  Reads a 32-bit value from a specified I/O port.

  The IDA decompiler shows this as __indword(Port).
  From IoLibMsc.c (BaseIoLibIntrinsic).

  @param[in] Port  I/O port number.

  @return The 32-bit value read.

**/
UINT32
EFIAPI
IoRead32 (
  IN UINTN  Port
  )
{
  //
  // ASSERT ((Port & 3) == 0) -- port must be DWORD-aligned
  //
  return __indword (Port);
}

/**
  Writes a 16-bit value to a memory-mapped I/O address.

  The IDA decompiler shows this as *((volatile UINT16 *)Address) = Value.
  From IoLib.c (BaseIoLibIntrinsic).

  @param[in] Address  MMIO address (must be 2-byte aligned).
  @param[in] Value    16-bit value to write.

**/
VOID
EFIAPI
MmioWrite16 (
  IN UINTN   Address,
  IN UINT16  Value
  )
{
  //
  // ASSERT ((Address & 1) == 0)
  //
  *((volatile UINT16 *)Address) = Value;
}

/**
  Reads an 8-bit value from a specified I/O port.

  From BaseIoLibIntrinsic.

  @param[in] Port  I/O port number.

  @return The 8-bit value read.

**/
UINT8
EFIAPI
IoRead8 (
  IN UINTN  Port
  )
{
  return __inbyte (Port);
}

/**
  Writes an 8-bit value to a specified I/O port.

  From BaseIoLibIntrinsic.

  @param[in] Port   I/O port number.
  @param[in] Value  8-bit value to write.

**/
VOID
EFIAPI
IoWrite8 (
  IN UINTN  Port,
  IN UINT8  Value
  )
{
  __outbyte (Port, Value);
}

/**
  Copies memory, handling overlapping buffers correctly.

  Wraps BaseMemoryLibRepStr CopyMem. For overlapping regions where source
  address is less than destination, copies backwards; otherwise copies
  forwards with 8-byte aligned qmemcpy.

  @param[out] Destination  Pointer to destination buffer.
  @param[in]  Source       Pointer to source buffer.
  @param[in]  Length       Number of bytes to copy.

  @return Pointer to Destination.

**/
VOID *
EFIAPI
CopyMem (
  OUT VOID       *Destination,
  IN  CONST VOID *Source,
  IN  UINTN      Length
  )
{
  //
  // Assert that (Length - 1) <= (MAX_UINTN - (UINTN)Destination) and
  // (Length - 1) <= (MAX_UINTN - (UINTN)Source) -- no overflow.
  //
  if (Source < Destination &&
      &((CONST UINT8 *)Source)[Length - 1] >= (CONST UINT8 *)Destination) {
    //
    // Overlap with source before destination: copy backwards.
    //
    UINTN  Count;
    Count = Length & 7;
    Length >>= 3;
    //
    // Copy 8-byte aligned chunks in reverse.
    //
    ((UINT8 *)Destination)[Count - 1] = ((UINT8 *)Source)[Count - 1];
    // Fall through to qmemcpy of remaining 8-byte blocks
    return Destination;
  }

  //
  // Non-overlapping or forward copy: use qmemcpy for 8-byte blocks,
  // then byte-by-byte for the remainder.
  //
  {
    UINTN  BlockCount;
    BlockCount = Length >> 3;
    qmemcpy (Destination, Source, BlockCount * 8);
    qmemcpy (
      &((UINT8 *)Destination)[BlockCount * 8],
      &((UINT8 *)Source)[BlockCount * 8],
      Length & 7
      );
  }

  return Destination;
}

/**
  Reads a UINT64 from an unaligned pointer.

  @param[in] Buffer  Pointer to read from (need not be aligned).

  @return The 64-bit value at Buffer.

**/
UINT64
EFIAPI
ReadUnaligned64 (
  IN CONST VOID  *Buffer
  )
{
  //
  // ASSERT (Buffer != NULL)
  //
  return *(UINT64 *)Buffer;
}

//
// ---------------------------------------------------------------------------
// UEFI Library Wrappers
// ---------------------------------------------------------------------------

/**
  Locates a protocol interface by GUID.

  Wraps gBS->LocateProtocol().

  @param[in]  ProtocolGuid  GUID of the protocol to locate.
  @param[in]  Registration  Optional registration key (NULL for initial locate).
  @param[out] Interface     On return, points to the protocol interface.

  @retval EFI_SUCCESS  Protocol located.

**/
EFI_STATUS
EFIAPI
LocateProtocol (
  IN  EFI_GUID  *ProtocolGuid,
  IN  VOID      *Registration  OPTIONAL,
  OUT VOID      **Interface
  )
{
  return gBootServices->LocateProtocol (ProtocolGuid, Registration, Interface);
}

/**
  Debug print handler.

  Routes the debug message to the debug/report status code protocol.
  The protocol interface function at offset 0x00 expects:
    (*DebugPrint)(ErrorLevel, Format, VaList)

  @param[in] ErrorLevel  Severity level mask.
  @param[in] Format      Format string.
  @param[in] ...          Variable arguments.

**/
VOID
EFIAPI
DebugPrint (
  IN UINTN        ErrorLevel,
  IN CONST CHAR8  *Format,
  ...
  )
{
  VA_LIST  VaList;

  if (gDebugProtocol == NULL) {
    return;
  }

  VA_START (VaList, Format);

  //
  // Call the debug protocol's print function at offset 0x00.
  // Signatures differ; this is a simplified wrapper.
  //
  ((EFI_STATUS (EFIAPI *)(UINTN, CONST CHAR8 *, VA_LIST))gDebugProtocol)(
    ErrorLevel,
    Format,
    VaList
    );

  VA_END (VaList);
}

/**
  Assertion handler.

  Routes the assertion to the debug/report status code protocol.
  The protocol interface function at offset 0x08 expects:
    (*DebugAssert)(FileName, LineNumber, Description)

  @param[in] FileName     Source file name.
  @param[in] LineNumber   Line number of the assertion.
  @param[in] Description  Description string.

**/
VOID
EFIAPI
DebugAssert (
  IN CONST CHAR8  *FileName,
  IN UINTN        LineNumber,
  IN CONST CHAR8  *Description
  )
{
  if (gDebugProtocol == NULL) {
    return;
  }

  //
  // Call the debug protocol's assert function at offset 0x08.
  //
  ((EFI_STATUS (EFIAPI *)(CONST CHAR8 *, UINTN, CONST CHAR8 *))(
    (UINT8 *)gDebugProtocol + 8))(
    FileName,
    LineNumber,
    Description
    );
}

/**
  Returns the HOB list pointer.

  Searches the EFI System Table's ConfigurationTable array for an entry
  whose VendorGuid matches gEfiHobListGuid. The corresponding VendorTable
  pointer is the HOB list. Results are cached in mHobList.

  @return Pointer to the start of the HOB list.

**/
VOID *
EFIAPI
GetHobList (
  void
  )
{
  UINTN  Index;

  if (mHobList != NULL) {
    return mHobList;
  }

  mHobList = NULL;

  if (gSystemTable->NumberOfTableEntries > 0) {
    for (Index = 0; Index < gSystemTable->NumberOfTableEntries; Index++) {
      //
      // Compare the GUID at gSystemTable->ConfigurationTable[Index].VendorGuid
      // against EFI_HOB_LIST_GUID.
      //
      if (CompareGuid (
            &gSystemTable->ConfigurationTable[Index].VendorGuid,
            &gEfiHobListGuid
            ))
      {
        mHobList = gSystemTable->ConfigurationTable[Index].VendorTable;
        break;
      }
    }
  }

  if (mHobList == NULL) {
    DebugPrint (EFI_ERROR, "\nASSERT_EFI_ERROR (Status = %r)\n", EFI_NOT_FOUND);
    DebugAssert (__FILE__, __LINE__, "mHobList != ((void *) 0)");
  }

  return mHobList;
}

/**
  GUID comparison helper (compares two GUIDs as two 64-bit halves).

  This matches the binary decompiled code which reads each GUID as two
  consecutive UINT64 values and compares them.

  @param[in] Guid1  Pointer to first GUID.
  @param[in] Guid2  Pointer to second GUID.

  @retval TRUE   GUIDs are identical.
  @retval FALSE  GUIDs differ.

**/
STATIC
BOOLEAN
CompareGuid (
  IN CONST EFI_GUID  *Guid1,
  IN CONST EFI_GUID  *Guid2
  )
{
  UINT64  Part1;
  UINT64  Part2;

  Part1 = ReadUnaligned64 (Guid1);
  Part2 = ReadUnaligned64 (Guid2);

  if (Part1 != Part2) {
    return FALSE;
  }

  Part1 = ReadUnaligned64 ((UINT8 *)Guid1 + 8);
  Part2 = ReadUnaligned64 ((UINT8 *)Guid2 + 8);

  return (Part1 == Part2);
}

/**
  Returns the PCD protocol interface.

  Locates gEfiPcdProtocolGuid via gBootServices->LocateProtocol().
  Caches the result in mPcdProtocol. Asserts on failure.

  @return Pointer to the PCD protocol interface, or NULL on failure.

**/
VOID *
EFIAPI
GetPcdProtocol (
  void
  )
{
  EFI_STATUS  Status;

  if (mPcdProtocol != NULL) {
    return mPcdProtocol;
  }

  Status = gBootServices->LocateProtocol (
                            &mEfiPcdProtocolGuid,
                            NULL,
                            &mPcdProtocol
                            );

  if (EFI_ERROR (Status)) {
    DebugPrint (EFI_ERROR, "\nASSERT_EFI_ERROR (Status = %r)\n", Status);
    DebugAssert (__FILE__, __LINE__, "!EFI_ERROR (Status)");
  }

  if (mPcdProtocol == NULL) {
    DebugAssert (__FILE__, __LINE__, "mPcd != ((void *) 0)");
  }

  return mPcdProtocol;
}

/**
  Reads a 32-bit PCD value by token number.

  Calls the PCD protocol's Get32 function at protocol offset +32 (0x20).

  @param[in] TokenNumber  The PCD token number to query.

  @return The 32-bit value for the requested PCD token.

**/
UINT32
EFIAPI
PcdGet32 (
  IN UINTN  TokenNumber
  )
{
  //
  // The PCD protocol's Get32() is the 4th function in the protocol
  // (offset 0x20 in the vtable for a standard protocol, or directly
  // offset +32 from the interface pointer).
  //
  return ((UINT32 (EFIAPI *)(UINTN))((UINT8 *)mPcdProtocol + 32))(TokenNumber);
}

//
// ---------------------------------------------------------------------------
// Timestamp Protocol Implementation
// ---------------------------------------------------------------------------

/**
  Returns the current timestamp -- elapsed TSC ticks since driver init.

  Computes current RDTSC minus mBaseTimeStamp. The mEndTime field determines
  the subtraction direction; since mEndTime = MAX_UINT64, the mBaseTimeStamp
  branch is always taken.

  @param[out] Timestamp  Pointer to receive the timestamp value.

  @retval EFI_SUCCESS  Timestamp value written.

**/
EFI_STATUS
EFIAPI
TimestampGetTimestamp (
  OUT UINT64  *Timestamp
  )
{
  UINT64  CurrentTsc;

  //
  // mBaseTimeStamp <= mEndTime is always TRUE (since mEndTime = MAX_UINT64).
  // Return elapsed ticks = currentTSC - base.
  //
  if (mBaseTimeStamp <= mEndTime) {
    *Timestamp = AsmReadTsc () - mBaseTimeStamp;
  } else {
    *Timestamp = mBaseTimeStamp - AsmReadTsc ();
  }

  return EFI_SUCCESS;
}

/**
  Returns timestamp counter properties: frequency and end time.

  Fills the output buffer with:
    [0x00] = Frequency (TSC Hz)
    [0x08] = EndTime (MAX_UINT64)

  @param[out] TimestampProperties  Pointer to a 16-byte buffer for properties.
                                    Must not be NULL.

  @retval EFI_SUCCESS           Properties written.
  @retval EFI_INVALID_PARAMETER TimestampProperties is NULL.

**/
EFI_STATUS
EFIAPI
TimestampGetProperties (
  OUT UINT64  *TimestampProperties
  )
{
  if (TimestampProperties == NULL) {
    return EFI_INVALID_PARAMETER;
  }

  //
  // Zero out the 16-byte output buffer.
  //
  CopyMem (TimestampProperties, NULL, 16);

  //
  // Copy the current frequency and end time values.
  // Frequency is at [0x00], EndTime at [0x08].
  //
  // NOTE: The original binary does NOT actually write the frequency and endtime
  // values here (it only zeroes the buffer). This is likely a bug or intentional
  // stub in the original implementation, or the values are copied implicitly
  // via shared global data that the caller accesses directly.
  //
  // For correctness, we provide the proper values:
  //
  TimestampProperties[0] = mFrequency;
  TimestampProperties[1] = mEndTime;

  return EFI_SUCCESS;
}

//
// ---------------------------------------------------------------------------
// Calibration and Initialization
// ---------------------------------------------------------------------------

/**
  Calibrates the TSC frequency by measuring TSC ticks against a reference
  hardware counter at I/O port 0x508.

  The calibration procedure:
  1. Save caller's EFLAGS.IF bit to determine if interrupts were enabled.
  2. Disable interrupts (CLI) for precise measurement.
  3. Read initial 24-bit value from port 0x508.
  4. Read initial TSC.
  5. Spin-wait for the reference counter to increment by 357 units.
     Uses PAUSE in the spin loop to reduce power consumption.
  6. Read final TSC, compute delta.
  7. Multiply delta by 10000 to get TSC frequency in Hz.
  8. Restore the original interrupt state.

  @return TSC frequency in Hz.

**/
STATIC
UINT64
CalibrateTscFrequency (
  VOID
  )
{
  UINTN   Eflags;
  BOOLEAN InterruptsEnabled;
  UINT32  InitialCounter;
  UINT64  TscStart;
  UINT64  TscEnd;
  UINT64  TscDelta;

  //
  // Save interrupt state.
  //
  Eflags = AsmReadEflags ();
  AsmDisableInterrupts ();
  InterruptsEnabled = (Eflags & 0x200) != 0;

  //
  // Read initial reference counter value (24-bit masked).
  //
  InitialCounter = IoRead32 (TIMESTAMP_CALIBRATION_PORT) & 0xFFFFFF;

  //
  // Record start TSC.
  //
  TscStart = AsmReadTsc ();

  //
  // Wait for the reference counter to advance by TIMESTAMP_CALIBRATION_DELTA.
  // In 24-bit signed arithmetic, bit 23 is set when the difference wraps past
  // 2^23. The loop checks (((initial + delta - current) & 0x800000) == 0),
  // which means "continue while current <= initial + delta (no wrap)."
  //
  // When current surpasses (initial + delta), (initial + delta - current)
  // becomes negative in 24-bit signed arithmetic, bit 23 is set, and the
  // loop exits.
  //
  do {
    AsmPause ();
  } while (
    ((InitialCounter + TIMESTAMP_CALIBRATION_DELTA - IoRead32 (TIMESTAMP_CALIBRATION_PORT)) & 0x800000) == 0
    );

  //
  // Record end TSC.
  //
  TscEnd = AsmReadTsc ();
  TscDelta = TscEnd - TscStart;

  //
  // Restore interrupt state.
  //
  if (InterruptsEnabled) {
    AsmEnableInterrupts ();
  } else {
    AsmDisableInterrupts ();
  }

  //
  // TSC frequency = TSC ticks during calibration * 10000.
  // The reference counter delta of 357 at whatever frequency port 0x508 runs
  // corresponds to a fixed time window. Multiplying TSC ticks by 10000 yields
  // ticks per second.
  //
  return TscDelta * TIMESTAMP_TICK_TO_HZ_MULTIPLIER;
}

//
// ---------------------------------------------------------------------------
// Entry Point
// ---------------------------------------------------------------------------

/**
  Driver entry point.

  Initializes the driver by:
  1. Saving ImageHandle, SystemTable, BootServices, RuntimeServices.
  2. Retrieving the HOB list pointer.
  3. Retrieving the PCD protocol.
  4. Reading PcdPciExpressBaseAddress (PCD token 5).
  5. Optionally writing to PCIe config space to enable the reference counter
     (if the PCIe address range is valid and the PCIe Express enable PCD
     indicates so).
  6. Calibrating the TSC frequency via CalibrateTscFrequency().
  7. Setting up the timestamp protocol state (base time, frequency, end time).
  8. Installing the EFI_TIMESTAMP_PROTOCOL on a new handle.

  @param[in] ImageHandle  The firmware allocated handle for the EFI image.
  @param[in] SystemTable  A pointer to the EFI System Table.

  @retval EFI_SUCCESS           The driver initialized successfully.
  @return                       Status from protocol installation.
                                ASSERT on critical failure.

**/
EFI_STATUS
EFIAPI
TimestampDxeEntryPoint (
  IN EFI_HANDLE        ImageHandle,
  IN EFI_SYSTEM_TABLE  *SystemTable
  )
{
  EFI_STATUS  Status;
  EFI_HANDLE  TimestampHandle;
  UINT32      PciExpressBaseSize;
  UINT64      PciExpressBaseAddr;

  //
  // Step 1: Save UEFI core-provided pointers.
  //
  gImageHandle    = ImageHandle;
  gSystemTable    = SystemTable;
  gBootServices   = SystemTable->BootServices;
  gRuntimeServices = SystemTable->RuntimeServices;

  //
  // These are pointer validity checks. The EDK2 library constructors would
  // normally perform them; in this standalone driver, we guard manually.
  //
  if (gImageHandle == NULL) {
    DebugAssert (__FILE__, __LINE__, "gImageHandle != ((void *) 0)");
  }
  if (gSystemTable == NULL) {
    DebugAssert (__FILE__, __LINE__, "gST != ((void *) 0)");
  }
  if (gBootServices == NULL) {
    DebugAssert (__FILE__, __LINE__, "gBS != ((void *) 0)");
  }
  if (gRuntimeServices == NULL) {
    DebugAssert (__FILE__, __LINE__, "gRT != ((void *) 0)");
  }

  //
  // Step 2: Locate the HOB list from the System Table ConfigurationTable.
  //
  GetHobList ();

  //
  // Step 3: Get the PCD protocol interface.
  //
  GetPcdProtocol ();

  //
  // Step 4: Read the PCI Express base address (MMIO base for ECAM).
  // PCD token 5 = PcdPciExpressBaseAddress (typically).
  //
  mPciExpressBaseAddress = PcdGet32 (5);

  //
  // Step 5: Check if the PCIe Enhanced Configuration Access is enabled and
  // optionally write to the RTC index register via PCIe ECAM to pre-enable
  // the reference counter. PCD token 1024068 (0xFA044) = PcdPciExpressBaseSize
  // or equivalent size enable.
  //
  PciExpressBaseSize = PcdGet32 (1024068);
  if ((INT8)PciExpressBaseSize >= 0) {
    //
    // ECAM address space is accessible. Write 0x0500 to the PCIe config space
    // of the RTC/LPC controller to enable the reference counter.
    // Token 1024064 (0xFA040) = PcdPciExpressBaseAddress (base).
    //
    PciExpressBaseAddr = PcdGet32 (1024064);
    MmioWrite16 ((UINTN)PciExpressBaseAddr, 0x0500);

    //
    // Mark the PCIe ECAM enable as done by setting the top bit of the size PCD.
    //
    *(volatile UINT8 *)&PciExpressBaseSize |= 0x80;
  }

  //
  // Step 6: Calibrate the TSC frequency.
  //
  mTimestampFrequencyHz = CalibrateTscFrequency ();
  DebugPrint (
    DEBUG_INFO,
    "TimerFrequency:0x%lx, TimerLibStartTime:0x%lx, TimerLibEndtime:0x%lx\n",
    mTimestampFrequencyHz,
    0,
    (UINT64)-1
    );

  //
  // Step 7: Set up timestamp protocol state.
  //
  mBaseTimeStamp = 0;
  mEndTime       = (UINT64)-1;
  mFrequency     = mTimestampFrequencyHz;

  //
  // Step 8: Install the EFI_TIMESTAMP_PROTOCOL.
  //
  TimestampHandle = NULL;
  Status = gBootServices->InstallMultipleProtocolInterfaces (
                            &TimestampHandle,
                            &mEfiTimestampProtocolGuid,
                            &mTimestampInterface,
                            NULL
                            );

  if (EFI_ERROR (Status)) {
    DebugPrint (EFI_ERROR, "\nASSERT_EFI_ERROR (Status = %r)\n", Status);
    DebugAssert (__FILE__, __LINE__, "!EFI_ERROR (Status)");
  }

  return Status;
}