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