Device layer rollup commit.
[dragonfly.git] / sys / i386 / i386 / perfmon.c
CommitLineData
984263bc
MD
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 * $FreeBSD: src/sys/i386/i386/perfmon.c,v 1.21 1999/09/25 18:24:04 phk Exp $
e4c9c0c8 30 * $DragonFly: src/sys/i386/i386/Attic/perfmon.c,v 1.8 2004/05/19 22:52:57 dillon Exp $
984263bc
MD
31 */
32
33#include <sys/param.h>
34#include <sys/systm.h>
35#include <sys/conf.h>
36#include <sys/fcntl.h>
545a1cd3 37#include <sys/lock.h>
984263bc
MD
38
39#ifndef SMP
40#include <machine/cputypes.h>
41#endif
42#include <machine/clock.h>
43#include <machine/perfmon.h>
44
45static int perfmon_inuse;
46static int perfmon_cpuok;
47#ifndef SMP
48static int msr_ctl[NPMC];
49#endif
50static int msr_pmc[NPMC];
51static unsigned int ctl_shadow[NPMC];
52static quad_t pmc_shadow[NPMC]; /* used when ctr is stopped on P5 */
53static int (*writectl)(int);
54#ifndef SMP
55static int writectl5(int);
56static int writectl6(int);
57#endif
58
59static d_close_t perfmon_close;
60static d_open_t perfmon_open;
61static d_ioctl_t perfmon_ioctl;
62
63#define CDEV_MAJOR 2 /* We're really a minor of mem.c */
64static struct cdevsw perfmon_cdevsw = {
fabb8ceb
MD
65 /* name */ "perfmon",
66 /* maj */ CDEV_MAJOR,
67 /* flags */ 0,
68 /* port */ NULL,
455fcd7e 69 /* clone */ NULL,
fabb8ceb 70
984263bc
MD
71 /* open */ perfmon_open,
72 /* close */ perfmon_close,
73 /* read */ noread,
74 /* write */ nowrite,
75 /* ioctl */ perfmon_ioctl,
76 /* poll */ nopoll,
77 /* mmap */ nommap,
78 /* strategy */ nostrategy,
984263bc 79 /* dump */ nodump,
fabb8ceb 80 /* psize */ nopsize
984263bc
MD
81};
82
83/*
84 * Must be called after cpu_class is set up.
85 */
86void
87perfmon_init(void)
88{
89#ifndef SMP
90 switch(cpu_class) {
91 case CPUCLASS_586:
92 perfmon_cpuok = 1;
93 msr_ctl[0] = 0x11;
94 msr_ctl[1] = 0x11;
95 msr_pmc[0] = 0x12;
96 msr_pmc[1] = 0x13;
97 writectl = writectl5;
98 break;
99 case CPUCLASS_686:
100 perfmon_cpuok = 1;
101 msr_ctl[0] = 0x186;
102 msr_ctl[1] = 0x187;
103 msr_pmc[0] = 0xc1;
104 msr_pmc[1] = 0xc2;
105 writectl = writectl6;
106 break;
107
108 default:
109 perfmon_cpuok = 0;
110 break;
111 }
112#endif /* SMP */
e4c9c0c8
MD
113
114 /* NOTE: really a minor of mem. perfmon gets 32-47 */
115 cdevsw_add(&perfmon_cdevsw, 0xf0, 32);
984263bc
MD
116 make_dev(&perfmon_cdevsw, 32, UID_ROOT, GID_KMEM, 0640, "perfmon");
117}
118
119int
120perfmon_avail(void)
121{
122 return perfmon_cpuok;
123}
124
125int
126perfmon_setup(int pmc, unsigned int control)
127{
128 if (pmc < 0 || pmc >= NPMC)
129 return EINVAL;
130
131 perfmon_inuse |= (1 << pmc);
132 control &= ~(PMCF_SYS_FLAGS << 16);
8a8d5d85 133 mpintr_lock(); /* doesn't have to be mpintr_lock YYY */
984263bc
MD
134 ctl_shadow[pmc] = control;
135 writectl(pmc);
136 wrmsr(msr_pmc[pmc], pmc_shadow[pmc] = 0);
8a8d5d85 137 mpintr_unlock();
984263bc
MD
138 return 0;
139}
140
141int
142perfmon_get(int pmc, unsigned int *control)
143{
144 if (pmc < 0 || pmc >= NPMC)
145 return EINVAL;
146
147 if (perfmon_inuse & (1 << pmc)) {
148 *control = ctl_shadow[pmc];
149 return 0;
150 }
151 return EBUSY; /* XXX reversed sense */
152}
153
154int
155perfmon_fini(int pmc)
156{
157 if (pmc < 0 || pmc >= NPMC)
158 return EINVAL;
159
160 if (perfmon_inuse & (1 << pmc)) {
161 perfmon_stop(pmc);
162 ctl_shadow[pmc] = 0;
163 perfmon_inuse &= ~(1 << pmc);
164 return 0;
165 }
166 return EBUSY; /* XXX reversed sense */
167}
168
169int
170perfmon_start(int pmc)
171{
172 if (pmc < 0 || pmc >= NPMC)
173 return EINVAL;
174
175 if (perfmon_inuse & (1 << pmc)) {
8a8d5d85 176 mpintr_lock(); /* doesn't have to be mpintr YYY */
984263bc
MD
177 ctl_shadow[pmc] |= (PMCF_EN << 16);
178 wrmsr(msr_pmc[pmc], pmc_shadow[pmc]);
179 writectl(pmc);
8a8d5d85 180 mpintr_unlock();
984263bc
MD
181 return 0;
182 }
183 return EBUSY;
184}
185
186int
187perfmon_stop(int pmc)
188{
189 if (pmc < 0 || pmc >= NPMC)
190 return EINVAL;
191
192 if (perfmon_inuse & (1 << pmc)) {
8a8d5d85 193 mpintr_lock();
984263bc
MD
194 pmc_shadow[pmc] = rdmsr(msr_pmc[pmc]) & 0xffffffffffULL;
195 ctl_shadow[pmc] &= ~(PMCF_EN << 16);
196 writectl(pmc);
8a8d5d85 197 mpintr_unlock();
984263bc
MD
198 return 0;
199 }
200 return EBUSY;
201}
202
203int
204perfmon_read(int pmc, quad_t *val)
205{
206 if (pmc < 0 || pmc >= NPMC)
207 return EINVAL;
208
209 if (perfmon_inuse & (1 << pmc)) {
210 if (ctl_shadow[pmc] & (PMCF_EN << 16))
211 *val = rdmsr(msr_pmc[pmc]) & 0xffffffffffULL;
212 else
213 *val = pmc_shadow[pmc];
214 return 0;
215 }
216
217 return EBUSY;
218}
219
220int
221perfmon_reset(int pmc)
222{
223 if (pmc < 0 || pmc >= NPMC)
224 return EINVAL;
225
226 if (perfmon_inuse & (1 << pmc)) {
227 wrmsr(msr_pmc[pmc], pmc_shadow[pmc] = 0);
228 return 0;
229 }
230 return EBUSY;
231}
232
233#ifndef SMP
234/*
235 * Unfortunately, the performance-monitoring registers are laid out
236 * differently in the P5 and P6. We keep everything in P6 format
237 * internally (except for the event code), and convert to P5
238 * format as needed on those CPUs. The writectl function pointer
239 * is set up to point to one of these functions by perfmon_init().
240 */
241int
242writectl6(int pmc)
243{
244 if (pmc > 0 && !(ctl_shadow[pmc] & (PMCF_EN << 16))) {
245 wrmsr(msr_ctl[pmc], 0);
246 } else {
247 wrmsr(msr_ctl[pmc], ctl_shadow[pmc]);
248 }
249 return 0;
250}
251
252#define P5FLAG_P 0x200
253#define P5FLAG_E 0x100
254#define P5FLAG_USR 0x80
255#define P5FLAG_OS 0x40
256
257int
258writectl5(int pmc)
259{
260 quad_t newval = 0;
261
262 if (ctl_shadow[1] & (PMCF_EN << 16)) {
263 if (ctl_shadow[1] & (PMCF_USR << 16))
264 newval |= P5FLAG_USR << 16;
265 if (ctl_shadow[1] & (PMCF_OS << 16))
266 newval |= P5FLAG_OS << 16;
267 if (!(ctl_shadow[1] & (PMCF_E << 16)))
268 newval |= P5FLAG_E << 16;
269 newval |= (ctl_shadow[1] & 0x3f) << 16;
270 }
271 if (ctl_shadow[0] & (PMCF_EN << 16)) {
272 if (ctl_shadow[0] & (PMCF_USR << 16))
273 newval |= P5FLAG_USR;
274 if (ctl_shadow[0] & (PMCF_OS << 16))
275 newval |= P5FLAG_OS;
276 if (!(ctl_shadow[0] & (PMCF_E << 16)))
277 newval |= P5FLAG_E;
278 newval |= ctl_shadow[0] & 0x3f;
279 }
280
281 wrmsr(msr_ctl[0], newval);
282 return 0; /* XXX should check for unimplemented bits */
283}
284#endif /* !SMP */
285
286/*
287 * Now the user-mode interface, called from a subdevice of mem.c.
288 */
289static int writer;
290static int writerpmc;
291
292static int
41c20dac 293perfmon_open(dev_t dev, int flags, int fmt, struct thread *td)
984263bc
MD
294{
295 if (!perfmon_cpuok)
296 return ENXIO;
297
298 if (flags & FWRITE) {
299 if (writer) {
300 return EBUSY;
301 } else {
302 writer = 1;
303 writerpmc = 0;
304 }
305 }
306 return 0;
307}
308
309static int
41c20dac 310perfmon_close(dev_t dev, int flags, int fmt, struct thread *td)
984263bc
MD
311{
312 if (flags & FWRITE) {
313 int i;
314
315 for (i = 0; i < NPMC; i++) {
316 if (writerpmc & (1 << i))
317 perfmon_fini(i);
318 }
319 writer = 0;
320 }
321 return 0;
322}
323
324static int
41c20dac 325perfmon_ioctl(dev_t dev, u_long cmd, caddr_t param, int flags, struct thread *td)
984263bc
MD
326{
327 struct pmc *pmc;
328 struct pmc_data *pmcd;
329 struct pmc_tstamp *pmct;
330 int *ip;
331 int rv;
332
333 switch(cmd) {
334 case PMIOSETUP:
335 if (!(flags & FWRITE))
336 return EPERM;
337 pmc = (struct pmc *)param;
338
339 rv = perfmon_setup(pmc->pmc_num, pmc->pmc_val);
340 if (!rv) {
341 writerpmc |= (1 << pmc->pmc_num);
342 }
343 break;
344
345 case PMIOGET:
346 pmc = (struct pmc *)param;
347 rv = perfmon_get(pmc->pmc_num, &pmc->pmc_val);
348 break;
349
350 case PMIOSTART:
351 if (!(flags & FWRITE))
352 return EPERM;
353
354 ip = (int *)param;
355 rv = perfmon_start(*ip);
356 break;
357
358 case PMIOSTOP:
359 if (!(flags & FWRITE))
360 return EPERM;
361
362 ip = (int *)param;
363 rv = perfmon_stop(*ip);
364 break;
365
366 case PMIORESET:
367 if (!(flags & FWRITE))
368 return EPERM;
369
370 ip = (int *)param;
371 rv = perfmon_reset(*ip);
372 break;
373
374 case PMIOREAD:
375 pmcd = (struct pmc_data *)param;
376 rv = perfmon_read(pmcd->pmcd_num, &pmcd->pmcd_value);
377 break;
378
379 case PMIOTSTAMP:
380 if (!tsc_freq) {
381 rv = ENOTTY;
382 break;
383 }
384 pmct = (struct pmc_tstamp *)param;
385 /* XXX interface loses precision. */
386 pmct->pmct_rate = tsc_freq / 1000000;
387 pmct->pmct_value = rdtsc();
388 rv = 0;
389 break;
390 default:
391 rv = ENOTTY;
392 }
393
394 return rv;
395}