perfmon.c revision 14888
1139825Simp/*
299663Sbenno * Copyright 1996 Massachusetts Institute of Technology
399663Sbenno *
499663Sbenno * Permission to use, copy, modify, and distribute this software and
599663Sbenno * its documentation for any purpose and without fee is hereby
699663Sbenno * granted, provided that both the above copyright notice and this
799663Sbenno * permission notice appear in all copies, that both the above
899663Sbenno * copyright notice and this permission notice appear in all
999663Sbenno * supporting documentation, and that the name of M.I.T. not be used
1099663Sbenno * in advertising or publicity pertaining to distribution of the
1199663Sbenno * software without specific, written prior permission.  M.I.T. makes
1299663Sbenno * no representations about the suitability of this software for any
1399663Sbenno * purpose.  It is provided "as is" without express or implied
1499663Sbenno * warranty.
1599663Sbenno *
1699663Sbenno * THIS SOFTWARE IS PROVIDED BY M.I.T. ``AS IS''.  M.I.T. DISCLAIMS
1799663Sbenno * ALL EXPRESS OR IMPLIED WARRANTIES WITH REGARD TO THIS SOFTWARE,
1899663Sbenno * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
1999663Sbenno * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT
2099663Sbenno * SHALL M.I.T. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
2199663Sbenno * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
2299663Sbenno * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
2399663Sbenno * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
2499663Sbenno * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
2599663Sbenno * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
2699663Sbenno * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2799663Sbenno * SUCH DAMAGE.
2899663Sbenno *
2999663Sbenno *	$Id: perfmon.c,v 1.2 1996/03/27 22:02:18 wollman Exp $
30131102Sgrehan */
3199663Sbenno
3299663Sbenno#include <sys/param.h>
3399663Sbenno#include <sys/systm.h>
3499663Sbenno#include <sys/fcntl.h>
3599663Sbenno#include <sys/ioccom.h>
3699663Sbenno
37183882Snwhitehorn#include <machine/cpu.h>
38186128Snwhitehorn#include <machine/cputypes.h>
3999663Sbenno#include <machine/clock.h>
4099663Sbenno#include <machine/perfmon.h>
4199663Sbenno
4299663Sbennostatic int perfmon_inuse;
4399663Sbennostatic int perfmon_cpuok;
44208149Snwhitehornstatic int msr_ctl[NPMC];
4599663Sbennostatic int msr_pmc[NPMC];
46174782Smarcelstatic unsigned int ctl_shadow[NPMC];
4799663Sbennostatic quad_t pmc_shadow[NPMC];	/* used when ctr is stopped on P5 */
4899663Sbennostatic int (*writectl)(int);
4999663Sbennostatic int writectl5(int);
5099663Sbennostatic int writectl6(int);
5199663Sbenno
5299663Sbenno/*
5399663Sbenno * Must be called after cpu_class is set up.
5499663Sbenno */
5599663Sbennovoid
56208149Snwhitehornperfmon_init(void)
57208149Snwhitehorn{
58208149Snwhitehorn	switch(cpu_class) {
5999663Sbenno	case CPUCLASS_586:
60208149Snwhitehorn		perfmon_cpuok = 1;
6199663Sbenno		msr_ctl[0] = 0x11;
6299663Sbenno		msr_ctl[1] = 0x11;
6399663Sbenno		msr_pmc[0] = 0x12;
6499663Sbenno		msr_pmc[1] = 0x13;
6599663Sbenno		writectl = writectl5;
66208149Snwhitehorn		break;
67208149Snwhitehorn	case CPUCLASS_686:
68208149Snwhitehorn		perfmon_cpuok = 1;
6999663Sbenno		msr_ctl[0] = 0x186;
7099663Sbenno		msr_ctl[1] = 0x187;
7199663Sbenno		msr_pmc[0] = 0xc1;
72208149Snwhitehorn		msr_pmc[1] = 0xc2;
73208149Snwhitehorn		writectl = writectl6;
74208149Snwhitehorn		break;
75294883Sjhibbits
76294883Sjhibbits	default:
77208149Snwhitehorn		perfmon_cpuok = 0;
78208149Snwhitehorn		break;
79208149Snwhitehorn	}
80208149Snwhitehorn}
81208149Snwhitehorn
82208149Snwhitehornint
83208149Snwhitehornperfmon_avail(void)
8499663Sbenno{
8599663Sbenno	return perfmon_cpuok;
86183882Snwhitehorn}
87183882Snwhitehorn
88183882Snwhitehornint
89208149Snwhitehornperfmon_setup(int pmc, unsigned int control)
90183882Snwhitehorn{
91183882Snwhitehorn	if (pmc < 0 || pmc >= NPMC)
92208149Snwhitehorn		return EINVAL;
9399663Sbenno
9499663Sbenno	perfmon_inuse |= (1 << pmc);
95208149Snwhitehorn	control &= ~(PMCF_SYS_FLAGS << 16);
96208149Snwhitehorn	disable_intr();
97208149Snwhitehorn	ctl_shadow[pmc] = control;
9899663Sbenno	writectl(pmc);
9999663Sbenno	wrmsr(msr_pmc[pmc], pmc_shadow[pmc] = 0);
10099663Sbenno	enable_intr();
101208149Snwhitehorn	return 0;
102208149Snwhitehorn}
10399663Sbenno
104208149Snwhitehornint
105208149Snwhitehornperfmon_get(int pmc, unsigned int *control)
10699663Sbenno{
10799663Sbenno	if (pmc < 0 || pmc >= NPMC)
108208149Snwhitehorn		return EINVAL;
109208149Snwhitehorn
110208149Snwhitehorn	if (perfmon_inuse & (1 << pmc)) {
111208149Snwhitehorn		*control = ctl_shadow[pmc];
11299663Sbenno		return 0;
113208149Snwhitehorn	}
114208149Snwhitehorn	return EBUSY;		/* XXX reversed sense */
115208149Snwhitehorn}
116208149Snwhitehorn
117208149Snwhitehornint
11899663Sbennoperfmon_fini(int pmc)
119208871Snwhitehorn{
120208871Snwhitehorn	if (pmc < 0 || pmc >= NPMC)
121208149Snwhitehorn		return EINVAL;
122208149Snwhitehorn
123208149Snwhitehorn	if (perfmon_inuse & (1 << pmc)) {
124208149Snwhitehorn		perfmon_stop(pmc);
125208149Snwhitehorn		ctl_shadow[pmc] = 0;
126208149Snwhitehorn		perfmon_inuse &= ~(1 << pmc);
127208149Snwhitehorn		return 0;
128183882Snwhitehorn	}
12999663Sbenno	return EBUSY;		/* XXX reversed sense */
13099663Sbenno}
13199663Sbenno
132208149Snwhitehornint
133208149Snwhitehornperfmon_start(int pmc)
134208149Snwhitehorn{
135208149Snwhitehorn	if (pmc < 0 || pmc >= NPMC)
13699663Sbenno		return EINVAL;
13799663Sbenno
138208149Snwhitehorn	if (perfmon_inuse & (1 << pmc)) {
13999663Sbenno		disable_intr();
140259284Sjhibbits		ctl_shadow[pmc] |= (PMCF_EN << 16);
141259284Sjhibbits		wrmsr(msr_pmc[pmc], pmc_shadow[pmc]);
142259284Sjhibbits		writectl(pmc);
143259284Sjhibbits		enable_intr();
144259284Sjhibbits		return 0;
145259284Sjhibbits	}
146259284Sjhibbits	return EBUSY;
147261513Snwhitehorn}
14899663Sbenno
149208149Snwhitehornint
150208149Snwhitehornperfmon_stop(int pmc)
151208149Snwhitehorn{
152208149Snwhitehorn	if (pmc < 0 || pmc >= NPMC)
153208149Snwhitehorn		return EINVAL;
15499663Sbenno
155209298Snwhitehorn	if (perfmon_inuse & (1 << pmc)) {
156208149Snwhitehorn		disable_intr();
157208149Snwhitehorn		pmc_shadow[pmc] = rdmsr(msr_pmc[pmc]);
158208149Snwhitehorn		ctl_shadow[pmc] &= ~(PMCF_EN << 16);
15999663Sbenno		writectl(pmc);
160208149Snwhitehorn		enable_intr();
161208149Snwhitehorn		return 0;
162208149Snwhitehorn	}
163208149Snwhitehorn	return EBUSY;
16499663Sbenno}
165208149Snwhitehorn
166208149Snwhitehornint
167208149Snwhitehornperfmon_read(int pmc, quad_t *val)
168208149Snwhitehorn{
169208149Snwhitehorn	if (pmc < 0 || pmc >= NPMC)
170208149Snwhitehorn		return EINVAL;
171208149Snwhitehorn
172208149Snwhitehorn	if (perfmon_inuse & (1 << pmc)) {
17399663Sbenno		if (ctl_shadow[pmc] & (PMCF_EN << 16))
174208149Snwhitehorn			*val = rdmsr(msr_pmc[pmc]);
175208149Snwhitehorn		else
17699663Sbenno			*val = pmc_shadow[pmc];
177208149Snwhitehorn		return 0;
178208149Snwhitehorn	}
179208149Snwhitehorn
180208149Snwhitehorn	return EBUSY;
181208149Snwhitehorn}
182208149Snwhitehorn
183208149Snwhitehornint
184208149Snwhitehornperfmon_reset(int pmc)
185208149Snwhitehorn{
186218184Smarcel	if (pmc < 0 || pmc >= NPMC)
187218184Smarcel		return EINVAL;
188208149Snwhitehorn
189218184Smarcel	if (perfmon_inuse & (1 << pmc)) {
190208149Snwhitehorn		wrmsr(msr_pmc[pmc], pmc_shadow[pmc] = 0);
191209298Snwhitehorn		return 0;
192218184Smarcel	}
193209298Snwhitehorn	return EBUSY;
194209299Snwhitehorn}
195209298Snwhitehorn
196209298Snwhitehorn/*
197218184Smarcel * Unfortunately, the performance-monitoring registers are laid out
198208149Snwhitehorn * differently in the P5 and P6.  We keep everything in P6 format
199183882Snwhitehorn * internally (except for the event code), and convert to P5
20099663Sbenno * format as needed on those CPUs.  The writectl function pointer
20199663Sbenno * is set up to point to one of these functions by perfmon_init().
202208149Snwhitehorn */
203208149Snwhitehornint
20499663Sbennowritectl6(int pmc)
205208149Snwhitehorn{
206208149Snwhitehorn	if (pmc > 0 && !(ctl_shadow[pmc] & (PMCF_EN << 16))) {
20799663Sbenno		wrmsr(msr_ctl[pmc], 0);
208208149Snwhitehorn	} else {
209208149Snwhitehorn		wrmsr(msr_ctl[pmc], ctl_shadow[pmc]);
210208149Snwhitehorn	}
21199663Sbenno	return 0;
212208149Snwhitehorn}
213208149Snwhitehorn
214208149Snwhitehorn#define	P5FLAG_P	0x200
215208149Snwhitehorn#define	P5FLAG_E	0x100
216208149Snwhitehorn#define	P5FLAG_USR	0x80
217208149Snwhitehorn#define	P5FLAG_OS	0x40
218208149Snwhitehorn
21999663Sbennoint
220208149Snwhitehornwritectl5(int pmc)
221259284Sjhibbits{
222208149Snwhitehorn	quad_t newval = 0;
223259284Sjhibbits	quad_t oldtsc;
224208149Snwhitehorn
225208149Snwhitehorn	if (ctl_shadow[1] & (PMCF_EN << 16)) {
22699663Sbenno		if (ctl_shadow[1] & (PMCF_USR << 16))
227208149Snwhitehorn			newval |= P5FLAG_USR << 16;
228259284Sjhibbits		if (ctl_shadow[1] & (PMCF_OS << 16))
229259284Sjhibbits			newval |= P5FLAG_OS << 16;
230259284Sjhibbits		if (ctl_shadow[1] & (PMCF_E << 16))
231259284Sjhibbits			newval |= P5FLAG_E << 16;
232259284Sjhibbits		newval |= (ctl_shadow[1] & 0x3f) << 16;
233208149Snwhitehorn	}
234183882Snwhitehorn	if (ctl_shadow[0] & (PMCF_EN << 16)) {
235208149Snwhitehorn		if (ctl_shadow[0] & (PMCF_USR << 16))
236259284Sjhibbits			newval |= P5FLAG_USR;
237259284Sjhibbits		if (ctl_shadow[0] & (PMCF_OS << 16))
238259284Sjhibbits			newval |= P5FLAG_OS;
239259284Sjhibbits		if (ctl_shadow[0] & (PMCF_E << 16))
240259284Sjhibbits			newval |= P5FLAG_E;
241259284Sjhibbits		newval |= ctl_shadow[0] & 0x3f;
242208149Snwhitehorn	}
243208149Snwhitehorn	/*
244259284Sjhibbits	 * ``...But this is the blackest of sins!''
245208149Snwhitehorn	 *
24699663Sbenno	 * According to the Harvard code, it is necessary to zero the
247208149Snwhitehorn	 * cycle counter before writing to the control MSR.  This must
248208149Snwhitehorn	 * be an Intel processor...  Hope we don't lose too many ticks.
249208149Snwhitehorn	 */
250208149Snwhitehorn	disable_intr();
251183882Snwhitehorn	oldtsc = rdtsc();
252208149Snwhitehorn	wrmsr(0x10 /* TSC */, 0);
253183882Snwhitehorn	wrmsr(msr_ctl[0], newval);
254208149Snwhitehorn	wrmsr(0x10, oldtsc);
25599663Sbenno	enable_intr();
25699663Sbenno	return 0;		/* XXX should check for errors */
257208149Snwhitehorn}
258208149Snwhitehorn
259208149Snwhitehorn/*
26099663Sbenno * Now the user-mode interface, called from a subdevice of mem.c.
261208149Snwhitehorn */
262208149Snwhitehornstatic int writer;
263208149Snwhitehornstatic int writerpmc;
26499663Sbenno
265208149Snwhitehornint
266208149Snwhitehornperfmon_open(dev_t dev, int flags, int fmt, struct proc *p)
267208149Snwhitehorn{
268208149Snwhitehorn	if (!perfmon_cpuok)
269208149Snwhitehorn		return ENXIO;
270208149Snwhitehorn
271208149Snwhitehorn	if (flags & FWRITE) {
272233188Sandreast		if (writer) {
273208149Snwhitehorn			return EBUSY;
274258272Snwhitehorn		} else {
275208149Snwhitehorn			writer = 1;
276233188Sandreast			writerpmc = 0;
277233188Sandreast		}
278208149Snwhitehorn	}
279208149Snwhitehorn	return 0;
280208149Snwhitehorn}
281208149Snwhitehorn
282208149Snwhitehornint
283208149Snwhitehornperfmon_close(dev_t dev, int flags, int fmt, struct proc *p)
28499663Sbenno{
28599663Sbenno	if (flags & FWRITE) {
286258272Snwhitehorn		int i;
287258272Snwhitehorn
288258272Snwhitehorn		for (i = 0; i < NPMC; i++) {
289208149Snwhitehorn			if (writerpmc & (1 << i))
290258272Snwhitehorn				perfmon_fini(i);
291258272Snwhitehorn		}
292258272Snwhitehorn		writer = 0;
293258272Snwhitehorn	}
294258272Snwhitehorn	return 0;
295258272Snwhitehorn}
296258272Snwhitehorn
297258272Snwhitehornint
298258272Snwhitehornperfmon_ioctl(dev_t dev, int cmd, caddr_t param, int flags, struct proc *p)
299258272Snwhitehorn{
300258272Snwhitehorn	struct pmc *pmc;
301208149Snwhitehorn	struct pmc_data *pmcd;
30299663Sbenno	struct pmc_tstamp *pmct;
303208149Snwhitehorn	int *ip;
304208149Snwhitehorn	int rv;
305179746Skevlo
306208149Snwhitehorn	switch(cmd) {
307179746Skevlo	case PMIOSETUP:
308179746Skevlo		if (!(flags & FWRITE))
309179746Skevlo			return EPERM;
31099663Sbenno		pmc = (struct pmc *)param;
311208149Snwhitehorn
312208149Snwhitehorn		rv = perfmon_setup(pmc->pmc_num, pmc->pmc_val);
313208149Snwhitehorn		if (!rv) {
314208149Snwhitehorn			writerpmc |= (1 << pmc->pmc_num);
315208149Snwhitehorn		}
316208149Snwhitehorn		break;
317208149Snwhitehorn
318208149Snwhitehorn	case PMIOGET:
319208149Snwhitehorn		pmc = (struct pmc *)param;
320208149Snwhitehorn		rv = perfmon_get(pmc->pmc_num, &pmc->pmc_val);
321259284Sjhibbits		break;
322259284Sjhibbits
323259284Sjhibbits	case PMIOSTART:
324208149Snwhitehorn		if (!(flags & FWRITE))
325208149Snwhitehorn			return EPERM;
326208149Snwhitehorn
327208149Snwhitehorn		ip = (int *)param;
328208149Snwhitehorn		rv = perfmon_start(*ip);
329208149Snwhitehorn		break;
330208149Snwhitehorn
331208149Snwhitehorn	case PMIOSTOP:
332208149Snwhitehorn		if (!(flags & FWRITE))
333208149Snwhitehorn			return EPERM;
33499663Sbenno
335208149Snwhitehorn		ip = (int *)param;
336208149Snwhitehorn		rv = perfmon_stop(*ip);
337208149Snwhitehorn		break;
338208149Snwhitehorn
339208149Snwhitehorn	case PMIORESET:
340233188Sandreast		if (!(flags & FWRITE))
341233188Sandreast			return EPERM;
342233188Sandreast
343233188Sandreast		ip = (int *)param;
344233188Sandreast		rv = perfmon_reset(*ip);
345233188Sandreast		break;
346233188Sandreast
347233188Sandreast	case PMIOREAD:
348233188Sandreast		pmcd = (struct pmc_data *)param;
349233188Sandreast		rv = perfmon_read(pmcd->pmcd_num, &pmcd->pmcd_value);
350233188Sandreast		break;
351233188Sandreast
352233188Sandreast	case PMIOTSTAMP:
353233188Sandreast		pmct = (struct pmc_tstamp *)param;
354233188Sandreast		pmct->pmct_rate = i586_ctr_rate >> I586_CTR_RATE_SHIFT;
355233188Sandreast		pmct->pmct_value = rdtsc();
356233188Sandreast		rv = 0;
357233188Sandreast		break;
358233188Sandreast
359233188Sandreast	default:
360233188Sandreast		rv = ENOTTY;
361233188Sandreast	}
362233188Sandreast
363233188Sandreast	return rv;
364233188Sandreast}
365233188Sandreast