kernel tree reorganization stage 1: Major cvs repository work (not logged as
[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 $
fabb8ceb 30 * $DragonFly: src/sys/i386/i386/Attic/perfmon.c,v 1.6 2003/07/21 05:50:39 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,
69 /* autoq */ 0,
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 */
113 make_dev(&perfmon_cdevsw, 32, UID_ROOT, GID_KMEM, 0640, "perfmon");
114}
115
116int
117perfmon_avail(void)
118{
119 return perfmon_cpuok;
120}
121
122int
123perfmon_setup(int pmc, unsigned int control)
124{
125 if (pmc < 0 || pmc >= NPMC)
126 return EINVAL;
127
128 perfmon_inuse |= (1 << pmc);
129 control &= ~(PMCF_SYS_FLAGS << 16);
8a8d5d85 130 mpintr_lock(); /* doesn't have to be mpintr_lock YYY */
984263bc
MD
131 ctl_shadow[pmc] = control;
132 writectl(pmc);
133 wrmsr(msr_pmc[pmc], pmc_shadow[pmc] = 0);
8a8d5d85 134 mpintr_unlock();
984263bc
MD
135 return 0;
136}
137
138int
139perfmon_get(int pmc, unsigned int *control)
140{
141 if (pmc < 0 || pmc >= NPMC)
142 return EINVAL;
143
144 if (perfmon_inuse & (1 << pmc)) {
145 *control = ctl_shadow[pmc];
146 return 0;
147 }
148 return EBUSY; /* XXX reversed sense */
149}
150
151int
152perfmon_fini(int pmc)
153{
154 if (pmc < 0 || pmc >= NPMC)
155 return EINVAL;
156
157 if (perfmon_inuse & (1 << pmc)) {
158 perfmon_stop(pmc);
159 ctl_shadow[pmc] = 0;
160 perfmon_inuse &= ~(1 << pmc);
161 return 0;
162 }
163 return EBUSY; /* XXX reversed sense */
164}
165
166int
167perfmon_start(int pmc)
168{
169 if (pmc < 0 || pmc >= NPMC)
170 return EINVAL;
171
172 if (perfmon_inuse & (1 << pmc)) {
8a8d5d85 173 mpintr_lock(); /* doesn't have to be mpintr YYY */
984263bc
MD
174 ctl_shadow[pmc] |= (PMCF_EN << 16);
175 wrmsr(msr_pmc[pmc], pmc_shadow[pmc]);
176 writectl(pmc);
8a8d5d85 177 mpintr_unlock();
984263bc
MD
178 return 0;
179 }
180 return EBUSY;
181}
182
183int
184perfmon_stop(int pmc)
185{
186 if (pmc < 0 || pmc >= NPMC)
187 return EINVAL;
188
189 if (perfmon_inuse & (1 << pmc)) {
8a8d5d85 190 mpintr_lock();
984263bc
MD
191 pmc_shadow[pmc] = rdmsr(msr_pmc[pmc]) & 0xffffffffffULL;
192 ctl_shadow[pmc] &= ~(PMCF_EN << 16);
193 writectl(pmc);
8a8d5d85 194 mpintr_unlock();
984263bc
MD
195 return 0;
196 }
197 return EBUSY;
198}
199
200int
201perfmon_read(int pmc, quad_t *val)
202{
203 if (pmc < 0 || pmc >= NPMC)
204 return EINVAL;
205
206 if (perfmon_inuse & (1 << pmc)) {
207 if (ctl_shadow[pmc] & (PMCF_EN << 16))
208 *val = rdmsr(msr_pmc[pmc]) & 0xffffffffffULL;
209 else
210 *val = pmc_shadow[pmc];
211 return 0;
212 }
213
214 return EBUSY;
215}
216
217int
218perfmon_reset(int pmc)
219{
220 if (pmc < 0 || pmc >= NPMC)
221 return EINVAL;
222
223 if (perfmon_inuse & (1 << pmc)) {
224 wrmsr(msr_pmc[pmc], pmc_shadow[pmc] = 0);
225 return 0;
226 }
227 return EBUSY;
228}
229
230#ifndef SMP
231/*
232 * Unfortunately, the performance-monitoring registers are laid out
233 * differently in the P5 and P6. We keep everything in P6 format
234 * internally (except for the event code), and convert to P5
235 * format as needed on those CPUs. The writectl function pointer
236 * is set up to point to one of these functions by perfmon_init().
237 */
238int
239writectl6(int pmc)
240{
241 if (pmc > 0 && !(ctl_shadow[pmc] & (PMCF_EN << 16))) {
242 wrmsr(msr_ctl[pmc], 0);
243 } else {
244 wrmsr(msr_ctl[pmc], ctl_shadow[pmc]);
245 }
246 return 0;
247}
248
249#define P5FLAG_P 0x200
250#define P5FLAG_E 0x100
251#define P5FLAG_USR 0x80
252#define P5FLAG_OS 0x40
253
254int
255writectl5(int pmc)
256{
257 quad_t newval = 0;
258
259 if (ctl_shadow[1] & (PMCF_EN << 16)) {
260 if (ctl_shadow[1] & (PMCF_USR << 16))
261 newval |= P5FLAG_USR << 16;
262 if (ctl_shadow[1] & (PMCF_OS << 16))
263 newval |= P5FLAG_OS << 16;
264 if (!(ctl_shadow[1] & (PMCF_E << 16)))
265 newval |= P5FLAG_E << 16;
266 newval |= (ctl_shadow[1] & 0x3f) << 16;
267 }
268 if (ctl_shadow[0] & (PMCF_EN << 16)) {
269 if (ctl_shadow[0] & (PMCF_USR << 16))
270 newval |= P5FLAG_USR;
271 if (ctl_shadow[0] & (PMCF_OS << 16))
272 newval |= P5FLAG_OS;
273 if (!(ctl_shadow[0] & (PMCF_E << 16)))
274 newval |= P5FLAG_E;
275 newval |= ctl_shadow[0] & 0x3f;
276 }
277
278 wrmsr(msr_ctl[0], newval);
279 return 0; /* XXX should check for unimplemented bits */
280}
281#endif /* !SMP */
282
283/*
284 * Now the user-mode interface, called from a subdevice of mem.c.
285 */
286static int writer;
287static int writerpmc;
288
289static int
41c20dac 290perfmon_open(dev_t dev, int flags, int fmt, struct thread *td)
984263bc
MD
291{
292 if (!perfmon_cpuok)
293 return ENXIO;
294
295 if (flags & FWRITE) {
296 if (writer) {
297 return EBUSY;
298 } else {
299 writer = 1;
300 writerpmc = 0;
301 }
302 }
303 return 0;
304}
305
306static int
41c20dac 307perfmon_close(dev_t dev, int flags, int fmt, struct thread *td)
984263bc
MD
308{
309 if (flags & FWRITE) {
310 int i;
311
312 for (i = 0; i < NPMC; i++) {
313 if (writerpmc & (1 << i))
314 perfmon_fini(i);
315 }
316 writer = 0;
317 }
318 return 0;
319}
320
321static int
41c20dac 322perfmon_ioctl(dev_t dev, u_long cmd, caddr_t param, int flags, struct thread *td)
984263bc
MD
323{
324 struct pmc *pmc;
325 struct pmc_data *pmcd;
326 struct pmc_tstamp *pmct;
327 int *ip;
328 int rv;
329
330 switch(cmd) {
331 case PMIOSETUP:
332 if (!(flags & FWRITE))
333 return EPERM;
334 pmc = (struct pmc *)param;
335
336 rv = perfmon_setup(pmc->pmc_num, pmc->pmc_val);
337 if (!rv) {
338 writerpmc |= (1 << pmc->pmc_num);
339 }
340 break;
341
342 case PMIOGET:
343 pmc = (struct pmc *)param;
344 rv = perfmon_get(pmc->pmc_num, &pmc->pmc_val);
345 break;
346
347 case PMIOSTART:
348 if (!(flags & FWRITE))
349 return EPERM;
350
351 ip = (int *)param;
352 rv = perfmon_start(*ip);
353 break;
354
355 case PMIOSTOP:
356 if (!(flags & FWRITE))
357 return EPERM;
358
359 ip = (int *)param;
360 rv = perfmon_stop(*ip);
361 break;
362
363 case PMIORESET:
364 if (!(flags & FWRITE))
365 return EPERM;
366
367 ip = (int *)param;
368 rv = perfmon_reset(*ip);
369 break;
370
371 case PMIOREAD:
372 pmcd = (struct pmc_data *)param;
373 rv = perfmon_read(pmcd->pmcd_num, &pmcd->pmcd_value);
374 break;
375
376 case PMIOTSTAMP:
377 if (!tsc_freq) {
378 rv = ENOTTY;
379 break;
380 }
381 pmct = (struct pmc_tstamp *)param;
382 /* XXX interface loses precision. */
383 pmct->pmct_rate = tsc_freq / 1000000;
384 pmct->pmct_value = rdtsc();
385 rv = 0;
386 break;
387 default:
388 rv = ENOTTY;
389 }
390
391 return rv;
392}