Pyrogenesis  13997
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
hpet.cpp
Go to the documentation of this file.
1 /* Copyright (c) 2010 Wildfire Games
2  *
3  * Permission is hereby granted, free of charge, to any person obtaining
4  * a copy of this software and associated documentation files (the
5  * "Software"), to deal in the Software without restriction, including
6  * without limitation the rights to use, copy, modify, merge, publish,
7  * distribute, sublicense, and/or sell copies of the Software, and to
8  * permit persons to whom the Software is furnished to do so, subject to
9  * the following conditions:
10  *
11  * The above copyright notice and this permission notice shall be included
12  * in all copies or substantial portions of the Software.
13  *
14  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
17  * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
18  * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
19  * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
20  * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21  */
22 
23 /*
24  * Timer implementation using High Precision Event Timer
25  */
26 
27 #include "precompiled.h"
29 
30 // for atomic 64-bit read/write:
31 #define HAVE_X64_MOVD ARCH_AMD64 && (ICC_VERSION || MSC_VERSION >= 1500)
32 #if HAVE_X64_MOVD
33 # include <intrin.h>
34 #else
35 # include <emmintrin.h>
36 #endif
37 
39 
40 #include "lib/sysdep/os/win/win.h"
42 #include "lib/sysdep/acpi.h"
43 #include "lib/bits.h"
44 
45 
46 class CounterHPET : public ICounter
47 {
48 public:
50  : m_hpetRegisters(0)
51  {
52  }
53 
54  virtual const wchar_t* Name() const
55  {
56  return L"HPET";
57  }
58 
60  {
62 
64 
65  // start the counter (if not already running)
67  // note: to avoid interfering with any other users of the timer
68  // (e.g. Vista QPC), we don't reset the counter value to 0.
69 
70  return INFO::OK;
71  }
72 
73  void Shutdown()
74  {
75  if(m_hpetRegisters)
76  {
78  m_hpetRegisters = 0;
79  }
80  }
81 
82  bool IsSafe() const
83  {
84  // the HPET having been created to address other timers' problems,
85  // it has no issues of its own.
86  return true;
87  }
88 
89  u64 Counter() const
90  {
91  // notes:
92  // - Read64 is atomic and avoids race conditions.
93  // - 32-bit counters (m_counterBits == 32) still allow
94  // reading the whole register (the upper bits are zero).
95  return Read64(COUNTER_VALUE);
96  }
97 
98  size_t CounterBits() const
99  {
100  return m_counterBits;
101  }
102 
103  double NominalFrequency() const
104  {
105  return m_frequency;
106  }
107 
108  double Resolution() const
109  {
110  return 1.0 / m_frequency;
111  }
112 
113 private:
114 #pragma pack(push, 1)
115 
117  {
124  };
125 
126 #pragma pack(pop)
127 
129  {
130  CAPS_AND_ID = 0x00,
131  CONFIG = 0x10,
133  MAX_OFFSET = 0x3FF
134  };
135 
136  static Status MapRegisters(volatile void*& registers)
137  {
139  return ERR::FAIL; // NOWARN (happens on Win2k)
140  RETURN_STATUS_IF_ERR(mahaf_Init()); // (fails without Administrator privileges)
141 
142  const HpetDescriptionTable* hpet = (const HpetDescriptionTable*)acpi_GetTable("HPET");
143  if(!hpet)
144  return ERR::NOT_SUPPORTED; // NOWARN (HPET not reported by BIOS)
145 
147  return ERR::NOT_SUPPORTED; // NOWARN (happens on some BIOSes)
148  // hpet->baseAddress.accessSize is reserved
149  const uintptr_t address = uintptr_t(hpet->baseAddress.address);
150  ENSURE(address % 8 == 0); // "registers are generally aligned on 64-bit boundaries"
151 
152  registers = mahaf_MapPhysicalMemory(address, MAX_OFFSET+1);
153  if(!registers)
155 
156  return INFO::OK;
157  }
158 
159  // note: this is atomic even on 32-bit CPUs (Pentium MMX and
160  // above have a 64-bit data bus and MOVQ instruction)
161  u64 Read64(size_t offset) const
162  {
163  ENSURE(offset <= MAX_OFFSET);
164  ENSURE(offset % 8 == 0);
165  const uintptr_t address = uintptr_t(m_hpetRegisters)+offset;
166  const __m128i value128 = _mm_loadl_epi64((__m128i*)address);
167 #if HAVE_X64_MOVD
168  return _mm_cvtsi128_si64x(value128);
169 #else
170  __declspec(align(16)) u32 values[4];
171  _mm_store_si128((__m128i*)values, value128);
172  return u64_from_u32(values[1], values[0]);
173 #endif
174  }
175 
176  void Write64(size_t offset, u64 value) const
177  {
178  ENSURE(offset <= MAX_OFFSET);
179  ENSURE(offset % 8 == 0);
180  ENSURE(offset != CAPS_AND_ID); // can't write to read-only registers
181  const uintptr_t address = uintptr_t(m_hpetRegisters)+offset;
182 #if HAVE_X64_MOVD
183  const __m128i value128 = _mm_cvtsi64x_si128(value);
184 #else
185  const __m128i value128 = _mm_set_epi32(0, 0, int(value >> 32), int(value & 0xFFFFFFFF));
186 #endif
187  _mm_storel_epi64((__m128i*)address, value128);
188  }
189 
190  Status VerifyCapabilities(double& frequency, u32& counterBits) const
191  {
192  // AMD document 43366 indicates the clock generator that drives the
193  // HPET is "spread-capable". Wikipedia's frequency hopping article
194  // explains that this reduces electromagnetic interference.
195  // The AMD document recommends BIOS writers add SMM hooks for
196  // reporting the resulting slightly different frequency.
197  // This apparently requires calibration triggered when the HPET is
198  // accessed, during which the config register is -1. We'll wait
199  // about 1 ms (MMIO is expected to take at least 1 us) and
200  // then ensure the HPET timer period is within reasonable bounds.
201  u64 caps_and_id = Read64(CAPS_AND_ID);
202  for(size_t reps = 0; reps < 1000; reps++)
203  {
204  if(caps_and_id != ~u64(0)) // register seems valid
205  break;
206  caps_and_id = Read64(CAPS_AND_ID);
207  }
208 
209  const u8 revision = (u8)bits(caps_and_id, 0, 7);
210  ENSURE(revision != 0); // "the value must NOT be 00h"
211  counterBits = (caps_and_id & Bit<u64>(13))? 64 : 32;
212  const u16 vendorID = (u16)bits(caps_and_id, 16, 31);
213  const u32 period_fs = (u32)bits(caps_and_id, 32, 63);
214  ENSURE(period_fs != 0); // "a value of 0 in this field is not permitted"
215  frequency = 1e15 / period_fs;
216  debug_printf(L"HPET: rev=%X vendor=%X bits=%d period=%08X freq=%g\n", revision, vendorID, counterBits, period_fs, frequency);
217 
218  if(period_fs > 0x05F5E100) // 100 ns (spec guarantees >= 10 MHz)
219  return ERR::CORRUPTED; // avoid using HPET (e.g. if calibration was still in progress)
220 
221  return INFO::OK;
222  }
223 
224  volatile void* m_hpetRegisters;
225  double m_frequency;
227 };
228 
229 ICounter* CreateCounterHPET(void* address, size_t size)
230 {
231  ENSURE(sizeof(CounterHPET) <= size);
232  return new(address) CounterHPET();
233 }
size_t CounterBits() const
Definition: hpet.cpp:98
#define u8
Definition: types.h:39
double m_frequency
Definition: hpet.cpp:225
void Shutdown()
Definition: hpet.cpp:73
Status mahaf_Init()
Definition: mahaf.cpp:390
static __declspec(align(64)) TimerState timerStates[2]
const Status OK
Definition: status.h:386
u8 addressSpaceId
Definition: acpi.h:58
Status Activate()
Definition: hpet.cpp:59
const Status CORRUPTED
Definition: status.h:413
const AcpiTable * acpi_GetTable(const char *signature)
Definition: acpi.cpp:362
ICounter * CreateCounterHPET(void *address, size_t size)
Definition: hpet.cpp:229
double Resolution() const
actual resolution [s].
Definition: hpet.cpp:108
u64 Read64(size_t offset) const
Definition: hpet.cpp:161
const Status NOT_SUPPORTED
Definition: status.h:429
#define ENSURE(expr)
ensure the expression &lt;expr&gt; evaluates to non-zero.
Definition: debug.h:282
static size_t counterBits
Definition: whrt.cpp:100
void mahaf_UnmapPhysicalMemory(volatile void *virtualAddress)
Definition: mahaf.cpp:142
void Write64(size_t offset, u64 value) const
Definition: hpet.cpp:176
u64 Counter() const
Definition: hpet.cpp:89
Definition: acpi.h:33
i64 Status
Error handling system.
Definition: status.h:171
T bits(T num, size_t lo_idx, size_t hi_idx)
extract the value of bits hi_idx:lo_idx within num
Definition: bits.h:97
volatile void * mahaf_MapPhysicalMemory(uintptr_t physicalAddress, size_t numBytes)
Definition: mahaf.cpp:122
CounterHPET()
Definition: hpet.cpp:49
static Status MapRegisters(volatile void *&registers)
Definition: hpet.cpp:136
#define u16
Definition: types.h:40
#define u64
Definition: types.h:42
virtual const wchar_t * Name() const
Definition: hpet.cpp:54
#define u32
Definition: types.h:41
RegisterOffsets
Definition: hpet.cpp:128
bool mahaf_IsPhysicalMappingDangerous()
Definition: mahaf.cpp:111
volatile void * m_hpetRegisters
Definition: hpet.cpp:224
u64 u64_from_u32(u32 hi, u32 lo)
return lower 16-bits
Definition: lib.cpp:65
#define WARN_RETURN(status)
Definition: status.h:255
const Status FAIL
Definition: status.h:406
Status VerifyCapabilities(double &frequency, u32 &counterBits) const
Definition: hpet.cpp:190
u32 m_counterBits
Definition: hpet.cpp:226
bool IsSafe() const
Definition: hpet.cpp:82
double NominalFrequency() const
initial measurement of the tick rate.
Definition: hpet.cpp:103
const Status NO_MEM
Definition: status.h:430
AcpiGenericAddress baseAddress
Definition: hpet.cpp:120
void debug_printf(const wchar_t *fmt,...)
write a formatted string to the debug channel, subject to filtering (see below).
Definition: debug.cpp:142
#define RETURN_STATUS_IF_ERR(expression)
Definition: status.h:276