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$");
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
107282658Sjhb	PMCDBG2(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);
126282658Sjhb	PMCDBG2(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
151282658Sjhb	PMCDBG3(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
163282658Sjhb	PMCDBG3(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;
290233628Sfabient		error = pmc_process_interrupt(cpu, PMC_HR, 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));
379282658Sjhb	PMCDBG1(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
424282658Sjhb	PMCDBG1(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 */
434234598Sfabient	pmc_mdep = pmc_mdep_alloc(1);
435233319Sgonzo
436233319Sgonzo	pmc_mdep->pmd_cputype = mips_pmc_spec.ps_cputype;
437233319Sgonzo
438233319Sgonzo	pcd = &pmc_mdep->pmd_classdep[PMC_MDEP_CLASS_INDEX_MIPS];
439233319Sgonzo	pcd->pcd_caps  = mips_pmc_spec.ps_capabilities;
440233319Sgonzo	pcd->pcd_class = mips_pmc_spec.ps_cpuclass;
441233319Sgonzo	pcd->pcd_num   = mips_npmcs;
442233319Sgonzo	pcd->pcd_ri    = pmc_mdep->pmd_npmc;
443233319Sgonzo	pcd->pcd_width = mips_pmc_spec.ps_counter_width;
444233319Sgonzo
445233319Sgonzo	pcd->pcd_allocate_pmc   = mips_allocate_pmc;
446233319Sgonzo	pcd->pcd_config_pmc     = mips_config_pmc;
447233319Sgonzo	pcd->pcd_pcpu_fini      = mips_pcpu_fini;
448233319Sgonzo	pcd->pcd_pcpu_init      = mips_pcpu_init;
449233319Sgonzo	pcd->pcd_describe       = mips_describe;
450233319Sgonzo	pcd->pcd_get_config	= mips_get_config;
451233319Sgonzo	pcd->pcd_read_pmc       = mips_read_pmc;
452233319Sgonzo	pcd->pcd_release_pmc    = mips_release_pmc;
453233319Sgonzo	pcd->pcd_start_pmc      = mips_start_pmc;
454233319Sgonzo	pcd->pcd_stop_pmc       = mips_stop_pmc;
455233319Sgonzo 	pcd->pcd_write_pmc      = mips_write_pmc;
456233319Sgonzo
457233319Sgonzo	pmc_mdep->pmd_intr       = mips_pmc_intr;
458233319Sgonzo	pmc_mdep->pmd_switch_in  = mips_pmc_switch_in;
459233319Sgonzo	pmc_mdep->pmd_switch_out = mips_pmc_switch_out;
460233319Sgonzo
461233319Sgonzo	pmc_mdep->pmd_npmc   += mips_npmcs;
462233319Sgonzo
463233319Sgonzo	return (pmc_mdep);
464233319Sgonzo}
465233319Sgonzo
466233319Sgonzovoid
467233319Sgonzopmc_mips_finalize(struct pmc_mdep *md)
468233319Sgonzo{
469233319Sgonzo	(void) md;
470233319Sgonzo}
471233319Sgonzo
472233319Sgonzo#ifdef	HWPMC_MIPS_BACKTRACE
473233319Sgonzo
474233319Sgonzostatic int
475232846Sgonzopmc_next_frame(register_t *pc, register_t *sp)
476232846Sgonzo{
477232846Sgonzo	InstFmt i;
478232846Sgonzo	uintptr_t va;
479232846Sgonzo	uint32_t instr, mask;
480232846Sgonzo	int more, stksize;
481232846Sgonzo	register_t ra = 0;
482232846Sgonzo
483232846Sgonzo	/* Jump here after a nonstandard (interrupt handler) frame */
484232846Sgonzo	stksize = 0;
485232846Sgonzo
486232846Sgonzo	/* check for bad SP: could foul up next frame */
487232846Sgonzo	if (!MIPS_IS_VALID_KERNELADDR(*sp)) {
488232846Sgonzo		goto error;
489232846Sgonzo	}
490232846Sgonzo
491232846Sgonzo	/* check for bad PC */
492232846Sgonzo	if (!MIPS_IS_VALID_KERNELADDR(*pc)) {
493232846Sgonzo		goto error;
494232846Sgonzo	}
495232846Sgonzo
496232846Sgonzo	/*
497232846Sgonzo	 * Find the beginning of the current subroutine by scanning
498232846Sgonzo	 * backwards from the current PC for the end of the previous
499232846Sgonzo	 * subroutine.
500232846Sgonzo	 */
501232846Sgonzo	va = *pc - sizeof(int);
502232846Sgonzo	while (1) {
503232846Sgonzo		instr = *((uint32_t *)va);
504232846Sgonzo
505232846Sgonzo		/* [d]addiu sp,sp,-X */
506232846Sgonzo		if (((instr & 0xffff8000) == 0x27bd8000)
507232846Sgonzo		    || ((instr & 0xffff8000) == 0x67bd8000))
508232846Sgonzo			break;
509232846Sgonzo
510232846Sgonzo		/* jr	ra */
511232846Sgonzo		if (instr == 0x03e00008) {
512232846Sgonzo			/* skip over branch-delay slot instruction */
513232846Sgonzo			va += 2 * sizeof(int);
514232846Sgonzo			break;
515232846Sgonzo		}
516232846Sgonzo
517232846Sgonzo		va -= sizeof(int);
518232846Sgonzo	}
519232846Sgonzo
520232846Sgonzo	/* skip over nulls which might separate .o files */
521232846Sgonzo	while ((instr = *((uint32_t *)va)) == 0)
522232846Sgonzo		va += sizeof(int);
523232846Sgonzo
524232846Sgonzo	/* scan forwards to find stack size and any saved registers */
525232846Sgonzo	stksize = 0;
526232846Sgonzo	more = 3;
527232846Sgonzo	mask = 0;
528232846Sgonzo	for (; more; va += sizeof(int),
529232846Sgonzo	    more = (more == 3) ? 3 : more - 1) {
530232846Sgonzo		/* stop if hit our current position */
531232846Sgonzo		if (va >= *pc)
532232846Sgonzo			break;
533232846Sgonzo		instr = *((uint32_t *)va);
534232846Sgonzo		i.word = instr;
535232846Sgonzo		switch (i.JType.op) {
536232846Sgonzo		case OP_SPECIAL:
537232846Sgonzo			switch (i.RType.func) {
538232846Sgonzo			case OP_JR:
539232846Sgonzo			case OP_JALR:
540232846Sgonzo				more = 2;	/* stop after next instruction */
541232846Sgonzo				break;
542232846Sgonzo
543232846Sgonzo			case OP_SYSCALL:
544232846Sgonzo			case OP_BREAK:
545232846Sgonzo				more = 1;	/* stop now */
546297793Spfg			}
547232846Sgonzo			break;
548232846Sgonzo
549232846Sgonzo		case OP_BCOND:
550232846Sgonzo		case OP_J:
551232846Sgonzo		case OP_JAL:
552232846Sgonzo		case OP_BEQ:
553232846Sgonzo		case OP_BNE:
554232846Sgonzo		case OP_BLEZ:
555232846Sgonzo		case OP_BGTZ:
556232846Sgonzo			more = 2;	/* stop after next instruction */
557232846Sgonzo			break;
558232846Sgonzo
559232846Sgonzo		case OP_COP0:
560232846Sgonzo		case OP_COP1:
561232846Sgonzo		case OP_COP2:
562232846Sgonzo		case OP_COP3:
563232846Sgonzo			switch (i.RType.rs) {
564232846Sgonzo			case OP_BCx:
565232846Sgonzo			case OP_BCy:
566232846Sgonzo				more = 2;	/* stop after next instruction */
567297793Spfg			}
568232846Sgonzo			break;
569232846Sgonzo
570232846Sgonzo		case OP_SW:
571232846Sgonzo		case OP_SD:
572232992Sgonzo			/*
573232992Sgonzo			 * SP is being saved using S8(FP). Most likely it indicates
574232992Sgonzo			 * that SP is modified in the function and we can't get
575232992Sgonzo			 * its value safely without emulating code backward
576232992Sgonzo			 * So just bail out on functions like this
577232992Sgonzo			 */
578232992Sgonzo			if ((i.IType.rs == 30) && (i.IType.rt = 29))
579232992Sgonzo				return (-1);
580232992Sgonzo
581232846Sgonzo			/* look for saved registers on the stack */
582232846Sgonzo			if (i.IType.rs != 29)
583232846Sgonzo				break;
584232846Sgonzo			/* only restore the first one */
585232846Sgonzo			if (mask & (1 << i.IType.rt))
586232846Sgonzo				break;
587232846Sgonzo			mask |= (1 << i.IType.rt);
588232846Sgonzo			if (i.IType.rt == 31)
589232846Sgonzo				ra = *((register_t *)(*sp + (short)i.IType.imm));
590232846Sgonzo			break;
591232846Sgonzo
592232846Sgonzo		case OP_ADDI:
593232846Sgonzo		case OP_ADDIU:
594232846Sgonzo		case OP_DADDI:
595232846Sgonzo		case OP_DADDIU:
596232846Sgonzo			/* look for stack pointer adjustment */
597232846Sgonzo			if (i.IType.rs != 29 || i.IType.rt != 29)
598232846Sgonzo				break;
599232846Sgonzo			stksize = -((short)i.IType.imm);
600232846Sgonzo		}
601232846Sgonzo	}
602232846Sgonzo
603232846Sgonzo	if (!MIPS_IS_VALID_KERNELADDR(ra))
604232846Sgonzo		return (-1);
605232846Sgonzo
606232846Sgonzo	*pc = ra;
607232846Sgonzo	*sp += stksize;
608232846Sgonzo
609232846Sgonzo	return (0);
610232846Sgonzo
611232846Sgonzoerror:
612232846Sgonzo	return (-1);
613232846Sgonzo}
614232846Sgonzo
615232846Sgonzostatic int
616232846Sgonzopmc_next_uframe(register_t *pc, register_t *sp, register_t *ra)
617232846Sgonzo{
618232846Sgonzo	int offset, registers_on_stack;
619232846Sgonzo	uint32_t opcode, mask;
620232846Sgonzo	register_t function_start;
621232846Sgonzo	int stksize;
622232846Sgonzo	InstFmt i;
623232846Sgonzo
624232846Sgonzo	registers_on_stack = 0;
625232846Sgonzo	mask = 0;
626232846Sgonzo	function_start = 0;
627232846Sgonzo	offset = 0;
628232846Sgonzo	stksize = 0;
629232846Sgonzo
630232846Sgonzo	while (offset < MAX_FUNCTION_SIZE) {
631232846Sgonzo		opcode = fuword32((void *)(*pc - offset));
632232846Sgonzo
633232846Sgonzo		/* [d]addiu sp, sp, -X*/
634232846Sgonzo		if (((opcode & 0xffff8000) == 0x27bd8000)
635232846Sgonzo		    || ((opcode & 0xffff8000) == 0x67bd8000)) {
636232846Sgonzo			function_start = *pc - offset;
637232846Sgonzo			registers_on_stack = 1;
638232846Sgonzo			break;
639232846Sgonzo		}
640232846Sgonzo
641232846Sgonzo		/* lui gp, X */
642232846Sgonzo		if ((opcode & 0xffff8000) == 0x3c1c0000) {
643232846Sgonzo			/*
644232846Sgonzo			 * Function might start with this instruction
645232846Sgonzo			 * Keep an eye on "jr ra" and sp correction
646232846Sgonzo			 * with positive value further on
647232846Sgonzo			 */
648232846Sgonzo			function_start = *pc - offset;
649232846Sgonzo		}
650232846Sgonzo
651232846Sgonzo		if (function_start) {
652232846Sgonzo			/*
653232846Sgonzo			 * Stop looking further. Possible end of
654232846Sgonzo			 * function instruction: it means there is no
655232846Sgonzo			 * stack modifications, sp is unchanged
656232846Sgonzo			 */
657232846Sgonzo
658232846Sgonzo			/* [d]addiu sp,sp,X */
659232846Sgonzo			if (((opcode & 0xffff8000) == 0x27bd0000)
660232846Sgonzo			    || ((opcode & 0xffff8000) == 0x67bd0000))
661232846Sgonzo				break;
662232846Sgonzo
663232846Sgonzo			if (opcode == 0x03e00008)
664232846Sgonzo				break;
665232846Sgonzo		}
666232846Sgonzo
667232846Sgonzo		offset += sizeof(int);
668232846Sgonzo	}
669232846Sgonzo
670232846Sgonzo	if (!function_start)
671232846Sgonzo		return (-1);
672232846Sgonzo
673232846Sgonzo	if (registers_on_stack) {
674232846Sgonzo		offset = 0;
675232846Sgonzo		while ((offset < MAX_PROLOGUE_SIZE)
676232846Sgonzo		    && ((function_start + offset) < *pc)) {
677232846Sgonzo			i.word = fuword32((void *)(function_start + offset));
678232846Sgonzo			switch (i.JType.op) {
679232846Sgonzo			case OP_SW:
680232846Sgonzo				/* look for saved registers on the stack */
681232846Sgonzo				if (i.IType.rs != 29)
682232846Sgonzo					break;
683232846Sgonzo				/* only restore the first one */
684232846Sgonzo				if (mask & (1 << i.IType.rt))
685232846Sgonzo					break;
686232846Sgonzo				mask |= (1 << i.IType.rt);
687232846Sgonzo				if (i.IType.rt == 31)
688232846Sgonzo					*ra = fuword32((void *)(*sp + (short)i.IType.imm));
689232846Sgonzo				break;
690232846Sgonzo
691232846Sgonzo#if defined(__mips_n64)
692232846Sgonzo			case OP_SD:
693232846Sgonzo				/* look for saved registers on the stack */
694232846Sgonzo				if (i.IType.rs != 29)
695232846Sgonzo					break;
696232846Sgonzo				/* only restore the first one */
697232846Sgonzo				if (mask & (1 << i.IType.rt))
698232846Sgonzo					break;
699232846Sgonzo				mask |= (1 << i.IType.rt);
700232846Sgonzo				/* ra */
701232846Sgonzo				if (i.IType.rt == 31)
702232846Sgonzo					*ra = fuword64((void *)(*sp + (short)i.IType.imm));
703232846Sgonzo			break;
704232846Sgonzo#endif
705232846Sgonzo
706232846Sgonzo			case OP_ADDI:
707232846Sgonzo			case OP_ADDIU:
708232846Sgonzo			case OP_DADDI:
709232846Sgonzo			case OP_DADDIU:
710232846Sgonzo				/* look for stack pointer adjustment */
711232846Sgonzo				if (i.IType.rs != 29 || i.IType.rt != 29)
712232846Sgonzo					break;
713232846Sgonzo				stksize = -((short)i.IType.imm);
714232846Sgonzo			}
715232846Sgonzo
716232846Sgonzo			offset += sizeof(int);
717232846Sgonzo		}
718232846Sgonzo	}
719232846Sgonzo
720232846Sgonzo	/*
721232846Sgonzo	 * We reached the end of backtrace
722232846Sgonzo	 */
723232846Sgonzo	if (*pc == *ra)
724232846Sgonzo		return (-1);
725232846Sgonzo
726232846Sgonzo	*pc = *ra;
727232846Sgonzo	*sp += stksize;
728232846Sgonzo
729232846Sgonzo	return (0);
730232846Sgonzo}
731232846Sgonzo
732233319Sgonzo#endif /* HWPMC_MIPS_BACKTRACE */
733233319Sgonzo
734204635Sgnnstruct pmc_mdep *
735204635Sgnnpmc_md_initialize()
736204635Sgnn{
737233319Sgonzo	return pmc_mips_initialize();
738204635Sgnn}
739204635Sgnn
740204635Sgnnvoid
741204635Sgnnpmc_md_finalize(struct pmc_mdep *md)
742204635Sgnn{
743233319Sgonzo	return pmc_mips_finalize(md);
744204635Sgnn}
745204635Sgnn
746204635Sgnnint
747232846Sgonzopmc_save_kernel_callchain(uintptr_t *cc, int nframes,
748204635Sgnn    struct trapframe *tf)
749204635Sgnn{
750232846Sgonzo	register_t pc, ra, sp;
751232846Sgonzo	int frames = 0;
752232846Sgonzo
753232992Sgonzo	pc = tf->pc;
754232992Sgonzo	sp = tf->sp;
755232992Sgonzo	ra = tf->ra;
756232846Sgonzo
757233319Sgonzo	cc[frames++] = pc;
758233319Sgonzo
759233319Sgonzo#ifdef	HWPMC_MIPS_BACKTRACE
760232846Sgonzo	/*
761232846Sgonzo	 * Unwind, and unwind, and unwind
762232846Sgonzo	 */
763232846Sgonzo	while (1) {
764232846Sgonzo		if (frames >= nframes)
765232846Sgonzo			break;
766232846Sgonzo
767232846Sgonzo		if (pmc_next_frame(&pc, &sp) < 0)
768232846Sgonzo			break;
769233319Sgonzo
770233319Sgonzo		cc[frames++] = pc;
771232846Sgonzo	}
772233319Sgonzo#endif
773232846Sgonzo
774232846Sgonzo	return (frames);
775204635Sgnn}
776204635Sgnn
777204635Sgnnint
778232846Sgonzopmc_save_user_callchain(uintptr_t *cc, int nframes,
779204635Sgnn    struct trapframe *tf)
780204635Sgnn{
781232846Sgonzo	register_t pc, ra, sp;
782232846Sgonzo	int frames = 0;
783232846Sgonzo
784232992Sgonzo	pc = tf->pc;
785232992Sgonzo	sp = tf->sp;
786232992Sgonzo	ra = tf->ra;
787232846Sgonzo
788233319Sgonzo	cc[frames++] = pc;
789233319Sgonzo
790233319Sgonzo#ifdef	HWPMC_MIPS_BACKTRACE
791233319Sgonzo
792232846Sgonzo	/*
793232846Sgonzo	 * Unwind, and unwind, and unwind
794232846Sgonzo	 */
795232846Sgonzo	while (1) {
796232846Sgonzo		if (frames >= nframes)
797232846Sgonzo			break;
798232846Sgonzo
799232846Sgonzo		if (pmc_next_uframe(&pc, &sp, &ra) < 0)
800232846Sgonzo			break;
801233319Sgonzo
802233319Sgonzo		cc[frames++] = pc;
803232846Sgonzo	}
804233319Sgonzo#endif
805232846Sgonzo
806232846Sgonzo	return (frames);
807204635Sgnn}
808