iPXE
rdtsc_timer.c
Go to the documentation of this file.
1 /*
2  * Copyright (C) 2008 Michael Brown <mbrown@fensystems.co.uk>.
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License as
6  * published by the Free Software Foundation; either version 2 of the
7  * License, or any later version.
8  *
9  * This program is distributed in the hope that it will be useful, but
10  * WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12  * General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
17  * 02110-1301, USA.
18  *
19  * You can also choose to distribute this program under the terms of
20  * the Unmodified Binary Distribution Licence (as given in the file
21  * COPYING.UBDL), provided that you have satisfied its requirements.
22  */
23 
24 FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
25 
26 /** @file
27  *
28  * RDTSC timer
29  *
30  */
31 
32 #include <string.h>
33 #include <errno.h>
34 #include <ipxe/timer.h>
35 #include <ipxe/cpuid.h>
36 #include <ipxe/pit8254.h>
37 
38 /** Number of microseconds to use for TSC calibration */
39 #define TSC_CALIBRATE_US 1024
40 
41 /** TSC increment per microsecond */
42 static unsigned long tsc_per_us;
43 
44 /** Minimum resolution for scaled TSC timer */
45 #define TSC_SCALED_HZ 32
46 
47 /** TSC scale (expressed as a bit shift)
48  *
49  * We use this to avoid the need for 64-bit divsion on 32-bit systems.
50  */
51 static unsigned int tsc_scale;
52 
53 /** Number of timer ticks per scaled TSC increment */
54 static unsigned long ticks_per_scaled_tsc;
55 
56 /** Colour for debug messages */
57 #define colour &tsc_per_us
58 
59 /**
60  * Get raw TSC value
61  *
62  * @ret tsc Raw TSC value
63  */
64 static inline __always_inline unsigned long rdtsc_raw ( void ) {
65  unsigned long raw;
66 
67  __asm__ __volatile__ ( "rdtsc\n\t" : "=a" ( raw ) : : "edx" );
68  return raw;
69 }
70 
71 /**
72  * Get TSC value, shifted to avoid rollover within a realistic timescale
73  *
74  * @ret tsc Scaled TSC value
75  */
76 static inline __always_inline unsigned long rdtsc_scaled ( void ) {
77  unsigned long scaled;
78 
79  __asm__ __volatile__ ( "rdtsc\n\t"
80  "shrdl %b1, %%edx, %%eax\n\t"
81  : "=a" ( scaled ) : "c" ( tsc_scale ) : "edx" );
82  return scaled;
83 }
84 
85 /**
86  * Get current system time in ticks
87  *
88  * @ret ticks Current time, in ticks
89  */
90 static unsigned long rdtsc_currticks ( void ) {
91  unsigned long scaled;
92 
93  scaled = rdtsc_scaled();
94  return ( scaled * ticks_per_scaled_tsc );
95 }
96 
97 /**
98  * Delay for a fixed number of microseconds
99  *
100  * @v usecs Number of microseconds for which to delay
101  */
102 static void rdtsc_udelay ( unsigned long usecs ) {
103  unsigned long start;
104  unsigned long elapsed;
105  unsigned long threshold;
106 
107  start = rdtsc_raw();
108  threshold = ( usecs * tsc_per_us );
109  do {
110  elapsed = ( rdtsc_raw() - start );
111  } while ( elapsed < threshold );
112 }
113 
114 /**
115  * Probe RDTSC timer
116  *
117  * @ret rc Return status code
118  */
119 static int rdtsc_probe ( void ) {
120  unsigned long before;
121  unsigned long after;
122  unsigned long elapsed;
123  uint32_t apm;
124  uint32_t discard_a;
125  uint32_t discard_b;
127  int rc;
128 
129  /* Check that TSC is invariant */
130  if ( ( rc = cpuid_supported ( CPUID_APM ) ) != 0 ) {
131  DBGC ( colour, "RDTSC cannot determine APM features: %s\n",
132  strerror ( rc ) );
133  return rc;
134  }
135  cpuid ( CPUID_APM, 0, &discard_a, &discard_b, &discard_c, &apm );
136  if ( ! ( apm & CPUID_APM_EDX_TSC_INVARIANT ) ) {
137  DBGC ( colour, "RDTSC has non-invariant TSC (%#08x)\n",
138  apm );
139  return -ENOTTY;
140  }
141 
142  /* Calibrate udelay() timer via 8254 PIT */
143  before = rdtsc_raw();
144  pit8254_udelay ( TSC_CALIBRATE_US );
145  after = rdtsc_raw();
146  elapsed = ( after - before );
147  tsc_per_us = ( elapsed / TSC_CALIBRATE_US );
148  if ( ! tsc_per_us ) {
149  DBGC ( colour, "RDTSC has zero TSC per microsecond\n" );
150  return -EIO;
151  }
152 
153  /* Calibrate currticks() scaling factor */
154  tsc_scale = 31;
155  ticks_per_scaled_tsc = ( ( 1UL << tsc_scale ) /
156  ( tsc_per_us * ( 1000000 / TICKS_PER_SEC ) ) );
158  tsc_scale--;
159  ticks_per_scaled_tsc >>= 1;
160  }
161  DBGC ( colour, "RDTSC has %ld tsc per us, %ld ticks per 2^%d tsc\n",
163  if ( ! ticks_per_scaled_tsc ) {
164  DBGC ( colour, "RDTSC has zero ticks per TSC\n" );
165  return -EIO;
166  }
167 
168  return 0;
169 }
170 
171 /** RDTSC timer */
172 struct timer rdtsc_timer __timer ( TIMER_PREFERRED ) = {
173  .name = "rdtsc",
174  .probe = rdtsc_probe,
175  .currticks = rdtsc_currticks,
176  .udelay = rdtsc_udelay,
177 };
int cpuid_supported(uint32_t function)
Check whether or not CPUID function is supported.
Definition: cpuid.c:75
struct arbelprm_rc_send_wqe rc
Definition: arbel.h:14
struct timer rdtsc_timer __timer(TIMER_PREFERRED)
RDTSC timer.
#define TICKS_PER_SEC
Number of ticks per second.
Definition: timer.h:15
Error codes.
#define DBGC(...)
Definition: compiler.h:505
int32_t before
Initial microcode version.
Definition: ucode.h:16
iPXE timers
#define colour
Colour for debug messages.
Definition: rdtsc_timer.c:57
FILE_LICENCE(GPL2_OR_LATER_OR_UBDL)
A timer.
Definition: timer.h:28
uint32_t start
Starting offset.
Definition: netvsc.h:12
#define CPUID_APM_EDX_TSC_INVARIANT
Invariant TSC.
Definition: cpuid.h:76
static int rdtsc_probe(void)
Probe RDTSC timer.
Definition: rdtsc_timer.c:119
static unsigned int tsc_scale
TSC scale (expressed as a bit shift)
Definition: rdtsc_timer.c:51
#define TIMER_PREFERRED
Preferred timer.
Definition: timer.h:62
static void rdtsc_udelay(unsigned long usecs)
Delay for a fixed number of microseconds.
Definition: rdtsc_timer.c:102
static __always_inline unsigned long rdtsc_scaled(void)
Get TSC value, shifted to avoid rollover within a realistic timescale.
Definition: rdtsc_timer.c:76
static unsigned long ticks_per_scaled_tsc
Number of timer ticks per scaled TSC increment.
Definition: rdtsc_timer.c:54
char * strerror(int errno)
Retrieve string representation of error number.
Definition: strerror.c:78
x86 CPU feature detection
#define __always_inline
Declare a function to be always inline.
Definition: compiler.h:611
8254 Programmable Interval Timer
__asm__ __volatile__("call *%9" :"=a"(result), "=c"(discard_ecx), "=d"(discard_edx) :"d"(0), "a"(code), "b"(0), "c"(in_phys), "D"(0), "S"(out_phys), "m"(hypercall))
#define TSC_CALIBRATE_US
Number of microseconds to use for TSC calibration.
Definition: rdtsc_timer.c:39
unsigned int uint32_t
Definition: stdint.h:12
__asm__(".section \".rodata\", \"a\", " PROGBITS "\n\t" "\nprivate_key_data:\n\t" ".size private_key_data, ( . - private_key_data )\n\t" ".equ private_key_len, ( . - private_key_data )\n\t" ".previous\n\t")
static __always_inline unsigned long rdtsc_raw(void)
Get raw TSC value.
Definition: rdtsc_timer.c:64
#define ENOTTY
Inappropriate I/O control operation.
Definition: errno.h:594
const char * name
Name.
Definition: timer.h:30
static unsigned long tsc_per_us
TSC increment per microsecond.
Definition: rdtsc_timer.c:42
int32_t after
Final microcode version.
Definition: ucode.h:18
#define CPUID_APM
Get APM information.
Definition: cpuid.h:73
#define EIO
Input/output error.
Definition: errno.h:433
__be32 raw[7]
Definition: CIB_PRM.h:28
long discard_c
Definition: bigint.h:32
#define TSC_SCALED_HZ
Minimum resolution for scaled TSC timer.
Definition: rdtsc_timer.c:45
String functions.
static unsigned long rdtsc_currticks(void)
Get current system time in ticks.
Definition: rdtsc_timer.c:90