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