summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMichael Brown2016-05-04 14:04:33 +0200
committerMichael Brown2016-05-04 14:38:33 +0200
commit757ab983811ac8d3f65efb65b8309738bd33bea3 (patch)
tree68e07447d247633a7f25cd85f3df1c59f3b8dada
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>
-rw-r--r--src/include/ipxe/efi/Protocol/Cpu.h302
-rw-r--r--src/include/ipxe/efi/efi_timer.h18
-rw-r--r--src/interface/efi/efi_timer.c118
3 files changed, 86 insertions, 352 deletions
diff --git a/src/include/ipxe/efi/Protocol/Cpu.h b/src/include/ipxe/efi/Protocol/Cpu.h
deleted file mode 100644
index 665924e8..00000000
--- a/src/include/ipxe/efi/Protocol/Cpu.h
+++ /dev/null
@@ -1,302 +0,0 @@
-/** @file
- CPU Architectural Protocol as defined in PI spec Volume 2 DXE
-
- This code abstracts the DXE core from processor implementation details.
-
- Copyright (c) 2006 - 2011, Intel Corporation. All rights reserved.<BR>
- This program and the accompanying materials
- are licensed and made available under the terms and conditions of the BSD License
- which accompanies this distribution. The full text of the license may be found at
- http://opensource.org/licenses/bsd-license.php
-
- THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
- WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
-
-**/
-
-#ifndef __ARCH_PROTOCOL_CPU_H__
-#define __ARCH_PROTOCOL_CPU_H__
-
-FILE_LICENCE ( BSD3 );
-
-#include <ipxe/efi/Protocol/DebugSupport.h>
-
-#define EFI_CPU_ARCH_PROTOCOL_GUID \
- { 0x26baccb1, 0x6f42, 0x11d4, {0xbc, 0xe7, 0x0, 0x80, 0xc7, 0x3c, 0x88, 0x81 } }
-
-typedef struct _EFI_CPU_ARCH_PROTOCOL EFI_CPU_ARCH_PROTOCOL;
-
-///
-/// The type of flush operation
-///
-typedef enum {
- EfiCpuFlushTypeWriteBackInvalidate,
- EfiCpuFlushTypeWriteBack,
- EfiCpuFlushTypeInvalidate,
- EfiCpuMaxFlushType
-} EFI_CPU_FLUSH_TYPE;
-
-///
-/// The type of processor INIT.
-///
-typedef enum {
- EfiCpuInit,
- EfiCpuMaxInitType
-} EFI_CPU_INIT_TYPE;
-
-/**
- EFI_CPU_INTERRUPT_HANDLER that is called when a processor interrupt occurs.
-
- @param InterruptType Defines the type of interrupt or exception that
- occurred on the processor.This parameter is processor architecture specific.
- @param SystemContext A pointer to the processor context when
- the interrupt occurred on the processor.
-
- @return None
-
-**/
-typedef
-VOID
-(EFIAPI *EFI_CPU_INTERRUPT_HANDLER)(
- IN CONST EFI_EXCEPTION_TYPE InterruptType,
- IN CONST EFI_SYSTEM_CONTEXT SystemContext
- );
-
-/**
- This function flushes the range of addresses from Start to Start+Length
- from the processor's data cache. If Start is not aligned to a cache line
- boundary, then the bytes before Start to the preceding cache line boundary
- are also flushed. If Start+Length is not aligned to a cache line boundary,
- then the bytes past Start+Length to the end of the next cache line boundary
- are also flushed. The FlushType of EfiCpuFlushTypeWriteBackInvalidate must be
- supported. If the data cache is fully coherent with all DMA operations, then
- this function can just return EFI_SUCCESS. If the processor does not support
- flushing a range of the data cache, then the entire data cache can be flushed.
-
- @param This The EFI_CPU_ARCH_PROTOCOL instance.
- @param Start The beginning physical address to flush from the processor's data
- cache.
- @param Length The number of bytes to flush from the processor's data cache. This
- function may flush more bytes than Length specifies depending upon
- the granularity of the flush operation that the processor supports.
- @param FlushType Specifies the type of flush operation to perform.
-
- @retval EFI_SUCCESS The address range from Start to Start+Length was flushed from
- the processor's data cache.
- @retval EFI_UNSUPPORTEDT The processor does not support the cache flush type specified
- by FlushType.
- @retval EFI_DEVICE_ERROR The address range from Start to Start+Length could not be flushed
- from the processor's data cache.
-
-**/
-typedef
-EFI_STATUS
-(EFIAPI *EFI_CPU_FLUSH_DATA_CACHE)(
- IN EFI_CPU_ARCH_PROTOCOL *This,
- IN EFI_PHYSICAL_ADDRESS Start,
- IN UINT64 Length,
- IN EFI_CPU_FLUSH_TYPE FlushType
- );
-
-
-/**
- This function enables interrupt processing by the processor.
-
- @param This The EFI_CPU_ARCH_PROTOCOL instance.
-
- @retval EFI_SUCCESS Interrupts are enabled on the processor.
- @retval EFI_DEVICE_ERROR Interrupts could not be enabled on the processor.
-
-**/
-typedef
-EFI_STATUS
-(EFIAPI *EFI_CPU_ENABLE_INTERRUPT)(
- IN EFI_CPU_ARCH_PROTOCOL *This
- );
-
-
-/**
- This function disables interrupt processing by the processor.
-
- @param This The EFI_CPU_ARCH_PROTOCOL instance.
-
- @retval EFI_SUCCESS Interrupts are disabled on the processor.
- @retval EFI_DEVICE_ERROR Interrupts could not be disabled on the processor.
-
-**/
-typedef
-EFI_STATUS
-(EFIAPI *EFI_CPU_DISABLE_INTERRUPT)(
- IN EFI_CPU_ARCH_PROTOCOL *This
- );
-
-
-/**
- This function retrieves the processor's current interrupt state a returns it in
- State. If interrupts are currently enabled, then TRUE is returned. If interrupts
- are currently disabled, then FALSE is returned.
-
- @param This The EFI_CPU_ARCH_PROTOCOL instance.
- @param State A pointer to the processor's current interrupt state. Set to TRUE if
- interrupts are enabled and FALSE if interrupts are disabled.
-
- @retval EFI_SUCCESS The processor's current interrupt state was returned in State.
- @retval EFI_INVALID_PARAMETER State is NULL.
-
-**/
-typedef
-EFI_STATUS
-(EFIAPI *EFI_CPU_GET_INTERRUPT_STATE)(
- IN EFI_CPU_ARCH_PROTOCOL *This,
- OUT BOOLEAN *State
- );
-
-
-/**
- This function generates an INIT on the processor. If this function succeeds, then the
- processor will be reset, and control will not be returned to the caller. If InitType is
- not supported by this processor, or the processor cannot programmatically generate an
- INIT without help from external hardware, then EFI_UNSUPPORTED is returned. If an error
- occurs attempting to generate an INIT, then EFI_DEVICE_ERROR is returned.
-
- @param This The EFI_CPU_ARCH_PROTOCOL instance.
- @param InitType The type of processor INIT to perform.
-
- @retval EFI_SUCCESS The processor INIT was performed. This return code should never be seen.
- @retval EFI_UNSUPPORTED The processor INIT operation specified by InitType is not supported
- by this processor.
- @retval EFI_DEVICE_ERROR The processor INIT failed.
-
-**/
-typedef
-EFI_STATUS
-(EFIAPI *EFI_CPU_INIT)(
- IN EFI_CPU_ARCH_PROTOCOL *This,
- IN EFI_CPU_INIT_TYPE InitType
- );
-
-
-/**
- This function registers and enables the handler specified by InterruptHandler for a processor
- interrupt or exception type specified by InterruptType. If InterruptHandler is NULL, then the
- handler for the processor interrupt or exception type specified by InterruptType is uninstalled.
- The installed handler is called once for each processor interrupt or exception.
-
- @param This The EFI_CPU_ARCH_PROTOCOL instance.
- @param InterruptType A pointer to the processor's current interrupt state. Set to TRUE if interrupts
- are enabled and FALSE if interrupts are disabled.
- @param InterruptHandler A pointer to a function of type EFI_CPU_INTERRUPT_HANDLER that is called
- when a processor interrupt occurs. If this parameter is NULL, then the handler
- will be uninstalled.
-
- @retval EFI_SUCCESS The handler for the processor interrupt was successfully installed or uninstalled.
- @retval EFI_ALREADY_STARTED InterruptHandler is not NULL, and a handler for InterruptType was
- previously installed.
- @retval EFI_INVALID_PARAMETER InterruptHandler is NULL, and a handler for InterruptType was not
- previously installed.
- @retval EFI_UNSUPPORTED The interrupt specified by InterruptType is not supported.
-
-**/
-typedef
-EFI_STATUS
-(EFIAPI *EFI_CPU_REGISTER_INTERRUPT_HANDLER)(
- IN EFI_CPU_ARCH_PROTOCOL *This,
- IN EFI_EXCEPTION_TYPE InterruptType,
- IN EFI_CPU_INTERRUPT_HANDLER InterruptHandler
- );
-
-
-/**
- This function reads the processor timer specified by TimerIndex and returns it in TimerValue.
-
- @param This The EFI_CPU_ARCH_PROTOCOL instance.
- @param TimerIndex Specifies which processor timer is to be returned in TimerValue. This parameter
- must be between 0 and NumberOfTimers-1.
- @param TimerValue Pointer to the returned timer value.
- @param TimerPeriod A pointer to the amount of time that passes in femtoseconds for each increment
- of TimerValue. If TimerValue does not increment at a predictable rate, then 0 is
- returned. This parameter is optional and may be NULL.
-
- @retval EFI_SUCCESS The processor timer value specified by TimerIndex was returned in TimerValue.
- @retval EFI_DEVICE_ERROR An error occurred attempting to read one of the processor's timers.
- @retval EFI_INVALID_PARAMETER TimerValue is NULL or TimerIndex is not valid.
- @retval EFI_UNSUPPORTED The processor does not have any readable timers.
-
-**/
-typedef
-EFI_STATUS
-(EFIAPI *EFI_CPU_GET_TIMER_VALUE)(
- IN EFI_CPU_ARCH_PROTOCOL *This,
- IN UINT32 TimerIndex,
- OUT UINT64 *TimerValue,
- OUT UINT64 *TimerPeriod OPTIONAL
- );
-
-
-/**
- This function modifies the attributes for the memory region specified by BaseAddress and
- Length from their current attributes to the attributes specified by Attributes.
-
- @param This The EFI_CPU_ARCH_PROTOCOL instance.
- @param BaseAddress The physical address that is the start address of a memory region.
- @param Length The size in bytes of the memory region.
- @param Attributes The bit mask of attributes to set for the memory region.
-
- @retval EFI_SUCCESS The attributes were set for the memory region.
- @retval EFI_ACCESS_DENIED The attributes for the memory resource range specified by
- BaseAddress and Length cannot be modified.
- @retval EFI_INVALID_PARAMETER Length is zero.
- Attributes specified an illegal combination of attributes that
- cannot be set together.
- @retval EFI_OUT_OF_RESOURCES There are not enough system resources to modify the attributes of
- the memory resource range.
- @retval EFI_UNSUPPORTED The processor does not support one or more bytes of the memory
- resource range specified by BaseAddress and Length.
- The bit mask of attributes is not support for the memory resource
- range specified by BaseAddress and Length.
-
-**/
-typedef
-EFI_STATUS
-(EFIAPI *EFI_CPU_SET_MEMORY_ATTRIBUTES)(
- IN EFI_CPU_ARCH_PROTOCOL *This,
- IN EFI_PHYSICAL_ADDRESS BaseAddress,
- IN UINT64 Length,
- IN UINT64 Attributes
- );
-
-
-///
-/// The EFI_CPU_ARCH_PROTOCOL is used to abstract processor-specific functions from the DXE
-/// Foundation. This includes flushing caches, enabling and disabling interrupts, hooking interrupt
-/// vectors and exception vectors, reading internal processor timers, resetting the processor, and
-/// determining the processor frequency.
-///
-struct _EFI_CPU_ARCH_PROTOCOL {
- EFI_CPU_FLUSH_DATA_CACHE FlushDataCache;
- EFI_CPU_ENABLE_INTERRUPT EnableInterrupt;
- EFI_CPU_DISABLE_INTERRUPT DisableInterrupt;
- EFI_CPU_GET_INTERRUPT_STATE GetInterruptState;
- EFI_CPU_INIT Init;
- EFI_CPU_REGISTER_INTERRUPT_HANDLER RegisterInterruptHandler;
- EFI_CPU_GET_TIMER_VALUE GetTimerValue;
- EFI_CPU_SET_MEMORY_ATTRIBUTES SetMemoryAttributes;
- ///
- /// The number of timers that are available in a processor. The value in this
- /// field is a constant that must not be modified after the CPU Architectural
- /// Protocol is installed. All consumers must treat this as a read-only field.
- ///
- UINT32 NumberOfTimers;
- ///
- /// The size, in bytes, of the alignment required for DMA buffer allocations.
- /// This is typically the size of the largest data cache line in the platform.
- /// The value in this field is a constant that must not be modified after the
- /// CPU Architectural Protocol is installed. All consumers must treat this as
- /// a read-only field.
- ///
- UINT32 DmaBufferAlignment;
-};
-
-extern EFI_GUID gEfiCpuArchProtocolGuid;
-
-#endif
diff --git a/src/include/ipxe/efi/efi_timer.h b/src/include/ipxe/efi/efi_timer.h
index c0376539..c4987598 100644
--- a/src/include/ipxe/efi/efi_timer.h
+++ b/src/include/ipxe/efi/efi_timer.h
@@ -15,4 +15,22 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#define TIMER_PREFIX_efi __efi_
#endif
+/**
+ * Number of ticks per second
+ *
+ * This is a policy decision.
+ */
+#define EFI_TICKS_PER_SEC 20
+
+/**
+ * Get number of ticks per second
+ *
+ * @ret ticks_per_sec Number of ticks per second
+ */
+static inline __attribute__ (( always_inline )) unsigned long
+TIMER_INLINE ( efi, ticks_per_sec ) ( void ) {
+
+ return EFI_TICKS_PER_SEC;
+}
+
#endif /* _IPXE_EFI_TIMER_H */
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 );