Selaa lähdekoodia

[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>
tags/v1.20.1
Michael Brown 8 vuotta sitten
vanhempi
commit
757ab98381
3 muutettua tiedostoa jossa 86 lisäystä ja 352 poistoa
  1. 0
    302
      src/include/ipxe/efi/Protocol/Cpu.h
  2. 18
    0
      src/include/ipxe/efi/efi_timer.h
  3. 68
    50
      src/interface/efi/efi_timer.c

+ 0
- 302
src/include/ipxe/efi/Protocol/Cpu.h Näytä tiedosto

@@ -1,302 +0,0 @@
1
-/** @file
2
-  CPU Architectural Protocol as defined in PI spec Volume 2 DXE
3
-
4
-  This code abstracts the DXE core from processor implementation details.
5
-
6
-  Copyright (c) 2006 - 2011, Intel Corporation. All rights reserved.<BR>
7
-  This program and the accompanying materials
8
-  are licensed and made available under the terms and conditions of the BSD License
9
-  which accompanies this distribution.  The full text of the license may be found at
10
-  http://opensource.org/licenses/bsd-license.php
11
-
12
-  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
13
-  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
14
-
15
-**/
16
-
17
-#ifndef __ARCH_PROTOCOL_CPU_H__
18
-#define __ARCH_PROTOCOL_CPU_H__
19
-
20
-FILE_LICENCE ( BSD3 );
21
-
22
-#include <ipxe/efi/Protocol/DebugSupport.h>
23
-
24
-#define EFI_CPU_ARCH_PROTOCOL_GUID \
25
-  { 0x26baccb1, 0x6f42, 0x11d4, {0xbc, 0xe7, 0x0, 0x80, 0xc7, 0x3c, 0x88, 0x81 } }
26
-
27
-typedef struct _EFI_CPU_ARCH_PROTOCOL   EFI_CPU_ARCH_PROTOCOL;
28
-
29
-///
30
-/// The type of flush operation
31
-///
32
-typedef enum {
33
-  EfiCpuFlushTypeWriteBackInvalidate,
34
-  EfiCpuFlushTypeWriteBack,
35
-  EfiCpuFlushTypeInvalidate,
36
-  EfiCpuMaxFlushType
37
-} EFI_CPU_FLUSH_TYPE;
38
-
39
-///
40
-/// The type of processor INIT.
41
-///
42
-typedef enum {
43
-  EfiCpuInit,
44
-  EfiCpuMaxInitType
45
-} EFI_CPU_INIT_TYPE;
46
-
47
-/**
48
-  EFI_CPU_INTERRUPT_HANDLER that is called when a processor interrupt occurs.
49
-
50
-  @param  InterruptType    Defines the type of interrupt or exception that
51
-                           occurred on the processor.This parameter is processor architecture specific.
52
-  @param  SystemContext    A pointer to the processor context when
53
-                           the interrupt occurred on the processor.
54
-
55
-  @return None
56
-
57
-**/
58
-typedef
59
-VOID
60
-(EFIAPI *EFI_CPU_INTERRUPT_HANDLER)(
61
-  IN CONST  EFI_EXCEPTION_TYPE  InterruptType,
62
-  IN CONST  EFI_SYSTEM_CONTEXT  SystemContext
63
-  );
64
-
65
-/**
66
-  This function flushes the range of addresses from Start to Start+Length
67
-  from the processor's data cache. If Start is not aligned to a cache line
68
-  boundary, then the bytes before Start to the preceding cache line boundary
69
-  are also flushed. If Start+Length is not aligned to a cache line boundary,
70
-  then the bytes past Start+Length to the end of the next cache line boundary
71
-  are also flushed. The FlushType of EfiCpuFlushTypeWriteBackInvalidate must be
72
-  supported. If the data cache is fully coherent with all DMA operations, then
73
-  this function can just return EFI_SUCCESS. If the processor does not support
74
-  flushing a range of the data cache, then the entire data cache can be flushed.
75
-
76
-  @param  This             The EFI_CPU_ARCH_PROTOCOL instance.
77
-  @param  Start            The beginning physical address to flush from the processor's data
78
-                           cache.
79
-  @param  Length           The number of bytes to flush from the processor's data cache. This
80
-                           function may flush more bytes than Length specifies depending upon
81
-                           the granularity of the flush operation that the processor supports.
82
-  @param  FlushType        Specifies the type of flush operation to perform.
83
-
84
-  @retval EFI_SUCCESS           The address range from Start to Start+Length was flushed from
85
-                                the processor's data cache.
86
-  @retval EFI_UNSUPPORTEDT      The processor does not support the cache flush type specified
87
-                                by FlushType.
88
-  @retval EFI_DEVICE_ERROR      The address range from Start to Start+Length could not be flushed
89
-                                from the processor's data cache.
90
-
91
-**/
92
-typedef
93
-EFI_STATUS
94
-(EFIAPI *EFI_CPU_FLUSH_DATA_CACHE)(
95
-  IN EFI_CPU_ARCH_PROTOCOL              *This,
96
-  IN EFI_PHYSICAL_ADDRESS               Start,
97
-  IN UINT64                             Length,
98
-  IN EFI_CPU_FLUSH_TYPE                 FlushType
99
-  );
100
-
101
-
102
-/**
103
-  This function enables interrupt processing by the processor.
104
-
105
-  @param  This             The EFI_CPU_ARCH_PROTOCOL instance.
106
-
107
-  @retval EFI_SUCCESS           Interrupts are enabled on the processor.
108
-  @retval EFI_DEVICE_ERROR      Interrupts could not be enabled on the processor.
109
-
110
-**/
111
-typedef
112
-EFI_STATUS
113
-(EFIAPI *EFI_CPU_ENABLE_INTERRUPT)(
114
-  IN EFI_CPU_ARCH_PROTOCOL              *This
115
-  );
116
-
117
-
118
-/**
119
-  This function disables interrupt processing by the processor.
120
-
121
-  @param  This             The EFI_CPU_ARCH_PROTOCOL instance.
122
-
123
-  @retval EFI_SUCCESS           Interrupts are disabled on the processor.
124
-  @retval EFI_DEVICE_ERROR      Interrupts could not be disabled on the processor.
125
-
126
-**/
127
-typedef
128
-EFI_STATUS
129
-(EFIAPI *EFI_CPU_DISABLE_INTERRUPT)(
130
-  IN EFI_CPU_ARCH_PROTOCOL              *This
131
-  );
132
-
133
-
134
-/**
135
-  This function retrieves the processor's current interrupt state a returns it in
136
-  State. If interrupts are currently enabled, then TRUE is returned. If interrupts
137
-  are currently disabled, then FALSE is returned.
138
-
139
-  @param  This             The EFI_CPU_ARCH_PROTOCOL instance.
140
-  @param  State            A pointer to the processor's current interrupt state. Set to TRUE if
141
-                           interrupts are enabled and FALSE if interrupts are disabled.
142
-
143
-  @retval EFI_SUCCESS           The processor's current interrupt state was returned in State.
144
-  @retval EFI_INVALID_PARAMETER State is NULL.
145
-
146
-**/
147
-typedef
148
-EFI_STATUS
149
-(EFIAPI *EFI_CPU_GET_INTERRUPT_STATE)(
150
-  IN EFI_CPU_ARCH_PROTOCOL              *This,
151
-  OUT BOOLEAN                           *State
152
-  );
153
-
154
-
155
-/**
156
-  This function generates an INIT on the processor. If this function succeeds, then the
157
-  processor will be reset, and control will not be returned to the caller. If InitType is
158
-  not supported by this processor, or the processor cannot programmatically generate an
159
-  INIT without help from external hardware, then EFI_UNSUPPORTED is returned. If an error
160
-  occurs attempting to generate an INIT, then EFI_DEVICE_ERROR is returned.
161
-
162
-  @param  This             The EFI_CPU_ARCH_PROTOCOL instance.
163
-  @param  InitType         The type of processor INIT to perform.
164
-
165
-  @retval EFI_SUCCESS           The processor INIT was performed. This return code should never be seen.
166
-  @retval EFI_UNSUPPORTED       The processor INIT operation specified by InitType is not supported
167
-                                by this processor.
168
-  @retval EFI_DEVICE_ERROR      The processor INIT failed.
169
-
170
-**/
171
-typedef
172
-EFI_STATUS
173
-(EFIAPI *EFI_CPU_INIT)(
174
-  IN EFI_CPU_ARCH_PROTOCOL              *This,
175
-  IN EFI_CPU_INIT_TYPE                  InitType
176
-  );
177
-
178
-
179
-/**
180
-  This function registers and enables the handler specified by InterruptHandler for a processor
181
-  interrupt or exception type specified by InterruptType. If InterruptHandler is NULL, then the
182
-  handler for the processor interrupt or exception type specified by InterruptType is uninstalled.
183
-  The installed handler is called once for each processor interrupt or exception.
184
-
185
-  @param  This             The EFI_CPU_ARCH_PROTOCOL instance.
186
-  @param  InterruptType    A pointer to the processor's current interrupt state. Set to TRUE if interrupts
187
-                           are enabled and FALSE if interrupts are disabled.
188
-  @param  InterruptHandler A pointer to a function of type EFI_CPU_INTERRUPT_HANDLER that is called
189
-                           when a processor interrupt occurs. If this parameter is NULL, then the handler
190
-                           will be uninstalled.
191
-
192
-  @retval EFI_SUCCESS           The handler for the processor interrupt was successfully installed or uninstalled.
193
-  @retval EFI_ALREADY_STARTED   InterruptHandler is not NULL, and a handler for InterruptType was
194
-                                previously installed.
195
-  @retval EFI_INVALID_PARAMETER InterruptHandler is NULL, and a handler for InterruptType was not
196
-                                previously installed.
197
-  @retval EFI_UNSUPPORTED       The interrupt specified by InterruptType is not supported.
198
-
199
-**/
200
-typedef
201
-EFI_STATUS
202
-(EFIAPI *EFI_CPU_REGISTER_INTERRUPT_HANDLER)(
203
-  IN EFI_CPU_ARCH_PROTOCOL              *This,
204
-  IN EFI_EXCEPTION_TYPE                 InterruptType,
205
-  IN EFI_CPU_INTERRUPT_HANDLER          InterruptHandler
206
-  );
207
-
208
-
209
-/**
210
-  This function reads the processor timer specified by TimerIndex and returns it in TimerValue.
211
-
212
-  @param  This             The EFI_CPU_ARCH_PROTOCOL instance.
213
-  @param  TimerIndex       Specifies which processor timer is to be returned in TimerValue. This parameter
214
-                           must be between 0 and NumberOfTimers-1.
215
-  @param  TimerValue       Pointer to the returned timer value.
216
-  @param  TimerPeriod      A pointer to the amount of time that passes in femtoseconds for each increment
217
-                           of TimerValue. If TimerValue does not increment at a predictable rate, then 0 is
218
-                           returned. This parameter is optional and may be NULL.
219
-
220
-  @retval EFI_SUCCESS           The processor timer value specified by TimerIndex was returned in TimerValue.
221
-  @retval EFI_DEVICE_ERROR      An error occurred attempting to read one of the processor's timers.
222
-  @retval EFI_INVALID_PARAMETER TimerValue is NULL or TimerIndex is not valid.
223
-  @retval EFI_UNSUPPORTED       The processor does not have any readable timers.
224
-
225
-**/
226
-typedef
227
-EFI_STATUS
228
-(EFIAPI *EFI_CPU_GET_TIMER_VALUE)(
229
-  IN EFI_CPU_ARCH_PROTOCOL              *This,
230
-  IN UINT32                             TimerIndex,
231
-  OUT UINT64                            *TimerValue,
232
-  OUT UINT64                            *TimerPeriod OPTIONAL
233
-  );
234
-
235
-
236
-/**
237
-  This function modifies the attributes for the memory region specified by BaseAddress and
238
-  Length from their current attributes to the attributes specified by Attributes.
239
-
240
-  @param  This             The EFI_CPU_ARCH_PROTOCOL instance.
241
-  @param  BaseAddress      The physical address that is the start address of a memory region.
242
-  @param  Length           The size in bytes of the memory region.
243
-  @param  Attributes       The bit mask of attributes to set for the memory region.
244
-
245
-  @retval EFI_SUCCESS           The attributes were set for the memory region.
246
-  @retval EFI_ACCESS_DENIED     The attributes for the memory resource range specified by
247
-                                BaseAddress and Length cannot be modified.
248
-  @retval EFI_INVALID_PARAMETER Length is zero.
249
-                                Attributes specified an illegal combination of attributes that
250
-                                cannot be set together.
251
-  @retval EFI_OUT_OF_RESOURCES  There are not enough system resources to modify the attributes of
252
-                                the memory resource range.
253
-  @retval EFI_UNSUPPORTED       The processor does not support one or more bytes of the memory
254
-                                resource range specified by BaseAddress and Length.
255
-                                The bit mask of attributes is not support for the memory resource
256
-                                range specified by BaseAddress and Length.
257
-
258
-**/
259
-typedef
260
-EFI_STATUS
261
-(EFIAPI *EFI_CPU_SET_MEMORY_ATTRIBUTES)(
262
-  IN EFI_CPU_ARCH_PROTOCOL              *This,
263
-  IN  EFI_PHYSICAL_ADDRESS              BaseAddress,
264
-  IN  UINT64                            Length,
265
-  IN  UINT64                            Attributes
266
-  );
267
-
268
-
269
-///
270
-/// The EFI_CPU_ARCH_PROTOCOL is used to abstract processor-specific functions from the DXE
271
-/// Foundation. This includes flushing caches, enabling and disabling interrupts, hooking interrupt
272
-/// vectors and exception vectors, reading internal processor timers, resetting the processor, and
273
-/// determining the processor frequency.
274
-///
275
-struct _EFI_CPU_ARCH_PROTOCOL {
276
-  EFI_CPU_FLUSH_DATA_CACHE            FlushDataCache;
277
-  EFI_CPU_ENABLE_INTERRUPT            EnableInterrupt;
278
-  EFI_CPU_DISABLE_INTERRUPT           DisableInterrupt;
279
-  EFI_CPU_GET_INTERRUPT_STATE         GetInterruptState;
280
-  EFI_CPU_INIT                        Init;
281
-  EFI_CPU_REGISTER_INTERRUPT_HANDLER  RegisterInterruptHandler;
282
-  EFI_CPU_GET_TIMER_VALUE             GetTimerValue;
283
-  EFI_CPU_SET_MEMORY_ATTRIBUTES       SetMemoryAttributes;
284
-  ///
285
-  /// The number of timers that are available in a processor. The value in this
286
-  /// field is a constant that must not be modified after the CPU Architectural
287
-  /// Protocol is installed. All consumers must treat this as a read-only field.
288
-  ///
289
-  UINT32                              NumberOfTimers;
290
-  ///
291
-  /// The size, in bytes, of the alignment required for DMA buffer allocations.
292
-  /// This is typically the size of the largest data cache line in the platform.
293
-  /// The value in this field is a constant that must not be modified after the
294
-  /// CPU Architectural Protocol is installed. All consumers must treat this as
295
-  /// a read-only field.
296
-  ///
297
-  UINT32                              DmaBufferAlignment;
298
-};
299
-
300
-extern EFI_GUID gEfiCpuArchProtocolGuid;
301
-
302
-#endif

+ 18
- 0
src/include/ipxe/efi/efi_timer.h Näytä tiedosto

@@ -15,4 +15,22 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
15 15
 #define TIMER_PREFIX_efi __efi_
16 16
 #endif
17 17
 
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
+
18 36
 #endif /* _IPXE_EFI_TIMER_H */

+ 68
- 50
src/interface/efi/efi_timer.c Näytä tiedosto

@@ -25,12 +25,10 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
25 25
 
26 26
 #include <string.h>
27 27
 #include <errno.h>
28
-#include <limits.h>
29
-#include <assert.h>
30 28
 #include <unistd.h>
31 29
 #include <ipxe/timer.h>
30
+#include <ipxe/init.h>
32 31
 #include <ipxe/efi/efi.h>
33
-#include <ipxe/efi/Protocol/Cpu.h>
34 32
 
35 33
 /** @file
36 34
  *
@@ -38,19 +36,14 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
38 36
  *
39 37
  */
40 38
 
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;
47 41
 
48
-/** Calibration time */
49
-#define EFI_CALIBRATE_DELAY_MS 1
42
+/** Timer tick event */
43
+static EFI_EVENT efi_tick_event;
50 44
 
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
54 47
 
55 48
 /**
56 49
  * Delay for a fixed number of microseconds
@@ -64,8 +57,8 @@ static void efi_udelay ( unsigned long usecs ) {
64 57
 
65 58
 	if ( ( efirc = bs->Stall ( usecs ) ) != 0 ) {
66 59
 		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 ) );
69 62
 		/* Probably screwed */
70 63
 	}
71 64
 }
@@ -76,53 +69,78 @@ static void efi_udelay ( unsigned long usecs ) {
76 69
  * @ret ticks		Current time, in ticks
77 70
  */
78 71
 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;
80 95
 	EFI_STATUS efirc;
81 96
 	int rc;
82 97
 
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 ) {
86 102
 		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;
90 107
 	}
91 108
 
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 );
93 120
 }
94 121
 
95 122
 /**
96
- * Get number of ticks per second
123
+ * Stop timer tick
97 124
  *
98
- * @ret ticks_per_sec	Number of ticks per second
125
+ * @v booting		System is shutting down in order to boot
99 126
  */
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;
122 129
 
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 );
124 136
 }
125 137
 
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
+
126 144
 PROVIDE_TIMER ( efi, udelay, efi_udelay );
127 145
 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 );

Loading…
Peruuta
Tallenna