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