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