hwpmc_mips.c revision 233319
1204635Sgnn/*-
2204635Sgnn * Copyright (c) 2010, George V. Neville-Neil <gnn@freebsd.org>
3204635Sgnn * All rights reserved.
4204635Sgnn *
5204635Sgnn * Redistribution and use in source and binary forms, with or without
6204635Sgnn * modification, are permitted provided that the following conditions
7204635Sgnn * are met:
8204635Sgnn * 1. Redistributions of source code must retain the above copyright
9204635Sgnn *    notice, this list of conditions and the following disclaimer.
10204635Sgnn * 2. Redistributions in binary form must reproduce the above copyright
11204635Sgnn *    notice, this list of conditions and the following disclaimer in the
12204635Sgnn *    documentation and/or other materials provided with the distribution.
13204635Sgnn *
14204635Sgnn * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15204635Sgnn * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16204635Sgnn * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17204635Sgnn * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18204635Sgnn * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19204635Sgnn * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20204635Sgnn * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21204635Sgnn * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22204635Sgnn * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23204635Sgnn * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24204635Sgnn * SUCH DAMAGE.
25204635Sgnn *
26204635Sgnn */
27204635Sgnn
28204635Sgnn#include <sys/cdefs.h>
29204635Sgnn__FBSDID("$FreeBSD: head/sys/dev/hwpmc/hwpmc_mips.c 233319 2012-03-22 18:01:23Z gonzo $");
30204635Sgnn
31233319Sgonzo#include "opt_hwpmc_hooks.h"
32233319Sgonzo
33204635Sgnn#include <sys/param.h>
34204635Sgnn#include <sys/pmc.h>
35233319Sgonzo#include <sys/pmckern.h>
36204635Sgnn#include <sys/systm.h>
37204635Sgnn
38204635Sgnn#include <machine/pmc_mdep.h>
39204635Sgnn#include <machine/md_var.h>
40232846Sgonzo#include <machine/mips_opcode.h>
41232846Sgonzo#include <machine/vmparam.h>
42204635Sgnn
43233319Sgonzoint mips_npmcs;
44233319Sgonzo
45233319Sgonzo/*
46233319Sgonzo * Per-processor information.
47233319Sgonzo */
48233319Sgonzostruct mips_cpu {
49233319Sgonzo	struct pmc_hw	*pc_mipspmcs;
50233319Sgonzo};
51233319Sgonzo
52233319Sgonzostatic struct mips_cpu **mips_pcpu;
53233319Sgonzo
54232846Sgonzo#if defined(__mips_n64)
55232846Sgonzo#	define	MIPS_IS_VALID_KERNELADDR(reg)	((((reg) & 3) == 0) && \
56232846Sgonzo					((vm_offset_t)(reg) >= MIPS_XKPHYS_START))
57232846Sgonzo#else
58232846Sgonzo#	define	MIPS_IS_VALID_KERNELADDR(reg)	((((reg) & 3) == 0) && \
59232846Sgonzo					((vm_offset_t)(reg) >= MIPS_KSEG0_START))
60232846Sgonzo#endif
61232846Sgonzo
62232846Sgonzo/*
63232846Sgonzo * We need some reasonable default to prevent backtrace code
64232846Sgonzo * from wandering too far
65232846Sgonzo */
66232846Sgonzo#define	MAX_FUNCTION_SIZE 0x10000
67232846Sgonzo#define	MAX_PROLOGUE_SIZE 0x100
68232846Sgonzo
69232846Sgonzostatic int
70233319Sgonzomips_allocate_pmc(int cpu, int ri, struct pmc *pm,
71233319Sgonzo  const struct pmc_op_pmcallocate *a)
72233319Sgonzo{
73233319Sgonzo	enum pmc_event pe;
74233319Sgonzo	uint32_t caps, config, counter;
75233319Sgonzo	uint32_t event;
76233319Sgonzo	int i;
77233319Sgonzo
78233319Sgonzo	KASSERT(cpu >= 0 && cpu < pmc_cpu_max(),
79233319Sgonzo	    ("[mips,%d] illegal CPU value %d", __LINE__, cpu));
80233319Sgonzo	KASSERT(ri >= 0 && ri < mips_npmcs,
81233319Sgonzo	    ("[mips,%d] illegal row index %d", __LINE__, ri));
82233319Sgonzo
83233319Sgonzo	caps = a->pm_caps;
84233319Sgonzo	if (a->pm_class != mips_pmc_spec.ps_cpuclass)
85233319Sgonzo		return (EINVAL);
86233319Sgonzo	pe = a->pm_ev;
87233319Sgonzo	counter = MIPS_CTR_ALL;
88233319Sgonzo	event = 0;
89233319Sgonzo	for (i = 0; i < mips_event_codes_size; i++) {
90233319Sgonzo		if (mips_event_codes[i].pe_ev == pe) {
91233319Sgonzo			event = mips_event_codes[i].pe_code;
92233319Sgonzo			counter =  mips_event_codes[i].pe_counter;
93233319Sgonzo			break;
94233319Sgonzo		}
95233319Sgonzo	}
96233319Sgonzo
97233319Sgonzo	if (i == mips_event_codes_size)
98233319Sgonzo		return (EINVAL);
99233319Sgonzo
100233319Sgonzo	if ((counter != MIPS_CTR_ALL) && (counter != ri))
101233319Sgonzo		return (EINVAL);
102233319Sgonzo
103233319Sgonzo	config = mips_get_perfctl(cpu, ri, event, caps);
104233319Sgonzo
105233319Sgonzo	pm->pm_md.pm_mips_evsel = config;
106233319Sgonzo
107233319Sgonzo	PMCDBG(MDP,ALL,2,"mips-allocate ri=%d -> config=0x%x", ri, config);
108233319Sgonzo
109233319Sgonzo	return 0;
110233319Sgonzo}
111233319Sgonzo
112233319Sgonzo
113233319Sgonzostatic int
114233319Sgonzomips_read_pmc(int cpu, int ri, pmc_value_t *v)
115233319Sgonzo{
116233319Sgonzo	struct pmc *pm;
117233319Sgonzo	pmc_value_t tmp;
118233319Sgonzo
119233319Sgonzo	KASSERT(cpu >= 0 && cpu < pmc_cpu_max(),
120233319Sgonzo	    ("[mips,%d] illegal CPU value %d", __LINE__, cpu));
121233319Sgonzo	KASSERT(ri >= 0 && ri < mips_npmcs,
122233319Sgonzo	    ("[mips,%d] illegal row index %d", __LINE__, ri));
123233319Sgonzo
124233319Sgonzo	pm  = mips_pcpu[cpu]->pc_mipspmcs[ri].phw_pmc;
125233319Sgonzo	tmp = mips_pmcn_read(ri);
126233319Sgonzo	PMCDBG(MDP,REA,2,"mips-read id=%d -> %jd", ri, tmp);
127233319Sgonzo
128233319Sgonzo	if (PMC_IS_SAMPLING_MODE(PMC_TO_MODE(pm)))
129233319Sgonzo		*v = tmp - (1UL << (mips_pmc_spec.ps_counter_width - 1));
130233319Sgonzo	else
131233319Sgonzo		*v = tmp;
132233319Sgonzo
133233319Sgonzo	return 0;
134233319Sgonzo}
135233319Sgonzo
136233319Sgonzostatic int
137233319Sgonzomips_write_pmc(int cpu, int ri, pmc_value_t v)
138233319Sgonzo{
139233319Sgonzo	struct pmc *pm;
140233319Sgonzo
141233319Sgonzo	KASSERT(cpu >= 0 && cpu < pmc_cpu_max(),
142233319Sgonzo	    ("[mips,%d] illegal CPU value %d", __LINE__, cpu));
143233319Sgonzo	KASSERT(ri >= 0 && ri < mips_npmcs,
144233319Sgonzo	    ("[mips,%d] illegal row-index %d", __LINE__, ri));
145233319Sgonzo
146233319Sgonzo	pm  = mips_pcpu[cpu]->pc_mipspmcs[ri].phw_pmc;
147233319Sgonzo
148233319Sgonzo	if (PMC_IS_SAMPLING_MODE(PMC_TO_MODE(pm)))
149233319Sgonzo		v = (1UL << (mips_pmc_spec.ps_counter_width - 1)) - v;
150233319Sgonzo
151233319Sgonzo	PMCDBG(MDP,WRI,1,"mips-write cpu=%d ri=%d v=%jx", cpu, ri, v);
152233319Sgonzo
153233319Sgonzo	mips_pmcn_write(ri, v);
154233319Sgonzo
155233319Sgonzo	return 0;
156233319Sgonzo}
157233319Sgonzo
158233319Sgonzostatic int
159233319Sgonzomips_config_pmc(int cpu, int ri, struct pmc *pm)
160233319Sgonzo{
161233319Sgonzo	struct pmc_hw *phw;
162233319Sgonzo
163233319Sgonzo	PMCDBG(MDP,CFG,1, "cpu=%d ri=%d pm=%p", cpu, ri, pm);
164233319Sgonzo
165233319Sgonzo	KASSERT(cpu >= 0 && cpu < pmc_cpu_max(),
166233319Sgonzo	    ("[mips,%d] illegal CPU value %d", __LINE__, cpu));
167233319Sgonzo	KASSERT(ri >= 0 && ri < mips_npmcs,
168233319Sgonzo	    ("[mips,%d] illegal row-index %d", __LINE__, ri));
169233319Sgonzo
170233319Sgonzo	phw = &mips_pcpu[cpu]->pc_mipspmcs[ri];
171233319Sgonzo
172233319Sgonzo	KASSERT(pm == NULL || phw->phw_pmc == NULL,
173233319Sgonzo	    ("[mips,%d] pm=%p phw->pm=%p hwpmc not unconfigured",
174233319Sgonzo	    __LINE__, pm, phw->phw_pmc));
175233319Sgonzo
176233319Sgonzo	phw->phw_pmc = pm;
177233319Sgonzo
178233319Sgonzo	return 0;
179233319Sgonzo}
180233319Sgonzo
181233319Sgonzostatic int
182233319Sgonzomips_start_pmc(int cpu, int ri)
183233319Sgonzo{
184233319Sgonzo	uint32_t config;
185233319Sgonzo        struct pmc *pm;
186233319Sgonzo        struct pmc_hw *phw;
187233319Sgonzo
188233319Sgonzo	phw    = &mips_pcpu[cpu]->pc_mipspmcs[ri];
189233319Sgonzo	pm     = phw->phw_pmc;
190233319Sgonzo	config = pm->pm_md.pm_mips_evsel;
191233319Sgonzo
192233319Sgonzo	/* Enable the PMC. */
193233319Sgonzo	switch (ri) {
194233319Sgonzo	case 0:
195233319Sgonzo		mips_wr_perfcnt0(config);
196233319Sgonzo		break;
197233319Sgonzo	case 1:
198233319Sgonzo		mips_wr_perfcnt2(config);
199233319Sgonzo		break;
200233319Sgonzo	default:
201233319Sgonzo		break;
202233319Sgonzo	}
203233319Sgonzo
204233319Sgonzo	return 0;
205233319Sgonzo}
206233319Sgonzo
207233319Sgonzostatic int
208233319Sgonzomips_stop_pmc(int cpu, int ri)
209233319Sgonzo{
210233319Sgonzo        struct pmc *pm;
211233319Sgonzo        struct pmc_hw *phw;
212233319Sgonzo
213233319Sgonzo	phw    = &mips_pcpu[cpu]->pc_mipspmcs[ri];
214233319Sgonzo	pm     = phw->phw_pmc;
215233319Sgonzo
216233319Sgonzo	/*
217233319Sgonzo	 * Disable the PMCs.
218233319Sgonzo	 *
219233319Sgonzo	 * Clearing the entire register turns the counter off as well
220233319Sgonzo	 * as removes the previously sampled event.
221233319Sgonzo	 */
222233319Sgonzo	switch (ri) {
223233319Sgonzo	case 0:
224233319Sgonzo		mips_wr_perfcnt0(0);
225233319Sgonzo		break;
226233319Sgonzo	case 1:
227233319Sgonzo		mips_wr_perfcnt2(0);
228233319Sgonzo		break;
229233319Sgonzo	default:
230233319Sgonzo		break;
231233319Sgonzo	}
232233319Sgonzo	return 0;
233233319Sgonzo}
234233319Sgonzo
235233319Sgonzostatic int
236233319Sgonzomips_release_pmc(int cpu, int ri, struct pmc *pmc)
237233319Sgonzo{
238233319Sgonzo	struct pmc_hw *phw;
239233319Sgonzo
240233319Sgonzo	KASSERT(cpu >= 0 && cpu < pmc_cpu_max(),
241233319Sgonzo	    ("[mips,%d] illegal CPU value %d", __LINE__, cpu));
242233319Sgonzo	KASSERT(ri >= 0 && ri < mips_npmcs,
243233319Sgonzo	    ("[mips,%d] illegal row-index %d", __LINE__, ri));
244233319Sgonzo
245233319Sgonzo	phw = &mips_pcpu[cpu]->pc_mipspmcs[ri];
246233319Sgonzo	KASSERT(phw->phw_pmc == NULL,
247233319Sgonzo	    ("[mips,%d] PHW pmc %p non-NULL", __LINE__, phw->phw_pmc));
248233319Sgonzo
249233319Sgonzo	return 0;
250233319Sgonzo}
251233319Sgonzo
252233319Sgonzostatic int
253233319Sgonzomips_pmc_intr(int cpu, struct trapframe *tf)
254233319Sgonzo{
255233319Sgonzo	int error;
256233319Sgonzo	int retval, ri;
257233319Sgonzo	struct pmc *pm;
258233319Sgonzo	struct mips_cpu *pc;
259233319Sgonzo	uint32_t r0, r2;
260233319Sgonzo	pmc_value_t r;
261233319Sgonzo
262233319Sgonzo	KASSERT(cpu >= 0 && cpu < pmc_cpu_max(),
263233319Sgonzo	    ("[mips,%d] CPU %d out of range", __LINE__, cpu));
264233319Sgonzo
265233319Sgonzo	retval = 0;
266233319Sgonzo	pc = mips_pcpu[cpu];
267233319Sgonzo
268233319Sgonzo	/* Stop PMCs without clearing the counter */
269233319Sgonzo	r0 = mips_rd_perfcnt0();
270233319Sgonzo	mips_wr_perfcnt0(r0 & ~(0x1f));
271233319Sgonzo	r2 = mips_rd_perfcnt2();
272233319Sgonzo	mips_wr_perfcnt2(r2 & ~(0x1f));
273233319Sgonzo
274233319Sgonzo	for (ri = 0; ri < mips_npmcs; ri++) {
275233319Sgonzo		pm = mips_pcpu[cpu]->pc_mipspmcs[ri].phw_pmc;
276233319Sgonzo		if (pm == NULL)
277233319Sgonzo			continue;
278233319Sgonzo		if (! PMC_IS_SAMPLING_MODE(PMC_TO_MODE(pm)))
279233319Sgonzo			continue;
280233319Sgonzo
281233319Sgonzo		r = mips_pmcn_read(ri);
282233319Sgonzo
283233319Sgonzo		/* If bit 31 is set, the counter has overflowed */
284233319Sgonzo		if ((r & (1UL << (mips_pmc_spec.ps_counter_width - 1))) == 0)
285233319Sgonzo			continue;
286233319Sgonzo
287233319Sgonzo		retval = 1;
288233319Sgonzo		if (pm->pm_state != PMC_STATE_RUNNING)
289233319Sgonzo			continue;
290233319Sgonzo		error = pmc_process_interrupt(cpu, pm, tf,
291233319Sgonzo		    TRAPF_USERMODE(tf));
292233319Sgonzo		if (error) {
293233319Sgonzo			/* Clear/disable the relevant counter */
294233319Sgonzo			if (ri == 0)
295233319Sgonzo				r0 = 0;
296233319Sgonzo			else if (ri == 1)
297233319Sgonzo				r2 = 0;
298233319Sgonzo			mips_stop_pmc(cpu, ri);
299233319Sgonzo		}
300233319Sgonzo
301233319Sgonzo		/* Reload sampling count */
302233319Sgonzo		mips_write_pmc(cpu, ri, pm->pm_sc.pm_reloadcount);
303233319Sgonzo	}
304233319Sgonzo
305233319Sgonzo	/*
306233319Sgonzo	 * Re-enable the PMC counters where they left off.
307233319Sgonzo	 *
308233319Sgonzo	 * Any counter which overflowed will have its sample count
309233319Sgonzo	 * reloaded in the loop above.
310233319Sgonzo	 */
311233319Sgonzo	mips_wr_perfcnt0(r0);
312233319Sgonzo	mips_wr_perfcnt2(r2);
313233319Sgonzo
314233319Sgonzo	return retval;
315233319Sgonzo}
316233319Sgonzo
317233319Sgonzostatic int
318233319Sgonzomips_describe(int cpu, int ri, struct pmc_info *pi, struct pmc **ppmc)
319233319Sgonzo{
320233319Sgonzo	int error;
321233319Sgonzo	struct pmc_hw *phw;
322233319Sgonzo	char mips_name[PMC_NAME_MAX];
323233319Sgonzo
324233319Sgonzo	KASSERT(cpu >= 0 && cpu < pmc_cpu_max(),
325233319Sgonzo	    ("[mips,%d], illegal CPU %d", __LINE__, cpu));
326233319Sgonzo	KASSERT(ri >= 0 && ri < mips_npmcs,
327233319Sgonzo	    ("[mips,%d] row-index %d out of range", __LINE__, ri));
328233319Sgonzo
329233319Sgonzo	phw = &mips_pcpu[cpu]->pc_mipspmcs[ri];
330233319Sgonzo	snprintf(mips_name, sizeof(mips_name), "MIPS-%d", ri);
331233319Sgonzo	if ((error = copystr(mips_name, pi->pm_name, PMC_NAME_MAX,
332233319Sgonzo	    NULL)) != 0)
333233319Sgonzo		return error;
334233319Sgonzo	pi->pm_class = mips_pmc_spec.ps_cpuclass;
335233319Sgonzo	if (phw->phw_state & PMC_PHW_FLAG_IS_ENABLED) {
336233319Sgonzo		pi->pm_enabled = TRUE;
337233319Sgonzo		*ppmc          = phw->phw_pmc;
338233319Sgonzo	} else {
339233319Sgonzo		pi->pm_enabled = FALSE;
340233319Sgonzo		*ppmc	       = NULL;
341233319Sgonzo	}
342233319Sgonzo
343233319Sgonzo	return (0);
344233319Sgonzo}
345233319Sgonzo
346233319Sgonzostatic int
347233319Sgonzomips_get_config(int cpu, int ri, struct pmc **ppm)
348233319Sgonzo{
349233319Sgonzo	*ppm = mips_pcpu[cpu]->pc_mipspmcs[ri].phw_pmc;
350233319Sgonzo
351233319Sgonzo	return 0;
352233319Sgonzo}
353233319Sgonzo
354233319Sgonzo/*
355233319Sgonzo * XXX don't know what we should do here.
356233319Sgonzo */
357233319Sgonzostatic int
358233319Sgonzomips_pmc_switch_in(struct pmc_cpu *pc, struct pmc_process *pp)
359233319Sgonzo{
360233319Sgonzo	return 0;
361233319Sgonzo}
362233319Sgonzo
363233319Sgonzostatic int
364233319Sgonzomips_pmc_switch_out(struct pmc_cpu *pc, struct pmc_process *pp)
365233319Sgonzo{
366233319Sgonzo	return 0;
367233319Sgonzo}
368233319Sgonzo
369233319Sgonzostatic int
370233319Sgonzomips_pcpu_init(struct pmc_mdep *md, int cpu)
371233319Sgonzo{
372233319Sgonzo	int first_ri, i;
373233319Sgonzo	struct pmc_cpu *pc;
374233319Sgonzo	struct mips_cpu *pac;
375233319Sgonzo	struct pmc_hw  *phw;
376233319Sgonzo
377233319Sgonzo	KASSERT(cpu >= 0 && cpu < pmc_cpu_max(),
378233319Sgonzo	    ("[mips,%d] wrong cpu number %d", __LINE__, cpu));
379233319Sgonzo	PMCDBG(MDP,INI,1,"mips-init cpu=%d", cpu);
380233319Sgonzo
381233319Sgonzo	mips_pcpu[cpu] = pac = malloc(sizeof(struct mips_cpu), M_PMC,
382233319Sgonzo	    M_WAITOK|M_ZERO);
383233319Sgonzo	pac->pc_mipspmcs = malloc(sizeof(struct pmc_hw) * mips_npmcs,
384233319Sgonzo	    M_PMC, M_WAITOK|M_ZERO);
385233319Sgonzo	pc = pmc_pcpu[cpu];
386233319Sgonzo	first_ri = md->pmd_classdep[PMC_MDEP_CLASS_INDEX_MIPS].pcd_ri;
387233319Sgonzo	KASSERT(pc != NULL, ("[mips,%d] NULL per-cpu pointer", __LINE__));
388233319Sgonzo
389233319Sgonzo	for (i = 0, phw = pac->pc_mipspmcs; i < mips_npmcs; i++, phw++) {
390233319Sgonzo		phw->phw_state    = PMC_PHW_FLAG_IS_ENABLED |
391233319Sgonzo		    PMC_PHW_CPU_TO_STATE(cpu) | PMC_PHW_INDEX_TO_STATE(i);
392233319Sgonzo		phw->phw_pmc      = NULL;
393233319Sgonzo		pc->pc_hwpmcs[i + first_ri] = phw;
394233319Sgonzo	}
395233319Sgonzo
396233319Sgonzo	/*
397233319Sgonzo	 * Clear the counter control register which has the effect
398233319Sgonzo	 * of disabling counting.
399233319Sgonzo	 */
400233319Sgonzo	for (i = 0; i < mips_npmcs; i++)
401233319Sgonzo		mips_pmcn_write(i, 0);
402233319Sgonzo
403233319Sgonzo	return 0;
404233319Sgonzo}
405233319Sgonzo
406233319Sgonzostatic int
407233319Sgonzomips_pcpu_fini(struct pmc_mdep *md, int cpu)
408233319Sgonzo{
409233319Sgonzo	return 0;
410233319Sgonzo}
411233319Sgonzo
412233319Sgonzostruct pmc_mdep *
413233319Sgonzopmc_mips_initialize()
414233319Sgonzo{
415233319Sgonzo	struct pmc_mdep *pmc_mdep;
416233319Sgonzo	struct pmc_classdep *pcd;
417233319Sgonzo
418233319Sgonzo	/*
419233319Sgonzo	 * TODO: Use More bit of PerfCntlX register to detect actual
420233319Sgonzo	 * number of counters
421233319Sgonzo	 */
422233319Sgonzo	mips_npmcs = 2;
423233319Sgonzo
424233319Sgonzo	PMCDBG(MDP,INI,1,"mips-init npmcs=%d", mips_npmcs);
425233319Sgonzo
426233319Sgonzo	/*
427233319Sgonzo	 * Allocate space for pointers to PMC HW descriptors and for
428233319Sgonzo	 * the MDEP structure used by MI code.
429233319Sgonzo	 */
430233319Sgonzo	mips_pcpu = malloc(sizeof(struct mips_cpu *) * pmc_cpu_max(), M_PMC,
431233319Sgonzo			   M_WAITOK|M_ZERO);
432233319Sgonzo
433233319Sgonzo	/* Just one class */
434233319Sgonzo	pmc_mdep = malloc(sizeof(struct pmc_mdep) + sizeof(struct pmc_classdep),
435233319Sgonzo			  M_PMC, M_WAITOK|M_ZERO);
436233319Sgonzo
437233319Sgonzo	pmc_mdep->pmd_cputype = mips_pmc_spec.ps_cputype;
438233319Sgonzo	pmc_mdep->pmd_nclass  = 1;
439233319Sgonzo
440233319Sgonzo	pcd = &pmc_mdep->pmd_classdep[PMC_MDEP_CLASS_INDEX_MIPS];
441233319Sgonzo	pcd->pcd_caps  = mips_pmc_spec.ps_capabilities;
442233319Sgonzo	pcd->pcd_class = mips_pmc_spec.ps_cpuclass;
443233319Sgonzo	pcd->pcd_num   = mips_npmcs;
444233319Sgonzo	pcd->pcd_ri    = pmc_mdep->pmd_npmc;
445233319Sgonzo	pcd->pcd_width = mips_pmc_spec.ps_counter_width;
446233319Sgonzo
447233319Sgonzo	pcd->pcd_allocate_pmc   = mips_allocate_pmc;
448233319Sgonzo	pcd->pcd_config_pmc     = mips_config_pmc;
449233319Sgonzo	pcd->pcd_pcpu_fini      = mips_pcpu_fini;
450233319Sgonzo	pcd->pcd_pcpu_init      = mips_pcpu_init;
451233319Sgonzo	pcd->pcd_describe       = mips_describe;
452233319Sgonzo	pcd->pcd_get_config	= mips_get_config;
453233319Sgonzo	pcd->pcd_read_pmc       = mips_read_pmc;
454233319Sgonzo	pcd->pcd_release_pmc    = mips_release_pmc;
455233319Sgonzo	pcd->pcd_start_pmc      = mips_start_pmc;
456233319Sgonzo	pcd->pcd_stop_pmc       = mips_stop_pmc;
457233319Sgonzo 	pcd->pcd_write_pmc      = mips_write_pmc;
458233319Sgonzo
459233319Sgonzo	pmc_mdep->pmd_intr       = mips_pmc_intr;
460233319Sgonzo	pmc_mdep->pmd_switch_in  = mips_pmc_switch_in;
461233319Sgonzo	pmc_mdep->pmd_switch_out = mips_pmc_switch_out;
462233319Sgonzo
463233319Sgonzo	pmc_mdep->pmd_npmc   += mips_npmcs;
464233319Sgonzo
465233319Sgonzo	return (pmc_mdep);
466233319Sgonzo}
467233319Sgonzo
468233319Sgonzovoid
469233319Sgonzopmc_mips_finalize(struct pmc_mdep *md)
470233319Sgonzo{
471233319Sgonzo	(void) md;
472233319Sgonzo}
473233319Sgonzo
474233319Sgonzo#ifdef	HWPMC_MIPS_BACKTRACE
475233319Sgonzo
476233319Sgonzostatic int
477232846Sgonzopmc_next_frame(register_t *pc, register_t *sp)
478232846Sgonzo{
479232846Sgonzo	InstFmt i;
480232846Sgonzo	uintptr_t va;
481232846Sgonzo	uint32_t instr, mask;
482232846Sgonzo	int more, stksize;
483232846Sgonzo	register_t ra = 0;
484232846Sgonzo
485232846Sgonzo	/* Jump here after a nonstandard (interrupt handler) frame */
486232846Sgonzo	stksize = 0;
487232846Sgonzo
488232846Sgonzo	/* check for bad SP: could foul up next frame */
489232846Sgonzo	if (!MIPS_IS_VALID_KERNELADDR(*sp)) {
490232846Sgonzo		goto error;
491232846Sgonzo	}
492232846Sgonzo
493232846Sgonzo	/* check for bad PC */
494232846Sgonzo	if (!MIPS_IS_VALID_KERNELADDR(*pc)) {
495232846Sgonzo		goto error;
496232846Sgonzo	}
497232846Sgonzo
498232846Sgonzo	/*
499232846Sgonzo	 * Find the beginning of the current subroutine by scanning
500232846Sgonzo	 * backwards from the current PC for the end of the previous
501232846Sgonzo	 * subroutine.
502232846Sgonzo	 */
503232846Sgonzo	va = *pc - sizeof(int);
504232846Sgonzo	while (1) {
505232846Sgonzo		instr = *((uint32_t *)va);
506232846Sgonzo
507232846Sgonzo		/* [d]addiu sp,sp,-X */
508232846Sgonzo		if (((instr & 0xffff8000) == 0x27bd8000)
509232846Sgonzo		    || ((instr & 0xffff8000) == 0x67bd8000))
510232846Sgonzo			break;
511232846Sgonzo
512232846Sgonzo		/* jr	ra */
513232846Sgonzo		if (instr == 0x03e00008) {
514232846Sgonzo			/* skip over branch-delay slot instruction */
515232846Sgonzo			va += 2 * sizeof(int);
516232846Sgonzo			break;
517232846Sgonzo		}
518232846Sgonzo
519232846Sgonzo		va -= sizeof(int);
520232846Sgonzo	}
521232846Sgonzo
522232846Sgonzo	/* skip over nulls which might separate .o files */
523232846Sgonzo	while ((instr = *((uint32_t *)va)) == 0)
524232846Sgonzo		va += sizeof(int);
525232846Sgonzo
526232846Sgonzo	/* scan forwards to find stack size and any saved registers */
527232846Sgonzo	stksize = 0;
528232846Sgonzo	more = 3;
529232846Sgonzo	mask = 0;
530232846Sgonzo	for (; more; va += sizeof(int),
531232846Sgonzo	    more = (more == 3) ? 3 : more - 1) {
532232846Sgonzo		/* stop if hit our current position */
533232846Sgonzo		if (va >= *pc)
534232846Sgonzo			break;
535232846Sgonzo		instr = *((uint32_t *)va);
536232846Sgonzo		i.word = instr;
537232846Sgonzo		switch (i.JType.op) {
538232846Sgonzo		case OP_SPECIAL:
539232846Sgonzo			switch (i.RType.func) {
540232846Sgonzo			case OP_JR:
541232846Sgonzo			case OP_JALR:
542232846Sgonzo				more = 2;	/* stop after next instruction */
543232846Sgonzo				break;
544232846Sgonzo
545232846Sgonzo			case OP_SYSCALL:
546232846Sgonzo			case OP_BREAK:
547232846Sgonzo				more = 1;	/* stop now */
548232846Sgonzo			};
549232846Sgonzo			break;
550232846Sgonzo
551232846Sgonzo		case OP_BCOND:
552232846Sgonzo		case OP_J:
553232846Sgonzo		case OP_JAL:
554232846Sgonzo		case OP_BEQ:
555232846Sgonzo		case OP_BNE:
556232846Sgonzo		case OP_BLEZ:
557232846Sgonzo		case OP_BGTZ:
558232846Sgonzo			more = 2;	/* stop after next instruction */
559232846Sgonzo			break;
560232846Sgonzo
561232846Sgonzo		case OP_COP0:
562232846Sgonzo		case OP_COP1:
563232846Sgonzo		case OP_COP2:
564232846Sgonzo		case OP_COP3:
565232846Sgonzo			switch (i.RType.rs) {
566232846Sgonzo			case OP_BCx:
567232846Sgonzo			case OP_BCy:
568232846Sgonzo				more = 2;	/* stop after next instruction */
569232846Sgonzo			};
570232846Sgonzo			break;
571232846Sgonzo
572232846Sgonzo		case OP_SW:
573232846Sgonzo		case OP_SD:
574232992Sgonzo			/*
575232992Sgonzo			 * SP is being saved using S8(FP). Most likely it indicates
576232992Sgonzo			 * that SP is modified in the function and we can't get
577232992Sgonzo			 * its value safely without emulating code backward
578232992Sgonzo			 * So just bail out on functions like this
579232992Sgonzo			 */
580232992Sgonzo			if ((i.IType.rs == 30) && (i.IType.rt = 29))
581232992Sgonzo				return (-1);
582232992Sgonzo
583232846Sgonzo			/* look for saved registers on the stack */
584232846Sgonzo			if (i.IType.rs != 29)
585232846Sgonzo				break;
586232846Sgonzo			/* only restore the first one */
587232846Sgonzo			if (mask & (1 << i.IType.rt))
588232846Sgonzo				break;
589232846Sgonzo			mask |= (1 << i.IType.rt);
590232846Sgonzo			if (i.IType.rt == 31)
591232846Sgonzo				ra = *((register_t *)(*sp + (short)i.IType.imm));
592232846Sgonzo			break;
593232846Sgonzo
594232846Sgonzo		case OP_ADDI:
595232846Sgonzo		case OP_ADDIU:
596232846Sgonzo		case OP_DADDI:
597232846Sgonzo		case OP_DADDIU:
598232846Sgonzo			/* look for stack pointer adjustment */
599232846Sgonzo			if (i.IType.rs != 29 || i.IType.rt != 29)
600232846Sgonzo				break;
601232846Sgonzo			stksize = -((short)i.IType.imm);
602232846Sgonzo		}
603232846Sgonzo	}
604232846Sgonzo
605232846Sgonzo	if (!MIPS_IS_VALID_KERNELADDR(ra))
606232846Sgonzo		return (-1);
607232846Sgonzo
608232846Sgonzo	*pc = ra;
609232846Sgonzo	*sp += stksize;
610232846Sgonzo
611232846Sgonzo	return (0);
612232846Sgonzo
613232846Sgonzoerror:
614232846Sgonzo	return (-1);
615232846Sgonzo}
616232846Sgonzo
617232846Sgonzostatic int
618232846Sgonzopmc_next_uframe(register_t *pc, register_t *sp, register_t *ra)
619232846Sgonzo{
620232846Sgonzo	int offset, registers_on_stack;
621232846Sgonzo	uint32_t opcode, mask;
622232846Sgonzo	register_t function_start;
623232846Sgonzo	int stksize;
624232846Sgonzo	InstFmt i;
625232846Sgonzo
626232846Sgonzo	registers_on_stack = 0;
627232846Sgonzo	mask = 0;
628232846Sgonzo	function_start = 0;
629232846Sgonzo	offset = 0;
630232846Sgonzo	stksize = 0;
631232846Sgonzo
632232846Sgonzo	while (offset < MAX_FUNCTION_SIZE) {
633232846Sgonzo		opcode = fuword32((void *)(*pc - offset));
634232846Sgonzo
635232846Sgonzo		/* [d]addiu sp, sp, -X*/
636232846Sgonzo		if (((opcode & 0xffff8000) == 0x27bd8000)
637232846Sgonzo		    || ((opcode & 0xffff8000) == 0x67bd8000)) {
638232846Sgonzo			function_start = *pc - offset;
639232846Sgonzo			registers_on_stack = 1;
640232846Sgonzo			break;
641232846Sgonzo		}
642232846Sgonzo
643232846Sgonzo		/* lui gp, X */
644232846Sgonzo		if ((opcode & 0xffff8000) == 0x3c1c0000) {
645232846Sgonzo			/*
646232846Sgonzo			 * Function might start with this instruction
647232846Sgonzo			 * Keep an eye on "jr ra" and sp correction
648232846Sgonzo			 * with positive value further on
649232846Sgonzo			 */
650232846Sgonzo			function_start = *pc - offset;
651232846Sgonzo		}
652232846Sgonzo
653232846Sgonzo		if (function_start) {
654232846Sgonzo			/*
655232846Sgonzo			 * Stop looking further. Possible end of
656232846Sgonzo			 * function instruction: it means there is no
657232846Sgonzo			 * stack modifications, sp is unchanged
658232846Sgonzo			 */
659232846Sgonzo
660232846Sgonzo			/* [d]addiu sp,sp,X */
661232846Sgonzo			if (((opcode & 0xffff8000) == 0x27bd0000)
662232846Sgonzo			    || ((opcode & 0xffff8000) == 0x67bd0000))
663232846Sgonzo				break;
664232846Sgonzo
665232846Sgonzo			if (opcode == 0x03e00008)
666232846Sgonzo				break;
667232846Sgonzo		}
668232846Sgonzo
669232846Sgonzo		offset += sizeof(int);
670232846Sgonzo	}
671232846Sgonzo
672232846Sgonzo	if (!function_start)
673232846Sgonzo		return (-1);
674232846Sgonzo
675232846Sgonzo	if (registers_on_stack) {
676232846Sgonzo		offset = 0;
677232846Sgonzo		while ((offset < MAX_PROLOGUE_SIZE)
678232846Sgonzo		    && ((function_start + offset) < *pc)) {
679232846Sgonzo			i.word = fuword32((void *)(function_start + offset));
680232846Sgonzo			switch (i.JType.op) {
681232846Sgonzo			case OP_SW:
682232846Sgonzo				/* look for saved registers on the stack */
683232846Sgonzo				if (i.IType.rs != 29)
684232846Sgonzo					break;
685232846Sgonzo				/* only restore the first one */
686232846Sgonzo				if (mask & (1 << i.IType.rt))
687232846Sgonzo					break;
688232846Sgonzo				mask |= (1 << i.IType.rt);
689232846Sgonzo				if (i.IType.rt == 31)
690232846Sgonzo					*ra = fuword32((void *)(*sp + (short)i.IType.imm));
691232846Sgonzo				break;
692232846Sgonzo
693232846Sgonzo#if defined(__mips_n64)
694232846Sgonzo			case OP_SD:
695232846Sgonzo				/* look for saved registers on the stack */
696232846Sgonzo				if (i.IType.rs != 29)
697232846Sgonzo					break;
698232846Sgonzo				/* only restore the first one */
699232846Sgonzo				if (mask & (1 << i.IType.rt))
700232846Sgonzo					break;
701232846Sgonzo				mask |= (1 << i.IType.rt);
702232846Sgonzo				/* ra */
703232846Sgonzo				if (i.IType.rt == 31)
704232846Sgonzo					*ra = fuword64((void *)(*sp + (short)i.IType.imm));
705232846Sgonzo			break;
706232846Sgonzo#endif
707232846Sgonzo
708232846Sgonzo			case OP_ADDI:
709232846Sgonzo			case OP_ADDIU:
710232846Sgonzo			case OP_DADDI:
711232846Sgonzo			case OP_DADDIU:
712232846Sgonzo				/* look for stack pointer adjustment */
713232846Sgonzo				if (i.IType.rs != 29 || i.IType.rt != 29)
714232846Sgonzo					break;
715232846Sgonzo				stksize = -((short)i.IType.imm);
716232846Sgonzo			}
717232846Sgonzo
718232846Sgonzo			offset += sizeof(int);
719232846Sgonzo		}
720232846Sgonzo	}
721232846Sgonzo
722232846Sgonzo	/*
723232846Sgonzo	 * We reached the end of backtrace
724232846Sgonzo	 */
725232846Sgonzo	if (*pc == *ra)
726232846Sgonzo		return (-1);
727232846Sgonzo
728232846Sgonzo	*pc = *ra;
729232846Sgonzo	*sp += stksize;
730232846Sgonzo
731232846Sgonzo	return (0);
732232846Sgonzo}
733232846Sgonzo
734233319Sgonzo#endif /* HWPMC_MIPS_BACKTRACE */
735233319Sgonzo
736204635Sgnnstruct pmc_mdep *
737204635Sgnnpmc_md_initialize()
738204635Sgnn{
739233319Sgonzo	return pmc_mips_initialize();
740204635Sgnn}
741204635Sgnn
742204635Sgnnvoid
743204635Sgnnpmc_md_finalize(struct pmc_mdep *md)
744204635Sgnn{
745233319Sgonzo	return pmc_mips_finalize(md);
746204635Sgnn}
747204635Sgnn
748204635Sgnnint
749232846Sgonzopmc_save_kernel_callchain(uintptr_t *cc, int nframes,
750204635Sgnn    struct trapframe *tf)
751204635Sgnn{
752232846Sgonzo	register_t pc, ra, sp;
753232846Sgonzo	int frames = 0;
754232846Sgonzo
755232992Sgonzo	pc = tf->pc;
756232992Sgonzo	sp = tf->sp;
757232992Sgonzo	ra = tf->ra;
758232846Sgonzo
759233319Sgonzo	cc[frames++] = pc;
760233319Sgonzo
761233319Sgonzo#ifdef	HWPMC_MIPS_BACKTRACE
762232846Sgonzo	/*
763232846Sgonzo	 * Unwind, and unwind, and unwind
764232846Sgonzo	 */
765232846Sgonzo	while (1) {
766232846Sgonzo		if (frames >= nframes)
767232846Sgonzo			break;
768232846Sgonzo
769232846Sgonzo		if (pmc_next_frame(&pc, &sp) < 0)
770232846Sgonzo			break;
771233319Sgonzo
772233319Sgonzo		cc[frames++] = pc;
773232846Sgonzo	}
774233319Sgonzo#endif
775232846Sgonzo
776232846Sgonzo	return (frames);
777204635Sgnn}
778204635Sgnn
779204635Sgnnint
780232846Sgonzopmc_save_user_callchain(uintptr_t *cc, int nframes,
781204635Sgnn    struct trapframe *tf)
782204635Sgnn{
783232846Sgonzo	register_t pc, ra, sp;
784232846Sgonzo	int frames = 0;
785232846Sgonzo
786232992Sgonzo	pc = tf->pc;
787232992Sgonzo	sp = tf->sp;
788232992Sgonzo	ra = tf->ra;
789232846Sgonzo
790233319Sgonzo	cc[frames++] = pc;
791233319Sgonzo
792233319Sgonzo#ifdef	HWPMC_MIPS_BACKTRACE
793233319Sgonzo
794232846Sgonzo	/*
795232846Sgonzo	 * Unwind, and unwind, and unwind
796232846Sgonzo	 */
797232846Sgonzo	while (1) {
798232846Sgonzo		if (frames >= nframes)
799232846Sgonzo			break;
800232846Sgonzo
801232846Sgonzo		if (pmc_next_uframe(&pc, &sp, &ra) < 0)
802232846Sgonzo			break;
803233319Sgonzo
804233319Sgonzo		cc[frames++] = pc;
805232846Sgonzo	}
806233319Sgonzo#endif
807232846Sgonzo
808232846Sgonzo	return (frames);
809204635Sgnn}
810