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