From ab879e4cb3f5a168d9902e307e44de01300ac9a7 Mon Sep 17 00:00:00 2001 From: Laszlo Ersek Date: Mon, 2 Feb 2015 12:01:58 +0000 Subject: [PATCH] ArmVirtualizationQemu: ask the hardware for the timer frequency Roughly, there are two ways to "measure ticks" in UEFI: - the SetTimer() boot service, which sets up a one-shot or periodic event callback, and takes the interval length in units of 100ns, - the Stall() boot service, which stalls the caller (but does not yield the CPU) for the interval specified. The interval is taken as a number of microseconds. If the platform in question also follows the PI (Platform Init) specification, then it is recommended to implement the above UEFI services on top of the following DXE Architectural Protocols (described in PI Volume 2): - Timer Architectural Protocol: "Used to set up a periodic timer interrupt using a platform specific timer, and a processor-specific interrupt vector. This protocol enables the use of the SetTimer() Boot Service. [...]" - Metronome Architectural Protocol: "Used to wait for ticks from a known time source in a platform. This protocol may be used to implement a simple version of the Stall() Boot Service. [...]" Edk2 in general, and ArmVirtualizationQemu in particular, follow the above pattern. SetTimer() works correctly. The underlying Timer Architectural Protocol is provided by "ArmPkg/Drivers/TimerDxe", and that driver calls the internal function ArmGenericTimerGetTimerFreq() to retrieve the timer frequency. Ultimately it boils down to reading the CNTFRQ_EL0 register. The correct behavior of SetTimer() can be observed for example: - in the grub-efi countdown ("grub-core/kern/arm/efi/init.c"), - in the Intel BDS front page countdown ("IntelFrameworkModulePkg/Universal/BdsDxe/FrontPage.c"). However, Stall() doesn't work correctly. The underlying Metronome Architectural Protocol is provided by "EmbeddedPkg/MetronomeDxe", which further delegates the job to the TimerLib library class. That in turn is resolved to the "ArmPkg/Library/ArmArchTimerLib" instance, which (finally!) takes the timer frequency from "PcdArmArchTimerFreqInHz". In ArmVirtualizationQemu we currently specify 100MHz for this PCD. Alas, that's incorect for: - both QEMU/TCG (which emulates 62.5MHz, see GTIMER_SCALE in "target-arm/internals.h"), - and KVM (where the host's virtualized timer can tick at 50 MHz, for example). Set the PCD to 0, asking ArmArchTimerLib to interrogate CNTFRQ_EL0 as well. The change can be tested with eg. the following callers of Stall(): - the UEFI Shell's countdown -- before it runs "startup.nsh" -- relies on Stall(), - the UEFI shell command "stall" also uses Stall(). (Time it with a stopwatch.) Contributed-under: TianoCore Contribution Agreement 1.0 Signed-off-by: Laszlo Ersek Reviewed-by: Olivier Martin git-svn-id: https://svn.code.sf.net/p/edk2/code/trunk/edk2@16692 6f19259b-4bc3-4df7-8a09-765794883524 --- ArmPlatformPkg/ArmVirtualizationPkg/ArmVirtualizationQemu.dsc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ArmPlatformPkg/ArmVirtualizationPkg/ArmVirtualizationQemu.dsc b/ArmPlatformPkg/ArmVirtualizationPkg/ArmVirtualizationQemu.dsc index dff4e25070..a65136a5cb 100644 --- a/ArmPlatformPkg/ArmVirtualizationPkg/ArmVirtualizationQemu.dsc +++ b/ArmPlatformPkg/ArmVirtualizationPkg/ArmVirtualizationQemu.dsc @@ -131,9 +131,9 @@ gArmPlatformTokenSpaceGuid.PcdPlatformBootTimeOut|3 # - # ARM Virtual Architectural Timer + # ARM Virtual Architectural Timer -- fetch frequency from QEMU (TCG) or KVM # - gArmTokenSpaceGuid.PcdArmArchTimerFreqInHz|100000000 + gArmTokenSpaceGuid.PcdArmArchTimerFreqInHz|0 # # NV Storage PCDs. Use base of 0x04000000 for NOR1 -- 2.39.2