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