diff options
author | Michael Brown | 2016-05-04 14:04:33 +0200 |
---|---|---|
committer | Michael Brown | 2016-05-04 14:38:33 +0200 |
commit | 757ab983811ac8d3f65efb65b8309738bd33bea3 (patch) | |
tree | 68e07447d247633a7f25cd85f3df1c59f3b8dada /src/interface/efi | |
parent | [tcpip] Do not fall back to using unoptimised TCP/IP checksumming (diff) | |
download | ipxe-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.c | 118 |
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 ); |