Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

496 lines
10 KiB

  1. /*++
  2. Copyright (c) 1998 Microsoft Corporation
  3. Module Name:
  4. cpu.c
  5. Abstract:
  6. Read CPU specifics performance counters.
  7. Author:
  8. Scott Field (sfield) 24-Sep-98
  9. --*/
  10. #ifndef KMODE_RNG
  11. #include <nt.h>
  12. #include <ntrtl.h>
  13. #include <nturtl.h>
  14. #include <windows.h>
  15. #else
  16. #include <ntifs.h>
  17. #include <windef.h>
  18. #endif // KMODE_RNG
  19. #include "cpu.h"
  20. unsigned int
  21. GatherCPUSpecificCountersPrivileged(
  22. IN unsigned char *pbCounterState,
  23. IN OUT unsigned long *pcbCounterState
  24. );
  25. #define X86_CAPS_RDTSC 0x01
  26. #define X86_CAPS_RDMSR 0x02
  27. #define X86_CAPS_RDPMC 0x04
  28. VOID
  29. X86_GetCapabilities(
  30. IN OUT BYTE *pbCapabilities
  31. );
  32. VOID
  33. X86_ReadRDTSC(
  34. IN PLARGE_INTEGER prdtsc // RDTSC
  35. );
  36. VOID
  37. X86_ReadRDMSR(
  38. IN PLARGE_INTEGER pc0, // counter0
  39. IN PLARGE_INTEGER pc1 // counter1
  40. );
  41. VOID
  42. X86_ReadRDPMC(
  43. IN PLARGE_INTEGER pc0, // counter0
  44. IN PLARGE_INTEGER pc1 // counter1
  45. );
  46. #ifdef KMODE_RNG
  47. #ifdef ALLOC_PRAGMA
  48. #pragma alloc_text(PAGE, GatherCPUSpecificCounters)
  49. #pragma alloc_text(PAGE, GatherCPUSpecificCountersPrivileged)
  50. #ifdef _X86_
  51. #pragma alloc_text(PAGE, X86_ReadRDTSC)
  52. #pragma alloc_text(PAGE, X86_ReadRDMSR)
  53. #pragma alloc_text(PAGE, X86_ReadRDPMC)
  54. #pragma alloc_text(PAGE, X86_GetCapabilities)
  55. #endif // _X86_
  56. #endif // ALLOC_PRAGMA
  57. #endif // KMODE_RNG
  58. unsigned int
  59. GatherCPUSpecificCounters(
  60. IN unsigned char *pbCounterState,
  61. IN OUT unsigned long *pcbCounterState
  62. )
  63. {
  64. #ifndef KMODE_RNG
  65. //
  66. // NT5 doesn't set CR4.PCE by default, so don't bother trying for usermode.
  67. //
  68. return FALSE;
  69. #else
  70. PAGED_CODE();
  71. //
  72. // kernel mode version of library: just call the privileged routine directly.
  73. // user mode version of library: try privileged routine first, if it fails, use
  74. // device driver provided interface.
  75. //
  76. if( pbCounterState == NULL || pcbCounterState == NULL )
  77. return FALSE;
  78. return GatherCPUSpecificCountersPrivileged( pbCounterState, pcbCounterState );
  79. #endif
  80. }
  81. unsigned int
  82. GatherCPUSpecificCountersPrivileged(
  83. IN unsigned char *pbCounterState,
  84. IN OUT unsigned long *pcbCounterState
  85. )
  86. /*++
  87. we are at ring0 in kernel mode, so we can issue the privileges CPU
  88. instructions directly. Note that this routine also serves as the
  89. core code which is executed by the ksecdd.sys device driver for user
  90. mode clients.
  91. This call can also be made directly in usermode by certain CPUs,
  92. or when certain CPUs are configured to allow such calls from ring3.
  93. --*/
  94. {
  95. #ifdef _X86_
  96. PLARGE_INTEGER prdtsc;
  97. PLARGE_INTEGER pc0;
  98. PLARGE_INTEGER pc1;
  99. DWORD cbCounters;
  100. BYTE ProcessorCaps;
  101. #endif // _X86_
  102. #ifdef KMODE_RNG
  103. PAGED_CODE();
  104. #endif // KMODE_RNG
  105. #ifdef _X86_
  106. cbCounters = 3 * sizeof( LARGE_INTEGER ) ;
  107. if( *pcbCounterState < cbCounters ) {
  108. *pcbCounterState = cbCounters;
  109. return FALSE;
  110. }
  111. cbCounters = 0;
  112. prdtsc = (PLARGE_INTEGER)pbCounterState;
  113. pc0 = prdtsc + 1;
  114. pc1 = pc0 + 1;
  115. //
  116. // make the initial determination about what the countertype is in this
  117. // system.
  118. // in theory, this could be cached, but things get a little complicated
  119. // in an SMP machine -- we'd have to track caps across all processors
  120. // to be correct. Since we don't really care about perf, just check the
  121. // caps every time.
  122. //
  123. __try {
  124. X86_GetCapabilities( &ProcessorCaps );
  125. } __except( EXCEPTION_EXECUTE_HANDLER ) {
  126. ProcessorCaps = 0;
  127. ; // swallow exception
  128. }
  129. //
  130. // wrap the query in a try/except. This is just paranoia. Since we aren't
  131. // particularly interested in the values of the perf counters for the normal
  132. // (eg: perfmon) reasons, introducing the extra overhead of try/except is
  133. // of no relevance to us.
  134. // note that in the case of the p6, we could be calling this from usermode,
  135. // and CR4.PCE could be toggled which could cause subsequent AV(s).
  136. // In theory, the KMODE build could avoid the try/except, but there is a
  137. // remote possiblity the code above which makes the initial countertype
  138. // determination may not be supported on every installed processor in a
  139. // SMP machine. The cost of try/except is well worth avoiding the possibility
  140. // of a access violation / bluescreen in usermode vs. kernel mode respectively.
  141. //
  142. if( ProcessorCaps & X86_CAPS_RDTSC ) {
  143. __try {
  144. X86_ReadRDTSC( prdtsc );
  145. cbCounters += sizeof( LARGE_INTEGER );
  146. } __except( EXCEPTION_EXECUTE_HANDLER ) {
  147. ; // swallow exception.
  148. }
  149. }
  150. if( ProcessorCaps & X86_CAPS_RDPMC ) {
  151. __try {
  152. X86_ReadRDPMC( pc0, pc1 );
  153. cbCounters += (2*sizeof( LARGE_INTEGER ));
  154. } __except( EXCEPTION_EXECUTE_HANDLER ) {
  155. ; // swallow exception.
  156. }
  157. }
  158. #ifdef KMODE_RNG
  159. else if ( ProcessorCaps & X86_CAPS_RDMSR ) {
  160. __try {
  161. X86_ReadRDMSR( pc0, pc1 );
  162. cbCounters += (2*sizeof( LARGE_INTEGER ));
  163. } __except( EXCEPTION_EXECUTE_HANDLER ) {
  164. ; // swallow exception.
  165. }
  166. }
  167. #endif // KMODE_RNG
  168. *pcbCounterState = cbCounters;
  169. return TRUE;
  170. #else // _X86_
  171. //
  172. // no non-x86 counter handling code at this time.
  173. //
  174. return FALSE;
  175. #endif
  176. }
  177. #ifdef _X86_
  178. VOID
  179. X86_ReadRDTSC(
  180. IN PLARGE_INTEGER prdtsc // RDTSC
  181. )
  182. {
  183. DWORD prdtscLow ;
  184. DWORD prdtscHigh ;
  185. #ifdef KMODE_RNG
  186. PAGED_CODE();
  187. #endif // KMODE_RNG
  188. __asm {
  189. push eax
  190. push ecx
  191. push edx
  192. // RDTSC
  193. _emit 0fh
  194. _emit 31h
  195. mov prdtscLow, eax
  196. mov prdtscHigh, edx
  197. pop edx
  198. pop ecx
  199. pop eax
  200. }
  201. prdtsc->LowPart = prdtscLow;
  202. prdtsc->HighPart = prdtscHigh;
  203. }
  204. VOID
  205. X86_ReadRDMSR(
  206. IN PLARGE_INTEGER pc0, // counter0
  207. IN PLARGE_INTEGER pc1 // counter1
  208. )
  209. {
  210. DWORD pc0Low ;
  211. DWORD pc0High ;
  212. DWORD pc1Low ;
  213. DWORD pc1High ;
  214. #ifdef KMODE_RNG
  215. PAGED_CODE();
  216. #endif // KMODE_RNG
  217. __asm {
  218. push eax
  219. push ecx
  220. push edx
  221. // RDMSR counter0
  222. mov ecx, 12h
  223. _emit 0fh
  224. _emit 32h
  225. mov pc0Low, eax
  226. mov pc0High, edx
  227. // RDMSR counter1
  228. mov ecx, 13h
  229. _emit 0fh
  230. _emit 32h
  231. mov pc1Low, eax
  232. mov pc1High, edx
  233. pop edx
  234. pop ecx
  235. pop eax
  236. }
  237. pc0->LowPart = pc0Low;
  238. pc0->HighPart = pc0High;
  239. pc1->LowPart = pc1Low;
  240. pc1->HighPart = pc1High;
  241. }
  242. VOID
  243. X86_ReadRDPMC(
  244. IN PLARGE_INTEGER pc0, // counter0
  245. IN PLARGE_INTEGER pc1 // counter1
  246. )
  247. {
  248. DWORD pc0Low ;
  249. DWORD pc0High ;
  250. DWORD pc1Low ;
  251. DWORD pc1High ;
  252. #ifdef KMODE_RNG
  253. PAGED_CODE();
  254. #endif // KMODE_RNG
  255. __asm {
  256. push eax
  257. push ecx
  258. push edx
  259. // RDPMC executes from ring3 if CR4.PCE is set, otherwise, runs from ring0 only.
  260. // RDPMC counter0
  261. xor ecx, ecx
  262. _emit 0fh
  263. _emit 33h
  264. mov pc0Low, eax
  265. mov pc0High, edx
  266. // RDPMC counter1
  267. mov ecx, 1
  268. _emit 0fh
  269. _emit 33h
  270. mov pc1Low, eax
  271. mov pc1High, edx
  272. pop edx
  273. pop ecx
  274. pop eax
  275. }
  276. pc0->LowPart = pc0Low;
  277. pc0->HighPart = pc0High;
  278. pc1->LowPart = pc1Low;
  279. pc1->HighPart = pc1High;
  280. }
  281. #if _MSC_FULL_VER >= 13008827 && defined(_M_IX86)
  282. #pragma warning(push)
  283. #pragma warning(disable:4731) // EBP modified with inline asm
  284. #endif
  285. VOID
  286. X86_GetCapabilities(
  287. IN OUT BYTE *pbCapabilities
  288. )
  289. {
  290. DWORD dwLevels;
  291. DWORD dwStdFeatures;
  292. DWORD dwVersionInfo;
  293. DWORD Family;
  294. DWORD Model;
  295. #ifdef KMODE_RNG
  296. PAGED_CODE();
  297. #endif // KMODE_RNG
  298. *pbCapabilities = 0;
  299. __asm {
  300. push eax
  301. push ecx
  302. push ebx
  303. push edx
  304. push edi
  305. push esi
  306. push ebp
  307. xor eax, eax
  308. _emit 0x0f
  309. _emit 0xa2
  310. mov dwLevels, eax
  311. pop ebp
  312. pop esi
  313. pop edi
  314. pop edx
  315. pop ebx
  316. pop ecx
  317. pop eax
  318. }
  319. if( dwLevels == 0 )
  320. return;
  321. //
  322. // try CPUID at level1 to get standard features.
  323. //
  324. __asm {
  325. push eax
  326. push ecx
  327. push ebx
  328. push edx
  329. push edi
  330. push esi
  331. push ebp
  332. mov eax, 1
  333. _emit 0x0f
  334. _emit 0xa2
  335. mov dwVersionInfo, eax
  336. mov dwStdFeatures, edx
  337. pop ebp
  338. pop esi
  339. pop edi
  340. pop edx
  341. pop ebx
  342. pop ecx
  343. pop eax
  344. }
  345. //
  346. // determine if RDTSC supported.
  347. //
  348. if( dwStdFeatures & 0x10 ) {
  349. *pbCapabilities |= X86_CAPS_RDTSC;
  350. }
  351. Model = (dwVersionInfo >> 4) & 0xf;
  352. Family = (dwVersionInfo >> 8) & 0xf;
  353. // AMD K6-2 model 8 proved buggy and left interrupts disabled during RDMSR
  354. #if 0
  355. //
  356. // determine if RDMSR supported.
  357. //
  358. if( dwStdFeatures & 0x20 && (Model == 1 || Model == 2) ) {
  359. *pbCapabilities |= X86_CAPS_RDMSR;
  360. }
  361. //
  362. // extract family, > pentium (family5) supports RDPMC
  363. //
  364. if( Family > 5 ) {
  365. *pbCapabilities |= X86_CAPS_RDPMC;
  366. }
  367. #endif
  368. }
  369. #if _MSC_FULL_VER >= 13008827
  370. #pragma warning(pop)
  371. #endif
  372. #endif // _X86_