在uefi 中一般通过下面的code来delay 10us
// SRST should assert for at least 5 us, we use 10 us for
// better compatibility
//
gBS->Stall (10);
其中gBs的stall是在mdemodulepkg/dxe/dxemain 中来赋值的
EFI_BOOT_SERVICES mBootServices = {
{
EFI_BOOT_SERVICES_SIGNATURE, // Signature
EFI_BOOT_SERVICES_REVISION, // Revision
sizeof (EFI_BOOT_SERVICES), // HeaderSize
0, // CRC32
0 // Reserved
},
(EFI_STALL) CoreStall, // Stall
而CoreStall 的实现如下:
EFI_STATUS
EFIAPI
CoreStall (
IN UINTN Microseconds
)
{
UINT64 Counter;
UINT32 Remainder;
UINTN Index;
if (gMetronome == NULL) {
return EFI_NOT_AVAILABLE_YET;
}
//
// Counter = Microseconds * 10 / gMetronome->TickPeriod
// 0x1999999999999999 = (2^64 - 1) / 10
//
由于我们delay的时间小于0x1999999999999999ULL,一次我们走else的case
if (Microseconds > 0x1999999999999999ULL) {
//
// Microseconds is too large to multiple by 10 first. Perform the divide
// operation first and loop 10 times to avoid 64-bit math overflow.
//
Counter = DivU64x32Remainder (
Microseconds,
gMetronome->TickPeriod,
&Remainder
);
for (Index = 0; Index < 10; Index++) {
CoreInternalWaitForTick (Counter);
}
if (Remainder != 0) {
//
// If Remainder was not zero, then normally, Counter would be rounded
// up by 1 tick. In this case, since a loop for 10 counts was used
// to emulate the multiply by 10 operation, Counter needs to be rounded
// up by 10 counts.
//
CoreInternalWaitForTick (10);
}
} else {
//
// Calculate the number of ticks by dividing the number of microseconds by
// the TickPeriod. Calculation is based on 100ns unit.
//
计算需要delay的counter
Counter = DivU64x32Remainder (
MultU64x32 (Microseconds, 10),
gMetronome->TickPeriod,
&Remainder
);
如果remainder 不为0,就说明counter 不是整数,因此我们直接加一,也就是说调用stall的时间最多比需要的时间多一个counter
if (Remainder != 0) {
//
// If Remainder is not zero, then round Counter up by one tick.
//
Counter++;
}
继续调用CoreInternalWaitForTick
CoreInternalWaitForTick (Counter);
}
return EFI_SUCCESS;
}
在CoreInternalWaitForTick 中直接调用gMetronome的WaitForTick
CoreInternalWaitForTick (
IN UINT64 Counter
)
{
while (RShiftU64 (Counter, 32) > 0) {
gMetronome->WaitForTick (gMetronome, 0xffffffff);
Counter -= 0xffffffff;
}
gMetronome->WaitForTick (gMetronome, (UINT32)Counter);
}
gMetronome的定义如下:
EFI_CORE_PROTOCOL_NOTIFY_ENTRY mArchProtocols[] = {
{ &gEfiSecurityArchProtocolGuid, (VOID **)&gSecurity, NULL, NULL, FALSE },
{ &gEfiCpuArchProtocolGuid, (VOID **)&gCpu, NULL, NULL, FALSE },
{ &gEfiMetronomeArchProtocolGuid, (VOID **)&gMetronome, NULL, NULL, FALSE },
}
gEfiCpuArchProtocolGuid定义如下:
EFI_STATUS
EFIAPI
WaitForTick (
IN EFI_METRONOME_ARCH_PROTOCOL *This,
IN UINT32 TickNumber
)
{
//
// Check the value of TickNumber, so a 32-bit overflow can be avoided
// when TickNumber of converted to nanosecond units
//
if (TickNumber < 10000000) {
//
// If TickNumber is small, then use NanoSecondDelay()
//
NanoSecondDelay (TickNumber * 100);
} else {
//
// If TickNumber is large, then use MicroSecondDelay()
//
MicroSecondDelay (TickNumber / 10);
}
return EFI_SUCCESS;
}
EFI_STATUS
EFIAPI
InstallMetronome (
IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable
)
{
EFI_STATUS Status;
//
// Make sure the Metronome Architectural Protocol is not already installed in the system
//
ASSERT_PROTOCOL_ALREADY_INSTALLED (NULL, &gEfiMetronomeArchProtocolGuid);
//
// Install on a new handle
//
Status = gBS->InstallMultipleProtocolInterfaces (
&mMetronomeHandle,
&gEfiMetronomeArchProtocolGuid, &mMetronome,
NULL
);
ASSERT_EFI_ERROR (Status);
return Status;
}
可见在gEfiCpuArchProtocolGuid 中最终是通过NanoSecondDelay或者MicroSecondDelay 来实现。