perfmon.c revision 41591
1/*
2 * Copyright 1996 Massachusetts Institute of Technology
3 *
4 * Permission to use, copy, modify, and distribute this software and
5 * its documentation for any purpose and without fee is hereby
6 * granted, provided that both the above copyright notice and this
7 * permission notice appear in all copies, that both the above
8 * copyright notice and this permission notice appear in all
9 * supporting documentation, and that the name of M.I.T. not be used
10 * in advertising or publicity pertaining to distribution of the
11 * software without specific, written prior permission.  M.I.T. makes
12 * no representations about the suitability of this software for any
13 * purpose.  It is provided "as is" without express or implied
14 * warranty.
15 *
16 * THIS SOFTWARE IS PROVIDED BY M.I.T. ``AS IS''.  M.I.T. DISCLAIMS
17 * ALL EXPRESS OR IMPLIED WARRANTIES WITH REGARD TO THIS SOFTWARE,
18 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
19 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT
20 * SHALL M.I.T. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
23 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
24 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
25 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
26 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 * SUCH DAMAGE.
28 *
29 *	$Id: perfmon.c,v 1.15 1997/12/29 23:36:56 phk Exp $
30 */
31
32#include <sys/param.h>
33#include <sys/systm.h>
34#include <sys/fcntl.h>
35
36#ifndef SMP
37#include <machine/cputypes.h>
38#endif
39#include <machine/clock.h>
40#include <machine/perfmon.h>
41
42static int perfmon_inuse;
43static int perfmon_cpuok;
44static int msr_ctl[NPMC];
45static int msr_pmc[NPMC];
46static unsigned int ctl_shadow[NPMC];
47static quad_t pmc_shadow[NPMC];	/* used when ctr is stopped on P5 */
48static int (*writectl)(int);
49#ifndef SMP
50static int writectl5(int);
51static int writectl6(int);
52#endif
53
54/*
55 * Must be called after cpu_class is set up.
56 */
57void
58perfmon_init(void)
59{
60#ifndef SMP
61	switch(cpu_class) {
62	case CPUCLASS_586:
63		perfmon_cpuok = 1;
64		msr_ctl[0] = 0x11;
65		msr_ctl[1] = 0x11;
66		msr_pmc[0] = 0x12;
67		msr_pmc[1] = 0x13;
68		writectl = writectl5;
69		break;
70	case CPUCLASS_686:
71		perfmon_cpuok = 1;
72		msr_ctl[0] = 0x186;
73		msr_ctl[1] = 0x187;
74		msr_pmc[0] = 0xc1;
75		msr_pmc[1] = 0xc2;
76		writectl = writectl6;
77		break;
78
79	default:
80		perfmon_cpuok = 0;
81		break;
82	}
83#endif /* SMP */
84}
85
86int
87perfmon_avail(void)
88{
89	return perfmon_cpuok;
90}
91
92int
93perfmon_setup(int pmc, unsigned int control)
94{
95	if (pmc < 0 || pmc >= NPMC)
96		return EINVAL;
97
98	perfmon_inuse |= (1 << pmc);
99	control &= ~(PMCF_SYS_FLAGS << 16);
100	disable_intr();
101	ctl_shadow[pmc] = control;
102	writectl(pmc);
103	wrmsr(msr_pmc[pmc], pmc_shadow[pmc] = 0);
104	enable_intr();
105	return 0;
106}
107
108int
109perfmon_get(int pmc, unsigned int *control)
110{
111	if (pmc < 0 || pmc >= NPMC)
112		return EINVAL;
113
114	if (perfmon_inuse & (1 << pmc)) {
115		*control = ctl_shadow[pmc];
116		return 0;
117	}
118	return EBUSY;		/* XXX reversed sense */
119}
120
121int
122perfmon_fini(int pmc)
123{
124	if (pmc < 0 || pmc >= NPMC)
125		return EINVAL;
126
127	if (perfmon_inuse & (1 << pmc)) {
128		perfmon_stop(pmc);
129		ctl_shadow[pmc] = 0;
130		perfmon_inuse &= ~(1 << pmc);
131		return 0;
132	}
133	return EBUSY;		/* XXX reversed sense */
134}
135
136int
137perfmon_start(int pmc)
138{
139	if (pmc < 0 || pmc >= NPMC)
140		return EINVAL;
141
142	if (perfmon_inuse & (1 << pmc)) {
143		disable_intr();
144		ctl_shadow[pmc] |= (PMCF_EN << 16);
145		wrmsr(msr_pmc[pmc], pmc_shadow[pmc]);
146		writectl(pmc);
147		enable_intr();
148		return 0;
149	}
150	return EBUSY;
151}
152
153int
154perfmon_stop(int pmc)
155{
156	if (pmc < 0 || pmc >= NPMC)
157		return EINVAL;
158
159	if (perfmon_inuse & (1 << pmc)) {
160		disable_intr();
161		pmc_shadow[pmc] = rdmsr(msr_pmc[pmc]);
162		ctl_shadow[pmc] &= ~(PMCF_EN << 16);
163		writectl(pmc);
164		enable_intr();
165		return 0;
166	}
167	return EBUSY;
168}
169
170int
171perfmon_read(int pmc, quad_t *val)
172{
173	if (pmc < 0 || pmc >= NPMC)
174		return EINVAL;
175
176	if (perfmon_inuse & (1 << pmc)) {
177		if (ctl_shadow[pmc] & (PMCF_EN << 16))
178			*val = rdmsr(msr_pmc[pmc]);
179		else
180			*val = pmc_shadow[pmc];
181		return 0;
182	}
183
184	return EBUSY;
185}
186
187int
188perfmon_reset(int pmc)
189{
190	if (pmc < 0 || pmc >= NPMC)
191		return EINVAL;
192
193	if (perfmon_inuse & (1 << pmc)) {
194		wrmsr(msr_pmc[pmc], pmc_shadow[pmc] = 0);
195		return 0;
196	}
197	return EBUSY;
198}
199
200#ifndef SMP
201/*
202 * Unfortunately, the performance-monitoring registers are laid out
203 * differently in the P5 and P6.  We keep everything in P6 format
204 * internally (except for the event code), and convert to P5
205 * format as needed on those CPUs.  The writectl function pointer
206 * is set up to point to one of these functions by perfmon_init().
207 */
208int
209writectl6(int pmc)
210{
211	if (pmc > 0 && !(ctl_shadow[pmc] & (PMCF_EN << 16))) {
212		wrmsr(msr_ctl[pmc], 0);
213	} else {
214		wrmsr(msr_ctl[pmc], ctl_shadow[pmc]);
215	}
216	return 0;
217}
218
219#define	P5FLAG_P	0x200
220#define	P5FLAG_E	0x100
221#define	P5FLAG_USR	0x80
222#define	P5FLAG_OS	0x40
223
224int
225writectl5(int pmc)
226{
227	quad_t newval = 0;
228
229	if (ctl_shadow[1] & (PMCF_EN << 16)) {
230		if (ctl_shadow[1] & (PMCF_USR << 16))
231			newval |= P5FLAG_USR << 16;
232		if (ctl_shadow[1] & (PMCF_OS << 16))
233			newval |= P5FLAG_OS << 16;
234		if (!(ctl_shadow[1] & (PMCF_E << 16)))
235			newval |= P5FLAG_E << 16;
236		newval |= (ctl_shadow[1] & 0x3f) << 16;
237	}
238	if (ctl_shadow[0] & (PMCF_EN << 16)) {
239		if (ctl_shadow[0] & (PMCF_USR << 16))
240			newval |= P5FLAG_USR;
241		if (ctl_shadow[0] & (PMCF_OS << 16))
242			newval |= P5FLAG_OS;
243		if (!(ctl_shadow[0] & (PMCF_E << 16)))
244			newval |= P5FLAG_E;
245		newval |= ctl_shadow[0] & 0x3f;
246	}
247
248	wrmsr(msr_ctl[0], newval);
249	return 0;		/* XXX should check for unimplemented bits */
250}
251#endif /* !SMP */
252
253/*
254 * Now the user-mode interface, called from a subdevice of mem.c.
255 */
256static int writer;
257static int writerpmc;
258
259int
260perfmon_open(dev_t dev, int flags, int fmt, struct proc *p)
261{
262	if (!perfmon_cpuok)
263		return ENXIO;
264
265	if (flags & FWRITE) {
266		if (writer) {
267			return EBUSY;
268		} else {
269			writer = 1;
270			writerpmc = 0;
271		}
272	}
273	return 0;
274}
275
276int
277perfmon_close(dev_t dev, int flags, int fmt, struct proc *p)
278{
279	if (flags & FWRITE) {
280		int i;
281
282		for (i = 0; i < NPMC; i++) {
283			if (writerpmc & (1 << i))
284				perfmon_fini(i);
285		}
286		writer = 0;
287	}
288	return 0;
289}
290
291int
292perfmon_ioctl(dev_t dev, int cmd, caddr_t param, int flags, struct proc *p)
293{
294	struct pmc *pmc;
295	struct pmc_data *pmcd;
296	struct pmc_tstamp *pmct;
297	int *ip;
298	int rv;
299
300	switch(cmd) {
301	case PMIOSETUP:
302		if (!(flags & FWRITE))
303			return EPERM;
304		pmc = (struct pmc *)param;
305
306		rv = perfmon_setup(pmc->pmc_num, pmc->pmc_val);
307		if (!rv) {
308			writerpmc |= (1 << pmc->pmc_num);
309		}
310		break;
311
312	case PMIOGET:
313		pmc = (struct pmc *)param;
314		rv = perfmon_get(pmc->pmc_num, &pmc->pmc_val);
315		break;
316
317	case PMIOSTART:
318		if (!(flags & FWRITE))
319			return EPERM;
320
321		ip = (int *)param;
322		rv = perfmon_start(*ip);
323		break;
324
325	case PMIOSTOP:
326		if (!(flags & FWRITE))
327			return EPERM;
328
329		ip = (int *)param;
330		rv = perfmon_stop(*ip);
331		break;
332
333	case PMIORESET:
334		if (!(flags & FWRITE))
335			return EPERM;
336
337		ip = (int *)param;
338		rv = perfmon_reset(*ip);
339		break;
340
341	case PMIOREAD:
342		pmcd = (struct pmc_data *)param;
343		rv = perfmon_read(pmcd->pmcd_num, &pmcd->pmcd_value);
344		break;
345
346	case PMIOTSTAMP:
347		if (!tsc_freq) {
348			rv = ENOTTY;
349			break;
350		}
351		pmct = (struct pmc_tstamp *)param;
352		/* XXX interface loses precision. */
353		pmct->pmct_rate = tsc_freq / 1000000;
354		pmct->pmct_value = rdtsc();
355		rv = 0;
356		break;
357	default:
358		rv = ENOTTY;
359	}
360
361	return rv;
362}
363