[time] Allow timer to be selected at runtime

Allow the active timer (providing udelay() and currticks()) to be
selected at runtime based on probing during the INIT_EARLY stage of
initialisation.

TICKS_PER_SEC is now a fixed compile-time constant for all builds, and
is independent of the underlying clock tick rate.  We choose the value
1024 to allow multiplications and divisions on seconds to be converted
to bit shifts.

TICKS_PER_MS is defined as 1, allowing multiplications and divisions
on milliseconds to be omitted entirely.  The 2% inaccuracy in this
definition is negligible when using the standard BIOS timer (running
at around 18.2Hz).

TIMER_RDTSC now checks for a constant TSC before claiming to be a
usable timer.  (This timer can be tested in KVM via the command-line
option "-cpu host,+invtsc".)

Signed-off-by: Michael Brown <mcb30@ipxe.org>
This commit is contained in:
Michael Brown
2017-01-25 20:59:15 +00:00
parent d37e025b81
commit 302f1eeb80
24 changed files with 369 additions and 318 deletions

View File

@@ -7,6 +7,8 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#define BDA_EBDA 0x000e
#define BDA_EQUIPMENT_WORD 0x0010
#define BDA_FBMS 0x0013
#define BDA_TICKS 0x006c
#define BDA_MIDNIGHT 0x0070
#define BDA_REBOOT 0x0072
#define BDA_REBOOT_WARM 0x1234
#define BDA_NUM_DRIVES 0x0075

View File

@@ -26,6 +26,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#define ERRFILE_rtc_entropy ( ERRFILE_ARCH | ERRFILE_CORE | 0x000f0000 )
#define ERRFILE_acpipwr ( ERRFILE_ARCH | ERRFILE_CORE | 0x00100000 )
#define ERRFILE_cpuid ( ERRFILE_ARCH | ERRFILE_CORE | 0x00110000 )
#define ERRFILE_rdtsc_timer ( ERRFILE_ARCH | ERRFILE_CORE | 0x00120000 )
#define ERRFILE_bootsector ( ERRFILE_ARCH | ERRFILE_IMAGE | 0x00000000 )
#define ERRFILE_bzimage ( ERRFILE_ARCH | ERRFILE_IMAGE | 0x00010000 )

View File

@@ -1,15 +0,0 @@
#ifndef _BITS_TIMER_H
#define _BITS_TIMER_H
/** @file
*
* x86-specific timer API implementations
*
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#include <ipxe/bios_timer.h>
#include <ipxe/rdtsc_timer.h>
#endif /* _BITS_TIMER_H */

View File

@@ -1,44 +0,0 @@
#ifndef _IPXE_BIOS_TIMER_H
#define _IPXE_BIOS_TIMER_H
/** @file
*
* BIOS timer
*
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#ifdef TIMER_PCBIOS
#define TIMER_PREFIX_pcbios
#else
#define TIMER_PREFIX_pcbios __pcbios_
#endif
#include <ipxe/pit8254.h>
/**
* Delay for a fixed number of microseconds
*
* @v usecs Number of microseconds for which to delay
*/
static inline __always_inline void
TIMER_INLINE ( pcbios, udelay ) ( unsigned long usecs ) {
/* BIOS timer is not high-resolution enough for udelay(), so
* we use the 8254 Programmable Interval Timer.
*/
pit8254_udelay ( usecs );
}
/**
* Get number of ticks per second
*
* @ret ticks_per_sec Number of ticks per second
*/
static inline __always_inline unsigned long
TIMER_INLINE ( pcbios, ticks_per_sec ) ( void ) {
/* BIOS timer ticks over at 18.2 ticks per second */
return 18;
}
#endif /* _IPXE_BIOS_TIMER_H */

View File

@@ -57,6 +57,12 @@ struct x86_features {
/** Get CPU model */
#define CPUID_MODEL 0x80000002UL
/** Get APM information */
#define CPUID_APM 0x80000007UL
/** Invariant TSC */
#define CPUID_APM_EDX_TSC_INVARIANT 0x00000100UL
/**
* Issue CPUID instruction
*

View File

@@ -1,39 +0,0 @@
#ifndef _IPXE_RDTSC_TIMER_H
#define _IPXE_RDTSC_TIMER_H
/** @file
*
* RDTSC timer
*
*/
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#ifdef TIMER_RDTSC
#define TIMER_PREFIX_rdtsc
#else
#define TIMER_PREFIX_rdtsc __rdtsc_
#endif
/**
* RDTSC values can easily overflow an unsigned long. We discard the
* low-order bits in order to obtain sensibly-scaled values.
*/
#define TSC_SHIFT 8
/**
* Get current system time in ticks
*
* @ret ticks Current time, in ticks
*/
static inline __always_inline unsigned long
TIMER_INLINE ( rdtsc, currticks ) ( void ) {
unsigned long ticks;
__asm__ __volatile__ ( "rdtsc\n\t"
"shrdl %1, %%edx, %%eax\n\t"
: "=a" ( ticks ) : "i" ( TSC_SHIFT ) : "edx" );
return ticks;
}
#endif /* _IPXE_RDTSC_TIMER_H */