2 * Copyright (c) 2003-2004 Apple Computer, Inc. All rights reserved.
4 * @APPLE_LICENSE_OSREFERENCE_HEADER_START@
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. The rights granted to you under the
10 * License may not be used to create, or enable the creation or
11 * redistribution of, unlawful or unlicensed copies of an Apple operating
12 * system, or to circumvent, violate, or enable the circumvention or
13 * violation of, any terms of an Apple operating system software license
16 * Please obtain a copy of the License at
17 * http://www.opensource.apple.com/apsl/ and read it before using this
20 * The Original Code and all software distributed under the License are
21 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
22 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
23 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
24 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
25 * Please see the License for the specific language governing rights and
26 * limitations under the License.
28 * @APPLE_LICENSE_OSREFERENCE_HEADER_END@
31 #include <mach/std_types.h>
32 #include <i386/cpu_data.h>
33 #include <i386/cpu_number.h>
34 #include <i386/perfmon.h>
35 #include <i386/proc_reg.h>
36 #include <i386/cpu_threads.h>
38 #include <i386/cpuid.h>
39 #include <i386/lock.h>
40 #include <vm/vm_kern.h>
41 #include <kern/task.h>
44 #define DBG(x...) kprintf(x)
49 decl_simple_lock_data(,pmc_lock
)
50 static task_t pmc_owner
= TASK_NULL
;
51 static int pmc_thread_count
= 0;
53 /* PMC Facility Owner:
54 * TASK_NULL - no one owns it
55 * kernel_task - owned by pmc
56 * other task - owned by another task
60 * Table of ESCRs and addresses associated with performance counters/CCCRs.
61 * See Intel SDM Vol 3, Table 15-4 (section 15.9):
63 static uint16_t pmc_escr_addr_table
[18][8] = {
65 [MSR_BSU_ESCR0
] 0x3a0,
66 [MSR_FSB_ESCR0
] 0x3a2,
67 [MSR_MOB_ESCR0
] 0x3aa,
68 [MSR_PMH_ESCR0
] 0x3ac,
69 [MSR_BPU_ESCR0
] 0x3b2,
71 [MSR_ITLB_ESCR0
] 0x3b6,
75 [MSR_BSU_ESCR0
] 0x3a0,
76 [MSR_FSB_ESCR0
] 0x3a2,
77 [MSR_MOB_ESCR0
] 0x3aa,
78 [MSR_PMH_ESCR0
] 0x3ac,
79 [MSR_BPU_ESCR0
] 0x3b2,
81 [MSR_ITLB_ESCR0
] 0x3b6,
85 [MSR_BSU_ESCR1
] 0x3a1,
86 [MSR_FSB_ESCR1
] 0x3a3,
87 [MSR_MOB_ESCR1
] 0x3ab,
88 [MSR_PMH_ESCR1
] 0x3ad,
89 [MSR_BPU_ESCR1
] 0x3b3,
91 [MSR_ITLB_ESCR1
] 0x3b7,
95 [MSR_BSU_ESCR1
] 0x3a1,
96 [MSR_FSB_ESCR1
] 0x3a3,
97 [MSR_MOB_ESCR1
] 0x3ab,
98 [MSR_PMH_ESCR1
] 0x3ad,
99 [MSR_BPU_ESCR1
] 0x3b3,
100 [MSR_IS_ESCR1
] 0x3b5,
101 [MSR_ITLB_ESCR1
] 0x3b7,
102 [MSR_IX_ESCR1
] 0x3c9,
105 [MSR_MS_ESCR1
] 0x3c1,
106 [MSR_TBPU_ESCR1
] 0x3c3,
107 [MSR_TC_ESCR1
] 0x3c5,
110 [MSR_MS_ESCR1
] 0x3c1,
111 [MSR_TBPU_ESCR1
] 0x3c3,
112 [MSR_TC_ESCR1
] 0x3c5,
115 [MSR_MS_ESCR1
] 0x3c1,
116 [MSR_TBPU_ESCR1
] 0x3c3,
117 [MSR_TC_ESCR1
] 0x3c5,
120 [MSR_MS_ESCR1
] 0x3c1,
121 [MSR_TBPU_ESCR1
] 0x3c3,
122 [MSR_TC_ESCR1
] 0x3c5,
124 [MSR_FLAME_COUNTER0
] {
125 [MSR_FIRM_ESCR0
] 0x3a4,
126 [MSR_FLAME_ESCR0
] 0x3a6,
127 [MSR_DAC_ESCR0
] 0x3a8,
128 [MSR_SAT_ESCR0
] 0x3ae,
129 [MSR_U2L_ESCR0
] 0x3b0,
131 [MSR_FLAME_COUNTER1
] {
132 [MSR_FIRM_ESCR0
] 0x3a4,
133 [MSR_FLAME_ESCR0
] 0x3a6,
134 [MSR_DAC_ESCR0
] 0x3a8,
135 [MSR_SAT_ESCR0
] 0x3ae,
136 [MSR_U2L_ESCR0
] 0x3b0,
138 [MSR_FLAME_COUNTER2
] {
139 [MSR_FIRM_ESCR1
] 0x3a5,
140 [MSR_FLAME_ESCR1
] 0x3a7,
141 [MSR_DAC_ESCR1
] 0x3a9,
142 [MSR_SAT_ESCR1
] 0x3af,
143 [MSR_U2L_ESCR1
] 0x3b1,
145 [MSR_FLAME_COUNTER3
] {
146 [MSR_FIRM_ESCR1
] 0x3a5,
147 [MSR_FLAME_ESCR1
] 0x3a7,
148 [MSR_DAC_ESCR1
] 0x3a9,
149 [MSR_SAT_ESCR1
] 0x3af,
150 [MSR_U2L_ESCR1
] 0x3b1,
153 [MSR_CRU_ESCR0
] 0x3b8,
154 [MSR_CRU_ESCR2
] 0x3cc,
155 [MSR_CRU_ESCR4
] 0x3e0,
156 [MSR_IQ_ESCR0
] 0x3ba,
157 [MSR_RAT_ESCR0
] 0x3bc,
158 [MSR_SSU_ESCR0
] 0x3be,
159 [MSR_AFL_ESCR0
] 0x3ca,
162 [MSR_CRU_ESCR0
] 0x3b8,
163 [MSR_CRU_ESCR2
] 0x3cc,
164 [MSR_CRU_ESCR4
] 0x3e0,
165 [MSR_IQ_ESCR0
] 0x3ba,
166 [MSR_RAT_ESCR0
] 0x3bc,
167 [MSR_SSU_ESCR0
] 0x3be,
168 [MSR_AFL_ESCR0
] 0x3ca,
171 [MSR_CRU_ESCR1
] 0x3b9,
172 [MSR_CRU_ESCR3
] 0x3cd,
173 [MSR_CRU_ESCR5
] 0x3e1,
174 [MSR_IQ_ESCR1
] 0x3bb,
175 [MSR_RAT_ESCR1
] 0x3bd,
176 [MSR_AFL_ESCR1
] 0x3cb,
179 [MSR_CRU_ESCR1
] 0x3b9,
180 [MSR_CRU_ESCR3
] 0x3cd,
181 [MSR_CRU_ESCR5
] 0x3e1,
182 [MSR_IQ_ESCR1
] 0x3bb,
183 [MSR_RAT_ESCR1
] 0x3bd,
184 [MSR_AFL_ESCR1
] 0x3cb,
187 [MSR_CRU_ESCR0
] 0x3b8,
188 [MSR_CRU_ESCR2
] 0x3cc,
189 [MSR_CRU_ESCR4
] 0x3e0,
190 [MSR_IQ_ESCR0
] 0x3ba,
191 [MSR_RAT_ESCR0
] 0x3bc,
192 [MSR_SSU_ESCR0
] 0x3be,
193 [MSR_AFL_ESCR0
] 0x3ca,
196 [MSR_CRU_ESCR1
] 0x3b9,
197 [MSR_CRU_ESCR3
] 0x3cd,
198 [MSR_CRU_ESCR5
] 0x3e1,
199 [MSR_IQ_ESCR1
] 0x3bb,
200 [MSR_RAT_ESCR1
] 0x3bd,
201 [MSR_AFL_ESCR1
] 0x3cb,
204 #define PMC_ESCR_ADDR(id,esid) pmc_escr_addr_table[id][esid]
207 pmc_id_t id_max
; /* Maximum counter id */
208 pmc_machine_t machine_type
; /* P6 or P4/Xeon */
209 uint32_t msr_counter_base
; /* First counter MSR */
210 uint32_t msr_control_base
; /* First control MSR */
211 boolean_t reserved
[18]; /* Max-sized arrays... */
212 pmc_ovf_func_t
*ovf_func
[18];
214 pmc_cccr_t cccr_shadow
[18]; /* Last cccr values set */
215 pmc_counter_t counter_shadow
[18]; /* Last counter values set */
216 uint32_t ovfs_unexpected
[18]; /* Count of unexpected intrs */
221 _pmc_machine_type(void)
223 i386_cpu_info_t
*infop
= cpuid_info();
225 if (strncmp(infop
->cpuid_vendor
, CPUID_VID_INTEL
, sizeof(CPUID_VID_INTEL
)) != 0)
228 if (!pmc_is_available())
231 switch (infop
->cpuid_family
) {
242 pmc_p4_intr(void *state
)
244 pmc_table_t
*pmc_table
= (pmc_table_t
*) cpu_core()->pmc
;
248 int my_logical_cpu
= cpu_to_logical_cpu(cpu_number());
251 * Scan through table for reserved counters with overflow and
252 * with a registered overflow function.
254 for (id
= 0; id
<= pmc_table
->id_max
; id
++) {
255 if (!pmc_table
->reserved
[id
])
257 cccr_addr
= pmc_table
->msr_control_base
+ id
;
258 cccr
.u_u64
= rdmsr64(cccr_addr
);
260 pmc_table
->cccr_shadow
[id
] = cccr
;
261 *((uint64_t *) &pmc_table
->counter_shadow
[id
]) =
262 rdmsr64(pmc_table
->msr_counter_base
+ id
);
264 if (cccr
.u_htt
.ovf
== 0)
266 if ((cccr
.u_htt
.ovf_pmi_t0
== 1 && my_logical_cpu
== 0) ||
267 (cccr
.u_htt
.ovf_pmi_t1
== 1 && my_logical_cpu
== 1)) {
268 if (pmc_table
->ovf_func
[id
]) {
269 (*pmc_table
->ovf_func
[id
])(id
, state
);
270 /* func expected to clear overflow */
274 /* Clear overflow for unexpected interrupt */
276 pmc_table
->ovfs_unexpected
[id
]++;
282 pmc_p6_intr(void *state
)
284 pmc_table_t
*pmc_table
= (pmc_table_t
*) cpu_core()->pmc
;
288 * Can't determine which counter has overflow
289 * so call all registered functions.
291 for (id
= 0; id
<= pmc_table
->id_max
; id
++)
292 if (pmc_table
->reserved
[id
] && pmc_table
->ovf_func
[id
])
293 (*pmc_table
->ovf_func
[id
])(id
, state
);
300 pmc_table_t
*pmc_table
;
301 pmc_machine_t pmc_type
;
303 pmc_type
= _pmc_machine_type();
304 if (pmc_type
== pmc_none
) {
308 ret
= kmem_alloc(kernel_map
,
309 (void *) &pmc_table
, sizeof(pmc_table_t
));
310 if (ret
!= KERN_SUCCESS
)
311 panic("pmc_init() kmem_alloc returned %d\n", ret
);
312 bzero((void *)pmc_table
, sizeof(pmc_table_t
));
314 pmc_table
->machine_type
= pmc_type
;
317 pmc_table
->id_max
= 17;
318 pmc_table
->msr_counter_base
= MSR_COUNTER_ADDR(0);
319 pmc_table
->msr_control_base
= MSR_CCCR_ADDR(0);
320 lapic_set_pmi_func(&pmc_p4_intr
);
323 pmc_table
->id_max
= 1;
324 pmc_table
->msr_counter_base
= MSR_P6_COUNTER_ADDR(0);
325 pmc_table
->msr_control_base
= MSR_P6_PES_ADDR(0);
326 lapic_set_pmi_func(&pmc_p6_intr
);
331 return (void *) pmc_table
;
335 static inline pmc_table_t
*
336 pmc_table_valid(pmc_id_t id
)
338 cpu_core_t
*my_core
= cpu_core();
339 pmc_table_t
*pmc_table
;
343 pmc_table
= (pmc_table_t
*) my_core
->pmc
;
344 return (pmc_table
== NULL
||
345 id
> pmc_table
->id_max
||
346 !pmc_table
->reserved
[id
]) ? NULL
: pmc_table
;
350 pmc_machine_type(pmc_machine_t
*type
)
352 cpu_core_t
*my_core
= cpu_core();
353 pmc_table_t
*pmc_table
;
357 pmc_table
= (pmc_table_t
*) my_core
->pmc
;
358 if (pmc_table
== NULL
)
361 *type
= pmc_table
->machine_type
;
367 pmc_reserve(pmc_id_t id
)
369 cpu_core_t
*my_core
= cpu_core();
370 pmc_table_t
*pmc_table
;
374 pmc_table
= (pmc_table_t
*) my_core
->pmc
;
375 if (pmc_table
== NULL
)
377 if (id
> pmc_table
->id_max
)
378 return KERN_INVALID_ARGUMENT
;
379 if (pmc_table
->reserved
[id
])
382 pmc_table
->reserved
[id
] = TRUE
;
388 pmc_is_reserved(pmc_id_t id
)
390 return pmc_table_valid(id
) != NULL
;
394 pmc_free(pmc_id_t id
)
396 pmc_table_t
*pmc_table
= pmc_table_valid(id
);
398 if (pmc_table
== NULL
)
399 return KERN_INVALID_ARGUMENT
;
401 pmc_cccr_write(id
, 0x0ULL
);
402 pmc_table
->reserved
[id
] = FALSE
;
403 pmc_table
->ovf_func
[id
] = NULL
;
409 pmc_counter_read(pmc_id_t id
, pmc_counter_t
*val
)
411 pmc_table_t
*pmc_table
= pmc_table_valid(id
);
413 if (pmc_table
== NULL
)
414 return KERN_INVALID_ARGUMENT
;
416 *(uint64_t *)val
= rdmsr64(pmc_table
->msr_counter_base
+ id
);
422 pmc_counter_write(pmc_id_t id
, pmc_counter_t
*val
)
424 pmc_table_t
*pmc_table
= pmc_table_valid(id
);
426 if (pmc_table
== NULL
)
427 return KERN_INVALID_ARGUMENT
;
429 wrmsr64(pmc_table
->msr_counter_base
+ id
, *(uint64_t *)val
);
435 pmc_cccr_read(pmc_id_t id
, pmc_cccr_t
*cccr
)
437 pmc_table_t
*pmc_table
= pmc_table_valid(id
);
439 if (pmc_table
== NULL
)
440 return KERN_INVALID_ARGUMENT
;
442 if (pmc_table
->machine_type
!= pmc_P4_Xeon
)
445 *(uint64_t *)cccr
= rdmsr64(pmc_table
->msr_control_base
+ id
);
451 pmc_cccr_write(pmc_id_t id
, pmc_cccr_t
*cccr
)
453 pmc_table_t
*pmc_table
= pmc_table_valid(id
);
455 if (pmc_table
== NULL
)
456 return KERN_INVALID_ARGUMENT
;
458 if (pmc_table
->machine_type
!= pmc_P4_Xeon
)
461 wrmsr64(pmc_table
->msr_control_base
+ id
, *(uint64_t *)cccr
);
467 pmc_evtsel_read(pmc_id_t id
, pmc_evtsel_t
*evtsel
)
469 pmc_table_t
*pmc_table
= pmc_table_valid(id
);
471 if (pmc_table
== NULL
)
472 return KERN_INVALID_ARGUMENT
;
474 if (pmc_table
->machine_type
!= pmc_P6
)
477 *(uint64_t *)evtsel
= rdmsr64(pmc_table
->msr_control_base
+ id
);
483 pmc_evtsel_write(pmc_id_t id
, pmc_evtsel_t
*evtsel
)
485 pmc_table_t
*pmc_table
= pmc_table_valid(id
);
487 if (pmc_table
== NULL
)
488 return KERN_INVALID_ARGUMENT
;
490 if (pmc_table
->machine_type
!= pmc_P4_Xeon
)
493 wrmsr64(pmc_table
->msr_control_base
+ id
, *(uint64_t *)evtsel
);
499 pmc_escr_read(pmc_id_t id
, pmc_escr_id_t esid
, pmc_escr_t
*escr
)
502 pmc_table_t
*pmc_table
= pmc_table_valid(id
);
504 if (pmc_table
== NULL
)
505 return KERN_INVALID_ARGUMENT
;
507 if (pmc_table
->machine_type
!= pmc_P4_Xeon
)
510 if (esid
> PMC_ESID_MAX
)
511 return KERN_INVALID_ARGUMENT
;
513 addr
= PMC_ESCR_ADDR(id
, esid
);
515 return KERN_INVALID_ARGUMENT
;
517 *(uint64_t *)escr
= rdmsr64(addr
);
523 pmc_escr_write(pmc_id_t id
, pmc_escr_id_t esid
, pmc_escr_t
*escr
)
526 pmc_table_t
*pmc_table
= pmc_table_valid(id
);
528 if (pmc_table
== NULL
)
531 if (pmc_table
->machine_type
!= pmc_P4_Xeon
)
534 if (esid
> PMC_ESID_MAX
)
535 return KERN_INVALID_ARGUMENT
;
537 addr
= PMC_ESCR_ADDR(id
, esid
);
539 return KERN_INVALID_ARGUMENT
;
541 wrmsr64(addr
, *(uint64_t *)escr
);
547 pmc_set_ovf_func(pmc_id_t id
, pmc_ovf_func_t func
)
549 pmc_table_t
*pmc_table
= pmc_table_valid(id
);
551 if (pmc_table
== NULL
)
552 return KERN_INVALID_ARGUMENT
;
554 pmc_table
->ovf_func
[id
] = func
;
560 pmc_acquire(task_t task
)
562 kern_return_t retval
= KERN_SUCCESS
;
564 simple_lock(&pmc_lock
);
566 if(pmc_owner
== task
) {
568 "ACQUIRED: already owner\n");
569 retval
= KERN_SUCCESS
;
571 } else if(pmc_owner
== TASK_NULL
) { /* no one owns it */
573 pmc_thread_count
= 0;
575 "ACQUIRED: no current owner - made new owner\n");
576 retval
= KERN_SUCCESS
;
577 } else { /* someone already owns it */
578 if(pmc_owner
== kernel_task
) {
579 if(pmc_thread_count
== 0) {
580 /* kernel owns it but no threads using it */
582 pmc_thread_count
= 0;
584 "ACQUIRED: owned by kernel, no threads\n");
585 retval
= KERN_SUCCESS
;
588 "DENIED: owned by kernel, in use\n");
589 retval
= KERN_RESOURCE_SHORTAGE
;
591 } else { /* non-kernel owner */
593 "DENIED: owned by another task\n");
594 retval
= KERN_RESOURCE_SHORTAGE
;
598 simple_unlock(&pmc_lock
);
603 pmc_release(task_t task
)
605 kern_return_t retval
= KERN_SUCCESS
;
606 task_t old_pmc_owner
= pmc_owner
;
608 simple_lock(&pmc_lock
);
610 if(task
!= pmc_owner
) {
611 retval
= KERN_NO_ACCESS
;
613 if(old_pmc_owner
== kernel_task
) {
614 if(pmc_thread_count
>0) {
616 "NOT RELEASED: owned by kernel, in use\n");
617 retval
= KERN_NO_ACCESS
;
620 "RELEASED: was owned by kernel\n");
621 pmc_owner
= TASK_NULL
;
622 retval
= KERN_SUCCESS
;
626 "RELEASED: was owned by user\n");
627 pmc_owner
= TASK_NULL
;
628 retval
= KERN_SUCCESS
;
632 simple_unlock(&pmc_lock
);