UefiCpuPkg/BaseRiscV64CpuTimerLib: Add constructor to initialize mTimeBase

Add a constructor function that calls GetPerformanceCounterProperties(NULL, NULL)
to ensure mTimeBase is initialized during DXE phase, before virtual memory is
enabled.

This fixes crashes on RISC-V platforms without fw_cfg device support (e.g.,
QEMU rvsp-ref) where Runtime Services may call GetPerformanceCounterProperties()
before mTimeBase has been initialized. When virtual memory is then enabled, the
uninitialized mTimeBase value causes HOB access to fail with a page fault.

Fixes: https://github.com/tianocore/edk2/issues/11747

Signed-off-by: Chao Liu <chao.liu.riscv@isrc.iscas.ac.cn>
Reported-by: Xiaofan Tan <xiaofan@iscas.ac.cn>
Suggested-by: Laszlo Ersek <lersek@redhat.com>
This commit is contained in:
Chao Liu 2026-01-05 18:00:00 +08:00 committed by mergify[bot]
parent b1707d99ae
commit f2c63dca1b
2 changed files with 64 additions and 50 deletions

View file

@ -16,6 +16,7 @@
VERSION_STRING = 1.0
LIBRARY_CLASS = TimerLib
MODULE_UNI_FILE = BaseRisV64CpuTimerLib.uni
CONSTRUCTOR = BaseRiscV64CpuTimerLibConstructor
[Sources]
CpuTimerLib.c

View file

@ -21,8 +21,6 @@
STATIC UINT64 mTimeBase;
#define GET_TIME_BASE() (mTimeBase ?: GetPerformanceCounterProperties(NULL, NULL))
/**
Stalls the CPU for at least the given number of ticks.
@ -67,7 +65,7 @@ MicroSecondDelay (
DivU64x32 (
MultU64x32 (
MicroSeconds,
GET_TIME_BASE ()
mTimeBase
),
1000000u
)
@ -95,7 +93,7 @@ NanoSecondDelay (
DivU64x32 (
MultU64x32 (
NanoSeconds,
GET_TIME_BASE ()
mTimeBase
),
1000000000u
)
@ -154,12 +152,6 @@ GetPerformanceCounterProperties (
OUT UINT64 *EndValue OPTIONAL
)
{
VOID *Hob;
RISCV_SEC_HANDOFF_DATA *SecData;
CONST EFI_GUID SecHobDataGuid = RISCV_SEC_HANDOFF_HOB_GUID;
UINT64 TimeBase;
CONST VOID *FdtBase;
if (StartValue != NULL) {
*StartValue = 0;
}
@ -168,9 +160,66 @@ GetPerformanceCounterProperties (
*EndValue = 32 - 1;
}
if (mTimeBase != 0) {
return mTimeBase;
}
return mTimeBase;
}
/**
Converts elapsed ticks of performance counter to time in nanoseconds.
This function converts the elapsed ticks of running performance counter to
time value in unit of nanoseconds.
@param Ticks The number of elapsed ticks of running performance counter.
@return The elapsed time in nanoseconds.
**/
UINT64
EFIAPI
GetTimeInNanoSecond (
IN UINT64 Ticks
)
{
UINT64 NanoSeconds;
UINT32 Remainder;
//
// Ticks
// Time = --------- x 1,000,000,000
// Frequency
//
NanoSeconds = MultU64x32 (DivU64x32Remainder (Ticks, mTimeBase, &Remainder), 1000000000u);
//
// Frequency < 0x100000000, so Remainder < 0x100000000, then (Remainder * 1,000,000,000)
// will not overflow 64-bit.
//
NanoSeconds += DivU64x32 (MultU64x32 ((UINT64)Remainder, 1000000000u), mTimeBase);
return NanoSeconds;
}
/**
Constructor function for the Timer Library.
This constructor function is called early during DXE phase to ensure that
GetPerformanceCounterProperties() is invoked and mTimeBase is initialized
before any code that depends on it.
@retval EFI_SUCCESS The constructor always returns success.
**/
EFI_STATUS
EFIAPI
BaseRiscV64CpuTimerLibConstructor (
VOID
)
{
VOID *Hob;
RISCV_SEC_HANDOFF_DATA *SecData;
CONST EFI_GUID SecHobDataGuid = RISCV_SEC_HANDOFF_HOB_GUID;
UINT64 TimeBase;
CONST VOID *FdtBase;
//
// Locate the FDT HOB and validate header
@ -224,41 +273,5 @@ GetPerformanceCounterProperties (
//
mTimeBase = TimeBase;
return TimeBase;
}
/**
Converts elapsed ticks of performance counter to time in nanoseconds.
This function converts the elapsed ticks of running performance counter to
time value in unit of nanoseconds.
@param Ticks The number of elapsed ticks of running performance counter.
@return The elapsed time in nanoseconds.
**/
UINT64
EFIAPI
GetTimeInNanoSecond (
IN UINT64 Ticks
)
{
UINT64 NanoSeconds;
UINT32 Remainder;
//
// Ticks
// Time = --------- x 1,000,000,000
// Frequency
//
NanoSeconds = MultU64x32 (DivU64x32Remainder (Ticks, GET_TIME_BASE (), &Remainder), 1000000000u);
//
// Frequency < 0x100000000, so Remainder < 0x100000000, then (Remainder * 1,000,000,000)
// will not overflow 64-bit.
//
NanoSeconds += DivU64x32 (MultU64x32 ((UINT64)Remainder, 1000000000u), GET_TIME_BASE ());
return NanoSeconds;
return EFI_SUCCESS;
}