summaryrefslogtreecommitdiffstats
path: root/src/interface/efi
diff options
context:
space:
mode:
authorMichael Brown2016-05-04 14:04:33 +0200
committerMichael Brown2016-05-04 14:38:33 +0200
commit757ab983811ac8d3f65efb65b8309738bd33bea3 (patch)
tree68e07447d247633a7f25cd85f3df1c59f3b8dada /src/interface/efi
parent[tcpip] Do not fall back to using unoptimised TCP/IP checksumming (diff)
downloadipxe-757ab983811ac8d3f65efb65b8309738bd33bea3.tar.gz
ipxe-757ab983811ac8d3f65efb65b8309738bd33bea3.tar.xz
ipxe-757ab983811ac8d3f65efb65b8309738bd33bea3.zip
[efi] Use a timer event to generate the currticks() timer
We currently use the EFI_CPU_ARCH_PROTOCOL's GetTimerValue() method to generate the currticks() timer, calibrated against a 1ms delay from the boot services Stall() method. This does not work on ARM platforms, where GetTimerValue() is an empty stub which just returns EFI_UNSUPPORTED. Fix by instead creating a periodic timer event, and using this event to increment a current tick counter. Signed-off-by: Michael Brown <mcb30@ipxe.org>
Diffstat (limited to 'src/interface/efi')
-rw-r--r--src/interface/efi/efi_timer.c118
1 files changed, 68 insertions, 50 deletions
diff --git a/src/interface/efi/efi_timer.c b/src/interface/efi/efi_timer.c
index 81620c92..a574e204 100644
--- a/src/interface/efi/efi_timer.c
+++ b/src/interface/efi/efi_timer.c
@@ -25,12 +25,10 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#include <string.h>
#include <errno.h>
-#include <limits.h>
-#include <assert.h>
#include <unistd.h>
#include <ipxe/timer.h>
+#include <ipxe/init.h>
#include <ipxe/efi/efi.h>
-#include <ipxe/efi/Protocol/Cpu.h>
/** @file
*
@@ -38,19 +36,14 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
*
*/
-/** Scale factor to apply to CPU timer 0
- *
- * The timer is scaled down in order to ensure that reasonable values
- * for "number of ticks" don't exceed the size of an unsigned long.
- */
-#define EFI_TIMER0_SHIFT 12
+/** Current tick count */
+static unsigned long efi_jiffies;
-/** Calibration time */
-#define EFI_CALIBRATE_DELAY_MS 1
+/** Timer tick event */
+static EFI_EVENT efi_tick_event;
-/** CPU protocol */
-static EFI_CPU_ARCH_PROTOCOL *cpu_arch;
-EFI_REQUIRE_PROTOCOL ( EFI_CPU_ARCH_PROTOCOL, &cpu_arch );
+/** Colour for debug messages */
+#define colour &efi_jiffies
/**
* Delay for a fixed number of microseconds
@@ -64,8 +57,8 @@ static void efi_udelay ( unsigned long usecs ) {
if ( ( efirc = bs->Stall ( usecs ) ) != 0 ) {
rc = -EEFI ( efirc );
- DBG ( "EFI could not delay for %ldus: %s\n",
- usecs, strerror ( rc ) );
+ DBGC ( colour, "EFI could not delay for %ldus: %s\n",
+ usecs, strerror ( rc ) );
/* Probably screwed */
}
}
@@ -76,53 +69,78 @@ static void efi_udelay ( unsigned long usecs ) {
* @ret ticks Current time, in ticks
*/
static unsigned long efi_currticks ( void ) {
- UINT64 time;
+
+ return efi_jiffies;
+}
+
+/**
+ * Timer tick
+ *
+ * @v event Timer tick event
+ * @v context Event context
+ */
+static EFIAPI void efi_tick ( EFI_EVENT event __unused,
+ void *context __unused ) {
+
+ /* Increment tick count */
+ efi_jiffies++;
+}
+
+/**
+ * Start timer tick
+ *
+ */
+static void efi_tick_startup ( void ) {
+ EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
EFI_STATUS efirc;
int rc;
- /* Read CPU timer 0 (TSC) */
- if ( ( efirc = cpu_arch->GetTimerValue ( cpu_arch, 0, &time,
- NULL ) ) != 0 ) {
+ /* Create timer tick event */
+ if ( ( efirc = bs->CreateEvent ( ( EVT_TIMER | EVT_NOTIFY_SIGNAL ),
+ TPL_CALLBACK, efi_tick, NULL,
+ &efi_tick_event ) ) != 0 ) {
rc = -EEFI ( efirc );
- DBG ( "EFI could not read CPU timer: %s\n", strerror ( rc ) );
- /* Probably screwed */
- return -1UL;
+ DBGC ( colour, "EFI could not create timer tick: %s\n",
+ strerror ( rc ) );
+ /* Nothing we can do about it */
+ return;
}
- return ( time >> EFI_TIMER0_SHIFT );
+ /* Start timer tick */
+ if ( ( efirc = bs->SetTimer ( efi_tick_event, TimerPeriodic,
+ ( 10000000 / EFI_TICKS_PER_SEC ) ) ) !=0){
+ rc = -EEFI ( efirc );
+ DBGC ( colour, "EFI could not start timer tick: %s\n",
+ strerror ( rc ) );
+ /* Nothing we can do about it */
+ return;
+ }
+ DBGC ( colour, "EFI timer started at %d ticks per second\n",
+ EFI_TICKS_PER_SEC );
}
/**
- * Get number of ticks per second
+ * Stop timer tick
*
- * @ret ticks_per_sec Number of ticks per second
+ * @v booting System is shutting down in order to boot
*/
-static unsigned long efi_ticks_per_sec ( void ) {
- static unsigned long ticks_per_sec = 0;
-
- /* Calibrate timer, if necessary. EFI does nominally provide
- * the timer speed via the (optional) TimerPeriod parameter to
- * the GetTimerValue() call, but it gets the speed slightly
- * wrong. By up to three orders of magnitude. Not helpful.
- */
- if ( ! ticks_per_sec ) {
- unsigned long start;
- unsigned long elapsed;
-
- DBG ( "Calibrating EFI timer with a %d ms delay\n",
- EFI_CALIBRATE_DELAY_MS );
- start = currticks();
- mdelay ( EFI_CALIBRATE_DELAY_MS );
- elapsed = ( currticks() - start );
- ticks_per_sec = ( elapsed * ( 1000 / EFI_CALIBRATE_DELAY_MS ));
- DBG ( "EFI CPU timer calibrated at %ld ticks in %d ms (%ld "
- "ticks/sec)\n", elapsed, EFI_CALIBRATE_DELAY_MS,
- ticks_per_sec );
- }
+static void efi_tick_shutdown ( int booting __unused ) {
+ EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
- return ticks_per_sec;
+ /* Stop timer tick */
+ bs->SetTimer ( efi_tick_event, TimerCancel, 0 );
+ DBGC ( colour, "EFI timer stopped\n" );
+
+ /* Destroy timer tick event */
+ bs->CloseEvent ( efi_tick_event );
}
+/** Timer tick startup function */
+struct startup_fn efi_tick_startup_fn __startup_fn ( STARTUP_EARLY ) = {
+ .startup = efi_tick_startup,
+ .shutdown = efi_tick_shutdown,
+};
+
PROVIDE_TIMER ( efi, udelay, efi_udelay );
PROVIDE_TIMER ( efi, currticks, efi_currticks );
-PROVIDE_TIMER ( efi, ticks_per_sec, efi_ticks_per_sec );
+PROVIDE_TIMER_INLINE ( efi, ticks_per_sec );