hwpmc_powerpc.c revision 260669
1/*-
2 * Copyright (c) 2011,2013 Justin Hibbits
3 * Copyright (c) 2005, Joseph Koshy
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 *    notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 *    notice, this list of conditions and the following disclaimer in the
13 *    documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25 * SUCH DAMAGE.
26 *
27 */
28
29#include <sys/cdefs.h>
30__FBSDID("$FreeBSD: stable/10/sys/dev/hwpmc/hwpmc_powerpc.c 260669 2014-01-15 04:44:52Z jhibbits $");
31
32#include <sys/param.h>
33#include <sys/pmc.h>
34#include <sys/pmckern.h>
35#include <sys/systm.h>
36
37#include <machine/pmc_mdep.h>
38#include <machine/spr.h>
39#include <machine/pte.h>
40#include <machine/sr.h>
41#include <machine/cpu.h>
42#include <machine/vmparam.h> /* For VM_MIN_KERNEL_ADDRESS/VM_MAX_KERNEL_ADDRESS */
43
44#include "hwpmc_powerpc.h"
45
46#define INKERNEL(x)	(((vm_offset_t)(x)) <= VM_MAX_KERNEL_ADDRESS && \
47		((vm_offset_t)(x)) >= VM_MIN_KERNEL_ADDRESS)
48#define INUSER(x)	(((vm_offset_t)(x)) <= VM_MAXUSER_ADDRESS && \
49		((vm_offset_t)(x)) >= VM_MIN_ADDRESS)
50
51struct powerpc_cpu **powerpc_pcpu;
52
53int
54pmc_save_kernel_callchain(uintptr_t *cc, int maxsamples,
55    struct trapframe *tf)
56{
57	int frames = 0;
58	uintptr_t *sp;
59
60	cc[frames++] = PMC_TRAPFRAME_TO_PC(tf);
61	sp = (uintptr_t *)PMC_TRAPFRAME_TO_FP(tf);
62
63	for (frames = 1; frames < maxsamples; frames++) {
64		if (!INKERNEL(sp))
65			break;
66		cc[frames++] = sp[1];
67		sp = (uintptr_t *)*sp;
68	}
69	return (frames);
70}
71
72static int
73powerpc_switch_in(struct pmc_cpu *pc, struct pmc_process *pp)
74{
75	return (0);
76}
77
78static int
79powerpc_switch_out(struct pmc_cpu *pc, struct pmc_process *pp)
80{
81	return (0);
82}
83
84int
85powerpc_describe(int cpu, int ri, struct pmc_info *pi, struct pmc **ppmc)
86{
87	int error;
88	struct pmc_hw *phw;
89	char powerpc_name[PMC_NAME_MAX];
90
91	KASSERT(cpu >= 0 && cpu < pmc_cpu_max(),
92	    ("[powerpc,%d], illegal CPU %d", __LINE__, cpu));
93
94	phw = &powerpc_pcpu[cpu]->pc_ppcpmcs[ri];
95	snprintf(powerpc_name, sizeof(powerpc_name), "POWERPC-%d", ri);
96	if ((error = copystr(powerpc_name, pi->pm_name, PMC_NAME_MAX,
97	    NULL)) != 0)
98		return error;
99	pi->pm_class = PMC_CLASS_PPC7450;
100	if (phw->phw_state & PMC_PHW_FLAG_IS_ENABLED) {
101		pi->pm_enabled = TRUE;
102		*ppmc          = phw->phw_pmc;
103	} else {
104		pi->pm_enabled = FALSE;
105		*ppmc	       = NULL;
106	}
107
108	return (0);
109}
110
111int
112powerpc_get_config(int cpu, int ri, struct pmc **ppm)
113{
114	*ppm = powerpc_pcpu[cpu]->pc_ppcpmcs[ri].phw_pmc;
115
116	return (0);
117}
118
119struct pmc_mdep *
120pmc_md_initialize()
121{
122	struct pmc_mdep *pmc_mdep;
123	int error;
124	uint16_t vers;
125
126	/*
127	 * Allocate space for pointers to PMC HW descriptors and for
128	 * the MDEP structure used by MI code.
129	 */
130	powerpc_pcpu = malloc(sizeof(struct powerpc_cpu *) * pmc_cpu_max(), M_PMC,
131			   M_WAITOK|M_ZERO);
132
133	/* Just one class */
134	pmc_mdep = pmc_mdep_alloc(1);
135
136	pmc_mdep->pmd_cputype = PMC_CPU_PPC_7450;
137
138	vers = mfpvr() >> 16;
139
140	pmc_mdep->pmd_switch_in  = powerpc_switch_in;
141	pmc_mdep->pmd_switch_out = powerpc_switch_out;
142
143	switch (vers) {
144	case MPC7447A:
145	case MPC7448:
146	case MPC7450:
147	case MPC7455:
148	case MPC7457:
149		error = pmc_mpc7xxx_initialize(pmc_mdep);
150	case IBM970:
151	case IBM970FX:
152	case IBM970MP:
153	default:
154		error = -1;
155		break;
156	}
157
158	if (error != 0) {
159		pmc_mdep_free(pmc_mdep);
160		pmc_mdep = NULL;
161		return NULL;
162	}
163
164	return (pmc_mdep);
165}
166
167void
168pmc_md_finalize(struct pmc_mdep *md)
169{
170	free(md, M_PMC);
171}
172
173int
174pmc_save_user_callchain(uintptr_t *cc, int maxsamples,
175    struct trapframe *tf)
176{
177	uintptr_t *sp;
178	int frames = 0;
179
180	cc[frames++] = PMC_TRAPFRAME_TO_PC(tf);
181	sp = (uintptr_t *)PMC_TRAPFRAME_TO_FP(tf);
182
183	for (frames = 1; frames < maxsamples; frames++) {
184		if (!INUSER(sp))
185			break;
186		cc[frames++] = fuword(sp + 1);
187		sp = (uintptr_t *)fuword(sp);
188	}
189	return (frames);
190}
191