/** @file
Metronome.c -- UEFI Metronome Arch Protocol Driver for HR650X BIOS.
Implements the EFI_METRONOME_ARCH_PROTOCOL.Wait() entry point using the
8254 PIT channel 2 and the CPU TSC (RDTSC) for microsecond-granularity
busy-wait loops. The driver:
1. Installs gEfiMetronomeArchProtocolGuid onto a new protocol handle.
2. Initialises the PIT channel 2 in rate-generator mode.
3. Provides MicrosecondDelay() which converts microseconds to PIT ticks
and spins on TSC-delta.
File: Metronome.c
Module: MdeModulePkg/Universal/Metronome/Metronome.c
Binary: Metronome.efi (SHA256: cf72be089c5360c263982d4f79971963696a1f8cd048a764f6b9c29f5c22c340)
Index: 0082 of HR650X BIOS PE files
Copyright (c) 2006 - 2019, Intel Corporation. All rights reserved.
SPDX-License-Identifier: BSD-2-Clause-Patent
**/
#include "Metronome.h"
// ==========================================================================
// Global data (from .data section at 0xFC0-0x1060)
// ==========================================================================
/// The EFI_HANDLE for this driver image (populated by UefiBootServicesTableLib).
EFI_HANDLE gImageHandle = NULL;
/// The EFI_SYSTEM_TABLE pointer (populated by UefiBootServicesTableLib).
UINT64 gSystemTable = 0;
/// The EFI_BOOT_SERVICES pointer (derived from gSystemTable->BootServices).
UINT64 gBootServices = 0;
/// The EFI_RUNTIME_SERVICES pointer (derived from gSystemTable->RuntimeServices).
UINT64 gRuntimeServices = 0;
/// Cached DebugLib protocol/PPI interface (initialised lazily).
UINT64 mDebugLib = 0;
/// Cached HOB list pointer (initialised lazily by GetHobList).
UINT64 mHobList = 0;
/// PCI Express MMIO base address (read from PCD via GetPcdProtocol).
UINT64 mPciExpressBaseAddress = 0;
/// Cached PCD protocol interface (initialised lazily by GetPcdProtocol).
UINT64 mPcd = 0;
/// EFI_METRONOME_ARCH_PROTOCOL instance installed by the driver.
/// Revision = 0x00010000, Wait = MetronomeWait.
///
/// NOTE: The following structure sits at 0x1000 (off_1000) in the original
/// binary. We declare it explicitly here.
EFI_METRONOME_ARCH_PROTOCOL mMetronome = {
EFI_METRONOME_ARCH_PROTOCOL_REVISION,
MetronomeWait
};
//
// GUID constants (from .rdata section).
//
/// gEfiMetronomeArchProtocolGuid
const UINT8 gEfiMetronomeArchProtocolGuid[16] = EFI_METRONOME_ARCH_PROTOCOL_GUID;
/// gEfiPcdMetronomeInternalGuid (the PCD protocol GUID used by DxePcdLib)
const UINT8 gPcdMetronomeInternalGuid[16] = PCD_METRONOME_INTERNAL_GUID;
/// The DebugLib singleton protocol GUID (gEfiDebugLibProtocolGuid placeholder)
const UINT8 gDebugLibProtocolGuid[16] = { 0 };
// ==========================================================================
// Thunk / intrinsic wrappers
// ==========================================================================
/**
Issues a CPU PAUSE instruction (rep; nop). Yields to the other hyper-thread
on an Intel HT-capable processor.
(Original: sub_2C0, "_mm_pause_w")
**/
VOID
_mm_pause_w (
VOID
)
{
__asm__ volatile ("pause");
}
/**
Reads the Time-Stamp Counter (RDTSC).
@return The current 64-bit TSC value.
(Original: sub_2D0, "__rdtsc_w")
**/
UINT64
__rdtsc_w (
VOID
)
{
UINT32 Lo, Hi;
__asm__ volatile ("rdtsc" : "=a" (Lo), "=d" (Hi));
return ((UINT64)Hi << 32) | Lo;
}
/**
Enables interrupts (STI).
(Original: sub_2E0, "_enable_w")
**/
VOID
_enable_w (
VOID
)
{
__asm__ volatile ("sti");
}
/**
Disables interrupts (CLI).
(Original: sub_2F0, "_disable_w")
**/
VOID
_disable_w (
VOID
)
{
__asm__ volatile ("cli");
}
/**
Reads the caller's EFLAGS (pushfq / pop).
@return The EFLAGS value.
(Original: sub_300, "__getcallerseflags_w")
**/
UINT64
__getcallerseflags_w (
VOID
)
{
UINT64 Flags;
__asm__ volatile ("pushfq; popq %0" : "=r" (Flags));
return Flags;
}
// ==========================================================================
// I/O and memory access helpers
// ==========================================================================
/**
Reads a 32-bit value from an I/O port with strict alignment checking.
@param[in] Port The I/O port number. Must be 4-byte aligned.
@return The 32-bit value read from the port.
(Original: sub_93C, "IoRead32")
**/
UINT32
IoRead32 (
IN UINT16 Port
)
{
//
// ASSERT ((Port & 3) == 0);
//
if ((Port & 3) != 0) {
InternalAssert (
(UINT64)"e:\\hs\\MdePkg\\Library\\BaseIoLibIntrinsic\\IoLibMsc.c",
193,
(UINT64)"(Port & 3) == 0"
);
}
return __indword (Port);
}
/**
Reads an unaligned 64-bit value from memory.
@param[in] Buffer Pointer to the unaligned value (may be NULL).
@return The 64-bit value read.
(Original: sub_96C, "ReadUnaligned64")
**/
UINT64
ReadUnaligned64 (
IN CONST VOID *Buffer
)
{
//
// ASSERT (Buffer != NULL);
//
if (Buffer == NULL) {
InternalAssert (
(UINT64)"e:\\hs\\MdePkg\\Library\\BaseLib\\Unaligned.c",
192,
(UINT64)"Buffer != ((void *) 0)"
);
}
return *(const UINT64 *)Buffer;
}
/**
Translates a PCI Express register offset to a full MMIO address.
@param[in] Address The PCI Express register offset.
@return Address + mPciExpressBaseAddress.
(Original: sub_858, "PciExpressGetAddress")
**/
UINT64
PciExpressGetAddress (
IN UINT64 Address
)
{
//
// ASSERT ((Address & ~0xFFFFFFF) == 0)
//
if ((Address & 0xFFFFFFFFF0000000ULL) != 0) {
InternalAssert (
(UINT64)"e:\\hs\\MdePkg\\Library\\SmmPciExpressLib\\PciExpressLib.c",
118,
(UINT64)"((Address) & ~0xfffffff) == 0"
);
}
return Address + mPciExpressBaseAddress;
}
/**
Writes 0x0500 (as a 16-bit value) to the given I/O port.
In the context of the UEFI Metronome driver, the port addressed is
0x4D0 (PIC Slave/Master interrupt control), and writing 0x0500
sets up the 8259 PIC for operation.
@param[in] Address Pointer to the target I/O port (must be WORD-aligned).
@return The value written (0x0500).
(Original: sub_908, "IoWrite16")
**/
UINT16
IoWrite16 (
IN UINT16 *Address
)
{
//
// ASSERT ((Address & 1) == 0);
//
if (((UINT64)Address & 1) != 0) {
InternalAssert (
(UINT64)"e:\\hs\\MdePkg\\Library\\BaseIoLibIntrinsic\\IoLib.c",
183,
(UINT64)"(Address & 1) == 0"
);
}
*Address = 0x0500;
return 0x0500;
}
// ==========================================================================
// Library initialisation helpers
// ==========================================================================
/**
Initialises the UEFI boot/runtime service globals from the SystemTable.
Performs NULL-pointer assertions for:
- gImageHandle
- gST (SystemTable)
- gBS (BootServices)
- gRT (RuntimeServices)
Then initialises the HOB list, PCD protocol, and PCI Express base address.
Reads the interrupt state, sets up the 8259 PIC, and calibrates a
microsecond delay using RDTSC and the PIT.
@param[in] ImageHandle The EFI_HANDLE for this driver image.
@param[in] SystemTable Pointer to the EFI_SYSTEM_TABLE.
(Original: sub_47C, the main initialisation dispatcher called from
_ModuleEntryPoint.)
**/
VOID
MetronomeDriverInit (
IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable
)
{
UINT64 Ticks;
UINT64 StartTSC;
UINT64 Elapsed;
UINT64 Microseconds;
BOOLEAN InterruptsEnabled;
UINT64 PcdProtocol;
//
// Save ImageHandle and SystemTable (UefiBootServicesTableLib init).
//
gImageHandle = ImageHandle;
if (ImageHandle == NULL) {
InternalAssert (
(UINT64)"e:\\hs\\MdePkg\\Library\\UefiBootServicesTableLib\\UefiBootServicesTableLib.c",
51,
(UINT64)"gImageHandle != ((void *) 0)"
);
}
gSystemTable = (UINT64)SystemTable;
if (SystemTable == NULL) {
InternalAssert (
(UINT64)"e:\\hs\\MdePkg\\Library\\UefiBootServicesTableLib\\UefiBootServicesTableLib.c",
57,
(UINT64)"gST != ((void *) 0)"
);
}
gBootServices = (UINT64)SystemTable->BootServices;
if (gBootServices == 0) {
InternalAssert (
(UINT64)"e:\\hs\\MdePkg\\Library\\UefiBootServicesTableLib\\UefiBootServicesTableLib.c",
63,
(UINT64)"gBS != ((void *) 0)"
);
}
gRuntimeServices = (UINT64)SystemTable->RuntimeServices;
if (gRuntimeServices == 0) {
InternalAssert (
(UINT64)"e:\\hs\\MdePkg\\Library\\UefiRuntimeServicesTableLib\\UefiRuntimeServicesTableLib.c",
47,
(UINT64)"gRT != ((void *) 0)"
);
}
//
// Initialise HOB list (DxeHobLib).
//
GetHobList ();
//
// Locate PCD protocol and read PcdPciExpressBaseAddress.
//
PcdProtocol = (UINT64)GetPcdProtocol ();
if (PcdProtocol != 0) {
//
// Call PcdGet64 (5) via the protocol's Get64 method at offset +32.
//
mPciExpressBaseAddress = (*(UINT64 (*)(UINT64))(PcdProtocol + 32))(5);
}
//
// If PCI Express config space is accessible (PcdPciExpressBaseAddress != 0),
// program the PIC via I/O ports.
//
if ((INT8)PciExpressGetAddress (1024068) >= 0) {
IoWrite16 ((UINT16 *)PciExpressGetAddress (1024064));
//
// Set bit 7 at the PCI express offset for byte 1024068
// (I/O port 0x4D1, PIC slave interrupt mask).
//
{
UINT8 *Ptr = (UINT8 *)PciExpressGetAddress (1024068);
*Ptr |= 0x80;
}
}
//
// Save interrupt state and disable interrupts.
//
InterruptsEnabled = (__getcallerseflags_w () & 0x200) != 0;
_disable_w ();
//
// Read the current TSC and calculate the number of ticks corresponding
// to 1288 microseconds. The calculation uses the PIT frequency:
//
// Ticks = (PIT_FREQUENCY * Microseconds) / 1000000
// = (3579545 * 1288) / 1000000
//
// The result captures the bottom 24 bits.
//
StartTSC = IoRead32 (1288) & 0xFFFFFF; // Read PIT counter 2 current value
//
// Spin until the PIT counter has decremented by the delta.
// RDTSC_WAIT_MASK handles 22-bit rollover.
//
Elapsed = StartTSC;
while (((Elapsed + RDTSC_WAIT_MASK - (UINT32)IoRead32 (1288)) & 0x800000) == 0) {
_mm_pause_w ();
}
//
// Capture the elapsed TSC value after the spin.
//
Microseconds = __rdtsc_w ();
//
// Restore interrupt state.
//
if (InterruptsEnabled) {
_enable_w ();
} else {
_disable_w ();
}
}
// ==========================================================================
// Public Metronome protocol entry
// ==========================================================================
/**
The EFI_METRONOME_ARCH_PROTOCOL.Wait() implementation.
Waits for the specified number of microseconds by programming the 8254 PIT
channel 2 and busy-waiting on the TSC.
@param[in] Microseconds The number of microseconds to wait.
Must be >= 10 (PIT minimum granularity).
@return EFI_SUCCESS always.
(Original: sub_5D0, "MetronomeWait")
**/
EFI_STATUS
EFIAPI
MetronomeWait (
IN UINT32 Microseconds
)
{
UINT64 Ticks;
//
// Convert Microseconds to PIT ticks:
//
// Ticks = (PIT_FREQUENCY * Microseconds) / 1000000
//
// For large delays (>= 1000000 us == 1 s) we divide first to avoid overflow:
// Ticks = 3579545 * (Microseconds / 10) / 100000
//
if (Microseconds >= MICROSECONDS_PER_SECOND) {
Ticks = 3579545 * (UINT64)(Microseconds / 10) / 100000;
} else {
Ticks = 3579545 * (UINT64)(100 * Microseconds) / 1000000000;
}
MicrosecondDelay ((UINT32)Ticks);
return EFI_SUCCESS;
}
// ==========================================================================
// Internal delay implementation
// ==========================================================================
/**
Busy-waits for the specified number of PIT ticks using the PIT channel 2
and TSC-based measurement.
The PIT channel 2 is programmed in mode 3 (square-wave) at a base rate
of 3.579545 MHz. The routine reads the current counter and spins until
the counter has decremented by the requested number of ticks.
@param[in] Ticks The number of PIT ticks to wait.
(Original: sub_894, "MicrosecondDelay")
**/
VOID
MicrosecondDelay (
IN UINT32 Ticks
)
{
UINT32 Remaining;
UINT32 Delta;
UINT32 CurrentCount;
Remaining = Ticks >> RDTSC_WAIT_SHIFT; // Number of 0x400000-tick chunks
Delta = Ticks & RDTSC_WAIT_MASK; // Remainder (< 0x400000)
do {
//
// Wait for the current 0x400000-tick chunk:
// target = Delta + (PIT counter current value & 0xFFFFFF)
//
UINT32 Target = Delta + (IoRead32 (1288) & 0xFFFFFF);
Delta = 0x400000; // Subsequent iterations wait a full chunk
//
// Spin until the PIT counter passes the target.
// The 0x800000 bit handles 24-bit counter rollover.
//
while (((Target - (UINT32)IoRead32 (1288)) & 0x800000) == 0) {
_mm_pause_w ();
}
} while (Remaining-- != 0);
}
// ==========================================================================
// Library support functions
// ==========================================================================
/**
Returns a cached pointer to the HOB list.
On first call, retrieves the HOB list from BootServices (gBS + 104).
If the HOB list pointer is NULL, triggers an ASSERT.
@return Pointer to the start of the HOB list.
(Original: sub_780, "GetHobList")
**/
VOID *
GetHobList (
VOID
)
{
VOID *HobList;
UINT64 HobCount;
UINT64 Index;
if (mHobList != 0) {
return (VOID *)mHobList;
}
//
// gSystemTable + 104 = Number of HOB entries
// gSystemTable + 112 = Pointer to HOB entry array
//
HobList = NULL;
mHobList = 0;
if (*(UINT64 *)(gSystemTable + 104) != 0) {
//
// Walk the HOB entries looking for a GUID match with the
// Metronome-specific GUID.
//
for (Index = 0; Index < *(UINT64 *)(gSystemTable + 104); Index++) {
if (GetNextGuidHob ((VOID *)(gSystemTable + 112 + Index * 24)) != NULL) {
//
// Found -- extract the data pointer at offset +16 of the HOB entry.
//
HobList = (VOID *)(*(UINT64 *)(*(UINT64 *)(gSystemTable + 112) + 24 * Index + 16));
mHobList = (UINT64)HobList;
break;
}
}
}
if (HobList == NULL) {
//
// ASSERT_EFI_ERROR (EFI_NOT_FOUND)
//
InternalAssertEfiError (0x80000000LL,
"\nASSERT_EFI_ERROR (Status = %r)\n",
0x800000000000000EULL);
InternalAssert (
(UINT64)"e:\\hs\\MdePkg\\Library\\DxeHobLib\\HobLib.c",
54,
(UINT64)"!EFI_ERROR (Status)"
);
}
//
// ASSERT (mHobList != NULL)
//
if (mHobList == 0) {
InternalAssert (
(UINT64)"e:\\hs\\MdePkg\\Library\\DxeHobLib\\HobLib.c",
55,
(UINT64)"mHobList != ((void *) 0)"
);
}
return (VOID *)mHobList;
}
/**
Locates the first HOB entry whose GUID matches the Metronome-specific GUID.
@param[in] Entry Pointer to the HOB entry structure (GUID at offset 0,
data at offset 8).
@return The entry if GUID matches, NULL otherwise.
(Original: sub_99C, "GetNextGuidHob")
**/
VOID *
GetNextGuidHob (
IN CONST VOID *Entry
)
{
UINT64 Guid1;
UINT64 Guid2;
UINT64 EntryGuid1;
UINT64 EntryGuid2;
//
// Read the GUID we are looking for (from the .data section at unk_FF0/FF8).
//
Guid1 = ReadUnaligned64 ((VOID *)((UINT8 *)&gEfiMetronomeArchProtocolGuid + 0));
Guid2 = ReadUnaligned64 ((VOID *)((UINT8 *)&gEfiMetronomeArchProtocolGuid + 8));
//
// Read the GUID from the HOB entry.
//
EntryGuid1 = ReadUnaligned64 (Entry);
EntryGuid2 = ReadUnaligned64 ((UINT8 *)Entry + 8);
//
// Compare.
//
if (Guid1 == EntryGuid1 && Guid2 == EntryGuid2) {
return (VOID *)Entry;
}
return NULL;
}
/**
Returns the singleton pointer to the gPcdMetronomeInternal protocol.
Uses BootServices->LocateProtocol() to find the PCD protocol.
Caches the result in mPcd.
@return Pointer to the PCD protocol interface.
(Original: sub_A0C, "GetPcdProtocol")
**/
VOID *
GetPcdProtocol (
VOID
)
{
UINT64 Status;
if (mPcd != 0) {
return (VOID *)mPcd;
}
//
// Call BootServices->LocateProtocol(&gPcdMetronomeInternalGuid, NULL, &mPcd)
// BootServices at gSystemTable + (BootServices offset) = gBootServices.
// LocateProtocol offset within BootServices = 320.
//
Status = (*(UINT64 (*)(VOID *, UINT64, UINT64 *))(gBootServices + 320))(
(VOID *)gPcdMetronomeInternalGuid,
0,
&mPcd
);
if (EFI_ERROR (Status)) {
InternalAssertEfiError (0x80000000LL,
"\nASSERT_EFI_ERROR (Status = %r)\n",
Status);
InternalAssert (
(UINT64)"e:\\hs\\MdePkg\\Library\\DxePcdLib\\DxePcdLib.c",
78,
(UINT64)"!EFI_ERROR (Status)"
);
}
//
// ASSERT (mPcd != NULL)
//
if (mPcd == 0) {
InternalAssert (
(UINT64)"e:\\hs\\MdePkg\\Library\\DxePcdLib\\DxePcdLib.c",
79,
(UINT64)"mPcd != ((void *) 0)"
);
}
return (VOID *)mPcd;
}
/**
Returns the singleton pointer to the DebugLib protocol.
Uses BootServices->LocateProtocol() to find the Debug protocol.
Only allocates the protocol if the image size is <= 16 pages.
@return Pointer to the Debug protocol interface, or NULL.
(Original: sub_638, "GetDebugLib")
**/
VOID *
GetDebugLib (
VOID
)
{
UINT64 PageCount;
UINT64 Status;
if (mDebugLib != 0) {
return (VOID *)mDebugLib;
}
//
// Call BootServices->GetMemoryMap() to determine the number of pages
// occupied by this image. For Metronome, this is a self-check.
// BootServices offsets: GetMemoryMap at +24, AllocatePages at +32.
//
PageCount = (*(UINT64 (*)(UINT64))(gBootServices + 24))(31);
(*(VOID (*)(UINT64))(gBootServices + 32))(PageCount);
//
// Only proceed if the image fits within 16 pages.
//
if (PageCount <= 16) {
Status = (*(UINT64 (*)(VOID *, UINT64, UINT64 *))(gBootServices + 320))(
(VOID *)gDebugLibProtocolGuid,
0,
&mDebugLib
);
if (EFI_ERROR (Status)) {
mDebugLib = 0;
}
} else {
mDebugLib = 0;
}
return (VOID *)mDebugLib;
}
/**
Internal ASSERT helper -- calls DebugAssert via the DebugLib protocol.
@param[in] FileName Source file name string.
@param[in] LineNumber Line number of the assertion.
@param[in] Description The assertion expression string.
(Original: sub_740, "InternalAssert")
**/
VOID
InternalAssert (
IN UINT64 FileName,
IN UINT64 LineNumber,
IN UINT64 Description
)
{
VOID *DebugLib;
DebugLib = GetDebugLib ();
if (DebugLib != NULL) {
//
// Call DebugAssert (DebugLib + 8) with (FileName, LineNumber, Description).
//
(*(VOID (*)(UINT64, UINT64, UINT64))((UINT64)DebugLib + 8))(
FileName,
LineNumber,
Description
);
}
}
/**
Internal ASSERT_EFI_ERROR helper -- calls DebugAssert and debug print
when Status has the error bit set.
@param[in] Status The EFI_STATUS to test.
@param[in] Format Format string.
@param[in] ... Variable arguments.
@return The (char) value returned by the DebugLib chain.
(Original: sub_6B8, "InternalAssertEfiError")
**/
UINT8
InternalAssertEfiError (
IN UINT64 Status,
IN CONST CHAR8 *Format,
...
)
{
VOID *DebugLib;
UINT64 ErrorMask;
UINT8 CmosReg;
UINT8 CmosData;
UINT8 Result;
va_list VaList;
va_start (VaList, Format);
DebugLib = GetDebugLib ();
ErrorMask = 0;
Result = 0;
if (DebugLib != NULL) {
//
// Read RTC CMOS status register D (0x0D) via ports 0x70/0x71.
// Preserve bit 7 (NMI enable) on the address port.
//
__outbyte (RTC_PORT_CMOS_ADDR, RTC_REGISTER_D | RTC_NMI_ENABLE_BIT);
CmosData = __inbyte (RTC_PORT_CMOS_DATA);
//
// Determine the error mask based on RTC power status.
// If CMOS register D > 3 and non-zero, derive the mask from the
// memory-mapped flag at 0xFDAF0490.
//
if (CmosData > 3) {
if (CmosData == 0) {
CmosData = (*(volatile UINT8 *)0xFDAF0490 & 2) | 1;
}
}
//
// Map CmosData to error mask:
// CmosData == 1 -> ErrorMask = 0x80000004 (EFI_INVALID_PARAMETER style)
// Otherwise -> ErrorMask = 0x80000006 (EFI_UNSUPPORTED style)
//
if ((CmosData - 1) <= 0xFD) {
ErrorMask = 0x80000006LL;
if (CmosData == 1) {
ErrorMask = 0x80000004LL;
}
}
//
// If the status matches the error mask, call DebugPrint via the protocol.
//
if ((ErrorMask & Status) != 0) {
Result = (*(UINT8 (*)(UINT64, const CHAR8 *, va_list))(DebugLib))(Status, Format, VaList);
}
}
va_end (VaList);
return Result;
}
// ==========================================================================
// Driver entry point
// ==========================================================================
/**
The UEFI entry point for the Metronome driver.
Installs the gEfiMetronomeArchProtocolGuid onto a newly created protocol
handle. If the protocol is already installed, triggers an ASSERT.
@param[in] ImageHandle The firmware-allocated handle for this driver image.
@param[in] SystemTable A pointer to the EFI System Table.
@return EFI_SUCCESS The protocol was installed.
@return EFI_ALREADY_STARTED The protocol is already in the database
(triggers ASSERT in debug builds).
@return Other Error from gBS->InstallProtocolInterface.
(Original: _ModuleEntryPoint at 0x3D4)
**/
EFI_STATUS
EFIAPI
_ModuleEntryPoint (
IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable
)
{
EFI_STATUS Status;
//
// Perform library and global initialisation.
//
MetronomeDriverInit (ImageHandle, SystemTable);
//
// Check whether the Metronome Arch Protocol is already installed.
//
{
VOID *Interface;
Status = (*(EFI_STATUS (*)(VOID *, UINT64, VOID **))(gBootServices + 320))(
(VOID *)gEfiMetronomeArchProtocolGuid,
0,
&Interface
);
if (!EFI_ERROR (Status)) {
//
// Protocol already installed -- ASSERT.
//
InternalAssert (
(UINT64)"e:\\hs\\MdeModulePkg\\Universal\\Metronome\\Metronome.c",
112,
(UINT64)"&gEfiMetronomeArchProtocolGuid already installed in database"
);
}
}
//
// Install the protocol onto a new handle.
// gBootServices + 328 = InstallProtocolInterface
//
Status = (*(EFI_STATUS (*)(VOID *, VOID *, VOID *, UINT64))(gBootServices + 328))(
(VOID *)&gImageHandle, // New handle
(VOID *)gEfiMetronomeArchProtocolGuid, // Protocol GUID
(VOID *)&mMetronome, // Protocol interface
0 // Not native (BOOLEAN = FALSE)
);
//
// ASSERT_EFI_ERROR (Status)
//
if (EFI_ERROR (Status)) {
InternalAssertEfiError (
0x80000000LL,
"\nASSERT_EFI_ERROR (Status = %r)\n",
Status
);
InternalAssert (
(UINT64)"e:\\hs\\MdeModulePkg\\Universal\\Metronome\\Metronome.c",
122,
(UINT64)"!EFI_ERROR (Status)"
);
}
return Status;
}