perfmon.c revision 32054
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.13 1997/12/26 20:41:37 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#include <machine/clock.h>
39#endif
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);
49static int writectl5(int);
50static int writectl6(int);
51
52/*
53 * Must be called after cpu_class is set up.
54 */
55void
56perfmon_init(void)
57{
58#ifndef SMP
59	switch(cpu_class) {
60	case CPUCLASS_586:
61		perfmon_cpuok = 1;
62		msr_ctl[0] = 0x11;
63		msr_ctl[1] = 0x11;
64		msr_pmc[0] = 0x12;
65		msr_pmc[1] = 0x13;
66		writectl = writectl5;
67		break;
68	case CPUCLASS_686:
69		perfmon_cpuok = 1;
70		msr_ctl[0] = 0x186;
71		msr_ctl[1] = 0x187;
72		msr_pmc[0] = 0xc1;
73		msr_pmc[1] = 0xc2;
74		writectl = writectl6;
75		break;
76
77	default:
78		perfmon_cpuok = 0;
79		break;
80	}
81#endif /* SMP */
82}
83
84int
85perfmon_avail(void)
86{
87	return perfmon_cpuok;
88}
89
90int
91perfmon_setup(int pmc, unsigned int control)
92{
93	if (pmc < 0 || pmc >= NPMC)
94		return EINVAL;
95
96	perfmon_inuse |= (1 << pmc);
97	control &= ~(PMCF_SYS_FLAGS << 16);
98	disable_intr();
99	ctl_shadow[pmc] = control;
100	writectl(pmc);
101	wrmsr(msr_pmc[pmc], pmc_shadow[pmc] = 0);
102	enable_intr();
103	return 0;
104}
105
106int
107perfmon_get(int pmc, unsigned int *control)
108{
109	if (pmc < 0 || pmc >= NPMC)
110		return EINVAL;
111
112	if (perfmon_inuse & (1 << pmc)) {
113		*control = ctl_shadow[pmc];
114		return 0;
115	}
116	return EBUSY;		/* XXX reversed sense */
117}
118
119int
120perfmon_fini(int pmc)
121{
122	if (pmc < 0 || pmc >= NPMC)
123		return EINVAL;
124
125	if (perfmon_inuse & (1 << pmc)) {
126		perfmon_stop(pmc);
127		ctl_shadow[pmc] = 0;
128		perfmon_inuse &= ~(1 << pmc);
129		return 0;
130	}
131	return EBUSY;		/* XXX reversed sense */
132}
133
134int
135perfmon_start(int pmc)
136{
137	if (pmc < 0 || pmc >= NPMC)
138		return EINVAL;
139
140	if (perfmon_inuse & (1 << pmc)) {
141		disable_intr();
142		ctl_shadow[pmc] |= (PMCF_EN << 16);
143		wrmsr(msr_pmc[pmc], pmc_shadow[pmc]);
144		writectl(pmc);
145		enable_intr();
146		return 0;
147	}
148	return EBUSY;
149}
150
151int
152perfmon_stop(int pmc)
153{
154	if (pmc < 0 || pmc >= NPMC)
155		return EINVAL;
156
157	if (perfmon_inuse & (1 << pmc)) {
158		disable_intr();
159		pmc_shadow[pmc] = rdmsr(msr_pmc[pmc]);
160		ctl_shadow[pmc] &= ~(PMCF_EN << 16);
161		writectl(pmc);
162		enable_intr();
163		return 0;
164	}
165	return EBUSY;
166}
167
168int
169perfmon_read(int pmc, quad_t *val)
170{
171	if (pmc < 0 || pmc >= NPMC)
172		return EINVAL;
173
174	if (perfmon_inuse & (1 << pmc)) {
175		if (ctl_shadow[pmc] & (PMCF_EN << 16))
176			*val = rdmsr(msr_pmc[pmc]);
177		else
178			*val = pmc_shadow[pmc];
179		return 0;
180	}
181
182	return EBUSY;
183}
184
185int
186perfmon_reset(int pmc)
187{
188	if (pmc < 0 || pmc >= NPMC)
189		return EINVAL;
190
191	if (perfmon_inuse & (1 << pmc)) {
192		wrmsr(msr_pmc[pmc], pmc_shadow[pmc] = 0);
193		return 0;
194	}
195	return EBUSY;
196}
197
198/*
199 * Unfortunately, the performance-monitoring registers are laid out
200 * differently in the P5 and P6.  We keep everything in P6 format
201 * internally (except for the event code), and convert to P5
202 * format as needed on those CPUs.  The writectl function pointer
203 * is set up to point to one of these functions by perfmon_init().
204 */
205int
206writectl6(int pmc)
207{
208	if (pmc > 0 && !(ctl_shadow[pmc] & (PMCF_EN << 16))) {
209		wrmsr(msr_ctl[pmc], 0);
210	} else {
211		wrmsr(msr_ctl[pmc], ctl_shadow[pmc]);
212	}
213	return 0;
214}
215
216#define	P5FLAG_P	0x200
217#define	P5FLAG_E	0x100
218#define	P5FLAG_USR	0x80
219#define	P5FLAG_OS	0x40
220
221int
222writectl5(int pmc)
223{
224	quad_t newval = 0;
225
226	if (ctl_shadow[1] & (PMCF_EN << 16)) {
227		if (ctl_shadow[1] & (PMCF_USR << 16))
228			newval |= P5FLAG_USR << 16;
229		if (ctl_shadow[1] & (PMCF_OS << 16))
230			newval |= P5FLAG_OS << 16;
231		if (!(ctl_shadow[1] & (PMCF_E << 16)))
232			newval |= P5FLAG_E << 16;
233		newval |= (ctl_shadow[1] & 0x3f) << 16;
234	}
235	if (ctl_shadow[0] & (PMCF_EN << 16)) {
236		if (ctl_shadow[0] & (PMCF_USR << 16))
237			newval |= P5FLAG_USR;
238		if (ctl_shadow[0] & (PMCF_OS << 16))
239			newval |= P5FLAG_OS;
240		if (!(ctl_shadow[0] & (PMCF_E << 16)))
241			newval |= P5FLAG_E;
242		newval |= ctl_shadow[0] & 0x3f;
243	}
244
245	wrmsr(msr_ctl[0], newval);
246	return 0;		/* XXX should check for unimplemented bits */
247}
248
249/*
250 * Now the user-mode interface, called from a subdevice of mem.c.
251 */
252static int writer;
253static int writerpmc;
254
255int
256perfmon_open(dev_t dev, int flags, int fmt, struct proc *p)
257{
258	if (!perfmon_cpuok)
259		return ENXIO;
260
261	if (flags & FWRITE) {
262		if (writer) {
263			return EBUSY;
264		} else {
265			writer = 1;
266			writerpmc = 0;
267		}
268	}
269	return 0;
270}
271
272int
273perfmon_close(dev_t dev, int flags, int fmt, struct proc *p)
274{
275	if (flags & FWRITE) {
276		int i;
277
278		for (i = 0; i < NPMC; i++) {
279			if (writerpmc & (1 << i))
280				perfmon_fini(i);
281		}
282		writer = 0;
283	}
284	return 0;
285}
286
287int
288perfmon_ioctl(dev_t dev, int cmd, caddr_t param, int flags, struct proc *p)
289{
290	struct pmc *pmc;
291	struct pmc_data *pmcd;
292	struct pmc_tstamp *pmct;
293	int *ip;
294	int rv;
295
296	switch(cmd) {
297	case PMIOSETUP:
298		if (!(flags & FWRITE))
299			return EPERM;
300		pmc = (struct pmc *)param;
301
302		rv = perfmon_setup(pmc->pmc_num, pmc->pmc_val);
303		if (!rv) {
304			writerpmc |= (1 << pmc->pmc_num);
305		}
306		break;
307
308	case PMIOGET:
309		pmc = (struct pmc *)param;
310		rv = perfmon_get(pmc->pmc_num, &pmc->pmc_val);
311		break;
312
313	case PMIOSTART:
314		if (!(flags & FWRITE))
315			return EPERM;
316
317		ip = (int *)param;
318		rv = perfmon_start(*ip);
319		break;
320
321	case PMIOSTOP:
322		if (!(flags & FWRITE))
323			return EPERM;
324
325		ip = (int *)param;
326		rv = perfmon_stop(*ip);
327		break;
328
329	case PMIORESET:
330		if (!(flags & FWRITE))
331			return EPERM;
332
333		ip = (int *)param;
334		rv = perfmon_reset(*ip);
335		break;
336
337	case PMIOREAD:
338		pmcd = (struct pmc_data *)param;
339		rv = perfmon_read(pmcd->pmcd_num, &pmcd->pmcd_value);
340		break;
341
342	case PMIOTSTAMP:
343		if (!tsc_freq) {
344			rv = ENOTTY;
345			break;
346		}
347		pmct = (struct pmc_tstamp *)param;
348		/* XXX interface loses precision. */
349		pmct->pmct_rate = tsc_freq / 1000000;
350		pmct->pmct_value = rdtsc();
351		rv = 0;
352		break;
353	default:
354		rv = ENOTTY;
355	}
356
357	return rv;
358}
359