Skip to content

Commit 757ab98

Browse files
committedMay 4, 2016
[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>
1 parent 1e06643 commit 757ab98

File tree

3 files changed

+86
-352
lines changed

3 files changed

+86
-352
lines changed
 

‎src/include/ipxe/efi/Protocol/Cpu.h

Lines changed: 0 additions & 302 deletions
This file was deleted.

‎src/include/ipxe/efi/efi_timer.h

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,4 +15,22 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
1515
#define TIMER_PREFIX_efi __efi_
1616
#endif
1717

18+
/**
19+
* Number of ticks per second
20+
*
21+
* This is a policy decision.
22+
*/
23+
#define EFI_TICKS_PER_SEC 20
24+
25+
/**
26+
* Get number of ticks per second
27+
*
28+
* @ret ticks_per_sec Number of ticks per second
29+
*/
30+
static inline __attribute__ (( always_inline )) unsigned long
31+
TIMER_INLINE ( efi, ticks_per_sec ) ( void ) {
32+
33+
return EFI_TICKS_PER_SEC;
34+
}
35+
1836
#endif /* _IPXE_EFI_TIMER_H */

‎src/interface/efi/efi_timer.c

Lines changed: 68 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -25,32 +25,25 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
2525

2626
#include <string.h>
2727
#include <errno.h>
28-
#include <limits.h>
29-
#include <assert.h>
3028
#include <unistd.h>
3129
#include <ipxe/timer.h>
30+
#include <ipxe/init.h>
3231
#include <ipxe/efi/efi.h>
33-
#include <ipxe/efi/Protocol/Cpu.h>
3432

3533
/** @file
3634
*
3735
* iPXE timer API for EFI
3836
*
3937
*/
4038

41-
/** Scale factor to apply to CPU timer 0
42-
*
43-
* The timer is scaled down in order to ensure that reasonable values
44-
* for "number of ticks" don't exceed the size of an unsigned long.
45-
*/
46-
#define EFI_TIMER0_SHIFT 12
39+
/** Current tick count */
40+
static unsigned long efi_jiffies;
4741

48-
/** Calibration time */
49-
#define EFI_CALIBRATE_DELAY_MS 1
42+
/** Timer tick event */
43+
static EFI_EVENT efi_tick_event;
5044

51-
/** CPU protocol */
52-
static EFI_CPU_ARCH_PROTOCOL *cpu_arch;
53-
EFI_REQUIRE_PROTOCOL ( EFI_CPU_ARCH_PROTOCOL, &cpu_arch );
45+
/** Colour for debug messages */
46+
#define colour &efi_jiffies
5447

5548
/**
5649
* Delay for a fixed number of microseconds
@@ -64,8 +57,8 @@ static void efi_udelay ( unsigned long usecs ) {
6457

6558
if ( ( efirc = bs->Stall ( usecs ) ) != 0 ) {
6659
rc = -EEFI ( efirc );
67-
DBG ( "EFI could not delay for %ldus: %s\n",
68-
usecs, strerror ( rc ) );
60+
DBGC ( colour, "EFI could not delay for %ldus: %s\n",
61+
usecs, strerror ( rc ) );
6962
/* Probably screwed */
7063
}
7164
}
@@ -76,53 +69,78 @@ static void efi_udelay ( unsigned long usecs ) {
7669
* @ret ticks Current time, in ticks
7770
*/
7871
static unsigned long efi_currticks ( void ) {
79-
UINT64 time;
72+
73+
return efi_jiffies;
74+
}
75+
76+
/**
77+
* Timer tick
78+
*
79+
* @v event Timer tick event
80+
* @v context Event context
81+
*/
82+
static EFIAPI void efi_tick ( EFI_EVENT event __unused,
83+
void *context __unused ) {
84+
85+
/* Increment tick count */
86+
efi_jiffies++;
87+
}
88+
89+
/**
90+
* Start timer tick
91+
*
92+
*/
93+
static void efi_tick_startup ( void ) {
94+
EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
8095
EFI_STATUS efirc;
8196
int rc;
8297

83-
/* Read CPU timer 0 (TSC) */
84-
if ( ( efirc = cpu_arch->GetTimerValue ( cpu_arch, 0, &time,
85-
NULL ) ) != 0 ) {
98+
/* Create timer tick event */
99+
if ( ( efirc = bs->CreateEvent ( ( EVT_TIMER | EVT_NOTIFY_SIGNAL ),
100+
TPL_CALLBACK, efi_tick, NULL,
101+
&efi_tick_event ) ) != 0 ) {
86102
rc = -EEFI ( efirc );
87-
DBG ( "EFI could not read CPU timer: %s\n", strerror ( rc ) );
88-
/* Probably screwed */
89-
return -1UL;
103+
DBGC ( colour, "EFI could not create timer tick: %s\n",
104+
strerror ( rc ) );
105+
/* Nothing we can do about it */
106+
return;
90107
}
91108

92-
return ( time >> EFI_TIMER0_SHIFT );
109+
/* Start timer tick */
110+
if ( ( efirc = bs->SetTimer ( efi_tick_event, TimerPeriodic,
111+
( 10000000 / EFI_TICKS_PER_SEC ) ) ) !=0){
112+
rc = -EEFI ( efirc );
113+
DBGC ( colour, "EFI could not start timer tick: %s\n",
114+
strerror ( rc ) );
115+
/* Nothing we can do about it */
116+
return;
117+
}
118+
DBGC ( colour, "EFI timer started at %d ticks per second\n",
119+
EFI_TICKS_PER_SEC );
93120
}
94121

95122
/**
96-
* Get number of ticks per second
123+
* Stop timer tick
97124
*
98-
* @ret ticks_per_sec Number of ticks per second
125+
* @v booting System is shutting down in order to boot
99126
*/
100-
static unsigned long efi_ticks_per_sec ( void ) {
101-
static unsigned long ticks_per_sec = 0;
102-
103-
/* Calibrate timer, if necessary. EFI does nominally provide
104-
* the timer speed via the (optional) TimerPeriod parameter to
105-
* the GetTimerValue() call, but it gets the speed slightly
106-
* wrong. By up to three orders of magnitude. Not helpful.
107-
*/
108-
if ( ! ticks_per_sec ) {
109-
unsigned long start;
110-
unsigned long elapsed;
111-
112-
DBG ( "Calibrating EFI timer with a %d ms delay\n",
113-
EFI_CALIBRATE_DELAY_MS );
114-
start = currticks();
115-
mdelay ( EFI_CALIBRATE_DELAY_MS );
116-
elapsed = ( currticks() - start );
117-
ticks_per_sec = ( elapsed * ( 1000 / EFI_CALIBRATE_DELAY_MS ));
118-
DBG ( "EFI CPU timer calibrated at %ld ticks in %d ms (%ld "
119-
"ticks/sec)\n", elapsed, EFI_CALIBRATE_DELAY_MS,
120-
ticks_per_sec );
121-
}
127+
static void efi_tick_shutdown ( int booting __unused ) {
128+
EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
122129

123-
return ticks_per_sec;
130+
/* Stop timer tick */
131+
bs->SetTimer ( efi_tick_event, TimerCancel, 0 );
132+
DBGC ( colour, "EFI timer stopped\n" );
133+
134+
/* Destroy timer tick event */
135+
bs->CloseEvent ( efi_tick_event );
124136
}
125137

138+
/** Timer tick startup function */
139+
struct startup_fn efi_tick_startup_fn __startup_fn ( STARTUP_EARLY ) = {
140+
.startup = efi_tick_startup,
141+
.shutdown = efi_tick_shutdown,
142+
};
143+
126144
PROVIDE_TIMER ( efi, udelay, efi_udelay );
127145
PROVIDE_TIMER ( efi, currticks, efi_currticks );
128-
PROVIDE_TIMER ( efi, ticks_per_sec, efi_ticks_per_sec );
146+
PROVIDE_TIMER_INLINE ( efi, ticks_per_sec );

0 commit comments

Comments
 (0)
Please sign in to comment.