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