perfmon.c revision 27132
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.10 1997/02/22 09:32:35 peter Exp $
30 */
31
32#include "opt_cpu.h"
33
34#include <sys/param.h>
35#include <sys/systm.h>
36#include <sys/fcntl.h>
37
38#ifndef SMP
39#include <machine/cpu.h>
40#include <machine/cputypes.h>
41#include <machine/clock.h>
42#endif
43#include <machine/perfmon.h>
44
45static int perfmon_inuse;
46static int perfmon_cpuok;
47static int msr_ctl[NPMC];
48static int msr_pmc[NPMC];
49static unsigned int ctl_shadow[NPMC];
50static quad_t pmc_shadow[NPMC];	/* used when ctr is stopped on P5 */
51static int (*writectl)(int);
52static int writectl5(int);
53static int writectl6(int);
54
55/*
56 * Must be called after cpu_class is set up.
57 */
58void
59perfmon_init(void)
60{
61#ifndef SMP
62	switch(cpu_class) {
63	case CPUCLASS_586:
64		perfmon_cpuok = 1;
65		msr_ctl[0] = 0x11;
66		msr_ctl[1] = 0x11;
67		msr_pmc[0] = 0x12;
68		msr_pmc[1] = 0x13;
69		writectl = writectl5;
70		break;
71	case CPUCLASS_686:
72		perfmon_cpuok = 1;
73		msr_ctl[0] = 0x186;
74		msr_ctl[1] = 0x187;
75		msr_pmc[0] = 0xc1;
76		msr_pmc[1] = 0xc2;
77		writectl = writectl6;
78		break;
79
80	default:
81		perfmon_cpuok = 0;
82		break;
83	}
84#endif /* SMP */
85}
86
87int
88perfmon_avail(void)
89{
90	return perfmon_cpuok;
91}
92
93int
94perfmon_setup(int pmc, unsigned int control)
95{
96	if (pmc < 0 || pmc >= NPMC)
97		return EINVAL;
98
99	perfmon_inuse |= (1 << pmc);
100	control &= ~(PMCF_SYS_FLAGS << 16);
101	disable_intr();
102	ctl_shadow[pmc] = control;
103	writectl(pmc);
104	wrmsr(msr_pmc[pmc], pmc_shadow[pmc] = 0);
105	enable_intr();
106	return 0;
107}
108
109int
110perfmon_get(int pmc, unsigned int *control)
111{
112	if (pmc < 0 || pmc >= NPMC)
113		return EINVAL;
114
115	if (perfmon_inuse & (1 << pmc)) {
116		*control = ctl_shadow[pmc];
117		return 0;
118	}
119	return EBUSY;		/* XXX reversed sense */
120}
121
122int
123perfmon_fini(int pmc)
124{
125	if (pmc < 0 || pmc >= NPMC)
126		return EINVAL;
127
128	if (perfmon_inuse & (1 << pmc)) {
129		perfmon_stop(pmc);
130		ctl_shadow[pmc] = 0;
131		perfmon_inuse &= ~(1 << pmc);
132		return 0;
133	}
134	return EBUSY;		/* XXX reversed sense */
135}
136
137int
138perfmon_start(int pmc)
139{
140	if (pmc < 0 || pmc >= NPMC)
141		return EINVAL;
142
143	if (perfmon_inuse & (1 << pmc)) {
144		disable_intr();
145		ctl_shadow[pmc] |= (PMCF_EN << 16);
146		wrmsr(msr_pmc[pmc], pmc_shadow[pmc]);
147		writectl(pmc);
148		enable_intr();
149		return 0;
150	}
151	return EBUSY;
152}
153
154int
155perfmon_stop(int pmc)
156{
157	if (pmc < 0 || pmc >= NPMC)
158		return EINVAL;
159
160	if (perfmon_inuse & (1 << pmc)) {
161		disable_intr();
162		pmc_shadow[pmc] = rdmsr(msr_pmc[pmc]);
163		ctl_shadow[pmc] &= ~(PMCF_EN << 16);
164		writectl(pmc);
165		enable_intr();
166		return 0;
167	}
168	return EBUSY;
169}
170
171int
172perfmon_read(int pmc, quad_t *val)
173{
174	if (pmc < 0 || pmc >= NPMC)
175		return EINVAL;
176
177	if (perfmon_inuse & (1 << pmc)) {
178		if (ctl_shadow[pmc] & (PMCF_EN << 16))
179			*val = rdmsr(msr_pmc[pmc]);
180		else
181			*val = pmc_shadow[pmc];
182		return 0;
183	}
184
185	return EBUSY;
186}
187
188int
189perfmon_reset(int pmc)
190{
191	if (pmc < 0 || pmc >= NPMC)
192		return EINVAL;
193
194	if (perfmon_inuse & (1 << pmc)) {
195		wrmsr(msr_pmc[pmc], pmc_shadow[pmc] = 0);
196		return 0;
197	}
198	return EBUSY;
199}
200
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
252/*
253 * Now the user-mode interface, called from a subdevice of mem.c.
254 */
255static int writer;
256static int writerpmc;
257
258int
259perfmon_open(dev_t dev, int flags, int fmt, struct proc *p)
260{
261	if (!perfmon_cpuok)
262		return ENXIO;
263
264	if (flags & FWRITE) {
265		if (writer) {
266			return EBUSY;
267		} else {
268			writer = 1;
269			writerpmc = 0;
270		}
271	}
272	return 0;
273}
274
275int
276perfmon_close(dev_t dev, int flags, int fmt, struct proc *p)
277{
278	if (flags & FWRITE) {
279		int i;
280
281		for (i = 0; i < NPMC; i++) {
282			if (writerpmc & (1 << i))
283				perfmon_fini(i);
284		}
285		writer = 0;
286	}
287	return 0;
288}
289
290int
291perfmon_ioctl(dev_t dev, int cmd, caddr_t param, int flags, struct proc *p)
292{
293	struct pmc *pmc;
294	struct pmc_data *pmcd;
295	struct pmc_tstamp *pmct;
296	int *ip;
297	int rv;
298
299	switch(cmd) {
300	case PMIOSETUP:
301		if (!(flags & FWRITE))
302			return EPERM;
303		pmc = (struct pmc *)param;
304
305		rv = perfmon_setup(pmc->pmc_num, pmc->pmc_val);
306		if (!rv) {
307			writerpmc |= (1 << pmc->pmc_num);
308		}
309		break;
310
311	case PMIOGET:
312		pmc = (struct pmc *)param;
313		rv = perfmon_get(pmc->pmc_num, &pmc->pmc_val);
314		break;
315
316	case PMIOSTART:
317		if (!(flags & FWRITE))
318			return EPERM;
319
320		ip = (int *)param;
321		rv = perfmon_start(*ip);
322		break;
323
324	case PMIOSTOP:
325		if (!(flags & FWRITE))
326			return EPERM;
327
328		ip = (int *)param;
329		rv = perfmon_stop(*ip);
330		break;
331
332	case PMIORESET:
333		if (!(flags & FWRITE))
334			return EPERM;
335
336		ip = (int *)param;
337		rv = perfmon_reset(*ip);
338		break;
339
340	case PMIOREAD:
341		pmcd = (struct pmc_data *)param;
342		rv = perfmon_read(pmcd->pmcd_num, &pmcd->pmcd_value);
343		break;
344
345#if (defined(I586_CPU) || defined(I686_CPU)) && !defined(SMP)
346	case PMIOTSTAMP:
347		pmct = (struct pmc_tstamp *)param;
348		/* XXX interface loses precision. */
349		pmct->pmct_rate = i586_ctr_freq / 1000000;
350		pmct->pmct_value = rdtsc();
351		rv = 0;
352		break;
353#endif
354
355	default:
356		rv = ENOTTY;
357	}
358
359	return rv;
360}
361