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.

689 lines
16 KiB

  1. /*++
  2. Copyright (c) 2002 Microsoft Corporation
  3. Module Name:
  4. Amd64.c
  5. Abstract:
  6. This module implements profiling functions for the Amd64 platform.
  7. Author:
  8. Steve Deng (sdeng) 18-Jun-2002
  9. Environment:
  10. Kernel mode only.
  11. --*/
  12. #include "halcmn.h"
  13. #include "mpprofil.h"
  14. #include "amd64.h"
  15. //
  16. // Local prototypes
  17. //
  18. VOID
  19. Amd64InitializeProfiling(
  20. VOID
  21. );
  22. NTSTATUS
  23. Amd64EnableMonitoring(
  24. KPROFILE_SOURCE ProfileSource
  25. );
  26. VOID
  27. Amd64DisableMonitoring(
  28. KPROFILE_SOURCE ProfileSource
  29. );
  30. NTSTATUS
  31. Amd64SetInterval(
  32. IN KPROFILE_SOURCE ProfileSource,
  33. IN OUT ULONG_PTR *Interval
  34. );
  35. NTSTATUS
  36. Amd64QueryInformation(
  37. IN HAL_QUERY_INFORMATION_CLASS InformationType,
  38. IN ULONG BufferSize,
  39. IN OUT PVOID Buffer,
  40. OUT PULONG ReturnedLength
  41. );
  42. VOID
  43. Amd64CheckOverflowStatus(
  44. POVERFLOW_STATUS pOverflowStatus
  45. );
  46. NTSTATUS
  47. HalpGetProfileDescriptor(
  48. KPROFILE_SOURCE ProfileSource,
  49. PAMD64_PROFILE_SOURCE_DESCRIPTOR *ProfileSourceDescriptor
  50. );
  51. NTSTATUS
  52. HalpAllocateCounter(
  53. KPROFILE_SOURCE ProfileSource,
  54. OUT PULONG Counter
  55. );
  56. VOID
  57. HalpFreeCounter(
  58. ULONG Counter
  59. );
  60. #pragma alloc_text(PAGE, Amd64QueryInformation)
  61. #pragma alloc_text(INIT, Amd64InitializeProfiling)
  62. PROFILE_INTERFACE Amd64PriofileInterface = {
  63. Amd64InitializeProfiling,
  64. Amd64EnableMonitoring,
  65. Amd64DisableMonitoring,
  66. Amd64SetInterval,
  67. Amd64QueryInformation,
  68. Amd64CheckOverflowStatus
  69. };
  70. //
  71. // The status of counter registers
  72. //
  73. typedef struct _COUNTER_STATUS {
  74. BOOLEAN Idle;
  75. KPROFILE_SOURCE ProfileSource;
  76. } COUNTER_STATUS;
  77. COUNTER_STATUS
  78. CounterStatus[MAXIMUM_PROCESSORS][AMD64_NUMBER_COUNTERS];
  79. //
  80. // The profile sources of overflowed counters
  81. //
  82. KPROFILE_SOURCE
  83. OverflowedProfileList[MAXIMUM_PROCESSORS][AMD64_NUMBER_COUNTERS];
  84. VOID
  85. Amd64InitializeProfiling(
  86. VOID
  87. )
  88. /*++
  89. Routine Description:
  90. This function does one time initialization of the performance monitoring
  91. registers and related data structures.
  92. Arguments:
  93. None.
  94. Return Value:
  95. None.
  96. --*/
  97. {
  98. LONG i;
  99. //
  100. // Initialize all PerfEvtSel registers to zero. This will effectively
  101. // disable all counters.
  102. //
  103. for (i = 0; i < AMD64_NUMBER_COUNTERS; i++) {
  104. WRMSR(MSR_PERF_EVT_SEL0 + i, 0);
  105. HalpFreeCounter(i);
  106. }
  107. }
  108. NTSTATUS
  109. Amd64EnableMonitoring(
  110. KPROFILE_SOURCE ProfileSource
  111. )
  112. /*++
  113. Routine Description:
  114. This function enables the monitoring of the hardware events specified
  115. by ProfileSource and set up MSRs to generate performance monitor
  116. interrupt (PMI) when counters overflow.
  117. Arguments:
  118. ProfileSource - Supplies the Profile Source
  119. Return Value:
  120. STATUS_SUCCESS - If the monitoring of the event is successfully enabled.
  121. STATUS_NOT_SUPPORTED - If the specified profile source is not supported.
  122. STATUS_DEVICE_BUSY - If no free counters available.
  123. --*/
  124. {
  125. PAMD64_PROFILE_SOURCE_DESCRIPTOR ProfileSourceDescriptor;
  126. NTSTATUS Status;
  127. ULONG i;
  128. //
  129. // If the specified ProfileSource is not supported, return immediately
  130. //
  131. Status = HalpGetProfileDescriptor(ProfileSource,
  132. &ProfileSourceDescriptor);
  133. if (!NT_SUCCESS(Status)) {
  134. return Status;
  135. }
  136. //
  137. // Get an idle counter if available. Otherwise return immediately.
  138. //
  139. Status = HalpAllocateCounter(ProfileSource, &i);
  140. if (!NT_SUCCESS(Status)) {
  141. return Status;
  142. }
  143. //
  144. // Set counter register to its initial value
  145. //
  146. WRMSR (MSR_PERF_CTR0 + i, 0 - ProfileSourceDescriptor->Interval);
  147. //
  148. // Enable counting and overflow interrupt
  149. //
  150. WRMSR (MSR_PERF_EVT_SEL0 + i,
  151. ProfileSourceDescriptor->PerfEvtSelDef |
  152. PERF_EVT_SEL_INTERRUPT |
  153. PERF_EVT_SEL_ENABLE);
  154. return STATUS_SUCCESS;
  155. }
  156. VOID
  157. Amd64DisableMonitoring(
  158. KPROFILE_SOURCE ProfileSource
  159. )
  160. /*++
  161. Routine Description:
  162. This function stops the monitoring of the hardware event specified
  163. by ProfileSource, and disables the interrupt associated with the event.
  164. Arguments:
  165. ProfileSource - Supplies the Profile Source
  166. Return Value:
  167. None.
  168. --*/
  169. {
  170. NTSTATUS Status;
  171. ULONG ProcessorNumber, i;
  172. PAMD64_PROFILE_SOURCE_DESCRIPTOR ProfileSourceDescriptor;
  173. //
  174. // If the specified ProfileSource is not supported, return immediately.
  175. //
  176. Status = HalpGetProfileDescriptor(ProfileSource, &ProfileSourceDescriptor);
  177. if (!NT_SUCCESS(Status)) {
  178. return;
  179. }
  180. ProcessorNumber = KeGetCurrentProcessorNumber();
  181. for (i = 0; i < AMD64_NUMBER_COUNTERS; i++) {
  182. //
  183. // Find out the counter assigned to the given profile source and
  184. // disable it
  185. //
  186. if (!(CounterStatus[ProcessorNumber][i].Idle) &&
  187. (ProfileSource == CounterStatus[ProcessorNumber][i].ProfileSource)){
  188. //
  189. // Disable counting and overflow interrupt
  190. //
  191. WRMSR( MSR_PERF_EVT_SEL0 + i,
  192. ProfileSourceDescriptor->PerfEvtSelDef &
  193. ~(PERF_EVT_SEL_INTERRUPT | PERF_EVT_SEL_ENABLE));
  194. //
  195. // Free up the counter
  196. //
  197. HalpFreeCounter (i);
  198. break;
  199. }
  200. }
  201. return;
  202. }
  203. NTSTATUS
  204. Amd64SetInterval(
  205. IN KPROFILE_SOURCE ProfileSource,
  206. IN OUT ULONG_PTR *Interval
  207. )
  208. /*++
  209. Routine Description:
  210. This function sets the interrupt interval of given profile source
  211. for Amd64 platform.
  212. It is assumed that all processors in the system use same interval
  213. value for same ProfileSource.
  214. Arguments:
  215. ProfileSource - Supplies the profile source.
  216. Interval - Supplies the interval value and returns the actual interval.
  217. Return Value:
  218. STATUS_SUCCESS - If the profile interval is successfully updated.
  219. STATUS_NOT_SUPPORTED - If the specified profile source is not supported.
  220. --*/
  221. {
  222. NTSTATUS Status;
  223. PAMD64_PROFILE_SOURCE_DESCRIPTOR ProfileSourceDescriptor;
  224. Status = HalpGetProfileDescriptor(ProfileSource, &ProfileSourceDescriptor);
  225. if (!NT_SUCCESS(Status)) {
  226. return Status;
  227. }
  228. if (*Interval < ProfileSourceDescriptor->MinInterval) {
  229. *Interval = ProfileSourceDescriptor->MinInterval;
  230. }
  231. if (*Interval > ProfileSourceDescriptor->MaxInterval) {
  232. *Interval = ProfileSourceDescriptor->MaxInterval;
  233. }
  234. ProfileSourceDescriptor->Interval = *Interval;
  235. return STATUS_SUCCESS;
  236. }
  237. VOID
  238. Amd64CheckOverflowStatus(
  239. POVERFLOW_STATUS pOverflowStatus
  240. )
  241. /*++
  242. Routine Description:
  243. This function find out the overflowed counters and return the related
  244. profile sources to the caller.
  245. Arguments:
  246. ProfileSource - Supplies the profile source.
  247. Return Value:
  248. None.
  249. --*/
  250. {
  251. PAMD64_PROFILE_SOURCE_DESCRIPTOR ProfileSourceDescriptor;
  252. KPROFILE_SOURCE ProfileSource;
  253. ULONG64 CurrentCount, Mask;
  254. NTSTATUS Status;
  255. LONG i, j;
  256. ULONG ProcessorNumber;
  257. ProcessorNumber = KeGetCurrentProcessorNumber();
  258. for(i = j = 0; i < AMD64_NUMBER_COUNTERS; i++) {
  259. if (!(CounterStatus[ProcessorNumber][i].Idle)) {
  260. ProfileSource = CounterStatus[ProcessorNumber][i].ProfileSource;
  261. Status = HalpGetProfileDescriptor (ProfileSource,
  262. &ProfileSourceDescriptor);
  263. if (NT_SUCCESS(Status)) {
  264. //
  265. // Mask off the reserved bits
  266. //
  267. Mask = (((ULONG64)1 << AMD64_COUNTER_RESOLUTION) - 1);
  268. CurrentCount = RDMSR(MSR_PERF_CTR0 + i);
  269. //
  270. // An overflow occured if the current value in counter
  271. // is smaller than the initial value
  272. //
  273. if ((CurrentCount & Mask) <
  274. ((ULONG64)(0 - ProfileSourceDescriptor->Interval) & Mask)) {
  275. //
  276. // Add it to the overflowed profile source list.
  277. //
  278. //
  279. OverflowedProfileList[ProcessorNumber][j] = ProfileSource;
  280. j++;
  281. }
  282. }
  283. }
  284. }
  285. //
  286. // Record the number of overflowed counters
  287. //
  288. pOverflowStatus->Number = j;
  289. if(j) {
  290. pOverflowStatus->pSource = &(OverflowedProfileList[ProcessorNumber][0]);
  291. }
  292. }
  293. NTSTATUS
  294. HalpGetProfileDescriptor(
  295. IN KPROFILE_SOURCE ProfileSource,
  296. IN OUT PAMD64_PROFILE_SOURCE_DESCRIPTOR *ProfileSourceDescriptor
  297. )
  298. /*++
  299. Routine Description:
  300. This function retrieves the descriptor of specified profile source.
  301. Arguments:
  302. ProfileSource - Supplies the profile source.
  303. ProfileSourceDescriptor - Where the pointer of descriptor is returned.
  304. Return Value:
  305. STATUS_SUCCESS - If the requested mapping is found.
  306. STATUS_NOT_SUPPORTED - If the specified profile source is not supported.
  307. --*/
  308. {
  309. LONG i;
  310. if ((ULONG)ProfileSource < ProfileMaximum) {
  311. //
  312. // This is a generic profile source
  313. //
  314. i = ProfileSource;
  315. }
  316. else if ((ULONG)ProfileSource < ProfileAmd64Maximum &&
  317. (ULONG)ProfileSource >= ProfileAmd64Minimum ) {
  318. //
  319. // This is an Amd64 specific profile source
  320. //
  321. i = ProfileSource - ProfileAmd64Minimum + ProfileMaximum;
  322. }
  323. else {
  324. return STATUS_NOT_SUPPORTED;
  325. }
  326. *ProfileSourceDescriptor = &(Amd64ProfileSourceDescriptorTable[i]);
  327. if (!((*ProfileSourceDescriptor)->Supported)) {
  328. return STATUS_NOT_SUPPORTED;
  329. }
  330. return STATUS_SUCCESS;
  331. }
  332. NTSTATUS
  333. HalpAllocateCounter(
  334. IN KPROFILE_SOURCE ProfileSource,
  335. OUT PULONG Counter
  336. )
  337. /*++
  338. Routine Description:
  339. This function finds an idle counter register and assigns the specified
  340. profile source to it.
  341. Arguments:
  342. ProfileSource - Supplies the profile source.
  343. Counter - Supplies a pointer where the index of the idle counter is returned.
  344. Return Value:
  345. STATUS_SUCCESS - If an idle counter register is found.
  346. STATUS_DEVICE_BUSY - If all counter registers are occupied.
  347. --*/
  348. {
  349. LONG i;
  350. ULONG ProcessorNumber;
  351. ProcessorNumber = KeGetCurrentProcessorNumber();
  352. for(i = 0; i < AMD64_NUMBER_COUNTERS; i++) {
  353. if (CounterStatus[ProcessorNumber][i].Idle == TRUE) {
  354. //
  355. // Found an idle counter. Mark it busy and assign ProfileSource
  356. // to it
  357. //
  358. CounterStatus[ProcessorNumber][i].Idle = FALSE;
  359. CounterStatus[ProcessorNumber][i].ProfileSource = ProfileSource;
  360. *Counter = i;
  361. return STATUS_SUCCESS;
  362. }
  363. }
  364. return STATUS_DEVICE_BUSY;
  365. }
  366. VOID
  367. HalpFreeCounter(
  368. ULONG Counter
  369. )
  370. /*++
  371. Routine Description:
  372. This function marks the specified counter idle.
  373. Arguments:
  374. Counter - The index of the counter to be freed.
  375. Return Value:
  376. None.
  377. --*/
  378. {
  379. ULONG ProcessorNumber;
  380. ProcessorNumber = KeGetCurrentProcessorNumber();
  381. CounterStatus[ProcessorNumber][Counter].Idle = TRUE;
  382. CounterStatus[ProcessorNumber][Counter].ProfileSource = 0;
  383. }
  384. NTSTATUS
  385. Amd64QueryInformation(
  386. IN HAL_QUERY_INFORMATION_CLASS InformationType,
  387. IN ULONG BufferSize,
  388. IN OUT PVOID Buffer,
  389. OUT PULONG ReturnedLength
  390. )
  391. /*++
  392. Routine Description:
  393. This function retrieves the information of profile sources.
  394. Arguments:
  395. InformationClass - Constant that describes the type of information .
  396. BufferSize - Size of the memory pointed to by Buffer.
  397. Buffer - Requested information described by InformationClass.
  398. ReturnedLength - The actual bytes returned or needed for the requested
  399. information.
  400. Return Value:
  401. STATUS_SUCCESS - If the requested information is retrieved successfully.
  402. STATUS_INFO_LENGTH_MISMATCH - If the incoming BufferSize is too small.
  403. STATUS_NOT_SUPPORTED - If the specified information class or profile
  404. source is not supported.
  405. --*/
  406. {
  407. NTSTATUS Status;
  408. ULONG i, TotalProfieSources;
  409. PHAL_PROFILE_SOURCE_INFORMATION ProfileSourceInformation;
  410. PHAL_PROFILE_SOURCE_LIST ProfileSourceList;
  411. PAMD64_PROFILE_SOURCE_DESCRIPTOR ProfileSourceDescriptor;
  412. switch (InformationType) {
  413. case HalQueryProfileSourceList:
  414. TotalProfieSources = sizeof(Amd64ProfileSourceDescriptorTable) /
  415. sizeof(AMD64_PROFILE_SOURCE_DESCRIPTOR);
  416. if (BufferSize == 0) {
  417. //
  418. // This indicates the caller just wants to know the
  419. // size of the buffer to allocate but is not prepared
  420. // to accept any data content. In this case the bytes
  421. // needed to store HAL_PROFILE_SOURCE_LIST structures
  422. // of all profile sources is returned.
  423. //
  424. *ReturnedLength = TotalProfieSources *
  425. sizeof(HAL_PROFILE_SOURCE_LIST);
  426. Status = STATUS_SUCCESS;
  427. break;
  428. }
  429. if (BufferSize < TotalProfieSources *
  430. sizeof(HAL_PROFILE_SOURCE_LIST)) {
  431. *ReturnedLength = 0;
  432. Status = STATUS_INFO_LENGTH_MISMATCH;
  433. break;
  434. }
  435. ProfileSourceList = (PHAL_PROFILE_SOURCE_LIST) Buffer;
  436. for (i = 0; i < TotalProfieSources; i++) {
  437. Status = HalpGetProfileDescriptor(i, &ProfileSourceDescriptor);
  438. if (NT_SUCCESS(Status)) {
  439. //
  440. // Filling in requested data
  441. //
  442. ProfileSourceList->Source = ProfileSourceDescriptor->ProfileSource;
  443. ProfileSourceList->Description = ProfileSourceDescriptor->Description;
  444. ProfileSourceList++;
  445. }
  446. }
  447. *ReturnedLength = (ULONG)((ULONG_PTR)ProfileSourceList -
  448. (ULONG_PTR)Buffer);
  449. Status = STATUS_SUCCESS;
  450. break;
  451. case HalProfileSourceInformation:
  452. if (BufferSize < sizeof(HAL_PROFILE_SOURCE_INFORMATION)) {
  453. *ReturnedLength = 0;
  454. Status = STATUS_INFO_LENGTH_MISMATCH;
  455. break;
  456. }
  457. ProfileSourceInformation = (PHAL_PROFILE_SOURCE_INFORMATION) Buffer;
  458. Status = HalpGetProfileDescriptor(ProfileSourceInformation->Source,
  459. &ProfileSourceDescriptor);
  460. if (!NT_SUCCESS(Status)) {
  461. *ReturnedLength = 0;
  462. Status = STATUS_NOT_SUPPORTED;
  463. break;
  464. }
  465. //
  466. // Filling in requested data
  467. //
  468. ProfileSourceInformation->Supported = ProfileSourceDescriptor->Supported;
  469. ProfileSourceInformation->Interval = (ULONG) ProfileSourceDescriptor->Interval;
  470. Status = STATUS_SUCCESS;
  471. break;
  472. default:
  473. *ReturnedLength = 0;
  474. Status = STATUS_NOT_SUPPORTED;
  475. break;
  476. }
  477. return Status;
  478. }