]> git.saurik.com Git - apple/xnu.git/blame - osfmk/ppc/hw_perfmon.c
xnu-792.6.22.tar.gz
[apple/xnu.git] / osfmk / ppc / hw_perfmon.c
CommitLineData
55e303ae
A
1/*
2 * Copyright (c) 2000-2002 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
e5568f75
A
6 * The contents of this file constitute Original Code as defined in and
7 * are subject to the Apple Public Source License Version 1.1 (the
8 * "License"). You may not use this file except in compliance with the
9 * License. Please obtain a copy of the License at
10 * http://www.apple.com/publicsource and read it before using this file.
55e303ae 11 *
e5568f75
A
12 * This Original Code and all software distributed under the License are
13 * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
55e303ae
A
14 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
15 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
e5568f75
A
16 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the
17 * License for the specific language governing rights and limitations
18 * under the License.
55e303ae
A
19 *
20 * @APPLE_LICENSE_HEADER_END@
21 */
22
23#include <kern/thread.h>
55e303ae
A
24#include <ppc/exception.h>
25#include <ppc/savearea.h>
26#include <ppc/hw_perfmon.h>
27#include <ppc/hw_perfmon_mmcr.h>
28
29decl_simple_lock_data(,hw_perfmon_lock)
30static task_t hw_perfmon_owner = TASK_NULL;
31static int hw_perfmon_thread_count = 0;
32
33/* Notes:
34 * -supervisor/user level filtering is unnecessary because of the way PMCs and MMCRs are context switched
35 * (can only count user events anyway)
36 * -marked filtering is unnecssary because each thread has its own virtualized set of PMCs and MMCRs
37 * -virtual counter PMI is passed up as a breakpoint exception
38 */
39
40int perfmon_init(void)
41{
42 simple_lock_init(&hw_perfmon_lock, FALSE);
43 return KERN_SUCCESS;
44}
45
46/* PMC Facility Owner:
47 * TASK_NULL - no one owns it
48 * kernel_task - owned by hw_perfmon
49 * other task - owned by another task
50 */
51
52int perfmon_acquire_facility(task_t task)
53{
54 kern_return_t retval = KERN_SUCCESS;
55
56 simple_lock(&hw_perfmon_lock);
57
58 if(hw_perfmon_owner==task) {
59#ifdef HWPERFMON_DEBUG
60 kprintf("perfmon_acquire_facility - ACQUIRED: already owner\n");
61#endif
62 retval = KERN_SUCCESS;
63 /* already own it */
64 } else if(hw_perfmon_owner==TASK_NULL) { /* no one owns it */
65 hw_perfmon_owner = task;
66 hw_perfmon_thread_count = 0;
67#ifdef HWPERFMON_DEBUG
68 kprintf("perfmon_acquire_facility - ACQUIRED: no current owner - made new owner\n");
69#endif
70 retval = KERN_SUCCESS;
71 } else { /* someone already owns it */
72 if(hw_perfmon_owner==kernel_task) {
73 if(hw_perfmon_thread_count==0) { /* kernel owns it but no threads using it */
74 hw_perfmon_owner = task;
75 hw_perfmon_thread_count = 0;
76#ifdef HWPERFMON_DEBUG
77 kprintf("perfmon_acquire_facility - ACQUIRED: kernel is current owner but no threads using it\n");
78#endif
79 retval = KERN_SUCCESS;
80 } else {
81#ifdef HWPERFMON_DEBUG
82 kprintf("perfmon_acquire_facility - DENIED: kernel is current owner and facility in use\n");
83#endif
84 retval = KERN_RESOURCE_SHORTAGE;
85 }
86 } else { /* non-kernel owner */
87#ifdef HWPERFMON_DEBUG
88 kprintf("perfmon_acquire_facility - DENIED: another active task owns the facility\n");
89#endif
90 retval = KERN_RESOURCE_SHORTAGE;
91 }
92 }
93
94 simple_unlock(&hw_perfmon_lock);
95 return retval;
96}
97
98int perfmon_release_facility(task_t task)
99{
100 kern_return_t retval = KERN_SUCCESS;
101 task_t old_perfmon_owner = hw_perfmon_owner;
102
103 simple_lock(&hw_perfmon_lock);
104
105 if(task!=hw_perfmon_owner) {
106 retval = KERN_NO_ACCESS;
107 } else {
108 if(old_perfmon_owner==kernel_task) {
109 if(hw_perfmon_thread_count>0) {
110#ifdef HWPERFMON_DEBUG
111 kprintf("perfmon_release_facility - NOT RELEASED: kernel task is owner and has active perfmon threads\n");
112#endif
113 retval = KERN_NO_ACCESS;
114 } else {
115#ifdef HWPERFMON_DEBUG
116 kprintf("perfmon_release_facility - RELEASED: kernel task was owner\n");
117#endif
118 hw_perfmon_owner = TASK_NULL;
119 retval = KERN_SUCCESS;
120 }
121 } else {
122#ifdef HWPERFMON_DEBUG
123 kprintf("perfmon_release_facility - RELEASED: user task was owner\n");
124#endif
125 hw_perfmon_owner = TASK_NULL;
126 retval = KERN_SUCCESS;
127 }
128 }
129
130 simple_unlock(&hw_perfmon_lock);
131 return retval;
132}
133
91447636 134int perfmon_enable(thread_t thread)
55e303ae 135{
91447636 136 struct savearea *sv = thread->machine.pcb;
55e303ae
A
137 kern_return_t kr;
138 kern_return_t retval = KERN_SUCCESS;
139 int curPMC;
140
91447636 141 if(thread->machine.specFlags & perfMonitor) {
55e303ae
A
142 return KERN_SUCCESS; /* already enabled */
143 } else if(perfmon_acquire_facility(kernel_task)!=KERN_SUCCESS) {
144 return KERN_RESOURCE_SHORTAGE; /* facility is in use */
145 } else { /* kernel_task owns the faciltity and this thread has not yet been counted */
146 simple_lock(&hw_perfmon_lock);
147 hw_perfmon_thread_count++;
148 simple_unlock(&hw_perfmon_lock);
149 }
150
151 sv->save_mmcr1 = 0;
152 sv->save_mmcr2 = 0;
153
91447636 154 switch(PerProcTable[0].ppe_vaddr->cpu_subtype) {
55e303ae
A
155 case CPU_SUBTYPE_POWERPC_750:
156 case CPU_SUBTYPE_POWERPC_7400:
157 case CPU_SUBTYPE_POWERPC_7450:
158 {
159 ppc32_mmcr0_reg_t mmcr0_reg;
160
161 mmcr0_reg.value = 0;
162 mmcr0_reg.field.disable_counters_always = TRUE;
163 mmcr0_reg.field.disable_counters_supervisor = TRUE; /* no choice */
164 sv->save_mmcr0 = mmcr0_reg.value;
165 }
166 break;
167 case CPU_SUBTYPE_POWERPC_970:
168 {
169 ppc64_mmcr0_reg_t mmcr0_reg;
170
171 mmcr0_reg.value = 0;
172 mmcr0_reg.field.disable_counters_always = TRUE;
173 mmcr0_reg.field.disable_counters_supervisor = TRUE; /* no choice */
174 sv->save_mmcr0 = mmcr0_reg.value;
175 }
176 break;
177 default:
178 retval = KERN_FAILURE;
179 break;
180 }
181
182 if(retval==KERN_SUCCESS) {
183 for(curPMC=0; curPMC<MAX_CPUPMC_COUNT; curPMC++) {
184 sv->save_pmc[curPMC] = 0;
91447636 185 thread->machine.pmcovfl[curPMC] = 0;
55e303ae 186 }
91447636
A
187 thread->machine.perfmonFlags = 0;
188 thread->machine.specFlags |= perfMonitor; /* enable perf monitor facility for this thread */
189 if(thread==current_thread()) {
190 getPerProc()->spcFlags |= perfMonitor; /* update per_proc */
55e303ae
A
191 }
192 }
193
194#ifdef HWPERFMON_DEBUG
195 kprintf("perfmon_enable - mmcr0=0x%llx mmcr1=0x%llx mmcr2=0x%llx\n", sv->save_mmcr0, sv->save_mmcr1, sv->save_mmcr2);
196#endif
197
198 return retval;
199}
200
91447636 201int perfmon_disable(thread_t thread)
55e303ae 202{
91447636 203 struct savearea *sv = thread->machine.pcb;
55e303ae
A
204 int curPMC;
205
91447636 206 if(!(thread->machine.specFlags & perfMonitor)) {
55e303ae
A
207 return KERN_NO_ACCESS; /* not enabled */
208 } else {
209 simple_lock(&hw_perfmon_lock);
210 hw_perfmon_thread_count--;
211 simple_unlock(&hw_perfmon_lock);
212 perfmon_release_facility(kernel_task); /* will release if hw_perfmon_thread_count is 0 */
213 }
214
91447636
A
215 thread->machine.specFlags &= ~perfMonitor; /* disable perf monitor facility for this thread */
216 if(thread==current_thread()) {
217 PerProcTable[cpu_number()].ppe_vaddr->spcFlags &= ~perfMonitor; /* update per_proc */
55e303ae
A
218 }
219 sv->save_mmcr0 = 0;
220 sv->save_mmcr1 = 0;
221 sv->save_mmcr2 = 0;
222
223 for(curPMC=0; curPMC<MAX_CPUPMC_COUNT; curPMC++) {
224 sv->save_pmc[curPMC] = 0;
91447636
A
225 thread->machine.pmcovfl[curPMC] = 0;
226 thread->machine.perfmonFlags = 0;
55e303ae
A
227 }
228
229#ifdef HWPERFMON_DEBUG
230 kprintf("perfmon_disable - mmcr0=0x%llx mmcr1=0x%llx mmcr2=0x%llx\n", sv->save_mmcr0, sv->save_mmcr1, sv->save_mmcr2);
231#endif
232
233 return KERN_SUCCESS;
234}
235
91447636 236int perfmon_clear_counters(thread_t thread)
55e303ae 237{
91447636 238 struct savearea *sv = thread->machine.pcb;
55e303ae
A
239 int curPMC;
240
241#ifdef HWPERFMON_DEBUG
242 kprintf("perfmon_clear_counters (CPU%d)\n", cpu_number());
243#endif
244
245 /* clear thread copy */
246 for(curPMC=0; curPMC<MAX_CPUPMC_COUNT; curPMC++) {
247 sv->save_pmc[curPMC] = 0;
91447636 248 thread->machine.pmcovfl[curPMC] = 0;
55e303ae
A
249 }
250
251 return KERN_SUCCESS;
252}
253
91447636 254int perfmon_write_counters(thread_t thread, uint64_t *pmcs)
55e303ae 255{
91447636 256 struct savearea *sv = thread->machine.pcb;
55e303ae
A
257 int curPMC;
258
259#ifdef HWPERFMON_DEBUG
260 kprintf("perfmon_write_counters (CPU%d): mmcr0 = %016llX, pmc1=%llX pmc2=%llX pmc3=%llX pmc4=%llX pmc5=%llX pmc6=%llX pmc7=%llX pmc8=%llX\n", cpu_number(), sv->save_mmcr0, pmcs[PMC_1], pmcs[PMC_2], pmcs[PMC_3], pmcs[PMC_4], pmcs[PMC_5], pmcs[PMC_6], pmcs[PMC_7], pmcs[PMC_8]);
261#endif
262
263 /* update thread copy */
264 for(curPMC=0; curPMC<MAX_CPUPMC_COUNT; curPMC++) {
265 sv->save_pmc[curPMC] = pmcs[curPMC] & 0x7FFFFFFF;
91447636 266 thread->machine.pmcovfl[curPMC] = (pmcs[curPMC]>>31) & 0xFFFFFFFF;
55e303ae
A
267 }
268
269 return KERN_SUCCESS;
270}
271
91447636 272int perfmon_read_counters(thread_t thread, uint64_t *pmcs)
55e303ae 273{
91447636 274 struct savearea *sv = thread->machine.pcb;
55e303ae
A
275 int curPMC;
276
277 /* retrieve from thread copy */
278 for(curPMC=0; curPMC<MAX_CPUPMC_COUNT; curPMC++) {
91447636 279 pmcs[curPMC] = thread->machine.pmcovfl[curPMC];
55e303ae
A
280 pmcs[curPMC] = pmcs[curPMC]<<31;
281 pmcs[curPMC] |= (sv->save_pmc[curPMC] & 0x7FFFFFFF);
282 }
283
284 /* zero any unused counters on this platform */
91447636 285 switch(PerProcTable[0].ppe_vaddr->cpu_subtype) {
55e303ae
A
286 case CPU_SUBTYPE_POWERPC_750:
287 case CPU_SUBTYPE_POWERPC_7400:
288 case CPU_SUBTYPE_POWERPC_7450:
289 pmcs[PMC_7] = 0;
290 pmcs[PMC_8] = 0;
291 break;
292 default:
293 break;
294 }
295
296#ifdef HWPERFMON_DEBUG
297 kprintf("perfmon_read_counters (CPU%d): mmcr0 = %016llX pmc1=%llX pmc2=%llX pmc3=%llX pmc4=%llX pmc5=%llX pmc6=%llX pmc7=%llX pmc8=%llX\n", cpu_number(), sv->save_mmcr0, pmcs[PMC_1], pmcs[PMC_2], pmcs[PMC_3], pmcs[PMC_4], pmcs[PMC_5], pmcs[PMC_6], pmcs[PMC_7], pmcs[PMC_8]);
298#endif
299
300 return KERN_SUCCESS;
301}
302
91447636 303int perfmon_start_counters(thread_t thread)
55e303ae 304{
91447636 305 struct savearea *sv = thread->machine.pcb;
55e303ae
A
306 kern_return_t retval = KERN_SUCCESS;
307
91447636 308 switch(PerProcTable[0].ppe_vaddr->cpu_subtype) {
55e303ae
A
309 case CPU_SUBTYPE_POWERPC_750:
310 case CPU_SUBTYPE_POWERPC_7400:
311 {
312 ppc32_mmcr0_reg_t mmcr0_reg;
313 mmcr0_reg.value = sv->save_mmcr0;
314 mmcr0_reg.field.disable_counters_always = FALSE;
315 /* XXXXX PMI broken on 750, 750CX, 750FX, 7400 and 7410 v1.2 and earlier XXXXX */
316 mmcr0_reg.field.on_pmi_stop_counting = FALSE;
317 mmcr0_reg.field.enable_pmi = FALSE;
318 mmcr0_reg.field.enable_pmi_on_pmc1 = FALSE;
319 mmcr0_reg.field.enable_pmi_on_pmcn = FALSE;
320 sv->save_mmcr0 = mmcr0_reg.value;
321 }
322 break;
323 case CPU_SUBTYPE_POWERPC_7450:
324 {
325 ppc32_mmcr0_reg_t mmcr0_reg;
326 mmcr0_reg.value = sv->save_mmcr0;
327 mmcr0_reg.field.disable_counters_always = FALSE;
328 mmcr0_reg.field.on_pmi_stop_counting = TRUE;
329 mmcr0_reg.field.enable_pmi = TRUE;
330 mmcr0_reg.field.enable_pmi_on_pmc1 = TRUE;
331 mmcr0_reg.field.enable_pmi_on_pmcn = TRUE;
332 sv->save_mmcr0 = mmcr0_reg.value;
333 }
334 break;
335 case CPU_SUBTYPE_POWERPC_970:
336 {
337 ppc64_mmcr0_reg_t mmcr0_reg;
338 mmcr0_reg.value = sv->save_mmcr0;
339 mmcr0_reg.field.disable_counters_always = FALSE;
340 mmcr0_reg.field.on_pmi_stop_counting = TRUE;
341 mmcr0_reg.field.enable_pmi = TRUE;
342 mmcr0_reg.field.enable_pmi_on_pmc1 = TRUE;
343 mmcr0_reg.field.enable_pmi_on_pmcn = TRUE;
344 sv->save_mmcr0 = mmcr0_reg.value;
345 }
346 break;
347 default:
348 retval = KERN_FAILURE;
349 break;
350 }
351
352#ifdef HWPERFMON_DEBUG
353 kprintf("perfmon_start_counters (CPU%d) - mmcr0=0x%llx mmcr1=0x%llx mmcr2=0x%llx\n", cpu_number(), sv->save_mmcr0, sv->save_mmcr1, sv->save_mmcr2);
354#endif
355
356 return retval;
357}
358
91447636 359int perfmon_stop_counters(thread_t thread)
55e303ae 360{
91447636 361 struct savearea *sv = thread->machine.pcb;
55e303ae
A
362 kern_return_t retval = KERN_SUCCESS;
363
91447636 364 switch(PerProcTable[0].ppe_vaddr->cpu_subtype) {
55e303ae
A
365 case CPU_SUBTYPE_POWERPC_750:
366 case CPU_SUBTYPE_POWERPC_7400:
367 case CPU_SUBTYPE_POWERPC_7450:
368 {
369 ppc32_mmcr0_reg_t mmcr0_reg;
370 mmcr0_reg.value = sv->save_mmcr0;
371 mmcr0_reg.field.disable_counters_always = TRUE;
372 sv->save_mmcr0 = mmcr0_reg.value;
373 }
374 break;
375 case CPU_SUBTYPE_POWERPC_970:
376 {
377 ppc64_mmcr0_reg_t mmcr0_reg;
378 mmcr0_reg.value = sv->save_mmcr0;
379 mmcr0_reg.field.disable_counters_always = TRUE;
380 sv->save_mmcr0 = mmcr0_reg.value;
381 }
382 break;
383 default:
384 retval = KERN_FAILURE;
385 break;
386 }
387
388#ifdef HWPERFMON_DEBUG
389 kprintf("perfmon_stop_counters (CPU%d) - mmcr0=0x%llx mmcr1=0x%llx mmcr2=0x%llx\n", cpu_number(), sv->save_mmcr0, sv->save_mmcr1, sv->save_mmcr2);
390#endif
391
392 return retval;
393}
394
91447636 395int perfmon_set_event(thread_t thread, int pmc, int event)
55e303ae 396{
91447636 397 struct savearea *sv = thread->machine.pcb;
55e303ae
A
398 kern_return_t retval = KERN_SUCCESS;
399
400#ifdef HWPERFMON_DEBUG
401 kprintf("perfmon_set_event b4 (CPU%d) - pmc=%d, event=%d - mmcr0=0x%llx mmcr1=0x%llx mmcr2=0x%llx\n", cpu_number(), pmc, event, sv->save_mmcr0, sv->save_mmcr1, sv->save_mmcr2);
402#endif
403
91447636 404 switch(PerProcTable[0].ppe_vaddr->cpu_subtype) {
55e303ae
A
405 case CPU_SUBTYPE_POWERPC_750:
406 case CPU_SUBTYPE_POWERPC_7400:
407 {
408 ppc32_mmcr0_reg_t mmcr0_reg;
409 ppc32_mmcr1_reg_t mmcr1_reg;
410
411 mmcr0_reg.value = sv->save_mmcr0;
412 mmcr1_reg.value = sv->save_mmcr1;
413
414 switch(pmc) {
415 case PMC_1:
416 mmcr0_reg.field.pmc1_event = event;
417 sv->save_mmcr0 = mmcr0_reg.value;
418 break;
419 case PMC_2:
420 mmcr0_reg.field.pmc2_event = event;
421 sv->save_mmcr0 = mmcr0_reg.value;
422 break;
423 case PMC_3:
424 mmcr1_reg.field.pmc3_event = event;
425 sv->save_mmcr1 = mmcr1_reg.value;
426 break;
427 case PMC_4:
428 mmcr1_reg.field.pmc4_event = event;
429 sv->save_mmcr1 = mmcr1_reg.value;
430 break;
431 default:
432 retval = KERN_FAILURE;
433 break;
434 }
435 }
436 break;
437 case CPU_SUBTYPE_POWERPC_7450:
438 {
439 ppc32_mmcr0_reg_t mmcr0_reg;
440 ppc32_mmcr1_reg_t mmcr1_reg;
441
442 mmcr0_reg.value = sv->save_mmcr0;
443 mmcr1_reg.value = sv->save_mmcr1;
444
445 switch(pmc) {
446 case PMC_1:
447 mmcr0_reg.field.pmc1_event = event;
448 sv->save_mmcr0 = mmcr0_reg.value;
449 break;
450 case PMC_2:
451 mmcr0_reg.field.pmc2_event = event;
452 sv->save_mmcr0 = mmcr0_reg.value;
453 break;
454 case PMC_3:
455 mmcr1_reg.field.pmc3_event = event;
456 sv->save_mmcr1 = mmcr1_reg.value;
457 break;
458 case PMC_4:
459 mmcr1_reg.field.pmc4_event = event;
460 sv->save_mmcr1 = mmcr1_reg.value;
461 break;
462 case PMC_5:
463 mmcr1_reg.field.pmc5_event = event;
464 sv->save_mmcr1 = mmcr1_reg.value;
465 break;
466 case PMC_6:
467 mmcr1_reg.field.pmc6_event = event;
468 sv->save_mmcr1 = mmcr1_reg.value;
469 break;
470 default:
471 retval = KERN_FAILURE;
472 break;
473 }
474 }
475 break;
476 case CPU_SUBTYPE_POWERPC_970:
477 {
478 ppc64_mmcr0_reg_t mmcr0_reg;
479 ppc64_mmcr1_reg_t mmcr1_reg;
480
481 mmcr0_reg.value = sv->save_mmcr0;
482 mmcr1_reg.value = sv->save_mmcr1;
483
484 switch(pmc) {
485 case PMC_1:
486 mmcr0_reg.field.pmc1_event = event;
487 sv->save_mmcr0 = mmcr0_reg.value;
488 break;
489 case PMC_2:
490 mmcr0_reg.field.pmc2_event = event;
491 sv->save_mmcr0 = mmcr0_reg.value;
492 break;
493 case PMC_3:
494 mmcr1_reg.field.pmc3_event = event;
495 sv->save_mmcr1 = mmcr1_reg.value;
496 break;
497 case PMC_4:
498 mmcr1_reg.field.pmc4_event = event;
499 sv->save_mmcr1 = mmcr1_reg.value;
500 break;
501 case PMC_5:
502 mmcr1_reg.field.pmc5_event = event;
503 sv->save_mmcr1 = mmcr1_reg.value;
504 break;
505 case PMC_6:
506 mmcr1_reg.field.pmc6_event = event;
507 sv->save_mmcr1 = mmcr1_reg.value;
508 break;
509 case PMC_7:
510 mmcr1_reg.field.pmc7_event = event;
511 sv->save_mmcr1 = mmcr1_reg.value;
512 break;
513 case PMC_8:
514 mmcr1_reg.field.pmc8_event = event;
515 sv->save_mmcr1 = mmcr1_reg.value;
516 break;
517 default:
518 retval = KERN_FAILURE;
519 break;
520 }
521 }
522 break;
523 default:
524 retval = KERN_FAILURE;
525 break;
526 }
527
528#ifdef HWPERFMON_DEBUG
529 kprintf("perfmon_set_event (CPU%d) - pmc=%d, event=%d - mmcr0=0x%llx mmcr1=0x%llx mmcr2=0x%llx\n", cpu_number(), pmc, event, sv->save_mmcr0, sv->save_mmcr1, sv->save_mmcr2);
530#endif
531
532 return retval;
533}
534
91447636 535int perfmon_set_event_func(thread_t thread, uint32_t f)
55e303ae 536{
91447636 537 struct savearea *sv = thread->machine.pcb;
55e303ae
A
538 kern_return_t retval = KERN_SUCCESS;
539
540#ifdef HWPERFMON_DEBUG
541 kprintf("perfmon_set_event_func - func=%s\n",
542 f==PPC_PERFMON_FUNC_FPU ? "FUNC" :
543 f==PPC_PERFMON_FUNC_ISU ? "ISU" :
544 f==PPC_PERFMON_FUNC_IFU ? "IFU" :
545 f==PPC_PERFMON_FUNC_VMX ? "VMX" :
546 f==PPC_PERFMON_FUNC_IDU ? "IDU" :
547 f==PPC_PERFMON_FUNC_GPS ? "GPS" :
548 f==PPC_PERFMON_FUNC_LSU0 ? "LSU0" :
549 f==PPC_PERFMON_FUNC_LSU1A ? "LSU1A" :
550 f==PPC_PERFMON_FUNC_LSU1B ? "LSU1B" :
551 f==PPC_PERFMON_FUNC_SPECA ? "SPECA" :
552 f==PPC_PERFMON_FUNC_SPECB ? "SPECB" :
553 f==PPC_PERFMON_FUNC_SPECC ? "SPECC" :
554 "UNKNOWN");
555#endif /* HWPERFMON_DEBUG */
556
91447636 557 switch(PerProcTable[0].ppe_vaddr->cpu_subtype) {
55e303ae
A
558 case CPU_SUBTYPE_POWERPC_750:
559 case CPU_SUBTYPE_POWERPC_7400:
560 case CPU_SUBTYPE_POWERPC_7450:
561 retval = KERN_FAILURE; /* event functional unit only applies to 970 */
562 break;
563 case CPU_SUBTYPE_POWERPC_970:
564 {
565 ppc64_mmcr1_reg_t mmcr1_reg;
566 ppc_func_unit_t func_unit;
567
568 func_unit.value = f;
569 mmcr1_reg.value = sv->save_mmcr1;
570
571 mmcr1_reg.field.ttm0_select = func_unit.field.TTM0SEL;
572 mmcr1_reg.field.ttm1_select = func_unit.field.TTM1SEL;
573 mmcr1_reg.field.ttm2_select = 0; /* not used */
574 mmcr1_reg.field.ttm3_select = func_unit.field.TTM3SEL;
575 mmcr1_reg.field.speculative_event = func_unit.field.SPECSEL;
576 mmcr1_reg.field.lane0_select = func_unit.field.TD_CP_DBGxSEL;
577 mmcr1_reg.field.lane1_select = func_unit.field.TD_CP_DBGxSEL;
578 mmcr1_reg.field.lane2_select = func_unit.field.TD_CP_DBGxSEL;
579 mmcr1_reg.field.lane3_select = func_unit.field.TD_CP_DBGxSEL;
580
581 sv->save_mmcr1 = mmcr1_reg.value;
582 }
583 break;
584 default:
585 retval = KERN_FAILURE;
586 break;
587 }
588
589 return retval;
590}
591
91447636 592int perfmon_set_threshold(thread_t thread, int threshold)
55e303ae 593{
91447636 594 struct savearea *sv = thread->machine.pcb;
55e303ae
A
595 kern_return_t retval = KERN_SUCCESS;
596
91447636 597 switch(PerProcTable[0].ppe_vaddr->cpu_subtype) {
55e303ae
A
598 case CPU_SUBTYPE_POWERPC_750:
599 {
600 ppc32_mmcr0_reg_t mmcr0_reg;
601
602 mmcr0_reg.value = sv->save_mmcr0;
603
604 if(threshold>63) { /* no multiplier on 750 */
605 int newThreshold = 63;
606#ifdef HWPERFMON_DEBUG
607 kprintf("perfmon_set_threshold - WARNING: supplied threshold (%d) exceeds max threshold value - clamping to %d\n", threshold, newThreshold);
608#endif
609 threshold = newThreshold;
610 }
611 mmcr0_reg.field.threshold_value = threshold;
612
613 sv->save_mmcr0 = mmcr0_reg.value;
614 }
615 break;
616
617 case CPU_SUBTYPE_POWERPC_7400:
618 case CPU_SUBTYPE_POWERPC_7450:
619 {
620 ppc32_mmcr0_reg_t mmcr0_reg;
621 ppc32_mmcr2_reg_t mmcr2_reg;
622
623 mmcr0_reg.value = sv->save_mmcr0;
624 mmcr2_reg.value = sv->save_mmcr2;
625
626 if(threshold<=(2*63)) { /* 2x multiplier */
627 if(threshold%2 != 0) {
628 int newThreshold = 2*(threshold/2);
629#ifdef HWPERFMON_DEBUG
630 kprintf("perfmon_set_threshold - WARNING: supplied threshold (%d) is not evenly divisible by 2x multiplier - using threshold of %d instead\n", threshold, newThreshold);
631#endif
632 threshold = newThreshold;
633 }
634 mmcr2_reg.field.threshold_multiplier = 0;
635 } else if(threshold<=(32*63)) { /* 32x multiplier */
636 if(threshold%32 != 0) {
637 int newThreshold = 32*(threshold/32);
638#ifdef HWPERFMON_DEBUG
639 kprintf("perfmon_set_threshold - WARNING: supplied threshold (%d) is not evenly divisible by 32x multiplier - using threshold of %d instead\n", threshold, newThreshold);
640#endif
641 threshold = newThreshold;
642 }
643 mmcr2_reg.field.threshold_multiplier = 1;
644 } else {
645 int newThreshold = 32*63;
646#ifdef HWPERFMON_DEBUG
647 kprintf("perfmon_set_threshold - WARNING: supplied threshold (%d) exceeds max threshold value - clamping to %d\n", threshold, newThreshold);
648#endif
649 threshold = newThreshold;
650 mmcr2_reg.field.threshold_multiplier = 1;
651 }
652 mmcr0_reg.field.threshold_value = threshold;
653
654 sv->save_mmcr0 = mmcr0_reg.value;
655 sv->save_mmcr2 = mmcr2_reg.value;
656
657 }
658 break;
659 case CPU_SUBTYPE_POWERPC_970:
660 {
661 ppc64_mmcr0_reg_t mmcr0_reg;
662
663 mmcr0_reg.value = sv->save_mmcr0;
664
665 if(threshold>63) { /* multiplier is in HID1 on 970 - not context switching HID1 so always 1x */
666 int newThreshold = 63;
667#ifdef HWPERFMON_DEBUG
668 kprintf("perfmon_set_threshold - WARNING: supplied threshold (%d) exceeds max threshold value - clamping to %d\n", threshold, newThreshold);
669#endif
670 threshold = newThreshold;
671 }
672 mmcr0_reg.field.threshold_value = threshold;
673
674 sv->save_mmcr0 = mmcr0_reg.value;
675 }
676 break;
677 default:
678 retval = KERN_FAILURE;
679 break;
680 }
681
682#ifdef HWPERFMON_DEBUG
683 kprintf("perfmon_set_threshold - threshold=%d - mmcr0=0x%llx mmcr1=0x%llx mmcr2=0x%llx\n", threshold, sv->save_mmcr0, sv->save_mmcr1, sv->save_mmcr2);
684#endif
685
686 return retval;
687}
688
91447636 689int perfmon_set_tbsel(thread_t thread, int tbsel)
55e303ae 690{
91447636 691 struct savearea *sv = thread->machine.pcb;
55e303ae
A
692 kern_return_t retval = KERN_SUCCESS;
693
91447636 694 switch(PerProcTable[0].ppe_vaddr->cpu_subtype) {
55e303ae
A
695 case CPU_SUBTYPE_POWERPC_750:
696 case CPU_SUBTYPE_POWERPC_7400:
697 case CPU_SUBTYPE_POWERPC_7450:
698 {
699 ppc32_mmcr0_reg_t mmcr0_reg;
700
701 mmcr0_reg.value = sv->save_mmcr0;
702 switch(tbsel) {
703 case 0x0:
704 case 0x1:
705 case 0x2:
706 case 0x3:
707 mmcr0_reg.field.timebase_bit_selector = tbsel;
708 break;
709 default:
710 retval = KERN_FAILURE;
711 }
712 sv->save_mmcr0 = mmcr0_reg.value;
713 }
714 break;
715 case CPU_SUBTYPE_POWERPC_970:
716 {
717 ppc64_mmcr0_reg_t mmcr0_reg;
718
719 mmcr0_reg.value = sv->save_mmcr0;
720 switch(tbsel) {
721 case 0x0:
722 case 0x1:
723 case 0x2:
724 case 0x3:
725 mmcr0_reg.field.timebase_bit_selector = tbsel;
726 break;
727 default:
728 retval = KERN_FAILURE;
729 }
730 sv->save_mmcr0 = mmcr0_reg.value;
731 }
732 break;
733 default:
734 retval = KERN_FAILURE;
735 break;
736 }
737
738#ifdef HWPERFMON_DEBUG
739 kprintf("perfmon_set_tbsel - tbsel=%d - mmcr0=0x%llx mmcr1=0x%llx mmcr2=0x%llx\n", tbsel, sv->save_mmcr0, sv->save_mmcr1, sv->save_mmcr2);
740#endif
741
742 return retval;
743}
744
745int perfmon_control(struct savearea *ssp)
746{
747 mach_port_t thr_port = CAST_DOWN(mach_port_t, ssp->save_r3);
748 int action = (int)ssp->save_r4;
749 int pmc = (int)ssp->save_r5;
750 int val = (int)ssp->save_r6;
751 uint64_t *usr_pmcs_p = CAST_DOWN(uint64_t *, ssp->save_r7);
91447636 752 thread_t thread = THREAD_NULL;
55e303ae
A
753 uint64_t kern_pmcs[MAX_CPUPMC_COUNT];
754 kern_return_t retval = KERN_SUCCESS;
755 int error;
756 boolean_t oldlevel;
757
91447636
A
758 thread = (thread_t) port_name_to_thread(thr_port); // convert user space thread port name to a thread_t
759 if(!thread) {
55e303ae
A
760 ssp->save_r3 = KERN_INVALID_ARGUMENT;
761 return 1; /* Return and check for ASTs... */
762 }
763
91447636
A
764 if(thread!=current_thread()) {
765 thread_suspend(thread);
55e303ae
A
766 }
767
768#ifdef HWPERFMON_DEBUG
769 // kprintf("perfmon_control: action=0x%x pmc=%d val=%d pmcs=0x%x\n", action, pmc, val, usr_pmcs_p);
770#endif
771
772 oldlevel = ml_set_interrupts_enabled(FALSE);
773
774 /* individual actions which do not require perfmon facility to be enabled */
775 if(action==PPC_PERFMON_DISABLE) {
91447636 776 retval = perfmon_disable(thread);
55e303ae
A
777 }
778 else if(action==PPC_PERFMON_ENABLE) {
91447636 779 retval = perfmon_enable(thread);
55e303ae
A
780 }
781
782 else { /* individual actions which do require perfmon facility to be enabled */
91447636 783 if(!(thread->machine.specFlags & perfMonitor)) { /* perfmon not enabled */
55e303ae
A
784#ifdef HWPERFMON_DEBUG
785 kprintf("perfmon_control: ERROR - perfmon not enabled for this thread\n");
786#endif
787 retval = KERN_NO_ACCESS;
788 goto perfmon_return;
789 }
790
791 if(action==PPC_PERFMON_SET_EVENT) {
91447636 792 retval = perfmon_set_event(thread, pmc, val);
55e303ae
A
793 }
794 else if(action==PPC_PERFMON_SET_THRESHOLD) {
91447636 795 retval = perfmon_set_threshold(thread, val);
55e303ae
A
796 }
797 else if(action==PPC_PERFMON_SET_TBSEL) {
91447636 798 retval = perfmon_set_tbsel(thread, val);
55e303ae
A
799 }
800 else if(action==PPC_PERFMON_SET_EVENT_FUNC) {
91447636 801 retval = perfmon_set_event_func(thread, val);
55e303ae
A
802 }
803 else if(action==PPC_PERFMON_ENABLE_PMI_BRKPT) {
804 if(val) {
91447636 805 thread->machine.perfmonFlags |= PERFMONFLAG_BREAKPOINT_FOR_PMI;
55e303ae 806 } else {
91447636 807 thread->machine.perfmonFlags &= ~PERFMONFLAG_BREAKPOINT_FOR_PMI;
55e303ae
A
808 }
809 retval = KERN_SUCCESS;
810 }
811
812 /* combinable actions */
813 else {
814 if(action & PPC_PERFMON_STOP_COUNTERS) {
91447636 815 error = perfmon_stop_counters(thread);
55e303ae
A
816 if(error!=KERN_SUCCESS) {
817 retval = error;
818 goto perfmon_return;
819 }
820 }
821 if(action & PPC_PERFMON_CLEAR_COUNTERS) {
91447636 822 error = perfmon_clear_counters(thread);
55e303ae
A
823 if(error!=KERN_SUCCESS) {
824 retval = error;
825 goto perfmon_return;
826 }
827 }
828 if(action & PPC_PERFMON_WRITE_COUNTERS) {
91447636 829 if(error = copyin(CAST_USER_ADDR_T(usr_pmcs_p), (void *)kern_pmcs, MAX_CPUPMC_COUNT*sizeof(uint64_t))) {
55e303ae
A
830 retval = error;
831 goto perfmon_return;
832 }
91447636 833 error = perfmon_write_counters(thread, kern_pmcs);
55e303ae
A
834 if(error!=KERN_SUCCESS) {
835 retval = error;
836 goto perfmon_return;
837 }
838 }
839 if(action & PPC_PERFMON_READ_COUNTERS) {
91447636 840 error = perfmon_read_counters(thread, kern_pmcs);
55e303ae
A
841 if(error!=KERN_SUCCESS) {
842 retval = error;
843 goto perfmon_return;
844 }
91447636 845 if(error = copyout((void *)kern_pmcs, CAST_USER_ADDR_T(usr_pmcs_p), MAX_CPUPMC_COUNT*sizeof(uint64_t))) {
55e303ae
A
846 retval = error;
847 goto perfmon_return;
848 }
849 }
850 if(action & PPC_PERFMON_START_COUNTERS) {
91447636 851 error = perfmon_start_counters(thread);
55e303ae
A
852 if(error!=KERN_SUCCESS) {
853 retval = error;
854 goto perfmon_return;
855 }
856 }
857 }
858 }
859
860 perfmon_return:
861 ml_set_interrupts_enabled(oldlevel);
862
863#ifdef HWPERFMON_DEBUG
864 kprintf("perfmon_control (CPU%d): mmcr0 = %016llX, pmc1=%X pmc2=%X pmc3=%X pmc4=%X pmc5=%X pmc6=%X pmc7=%X pmc8=%X\n", cpu_number(), ssp->save_mmcr0, ssp->save_pmc[PMC_1], ssp->save_pmc[PMC_2], ssp->save_pmc[PMC_3], ssp->save_pmc[PMC_4], ssp->save_pmc[PMC_5], ssp->save_pmc[PMC_6], ssp->save_pmc[PMC_7], ssp->save_pmc[PMC_8]);
865#endif
866
91447636
A
867 if(thread!=current_thread()) {
868 thread_resume(thread);
55e303ae
A
869 }
870
871#ifdef HWPERFMON_DEBUG
872 if(retval!=KERN_SUCCESS) {
873 kprintf("perfmon_control - ERROR: retval=%d\n", retval);
874 }
875#endif /* HWPERFMON_DEBUG */
876
877 ssp->save_r3 = retval;
878 return 1; /* Return and check for ASTs... */
879}
880
881int perfmon_handle_pmi(struct savearea *ssp)
882{
883 int curPMC;
884 kern_return_t retval = KERN_SUCCESS;
91447636 885 thread_t thread = current_thread();
55e303ae
A
886
887#ifdef HWPERFMON_DEBUG
888 kprintf("perfmon_handle_pmi: got rupt\n");
889#endif
890
91447636 891 if(!(thread->machine.specFlags & perfMonitor)) { /* perfmon not enabled */
55e303ae
A
892#ifdef HWPERFMON_DEBUG
893 kprintf("perfmon_handle_pmi: ERROR - perfmon not enabled for this thread\n");
894#endif
895 return KERN_FAILURE;
896 }
897
898 for(curPMC=0; curPMC<MAX_CPUPMC_COUNT; curPMC++) {
91447636
A
899 if(thread->machine.pcb->save_pmc[curPMC] & 0x80000000) {
900 if(thread->machine.pmcovfl[curPMC]==0xFFFFFFFF && (thread->machine.perfmonFlags & PERFMONFLAG_BREAKPOINT_FOR_PMI)) {
55e303ae
A
901 doexception(EXC_BREAKPOINT, EXC_PPC_PERFMON, (unsigned int)ssp->save_srr0); // pass up a breakpoint exception
902 return KERN_SUCCESS;
903 } else {
91447636
A
904 thread->machine.pmcovfl[curPMC]++;
905 thread->machine.pcb->save_pmc[curPMC] = 0;
55e303ae
A
906 }
907 }
908 }
909
910 if(retval==KERN_SUCCESS) {
91447636 911 switch(PerProcTable[0].ppe_vaddr->cpu_subtype) {
55e303ae
A
912 case CPU_SUBTYPE_POWERPC_7450:
913 {
914 ppc32_mmcr0_reg_t mmcr0_reg;
915
91447636 916 mmcr0_reg.value = thread->machine.pcb->save_mmcr0;
55e303ae
A
917 mmcr0_reg.field.disable_counters_always = FALSE;
918 mmcr0_reg.field.enable_pmi = TRUE;
91447636 919 thread->machine.pcb->save_mmcr0 = mmcr0_reg.value;
55e303ae
A
920 }
921 retval = KERN_SUCCESS;
922 break;
923 case CPU_SUBTYPE_POWERPC_970:
924 {
925 ppc64_mmcr0_reg_t mmcr0_reg;
926
91447636 927 mmcr0_reg.value = thread->machine.pcb->save_mmcr0;
55e303ae
A
928 mmcr0_reg.field.disable_counters_always = FALSE;
929 mmcr0_reg.field.enable_pmi = TRUE;
91447636 930 thread->machine.pcb->save_mmcr0 = mmcr0_reg.value;
55e303ae
A
931 }
932 retval = KERN_SUCCESS;
933 break;
934 default:
935 retval = KERN_FAILURE;
936 break;
937 }
938 }
939
940 return retval;
941}