Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.

rdtsc_timer.c 4.5KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177
  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. FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
  24. /** @file
  25. *
  26. * RDTSC timer
  27. *
  28. */
  29. #include <string.h>
  30. #include <errno.h>
  31. #include <ipxe/timer.h>
  32. #include <ipxe/cpuid.h>
  33. #include <ipxe/pit8254.h>
  34. /** Number of microseconds to use for TSC calibration */
  35. #define TSC_CALIBRATE_US 1024
  36. /** TSC increment per microsecond */
  37. static unsigned long tsc_per_us;
  38. /** Minimum resolution for scaled TSC timer */
  39. #define TSC_SCALED_HZ 32
  40. /** TSC scale (expressed as a bit shift)
  41. *
  42. * We use this to avoid the need for 64-bit divsion on 32-bit systems.
  43. */
  44. static unsigned int tsc_scale;
  45. /** Number of timer ticks per scaled TSC increment */
  46. static unsigned long ticks_per_scaled_tsc;
  47. /** Colour for debug messages */
  48. #define colour &tsc_per_us
  49. /**
  50. * Get raw TSC value
  51. *
  52. * @ret tsc Raw TSC value
  53. */
  54. static inline __always_inline unsigned long rdtsc_raw ( void ) {
  55. unsigned long raw;
  56. __asm__ __volatile__ ( "rdtsc\n\t" : "=a" ( raw ) : : "edx" );
  57. return raw;
  58. }
  59. /**
  60. * Get TSC value, shifted to avoid rollover within a realistic timescale
  61. *
  62. * @ret tsc Scaled TSC value
  63. */
  64. static inline __always_inline unsigned long rdtsc_scaled ( void ) {
  65. unsigned long scaled;
  66. __asm__ __volatile__ ( "rdtsc\n\t"
  67. "shrdl %b1, %%edx, %%eax\n\t"
  68. : "=a" ( scaled ) : "c" ( tsc_scale ) : "edx" );
  69. return scaled;
  70. }
  71. /**
  72. * Get current system time in ticks
  73. *
  74. * @ret ticks Current time, in ticks
  75. */
  76. static unsigned long rdtsc_currticks ( void ) {
  77. unsigned long scaled;
  78. scaled = rdtsc_scaled();
  79. return ( scaled * ticks_per_scaled_tsc );
  80. }
  81. /**
  82. * Delay for a fixed number of microseconds
  83. *
  84. * @v usecs Number of microseconds for which to delay
  85. */
  86. static void rdtsc_udelay ( unsigned long usecs ) {
  87. unsigned long start;
  88. unsigned long elapsed;
  89. unsigned long threshold;
  90. start = rdtsc_raw();
  91. threshold = ( usecs * tsc_per_us );
  92. do {
  93. elapsed = ( rdtsc_raw() - start );
  94. } while ( elapsed < threshold );
  95. }
  96. /**
  97. * Probe RDTSC timer
  98. *
  99. * @ret rc Return status code
  100. */
  101. static int rdtsc_probe ( void ) {
  102. unsigned long before;
  103. unsigned long after;
  104. unsigned long elapsed;
  105. uint32_t apm;
  106. uint32_t discard_a;
  107. uint32_t discard_b;
  108. uint32_t discard_c;
  109. int rc;
  110. /* Check that TSC is invariant */
  111. if ( ( rc = cpuid_supported ( CPUID_APM ) ) != 0 ) {
  112. DBGC ( colour, "RDTSC cannot determine APM features: %s\n",
  113. strerror ( rc ) );
  114. return rc;
  115. }
  116. cpuid ( CPUID_APM, 0, &discard_a, &discard_b, &discard_c, &apm );
  117. if ( ! ( apm & CPUID_APM_EDX_TSC_INVARIANT ) ) {
  118. DBGC ( colour, "RDTSC has non-invariant TSC (%#08x)\n",
  119. apm );
  120. return -ENOTTY;
  121. }
  122. /* Calibrate udelay() timer via 8254 PIT */
  123. before = rdtsc_raw();
  124. pit8254_udelay ( TSC_CALIBRATE_US );
  125. after = rdtsc_raw();
  126. elapsed = ( after - before );
  127. tsc_per_us = ( elapsed / TSC_CALIBRATE_US );
  128. if ( ! tsc_per_us ) {
  129. DBGC ( colour, "RDTSC has zero TSC per microsecond\n" );
  130. return -EIO;
  131. }
  132. /* Calibrate currticks() scaling factor */
  133. tsc_scale = 31;
  134. ticks_per_scaled_tsc = ( ( 1UL << tsc_scale ) /
  135. ( tsc_per_us * ( 1000000 / TICKS_PER_SEC ) ) );
  136. while ( ticks_per_scaled_tsc > ( TICKS_PER_SEC / TSC_SCALED_HZ ) ) {
  137. tsc_scale--;
  138. ticks_per_scaled_tsc >>= 1;
  139. }
  140. DBGC ( colour, "RDTSC has %ld tsc per us, %ld ticks per 2^%d tsc\n",
  141. tsc_per_us, ticks_per_scaled_tsc, tsc_scale );
  142. if ( ! ticks_per_scaled_tsc ) {
  143. DBGC ( colour, "RDTSC has zero ticks per TSC\n" );
  144. return -EIO;
  145. }
  146. return 0;
  147. }
  148. /** RDTSC timer */
  149. struct timer rdtsc_timer __timer ( TIMER_PREFERRED ) = {
  150. .name = "rdtsc",
  151. .probe = rdtsc_probe,
  152. .currticks = rdtsc_currticks,
  153. .udelay = rdtsc_udelay,
  154. };