]> git.ipfire.org Git - thirdparty/ipxe.git/commitdiff
[efi] Use a timer event to generate the currticks() timer
authorMichael Brown <mcb30@ipxe.org>
Wed, 4 May 2016 12:04:33 +0000 (13:04 +0100)
committerMichael Brown <mcb30@ipxe.org>
Wed, 4 May 2016 12:38:33 +0000 (13:38 +0100)
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>
src/include/ipxe/efi/Protocol/Cpu.h [deleted file]
src/include/ipxe/efi/efi_timer.h
src/interface/efi/efi_timer.c

diff --git a/src/include/ipxe/efi/Protocol/Cpu.h b/src/include/ipxe/efi/Protocol/Cpu.h
deleted file mode 100644 (file)
index 665924e..0000000
+++ /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
index c037653932b537ef7275d15c3391982520163154..c4987598839e653038c40961dafa51507337fe0b 100644 (file)
@@ -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 */
index 81620c92ce44327dd4a1f0fdc4334484a73e602b..a574e2043df81b124302fcd6d057f475e43d7450 100644 (file)
@@ -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 );