2 * Copyright (c) 2003-2004 Apple Computer, Inc. All rights reserved.
4 * @APPLE_OSREFERENCE_LICENSE_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 License
10 * may not be used to create, or enable the creation or redistribution of,
11 * unlawful or unlicensed copies of an Apple operating system, or to
12 * circumvent, violate, or enable the circumvention or violation of, any
13 * terms of an Apple operating system software license agreement.
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
18 * The Original Code and all software distributed under the License are
19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23 * Please see the License for the specific language governing rights and
24 * limitations under the License.
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
29 #include <mach/std_types.h>
30 #include <i386/cpu_data.h>
31 #include <i386/cpu_number.h>
32 #include <i386/perfmon.h>
33 #include <i386/proc_reg.h>
34 #include <i386/cpu_threads.h>
36 #include <i386/cpuid.h>
37 #include <i386/lock.h>
38 #include <vm/vm_kern.h>
39 #include <kern/task.h>
42 #define DBG(x...) kprintf(x)
47 decl_simple_lock_data(,pmc_lock
)
48 static task_t pmc_owner
= TASK_NULL
;
49 static int pmc_thread_count
= 0;
51 /* PMC Facility Owner:
52 * TASK_NULL - no one owns it
53 * kernel_task - owned by pmc
54 * other task - owned by another task
58 * Table of ESCRs and addresses associated with performance counters/CCCRs.
59 * See Intel SDM Vol 3, Table 15-4 (section 15.9):
61 static uint16_t pmc_escr_addr_table
[18][8] = {
63 [MSR_BSU_ESCR0
] 0x3a0,
64 [MSR_FSB_ESCR0
] 0x3a2,
65 [MSR_MOB_ESCR0
] 0x3aa,
66 [MSR_PMH_ESCR0
] 0x3ac,
67 [MSR_BPU_ESCR0
] 0x3b2,
69 [MSR_ITLB_ESCR0
] 0x3b6,
73 [MSR_BSU_ESCR0
] 0x3a0,
74 [MSR_FSB_ESCR0
] 0x3a2,
75 [MSR_MOB_ESCR0
] 0x3aa,
76 [MSR_PMH_ESCR0
] 0x3ac,
77 [MSR_BPU_ESCR0
] 0x3b2,
79 [MSR_ITLB_ESCR0
] 0x3b6,
83 [MSR_BSU_ESCR1
] 0x3a1,
84 [MSR_FSB_ESCR1
] 0x3a3,
85 [MSR_MOB_ESCR1
] 0x3ab,
86 [MSR_PMH_ESCR1
] 0x3ad,
87 [MSR_BPU_ESCR1
] 0x3b3,
89 [MSR_ITLB_ESCR1
] 0x3b7,
93 [MSR_BSU_ESCR1
] 0x3a1,
94 [MSR_FSB_ESCR1
] 0x3a3,
95 [MSR_MOB_ESCR1
] 0x3ab,
96 [MSR_PMH_ESCR1
] 0x3ad,
97 [MSR_BPU_ESCR1
] 0x3b3,
99 [MSR_ITLB_ESCR1
] 0x3b7,
100 [MSR_IX_ESCR1
] 0x3c9,
103 [MSR_MS_ESCR1
] 0x3c1,
104 [MSR_TBPU_ESCR1
] 0x3c3,
105 [MSR_TC_ESCR1
] 0x3c5,
108 [MSR_MS_ESCR1
] 0x3c1,
109 [MSR_TBPU_ESCR1
] 0x3c3,
110 [MSR_TC_ESCR1
] 0x3c5,
113 [MSR_MS_ESCR1
] 0x3c1,
114 [MSR_TBPU_ESCR1
] 0x3c3,
115 [MSR_TC_ESCR1
] 0x3c5,
118 [MSR_MS_ESCR1
] 0x3c1,
119 [MSR_TBPU_ESCR1
] 0x3c3,
120 [MSR_TC_ESCR1
] 0x3c5,
122 [MSR_FLAME_COUNTER0
] {
123 [MSR_FIRM_ESCR0
] 0x3a4,
124 [MSR_FLAME_ESCR0
] 0x3a6,
125 [MSR_DAC_ESCR0
] 0x3a8,
126 [MSR_SAT_ESCR0
] 0x3ae,
127 [MSR_U2L_ESCR0
] 0x3b0,
129 [MSR_FLAME_COUNTER1
] {
130 [MSR_FIRM_ESCR0
] 0x3a4,
131 [MSR_FLAME_ESCR0
] 0x3a6,
132 [MSR_DAC_ESCR0
] 0x3a8,
133 [MSR_SAT_ESCR0
] 0x3ae,
134 [MSR_U2L_ESCR0
] 0x3b0,
136 [MSR_FLAME_COUNTER2
] {
137 [MSR_FIRM_ESCR1
] 0x3a5,
138 [MSR_FLAME_ESCR1
] 0x3a7,
139 [MSR_DAC_ESCR1
] 0x3a9,
140 [MSR_SAT_ESCR1
] 0x3af,
141 [MSR_U2L_ESCR1
] 0x3b1,
143 [MSR_FLAME_COUNTER3
] {
144 [MSR_FIRM_ESCR1
] 0x3a5,
145 [MSR_FLAME_ESCR1
] 0x3a7,
146 [MSR_DAC_ESCR1
] 0x3a9,
147 [MSR_SAT_ESCR1
] 0x3af,
148 [MSR_U2L_ESCR1
] 0x3b1,
151 [MSR_CRU_ESCR0
] 0x3b8,
152 [MSR_CRU_ESCR2
] 0x3cc,
153 [MSR_CRU_ESCR4
] 0x3e0,
154 [MSR_IQ_ESCR0
] 0x3ba,
155 [MSR_RAT_ESCR0
] 0x3bc,
156 [MSR_SSU_ESCR0
] 0x3be,
157 [MSR_AFL_ESCR0
] 0x3ca,
160 [MSR_CRU_ESCR0
] 0x3b8,
161 [MSR_CRU_ESCR2
] 0x3cc,
162 [MSR_CRU_ESCR4
] 0x3e0,
163 [MSR_IQ_ESCR0
] 0x3ba,
164 [MSR_RAT_ESCR0
] 0x3bc,
165 [MSR_SSU_ESCR0
] 0x3be,
166 [MSR_AFL_ESCR0
] 0x3ca,
169 [MSR_CRU_ESCR1
] 0x3b9,
170 [MSR_CRU_ESCR3
] 0x3cd,
171 [MSR_CRU_ESCR5
] 0x3e1,
172 [MSR_IQ_ESCR1
] 0x3bb,
173 [MSR_RAT_ESCR1
] 0x3bd,
174 [MSR_AFL_ESCR1
] 0x3cb,
177 [MSR_CRU_ESCR1
] 0x3b9,
178 [MSR_CRU_ESCR3
] 0x3cd,
179 [MSR_CRU_ESCR5
] 0x3e1,
180 [MSR_IQ_ESCR1
] 0x3bb,
181 [MSR_RAT_ESCR1
] 0x3bd,
182 [MSR_AFL_ESCR1
] 0x3cb,
185 [MSR_CRU_ESCR0
] 0x3b8,
186 [MSR_CRU_ESCR2
] 0x3cc,
187 [MSR_CRU_ESCR4
] 0x3e0,
188 [MSR_IQ_ESCR0
] 0x3ba,
189 [MSR_RAT_ESCR0
] 0x3bc,
190 [MSR_SSU_ESCR0
] 0x3be,
191 [MSR_AFL_ESCR0
] 0x3ca,
194 [MSR_CRU_ESCR1
] 0x3b9,
195 [MSR_CRU_ESCR3
] 0x3cd,
196 [MSR_CRU_ESCR5
] 0x3e1,
197 [MSR_IQ_ESCR1
] 0x3bb,
198 [MSR_RAT_ESCR1
] 0x3bd,
199 [MSR_AFL_ESCR1
] 0x3cb,
202 #define PMC_ESCR_ADDR(id,esid) pmc_escr_addr_table[id][esid]
205 pmc_id_t id_max
; /* Maximum counter id */
206 pmc_machine_t machine_type
; /* P6 or P4/Xeon */
207 uint32_t msr_counter_base
; /* First counter MSR */
208 uint32_t msr_control_base
; /* First control MSR */
209 boolean_t reserved
[18]; /* Max-sized arrays... */
210 pmc_ovf_func_t
*ovf_func
[18];
212 pmc_cccr_t cccr_shadow
[18]; /* Last cccr values set */
213 pmc_counter_t counter_shadow
[18]; /* Last counter values set */
214 uint32_t ovfs_unexpected
[18]; /* Count of unexpected intrs */
219 _pmc_machine_type(void)
221 i386_cpu_info_t
*infop
= cpuid_info();
223 if (strncmp(infop
->cpuid_vendor
, CPUID_VID_INTEL
, sizeof(CPUID_VID_INTEL
)) != 0)
226 if (!pmc_is_available())
229 switch (infop
->cpuid_family
) {
240 pmc_p4_intr(void *state
)
242 pmc_table_t
*pmc_table
= (pmc_table_t
*) cpu_core()->pmc
;
246 int my_logical_cpu
= cpu_to_logical_cpu(cpu_number());
249 * Scan through table for reserved counters with overflow and
250 * with a registered overflow function.
252 for (id
= 0; id
<= pmc_table
->id_max
; id
++) {
253 if (!pmc_table
->reserved
[id
])
255 cccr_addr
= pmc_table
->msr_control_base
+ id
;
256 cccr
.u_u64
= rdmsr64(cccr_addr
);
258 pmc_table
->cccr_shadow
[id
] = cccr
;
259 *((uint64_t *) &pmc_table
->counter_shadow
[id
]) =
260 rdmsr64(pmc_table
->msr_counter_base
+ id
);
262 if (cccr
.u_htt
.ovf
== 0)
264 if ((cccr
.u_htt
.ovf_pmi_t0
== 1 && my_logical_cpu
== 0) ||
265 (cccr
.u_htt
.ovf_pmi_t1
== 1 && my_logical_cpu
== 1)) {
266 if (pmc_table
->ovf_func
[id
]) {
267 (*pmc_table
->ovf_func
[id
])(id
, state
);
268 /* func expected to clear overflow */
272 /* Clear overflow for unexpected interrupt */
274 pmc_table
->ovfs_unexpected
[id
]++;
280 pmc_p6_intr(void *state
)
282 pmc_table_t
*pmc_table
= (pmc_table_t
*) cpu_core()->pmc
;
286 * Can't determine which counter has overflow
287 * so call all registered functions.
289 for (id
= 0; id
<= pmc_table
->id_max
; id
++)
290 if (pmc_table
->reserved
[id
] && pmc_table
->ovf_func
[id
])
291 (*pmc_table
->ovf_func
[id
])(id
, state
);
298 pmc_table_t
*pmc_table
;
299 pmc_machine_t pmc_type
;
301 pmc_type
= _pmc_machine_type();
302 if (pmc_type
== pmc_none
) {
306 ret
= kmem_alloc(kernel_map
,
307 (void *) &pmc_table
, sizeof(pmc_table_t
));
308 if (ret
!= KERN_SUCCESS
)
309 panic("pmc_init() kmem_alloc returned %d\n", ret
);
310 bzero((void *)pmc_table
, sizeof(pmc_table_t
));
312 pmc_table
->machine_type
= pmc_type
;
315 pmc_table
->id_max
= 17;
316 pmc_table
->msr_counter_base
= MSR_COUNTER_ADDR(0);
317 pmc_table
->msr_control_base
= MSR_CCCR_ADDR(0);
318 lapic_set_pmi_func(&pmc_p4_intr
);
321 pmc_table
->id_max
= 1;
322 pmc_table
->msr_counter_base
= MSR_P6_COUNTER_ADDR(0);
323 pmc_table
->msr_control_base
= MSR_P6_PES_ADDR(0);
324 lapic_set_pmi_func(&pmc_p6_intr
);
329 return (void *) pmc_table
;
333 static inline pmc_table_t
*
334 pmc_table_valid(pmc_id_t id
)
336 cpu_core_t
*my_core
= cpu_core();
337 pmc_table_t
*pmc_table
;
341 pmc_table
= (pmc_table_t
*) my_core
->pmc
;
342 return (pmc_table
== NULL
||
343 id
> pmc_table
->id_max
||
344 !pmc_table
->reserved
[id
]) ? NULL
: pmc_table
;
348 pmc_machine_type(pmc_machine_t
*type
)
350 cpu_core_t
*my_core
= cpu_core();
351 pmc_table_t
*pmc_table
;
355 pmc_table
= (pmc_table_t
*) my_core
->pmc
;
356 if (pmc_table
== NULL
)
359 *type
= pmc_table
->machine_type
;
365 pmc_reserve(pmc_id_t id
)
367 cpu_core_t
*my_core
= cpu_core();
368 pmc_table_t
*pmc_table
;
372 pmc_table
= (pmc_table_t
*) my_core
->pmc
;
373 if (pmc_table
== NULL
)
375 if (id
> pmc_table
->id_max
)
376 return KERN_INVALID_ARGUMENT
;
377 if (pmc_table
->reserved
[id
])
380 pmc_table
->reserved
[id
] = TRUE
;
386 pmc_is_reserved(pmc_id_t id
)
388 return pmc_table_valid(id
) != NULL
;
392 pmc_free(pmc_id_t id
)
394 pmc_table_t
*pmc_table
= pmc_table_valid(id
);
396 if (pmc_table
== NULL
)
397 return KERN_INVALID_ARGUMENT
;
399 pmc_cccr_write(id
, 0x0ULL
);
400 pmc_table
->reserved
[id
] = FALSE
;
401 pmc_table
->ovf_func
[id
] = NULL
;
407 pmc_counter_read(pmc_id_t id
, pmc_counter_t
*val
)
409 pmc_table_t
*pmc_table
= pmc_table_valid(id
);
411 if (pmc_table
== NULL
)
412 return KERN_INVALID_ARGUMENT
;
414 *(uint64_t *)val
= rdmsr64(pmc_table
->msr_counter_base
+ id
);
420 pmc_counter_write(pmc_id_t id
, pmc_counter_t
*val
)
422 pmc_table_t
*pmc_table
= pmc_table_valid(id
);
424 if (pmc_table
== NULL
)
425 return KERN_INVALID_ARGUMENT
;
427 wrmsr64(pmc_table
->msr_counter_base
+ id
, *(uint64_t *)val
);
433 pmc_cccr_read(pmc_id_t id
, pmc_cccr_t
*cccr
)
435 pmc_table_t
*pmc_table
= pmc_table_valid(id
);
437 if (pmc_table
== NULL
)
438 return KERN_INVALID_ARGUMENT
;
440 if (pmc_table
->machine_type
!= pmc_P4_Xeon
)
443 *(uint64_t *)cccr
= rdmsr64(pmc_table
->msr_control_base
+ id
);
449 pmc_cccr_write(pmc_id_t id
, pmc_cccr_t
*cccr
)
451 pmc_table_t
*pmc_table
= pmc_table_valid(id
);
453 if (pmc_table
== NULL
)
454 return KERN_INVALID_ARGUMENT
;
456 if (pmc_table
->machine_type
!= pmc_P4_Xeon
)
459 wrmsr64(pmc_table
->msr_control_base
+ id
, *(uint64_t *)cccr
);
465 pmc_evtsel_read(pmc_id_t id
, pmc_evtsel_t
*evtsel
)
467 pmc_table_t
*pmc_table
= pmc_table_valid(id
);
469 if (pmc_table
== NULL
)
470 return KERN_INVALID_ARGUMENT
;
472 if (pmc_table
->machine_type
!= pmc_P6
)
475 *(uint64_t *)evtsel
= rdmsr64(pmc_table
->msr_control_base
+ id
);
481 pmc_evtsel_write(pmc_id_t id
, pmc_evtsel_t
*evtsel
)
483 pmc_table_t
*pmc_table
= pmc_table_valid(id
);
485 if (pmc_table
== NULL
)
486 return KERN_INVALID_ARGUMENT
;
488 if (pmc_table
->machine_type
!= pmc_P4_Xeon
)
491 wrmsr64(pmc_table
->msr_control_base
+ id
, *(uint64_t *)evtsel
);
497 pmc_escr_read(pmc_id_t id
, pmc_escr_id_t esid
, pmc_escr_t
*escr
)
500 pmc_table_t
*pmc_table
= pmc_table_valid(id
);
502 if (pmc_table
== NULL
)
503 return KERN_INVALID_ARGUMENT
;
505 if (pmc_table
->machine_type
!= pmc_P4_Xeon
)
508 if (esid
> PMC_ESID_MAX
)
509 return KERN_INVALID_ARGUMENT
;
511 addr
= PMC_ESCR_ADDR(id
, esid
);
513 return KERN_INVALID_ARGUMENT
;
515 *(uint64_t *)escr
= rdmsr64(addr
);
521 pmc_escr_write(pmc_id_t id
, pmc_escr_id_t esid
, pmc_escr_t
*escr
)
524 pmc_table_t
*pmc_table
= pmc_table_valid(id
);
526 if (pmc_table
== NULL
)
529 if (pmc_table
->machine_type
!= pmc_P4_Xeon
)
532 if (esid
> PMC_ESID_MAX
)
533 return KERN_INVALID_ARGUMENT
;
535 addr
= PMC_ESCR_ADDR(id
, esid
);
537 return KERN_INVALID_ARGUMENT
;
539 wrmsr64(addr
, *(uint64_t *)escr
);
545 pmc_set_ovf_func(pmc_id_t id
, pmc_ovf_func_t func
)
547 pmc_table_t
*pmc_table
= pmc_table_valid(id
);
549 if (pmc_table
== NULL
)
550 return KERN_INVALID_ARGUMENT
;
552 pmc_table
->ovf_func
[id
] = func
;
558 pmc_acquire(task_t task
)
560 kern_return_t retval
= KERN_SUCCESS
;
562 simple_lock(&pmc_lock
);
564 if(pmc_owner
== task
) {
566 "ACQUIRED: already owner\n");
567 retval
= KERN_SUCCESS
;
569 } else if(pmc_owner
== TASK_NULL
) { /* no one owns it */
571 pmc_thread_count
= 0;
573 "ACQUIRED: no current owner - made new owner\n");
574 retval
= KERN_SUCCESS
;
575 } else { /* someone already owns it */
576 if(pmc_owner
== kernel_task
) {
577 if(pmc_thread_count
== 0) {
578 /* kernel owns it but no threads using it */
580 pmc_thread_count
= 0;
582 "ACQUIRED: owned by kernel, no threads\n");
583 retval
= KERN_SUCCESS
;
586 "DENIED: owned by kernel, in use\n");
587 retval
= KERN_RESOURCE_SHORTAGE
;
589 } else { /* non-kernel owner */
591 "DENIED: owned by another task\n");
592 retval
= KERN_RESOURCE_SHORTAGE
;
596 simple_unlock(&pmc_lock
);
601 pmc_release(task_t task
)
603 kern_return_t retval
= KERN_SUCCESS
;
604 task_t old_pmc_owner
= pmc_owner
;
606 simple_lock(&pmc_lock
);
608 if(task
!= pmc_owner
) {
609 retval
= KERN_NO_ACCESS
;
611 if(old_pmc_owner
== kernel_task
) {
612 if(pmc_thread_count
>0) {
614 "NOT RELEASED: owned by kernel, in use\n");
615 retval
= KERN_NO_ACCESS
;
618 "RELEASED: was owned by kernel\n");
619 pmc_owner
= TASK_NULL
;
620 retval
= KERN_SUCCESS
;
624 "RELEASED: was owned by user\n");
625 pmc_owner
= TASK_NULL
;
626 retval
= KERN_SUCCESS
;
630 simple_unlock(&pmc_lock
);