1/*- 2 * Copyright (c) 2003-2005,2008 Joseph Koshy 3 * Copyright (c) 2007 The FreeBSD Foundation 4 * All rights reserved. 5 * 6 * Portions of this software were developed by A. Joseph Koshy under 7 * sponsorship from the FreeBSD Foundation and Google, Inc. 8 * 9 * Redistribution and use in source and binary forms, with or without 10 * modification, are permitted provided that the following conditions 11 * are met: 12 * 1. Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer. 14 * 2. Redistributions in binary form must reproduce the above copyright 15 * notice, this list of conditions and the following disclaimer in the 16 * documentation and/or other materials provided with the distribution. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 19 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 24 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 26 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 27 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 28 * SUCH DAMAGE. 29 */ 30 31#include <sys/cdefs.h> 32__FBSDID("$FreeBSD$"); 33 34#include <sys/param.h> 35#include <sys/bus.h> 36#include <sys/lock.h> 37#include <sys/mutex.h> 38#include <sys/pmc.h> 39#include <sys/pmckern.h> 40#include <sys/smp.h> 41#include <sys/systm.h> 42 43#include <machine/intr_machdep.h> 44#include <x86/apicvar.h> 45#include <machine/cpu.h> 46#include <machine/cpufunc.h> 47#include <machine/cputypes.h> 48#include <machine/md_var.h> 49#include <machine/pmc_mdep.h> 50#include <machine/specialreg.h> 51 52/* 53 * PENTIUM PRO SUPPORT 54 * 55 * Quirks: 56 * 57 * - Both PMCs are enabled by a single bit P6_EVSEL_EN in performance 58 * counter '0'. This bit needs to be '1' if any of the two 59 * performance counters are in use. Perf counters can also be 60 * switched off by writing zeros to their EVSEL register. 61 * 62 * - While the width of these counters is 40 bits, we do not appear to 63 * have a way of writing 40 bits to the counter MSRs. A WRMSR 64 * instruction will sign extend bit 31 of the value being written to 65 * the perf counter -- a value of 0x80000000 written to an perf 66 * counter register will be sign extended to 0xFF80000000. 67 * 68 * This quirk primarily affects thread-mode PMCs in counting mode, as 69 * these PMCs read and write PMC registers at every context switch. 70 */ 71 72struct p6pmc_descr { 73 struct pmc_descr pm_descr; /* common information */ 74 uint32_t pm_pmc_msr; 75 uint32_t pm_evsel_msr; 76}; 77 78static struct p6pmc_descr p6_pmcdesc[P6_NPMCS] = { 79 80#define P6_PMC_CAPS (PMC_CAP_INTERRUPT | PMC_CAP_USER | PMC_CAP_SYSTEM | \ 81 PMC_CAP_EDGE | PMC_CAP_THRESHOLD | PMC_CAP_READ | PMC_CAP_WRITE | \ 82 PMC_CAP_INVERT | PMC_CAP_QUALIFIER) 83 84 /* PMC 0 */ 85 { 86 .pm_descr = 87 { 88 .pd_name ="P6-0", 89 .pd_class = PMC_CLASS_P6, 90 .pd_caps = P6_PMC_CAPS, 91 .pd_width = 40 92 }, 93 .pm_pmc_msr = P6_MSR_PERFCTR0, 94 .pm_evsel_msr = P6_MSR_EVSEL0 95 }, 96 97 /* PMC 1 */ 98 { 99 .pm_descr = 100 { 101 .pd_name ="P6-1", 102 .pd_class = PMC_CLASS_P6, 103 .pd_caps = P6_PMC_CAPS, 104 .pd_width = 40 105 }, 106 .pm_pmc_msr = P6_MSR_PERFCTR1, 107 .pm_evsel_msr = P6_MSR_EVSEL1 108 } 109}; 110 111static enum pmc_cputype p6_cputype; 112 113/* 114 * P6 Event descriptor 115 * 116 * The 'pm_flags' field has the following structure: 117 * - The upper 4 bits are used to track which counter an event is valid on. 118 * - The lower bits form a bitmask of flags indicating support for the event 119 * on a given CPU. 120 */ 121 122struct p6_event_descr { 123 const enum pmc_event pm_event; 124 uint32_t pm_evsel; 125 uint32_t pm_flags; 126 uint32_t pm_unitmask; 127}; 128 129#define P6F_CTR(C) (1 << (28 + (C))) 130#define P6F_CTR0 P6F_CTR(0) 131#define P6F_CTR1 P6F_CTR(1) 132#define P6F(CPU) (1 << ((CPU) - PMC_CPU_INTEL_P6)) 133#define _P6F(C) P6F(PMC_CPU_INTEL_##C) 134#define P6F_P6 _P6F(P6) 135#define P6F_CL _P6F(CL) 136#define P6F_PII _P6F(PII) 137#define P6F_PIII _P6F(PIII) 138#define P6F_PM _P6F(PM) 139#define P6F_ALL_CPUS (P6F_P6 | P6F_PII | P6F_CL | P6F_PIII | P6F_PM) 140#define P6F_ALL_CTRS (P6F_CTR0 | P6F_CTR1) 141#define P6F_ALL (P6F_ALL_CPUS | P6F_ALL_CTRS) 142 143#define P6_EVENT_VALID_FOR_CPU(P,CPU) ((P)->pm_flags & P6F(CPU)) 144#define P6_EVENT_VALID_FOR_CTR(P,CTR) ((P)->pm_flags & P6F_CTR(CTR)) 145 146static const struct p6_event_descr p6_events[] = { 147 148#define P6_EVDESCR(NAME, EVSEL, FLAGS, UMASK) \ 149 { \ 150 .pm_event = PMC_EV_P6_##NAME, \ 151 .pm_evsel = (EVSEL), \ 152 .pm_flags = (FLAGS), \ 153 .pm_unitmask = (UMASK) \ 154 } 155 156P6_EVDESCR(DATA_MEM_REFS, 0x43, P6F_ALL, 0x00), 157P6_EVDESCR(DCU_LINES_IN, 0x45, P6F_ALL, 0x00), 158P6_EVDESCR(DCU_M_LINES_IN, 0x46, P6F_ALL, 0x00), 159P6_EVDESCR(DCU_M_LINES_OUT, 0x47, P6F_ALL, 0x00), 160P6_EVDESCR(DCU_MISS_OUTSTANDING, 0x47, P6F_ALL, 0x00), 161P6_EVDESCR(IFU_FETCH, 0x80, P6F_ALL, 0x00), 162P6_EVDESCR(IFU_FETCH_MISS, 0x81, P6F_ALL, 0x00), 163P6_EVDESCR(ITLB_MISS, 0x85, P6F_ALL, 0x00), 164P6_EVDESCR(IFU_MEM_STALL, 0x86, P6F_ALL, 0x00), 165P6_EVDESCR(ILD_STALL, 0x87, P6F_ALL, 0x00), 166P6_EVDESCR(L2_IFETCH, 0x28, P6F_ALL, 0x0F), 167P6_EVDESCR(L2_LD, 0x29, P6F_ALL, 0x0F), 168P6_EVDESCR(L2_ST, 0x2A, P6F_ALL, 0x0F), 169P6_EVDESCR(L2_LINES_IN, 0x24, P6F_ALL, 0x0F), 170P6_EVDESCR(L2_LINES_OUT, 0x26, P6F_ALL, 0x0F), 171P6_EVDESCR(L2_M_LINES_INM, 0x25, P6F_ALL, 0x00), 172P6_EVDESCR(L2_M_LINES_OUTM, 0x27, P6F_ALL, 0x0F), 173P6_EVDESCR(L2_RQSTS, 0x2E, P6F_ALL, 0x0F), 174P6_EVDESCR(L2_ADS, 0x21, P6F_ALL, 0x00), 175P6_EVDESCR(L2_DBUS_BUSY, 0x22, P6F_ALL, 0x00), 176P6_EVDESCR(L2_DBUS_BUSY_RD, 0x23, P6F_ALL, 0x00), 177P6_EVDESCR(BUS_DRDY_CLOCKS, 0x62, P6F_ALL, 0x20), 178P6_EVDESCR(BUS_LOCK_CLOCKS, 0x63, P6F_ALL, 0x20), 179P6_EVDESCR(BUS_REQ_OUTSTANDING, 0x60, P6F_ALL, 0x00), 180P6_EVDESCR(BUS_TRAN_BRD, 0x65, P6F_ALL, 0x20), 181P6_EVDESCR(BUS_TRAN_RFO, 0x66, P6F_ALL, 0x20), 182P6_EVDESCR(BUS_TRANS_WB, 0x67, P6F_ALL, 0x20), 183P6_EVDESCR(BUS_TRAN_IFETCH, 0x68, P6F_ALL, 0x20), 184P6_EVDESCR(BUS_TRAN_INVAL, 0x69, P6F_ALL, 0x20), 185P6_EVDESCR(BUS_TRAN_PWR, 0x6A, P6F_ALL, 0x20), 186P6_EVDESCR(BUS_TRANS_P, 0x6B, P6F_ALL, 0x20), 187P6_EVDESCR(BUS_TRANS_IO, 0x6C, P6F_ALL, 0x20), 188P6_EVDESCR(BUS_TRAN_DEF, 0x6D, P6F_ALL, 0x20), 189P6_EVDESCR(BUS_TRAN_BURST, 0x6E, P6F_ALL, 0x20), 190P6_EVDESCR(BUS_TRAN_ANY, 0x70, P6F_ALL, 0x20), 191P6_EVDESCR(BUS_TRAN_MEM, 0x6F, P6F_ALL, 0x20), 192P6_EVDESCR(BUS_DATA_RCV, 0x64, P6F_ALL, 0x00), 193P6_EVDESCR(BUS_BNR_DRV, 0x61, P6F_ALL, 0x00), 194P6_EVDESCR(BUS_HIT_DRV, 0x7A, P6F_ALL, 0x00), 195P6_EVDESCR(BUS_HITM_DRV, 0x7B, P6F_ALL, 0x00), 196P6_EVDESCR(BUS_SNOOP_STALL, 0x7E, P6F_ALL, 0x00), 197P6_EVDESCR(FLOPS, 0xC1, P6F_ALL_CPUS | P6F_CTR0, 0x00), 198P6_EVDESCR(FP_COMPS_OPS_EXE, 0x10, P6F_ALL_CPUS | P6F_CTR0, 0x00), 199P6_EVDESCR(FP_ASSIST, 0x11, P6F_ALL_CPUS | P6F_CTR1, 0x00), 200P6_EVDESCR(MUL, 0x12, P6F_ALL_CPUS | P6F_CTR1, 0x00), 201P6_EVDESCR(DIV, 0x13, P6F_ALL_CPUS | P6F_CTR1, 0x00), 202P6_EVDESCR(CYCLES_DIV_BUSY, 0x14, P6F_ALL_CPUS | P6F_CTR0, 0x00), 203P6_EVDESCR(LD_BLOCKS, 0x03, P6F_ALL, 0x00), 204P6_EVDESCR(SB_DRAINS, 0x04, P6F_ALL, 0x00), 205P6_EVDESCR(MISALIGN_MEM_REF, 0x05, P6F_ALL, 0x00), 206P6_EVDESCR(EMON_KNI_PREF_DISPATCHED, 0x07, P6F_PIII | P6F_ALL_CTRS, 0x03), 207P6_EVDESCR(EMON_KNI_PREF_MISS, 0x4B, P6F_PIII | P6F_ALL_CTRS, 0x03), 208P6_EVDESCR(INST_RETIRED, 0xC0, P6F_ALL, 0x00), 209P6_EVDESCR(UOPS_RETIRED, 0xC2, P6F_ALL, 0x00), 210P6_EVDESCR(INST_DECODED, 0xD0, P6F_ALL, 0x00), 211P6_EVDESCR(EMON_KNI_INST_RETIRED, 0xD8, P6F_PIII | P6F_ALL_CTRS, 0x01), 212P6_EVDESCR(EMON_KNI_COMP_INST_RET, 0xD9, P6F_PIII | P6F_ALL_CTRS, 0x01), 213P6_EVDESCR(HW_INT_RX, 0xC8, P6F_ALL, 0x00), 214P6_EVDESCR(CYCLES_INT_MASKED, 0xC6, P6F_ALL, 0x00), 215P6_EVDESCR(CYCLES_INT_PENDING_AND_MASKED, 0xC7, P6F_ALL, 0x00), 216P6_EVDESCR(BR_INST_RETIRED, 0xC4, P6F_ALL, 0x00), 217P6_EVDESCR(BR_MISS_PRED_RETIRED, 0xC5, P6F_ALL, 0x00), 218P6_EVDESCR(BR_TAKEN_RETIRED, 0xC9, P6F_ALL, 0x00), 219P6_EVDESCR(BR_MISS_PRED_TAKEN_RET, 0xCA, P6F_ALL, 0x00), 220P6_EVDESCR(BR_INST_DECODED, 0xE0, P6F_ALL, 0x00), 221P6_EVDESCR(BTB_MISSES, 0xE2, P6F_ALL, 0x00), 222P6_EVDESCR(BR_BOGUS, 0xE4, P6F_ALL, 0x00), 223P6_EVDESCR(BACLEARS, 0xE6, P6F_ALL, 0x00), 224P6_EVDESCR(RESOURCE_STALLS, 0xA2, P6F_ALL, 0x00), 225P6_EVDESCR(PARTIAL_RAT_STALLS, 0xD2, P6F_ALL, 0x00), 226P6_EVDESCR(SEGMENT_REG_LOADS, 0x06, P6F_ALL, 0x00), 227P6_EVDESCR(CPU_CLK_UNHALTED, 0x79, P6F_ALL, 0x00), 228P6_EVDESCR(MMX_INSTR_EXEC, 0xB0, 229 P6F_ALL_CTRS | P6F_CL | P6F_PII, 0x00), 230P6_EVDESCR(MMX_SAT_INSTR_EXEC, 0xB1, 231 P6F_ALL_CTRS | P6F_PII | P6F_PIII, 0x00), 232P6_EVDESCR(MMX_UOPS_EXEC, 0xB2, 233 P6F_ALL_CTRS | P6F_PII | P6F_PIII, 0x0F), 234P6_EVDESCR(MMX_INSTR_TYPE_EXEC, 0xB3, 235 P6F_ALL_CTRS | P6F_PII | P6F_PIII, 0x3F), 236P6_EVDESCR(FP_MMX_TRANS, 0xCC, 237 P6F_ALL_CTRS | P6F_PII | P6F_PIII, 0x01), 238P6_EVDESCR(MMX_ASSIST, 0xCD, 239 P6F_ALL_CTRS | P6F_PII | P6F_PIII, 0x00), 240P6_EVDESCR(MMX_INSTR_RET, 0xCE, P6F_ALL_CTRS | P6F_PII, 0x00), 241P6_EVDESCR(SEG_RENAME_STALLS, 0xD4, 242 P6F_ALL_CTRS | P6F_PII | P6F_PIII, 0x0F), 243P6_EVDESCR(SEG_REG_RENAMES, 0xD5, 244 P6F_ALL_CTRS | P6F_PII | P6F_PIII, 0x0F), 245P6_EVDESCR(RET_SEG_RENAMES, 0xD6, 246 P6F_ALL_CTRS | P6F_PII | P6F_PIII, 0x00), 247P6_EVDESCR(EMON_EST_TRANS, 0x58, P6F_ALL_CTRS | P6F_PM, 0x02), 248P6_EVDESCR(EMON_THERMAL_TRIP, 0x59, P6F_ALL_CTRS | P6F_PM, 0x00), 249P6_EVDESCR(BR_INST_EXEC, 0x88, P6F_ALL_CTRS | P6F_PM, 0x00), 250P6_EVDESCR(BR_MISSP_EXEC, 0x89, P6F_ALL_CTRS | P6F_PM, 0x00), 251P6_EVDESCR(BR_BAC_MISSP_EXEC, 0x8A, P6F_ALL_CTRS | P6F_PM, 0x00), 252P6_EVDESCR(BR_CND_EXEC, 0x8B, P6F_ALL_CTRS | P6F_PM, 0x00), 253P6_EVDESCR(BR_CND_MISSP_EXEC, 0x8C, P6F_ALL_CTRS | P6F_PM, 0x00), 254P6_EVDESCR(BR_IND_EXEC, 0x8D, P6F_ALL_CTRS | P6F_PM, 0x00), 255P6_EVDESCR(BR_IND_MISSP_EXEC, 0x8E, P6F_ALL_CTRS | P6F_PM, 0x00), 256P6_EVDESCR(BR_RET_EXEC, 0x8F, P6F_ALL_CTRS | P6F_PM, 0x00), 257P6_EVDESCR(BR_RET_MISSP_EXEC, 0x90, P6F_ALL_CTRS | P6F_PM, 0x00), 258P6_EVDESCR(BR_RET_BAC_MISSP_EXEC, 0x91, P6F_ALL_CTRS | P6F_PM, 0x00), 259P6_EVDESCR(BR_CALL_EXEC, 0x92, P6F_ALL_CTRS | P6F_PM, 0x00), 260P6_EVDESCR(BR_CALL_MISSP_EXEC, 0x93, P6F_ALL_CTRS | P6F_PM, 0x00), 261P6_EVDESCR(BR_IND_CALL_EXEC, 0x94, P6F_ALL_CTRS | P6F_PM, 0x00), 262P6_EVDESCR(EMON_SIMD_INSTR_RETIRED, 0xCE, P6F_ALL_CTRS | P6F_PM, 0x00), 263P6_EVDESCR(EMON_SYNCH_UOPS, 0xD3, P6F_ALL_CTRS | P6F_PM, 0x00), 264P6_EVDESCR(EMON_ESP_UOPS, 0xD7, P6F_ALL_CTRS | P6F_PM, 0x00), 265P6_EVDESCR(EMON_FUSED_UOPS_RET, 0xDA, P6F_ALL_CTRS | P6F_PM, 0x03), 266P6_EVDESCR(EMON_UNFUSION, 0xDB, P6F_ALL_CTRS | P6F_PM, 0x00), 267P6_EVDESCR(EMON_PREF_RQSTS_UP, 0xF0, P6F_ALL_CTRS | P6F_PM, 0x00), 268P6_EVDESCR(EMON_PREF_RQSTS_DN, 0xD8, P6F_ALL_CTRS | P6F_PM, 0x00), 269P6_EVDESCR(EMON_SSE_SSE2_INST_RETIRED, 0xD8, P6F_ALL_CTRS | P6F_PM, 0x03), 270P6_EVDESCR(EMON_SSE_SSE2_COMP_INST_RETIRED, 0xD9, P6F_ALL_CTRS | P6F_PM, 0x03) 271 272#undef P6_EVDESCR 273}; 274 275#define P6_NEVENTS (PMC_EV_P6_LAST - PMC_EV_P6_FIRST + 1) 276 277static const struct p6_event_descr * 278p6_find_event(enum pmc_event ev) 279{ 280 int n; 281 282 for (n = 0; n < P6_NEVENTS; n++) 283 if (p6_events[n].pm_event == ev) 284 break; 285 if (n == P6_NEVENTS) 286 return NULL; 287 return &p6_events[n]; 288} 289 290/* 291 * Per-CPU data structure for P6 class CPUs 292 * 293 * [common stuff] 294 * [flags for maintaining PMC start/stop state] 295 * [3 struct pmc_hw pointers] 296 * [3 struct pmc_hw structures] 297 */ 298 299struct p6_cpu { 300 struct pmc_hw pc_p6pmcs[P6_NPMCS]; 301 uint32_t pc_state; 302}; 303 304static struct p6_cpu **p6_pcpu; 305 306/* 307 * If CTR1 is active, we need to keep the 'EN' bit if CTR0 set, 308 * with the rest of CTR0 being zero'ed out. 309 */ 310#define P6_SYNC_CTR_STATE(PC) do { \ 311 uint32_t _config, _enable; \ 312 _enable = 0; \ 313 if ((PC)->pc_state & 0x02) \ 314 _enable |= P6_EVSEL_EN; \ 315 if ((PC)->pc_state & 0x01) \ 316 _config = rdmsr(P6_MSR_EVSEL0) | \ 317 P6_EVSEL_EN; \ 318 else \ 319 _config = 0; \ 320 wrmsr(P6_MSR_EVSEL0, _config | _enable); \ 321 } while (0) 322 323#define P6_MARK_STARTED(PC,RI) do { \ 324 (PC)->pc_state |= (1 << ((RI)-1)); \ 325 } while (0) 326 327#define P6_MARK_STOPPED(PC,RI) do { \ 328 (PC)->pc_state &= ~(1<< ((RI)-1)); \ 329 } while (0) 330 331static int 332p6_pcpu_init(struct pmc_mdep *md, int cpu) 333{ 334 int first_ri, n; 335 struct p6_cpu *p6c; 336 struct pmc_cpu *pc; 337 struct pmc_hw *phw; 338 339 KASSERT(cpu >= 0 && cpu < pmc_cpu_max(), 340 ("[p6,%d] bad cpu %d", __LINE__, cpu)); 341 342 PMCDBG1(MDP,INI,0,"p6-init cpu=%d", cpu); 343 344 p6c = malloc(sizeof (struct p6_cpu), M_PMC, M_WAITOK|M_ZERO); 345 pc = pmc_pcpu[cpu]; 346 347 KASSERT(pc != NULL, ("[p6,%d] cpu %d null per-cpu", __LINE__, cpu)); 348 349 phw = p6c->pc_p6pmcs; 350 p6_pcpu[cpu] = p6c; 351 352 first_ri = md->pmd_classdep[PMC_MDEP_CLASS_INDEX_P6].pcd_ri; 353 354 for (n = 0; n < P6_NPMCS; n++, phw++) { 355 phw->phw_state = PMC_PHW_FLAG_IS_ENABLED | 356 PMC_PHW_CPU_TO_STATE(cpu) | PMC_PHW_INDEX_TO_STATE(n); 357 phw->phw_pmc = NULL; 358 pc->pc_hwpmcs[n + first_ri] = phw; 359 } 360 361 return (0); 362} 363 364static int 365p6_pcpu_fini(struct pmc_mdep *md, int cpu) 366{ 367 int first_ri, n; 368 struct p6_cpu *p6c; 369 struct pmc_cpu *pc; 370 371 KASSERT(cpu >= 0 && cpu < pmc_cpu_max(), 372 ("[p6,%d] bad cpu %d", __LINE__, cpu)); 373 374 PMCDBG1(MDP,INI,0,"p6-cleanup cpu=%d", cpu); 375 376 p6c = p6_pcpu[cpu]; 377 p6_pcpu[cpu] = NULL; 378 379 KASSERT(p6c != NULL, ("[p6,%d] null pcpu", __LINE__)); 380 381 free(p6c, M_PMC); 382 383 first_ri = md->pmd_classdep[PMC_MDEP_CLASS_INDEX_P6].pcd_ri; 384 pc = pmc_pcpu[cpu]; 385 for (n = 0; n < P6_NPMCS; n++) 386 pc->pc_hwpmcs[n + first_ri] = NULL; 387 388 return (0); 389} 390 391static int 392p6_read_pmc(int cpu, int ri, pmc_value_t *v) 393{ 394 struct pmc *pm; 395 struct p6pmc_descr *pd; 396 pmc_value_t tmp; 397 398 KASSERT(cpu >= 0 && cpu < pmc_cpu_max(), 399 ("[p6,%d] illegal cpu value %d", __LINE__, cpu)); 400 KASSERT(ri >= 0 && ri < P6_NPMCS, 401 ("[p6,%d] illegal row-index %d", __LINE__, ri)); 402 403 pm = p6_pcpu[cpu]->pc_p6pmcs[ri].phw_pmc; 404 pd = &p6_pmcdesc[ri]; 405 406 KASSERT(pm, 407 ("[p6,%d] cpu %d ri %d pmc not configured", __LINE__, cpu, ri)); 408 409 tmp = rdmsr(pd->pm_pmc_msr) & P6_PERFCTR_READ_MASK; 410 if (PMC_IS_SAMPLING_MODE(PMC_TO_MODE(pm))) 411 *v = P6_PERFCTR_VALUE_TO_RELOAD_COUNT(tmp); 412 else 413 *v = tmp; 414 415 PMCDBG4(MDP,REA,1, "p6-read cpu=%d ri=%d msr=0x%x -> v=%jx", cpu, ri, 416 pd->pm_pmc_msr, *v); 417 418 return (0); 419} 420 421static int 422p6_write_pmc(int cpu, int ri, pmc_value_t v) 423{ 424 struct pmc *pm; 425 struct p6pmc_descr *pd; 426 427 KASSERT(cpu >= 0 && cpu < pmc_cpu_max(), 428 ("[p6,%d] illegal cpu value %d", __LINE__, cpu)); 429 KASSERT(ri >= 0 && ri < P6_NPMCS, 430 ("[p6,%d] illegal row-index %d", __LINE__, ri)); 431 432 pm = p6_pcpu[cpu]->pc_p6pmcs[ri].phw_pmc; 433 pd = &p6_pmcdesc[ri]; 434 435 KASSERT(pm, 436 ("[p6,%d] cpu %d ri %d pmc not configured", __LINE__, cpu, ri)); 437 438 PMCDBG4(MDP,WRI,1, "p6-write cpu=%d ri=%d msr=0x%x v=%jx", cpu, ri, 439 pd->pm_pmc_msr, v); 440 441 if (PMC_IS_SAMPLING_MODE(PMC_TO_MODE(pm))) 442 v = P6_RELOAD_COUNT_TO_PERFCTR_VALUE(v); 443 444 wrmsr(pd->pm_pmc_msr, v & P6_PERFCTR_WRITE_MASK); 445 446 return (0); 447} 448 449static int 450p6_config_pmc(int cpu, int ri, struct pmc *pm) 451{ 452 KASSERT(cpu >= 0 && cpu < pmc_cpu_max(), 453 ("[p6,%d] illegal CPU %d", __LINE__, cpu)); 454 455 KASSERT(ri >= 0 && ri < P6_NPMCS, 456 ("[p6,%d] illegal row-index %d", __LINE__, ri)); 457 458 PMCDBG3(MDP,CFG,1, "p6-config cpu=%d ri=%d pm=%p", cpu, ri, pm); 459 460 KASSERT(p6_pcpu[cpu] != NULL, ("[p6,%d] null per-cpu %d", __LINE__, 461 cpu)); 462 463 p6_pcpu[cpu]->pc_p6pmcs[ri].phw_pmc = pm; 464 465 return (0); 466} 467 468/* 469 * Retrieve a configured PMC pointer from hardware state. 470 */ 471 472static int 473p6_get_config(int cpu, int ri, struct pmc **ppm) 474{ 475 476 KASSERT(cpu >= 0 && cpu < pmc_cpu_max(), 477 ("[p6,%d] illegal CPU %d", __LINE__, cpu)); 478 KASSERT(ri >= 0 && ri < P6_NPMCS, 479 ("[p6,%d] illegal row-index %d", __LINE__, ri)); 480 481 *ppm = p6_pcpu[cpu]->pc_p6pmcs[ri].phw_pmc; 482 483 return (0); 484} 485 486 487/* 488 * A pmc may be allocated to a given row index if: 489 * - the event is valid for this CPU 490 * - the event is valid for this counter index 491 */ 492 493static int 494p6_allocate_pmc(int cpu, int ri, struct pmc *pm, 495 const struct pmc_op_pmcallocate *a) 496{ 497 uint32_t allowed_unitmask, caps, config, unitmask; 498 const struct p6pmc_descr *pd; 499 const struct p6_event_descr *pevent; 500 enum pmc_event ev; 501 502 (void) cpu; 503 504 KASSERT(cpu >= 0 && cpu < pmc_cpu_max(), 505 ("[p6,%d] illegal CPU %d", __LINE__, cpu)); 506 KASSERT(ri >= 0 && ri < P6_NPMCS, 507 ("[p6,%d] illegal row-index value %d", __LINE__, ri)); 508 509 pd = &p6_pmcdesc[ri]; 510 511 PMCDBG4(MDP,ALL,1, "p6-allocate ri=%d class=%d pmccaps=0x%x " 512 "reqcaps=0x%x", ri, pd->pm_descr.pd_class, pd->pm_descr.pd_caps, 513 pm->pm_caps); 514 515 /* check class */ 516 if (pd->pm_descr.pd_class != a->pm_class) 517 return (EINVAL); 518 519 /* check requested capabilities */ 520 caps = a->pm_caps; 521 if ((pd->pm_descr.pd_caps & caps) != caps) 522 return (EPERM); 523 524 ev = pm->pm_event; 525 526 if (ev < PMC_EV_P6_FIRST || ev > PMC_EV_P6_LAST) 527 return (EINVAL); 528 529 if ((pevent = p6_find_event(ev)) == NULL) 530 return (ESRCH); 531 532 if (!P6_EVENT_VALID_FOR_CPU(pevent, p6_cputype) || 533 !P6_EVENT_VALID_FOR_CTR(pevent, (ri-1))) 534 return (EINVAL); 535 536 /* For certain events, Pentium M differs from the stock P6 */ 537 allowed_unitmask = 0; 538 if (p6_cputype == PMC_CPU_INTEL_PM) { 539 if (ev == PMC_EV_P6_L2_LD || ev == PMC_EV_P6_L2_LINES_IN || 540 ev == PMC_EV_P6_L2_LINES_OUT) 541 allowed_unitmask = P6_EVSEL_TO_UMASK(0x3F); 542 else if (ev == PMC_EV_P6_L2_M_LINES_OUTM) 543 allowed_unitmask = P6_EVSEL_TO_UMASK(0x30); 544 } else 545 allowed_unitmask = P6_EVSEL_TO_UMASK(pevent->pm_unitmask); 546 547 unitmask = a->pm_md.pm_ppro.pm_ppro_config & P6_EVSEL_UMASK_MASK; 548 if (unitmask & ~allowed_unitmask) /* disallow reserved bits */ 549 return (EINVAL); 550 551 if (ev == PMC_EV_P6_MMX_UOPS_EXEC) /* hardcoded mask */ 552 unitmask = P6_EVSEL_TO_UMASK(0x0F); 553 554 config = 0; 555 556 config |= P6_EVSEL_EVENT_SELECT(pevent->pm_evsel); 557 558 if (unitmask & (caps & PMC_CAP_QUALIFIER)) 559 config |= unitmask; 560 561 if (caps & PMC_CAP_THRESHOLD) 562 config |= a->pm_md.pm_ppro.pm_ppro_config & 563 P6_EVSEL_CMASK_MASK; 564 565 /* set at least one of the 'usr' or 'os' caps */ 566 if (caps & PMC_CAP_USER) 567 config |= P6_EVSEL_USR; 568 if (caps & PMC_CAP_SYSTEM) 569 config |= P6_EVSEL_OS; 570 if ((caps & (PMC_CAP_USER|PMC_CAP_SYSTEM)) == 0) 571 config |= (P6_EVSEL_USR|P6_EVSEL_OS); 572 573 if (caps & PMC_CAP_EDGE) 574 config |= P6_EVSEL_E; 575 if (caps & PMC_CAP_INVERT) 576 config |= P6_EVSEL_INV; 577 if (caps & PMC_CAP_INTERRUPT) 578 config |= P6_EVSEL_INT; 579 580 pm->pm_md.pm_ppro.pm_ppro_evsel = config; 581 582 PMCDBG1(MDP,ALL,2, "p6-allocate config=0x%x", config); 583 584 return (0); 585} 586 587static int 588p6_release_pmc(int cpu, int ri, struct pmc *pm) 589{ 590 (void) pm; 591 592 PMCDBG3(MDP,REL,1, "p6-release cpu=%d ri=%d pm=%p", cpu, ri, pm); 593 594 KASSERT(cpu >= 0 && cpu < pmc_cpu_max(), 595 ("[p6,%d] illegal CPU value %d", __LINE__, cpu)); 596 KASSERT(ri >= 0 && ri < P6_NPMCS, 597 ("[p6,%d] illegal row-index %d", __LINE__, ri)); 598 599 KASSERT(p6_pcpu[cpu]->pc_p6pmcs[ri].phw_pmc == NULL, 600 ("[p6,%d] PHW pmc non-NULL", __LINE__)); 601 602 return (0); 603} 604 605static int 606p6_start_pmc(int cpu, int ri) 607{ 608 uint32_t config; 609 struct pmc *pm; 610 struct p6_cpu *pc; 611 const struct p6pmc_descr *pd; 612 613 KASSERT(cpu >= 0 && cpu < pmc_cpu_max(), 614 ("[p6,%d] illegal CPU value %d", __LINE__, cpu)); 615 KASSERT(ri >= 0 && ri < P6_NPMCS, 616 ("[p6,%d] illegal row-index %d", __LINE__, ri)); 617 618 pc = p6_pcpu[cpu]; 619 pm = pc->pc_p6pmcs[ri].phw_pmc; 620 pd = &p6_pmcdesc[ri]; 621 622 KASSERT(pm, 623 ("[p6,%d] starting cpu%d,ri%d with no pmc configured", 624 __LINE__, cpu, ri)); 625 626 PMCDBG2(MDP,STA,1, "p6-start cpu=%d ri=%d", cpu, ri); 627 628 config = pm->pm_md.pm_ppro.pm_ppro_evsel; 629 630 PMCDBG4(MDP,STA,2, "p6-start/2 cpu=%d ri=%d evselmsr=0x%x config=0x%x", 631 cpu, ri, pd->pm_evsel_msr, config); 632 633 P6_MARK_STARTED(pc, ri); 634 wrmsr(pd->pm_evsel_msr, config); 635 636 P6_SYNC_CTR_STATE(pc); 637 638 return (0); 639} 640 641static int 642p6_stop_pmc(int cpu, int ri) 643{ 644 struct pmc *pm; 645 struct p6_cpu *pc; 646 struct p6pmc_descr *pd; 647 648 KASSERT(cpu >= 0 && cpu < pmc_cpu_max(), 649 ("[p6,%d] illegal cpu value %d", __LINE__, cpu)); 650 KASSERT(ri >= 0 && ri < P6_NPMCS, 651 ("[p6,%d] illegal row index %d", __LINE__, ri)); 652 653 pc = p6_pcpu[cpu]; 654 pm = pc->pc_p6pmcs[ri].phw_pmc; 655 pd = &p6_pmcdesc[ri]; 656 657 KASSERT(pm, 658 ("[p6,%d] cpu%d ri%d no configured PMC to stop", __LINE__, 659 cpu, ri)); 660 661 PMCDBG2(MDP,STO,1, "p6-stop cpu=%d ri=%d", cpu, ri); 662 663 wrmsr(pd->pm_evsel_msr, 0); /* stop hw */ 664 P6_MARK_STOPPED(pc, ri); /* update software state */ 665 666 P6_SYNC_CTR_STATE(pc); /* restart CTR1 if need be */ 667 668 PMCDBG2(MDP,STO,2, "p6-stop/2 cpu=%d ri=%d", cpu, ri); 669 670 return (0); 671} 672 673static int 674p6_intr(int cpu, struct trapframe *tf) 675{ 676 int error, retval, ri; 677 uint32_t perf0cfg; 678 struct pmc *pm; 679 struct p6_cpu *pc; 680 pmc_value_t v; 681 682 KASSERT(cpu >= 0 && cpu < pmc_cpu_max(), 683 ("[p6,%d] CPU %d out of range", __LINE__, cpu)); 684 685 retval = 0; 686 pc = p6_pcpu[cpu]; 687 688 /* stop both PMCs */ 689 perf0cfg = rdmsr(P6_MSR_EVSEL0); 690 wrmsr(P6_MSR_EVSEL0, perf0cfg & ~P6_EVSEL_EN); 691 692 for (ri = 0; ri < P6_NPMCS; ri++) { 693 694 if ((pm = pc->pc_p6pmcs[ri].phw_pmc) == NULL || 695 !PMC_IS_SAMPLING_MODE(PMC_TO_MODE(pm))) { 696 continue; 697 } 698 699 if (!P6_PMC_HAS_OVERFLOWED(ri)) 700 continue; 701 702 retval = 1; 703 704 if (pm->pm_state != PMC_STATE_RUNNING) 705 continue; 706 707 error = pmc_process_interrupt(cpu, PMC_HR, pm, tf, 708 TRAPF_USERMODE(tf)); 709 if (error) 710 P6_MARK_STOPPED(pc,ri); 711 712 /* reload sampling count */ 713 v = pm->pm_sc.pm_reloadcount; 714 wrmsr(P6_MSR_PERFCTR0 + ri, 715 P6_RELOAD_COUNT_TO_PERFCTR_VALUE(v)); 716 717 } 718 719 /* 720 * On P6 processors, the LAPIC needs to have its PMC interrupt 721 * unmasked after a PMC interrupt. 722 */ 723 if (retval) 724 lapic_reenable_pmc(); 725 726 atomic_add_int(retval ? &pmc_stats.pm_intr_processed : 727 &pmc_stats.pm_intr_ignored, 1); 728 729 /* restart counters that can be restarted */ 730 P6_SYNC_CTR_STATE(pc); 731 732 return (retval); 733} 734 735static int 736p6_describe(int cpu, int ri, struct pmc_info *pi, 737 struct pmc **ppmc) 738{ 739 int error; 740 size_t copied; 741 struct pmc_hw *phw; 742 struct p6pmc_descr *pd; 743 744 KASSERT(cpu >= 0 && cpu < pmc_cpu_max(), 745 ("[p6,%d] illegal CPU %d", __LINE__, cpu)); 746 KASSERT(ri >= 0 && ri < P6_NPMCS, 747 ("[p6,%d] row-index %d out of range", __LINE__, ri)); 748 749 phw = pmc_pcpu[cpu]->pc_hwpmcs[ri]; 750 pd = &p6_pmcdesc[ri]; 751 752 KASSERT(phw == &p6_pcpu[cpu]->pc_p6pmcs[ri], 753 ("[p6,%d] phw mismatch", __LINE__)); 754 755 if ((error = copystr(pd->pm_descr.pd_name, pi->pm_name, 756 PMC_NAME_MAX, &copied)) != 0) 757 return (error); 758 759 pi->pm_class = pd->pm_descr.pd_class; 760 761 if (phw->phw_state & PMC_PHW_FLAG_IS_ENABLED) { 762 pi->pm_enabled = TRUE; 763 *ppmc = phw->phw_pmc; 764 } else { 765 pi->pm_enabled = FALSE; 766 *ppmc = NULL; 767 } 768 769 return (0); 770} 771 772static int 773p6_get_msr(int ri, uint32_t *msr) 774{ 775 KASSERT(ri >= 0 && ri < P6_NPMCS, 776 ("[p6,%d ri %d out of range", __LINE__, ri)); 777 778 *msr = p6_pmcdesc[ri].pm_pmc_msr - P6_MSR_PERFCTR0; 779 780 return (0); 781} 782 783int 784pmc_p6_initialize(struct pmc_mdep *md, int ncpus) 785{ 786 struct pmc_classdep *pcd; 787 788 KASSERT(cpu_vendor_id == CPU_VENDOR_INTEL, 789 ("[p6,%d] Initializing non-intel processor", __LINE__)); 790 791 PMCDBG0(MDP,INI,1, "p6-initialize"); 792 793 /* Allocate space for pointers to per-cpu descriptors. */ 794 p6_pcpu = malloc(sizeof(struct p6_cpu **) * ncpus, M_PMC, 795 M_ZERO|M_WAITOK); 796 797 /* Fill in the class dependent descriptor. */ 798 pcd = &md->pmd_classdep[PMC_MDEP_CLASS_INDEX_P6]; 799 800 switch (md->pmd_cputype) { 801 802 /* 803 * P6 Family Processors 804 */ 805 case PMC_CPU_INTEL_P6: 806 case PMC_CPU_INTEL_CL: 807 case PMC_CPU_INTEL_PII: 808 case PMC_CPU_INTEL_PIII: 809 case PMC_CPU_INTEL_PM: 810 811 p6_cputype = md->pmd_cputype; 812 813 pcd->pcd_caps = P6_PMC_CAPS; 814 pcd->pcd_class = PMC_CLASS_P6; 815 pcd->pcd_num = P6_NPMCS; 816 pcd->pcd_ri = md->pmd_npmc; 817 pcd->pcd_width = 40; 818 819 pcd->pcd_allocate_pmc = p6_allocate_pmc; 820 pcd->pcd_config_pmc = p6_config_pmc; 821 pcd->pcd_describe = p6_describe; 822 pcd->pcd_get_config = p6_get_config; 823 pcd->pcd_get_msr = p6_get_msr; 824 pcd->pcd_pcpu_fini = p6_pcpu_fini; 825 pcd->pcd_pcpu_init = p6_pcpu_init; 826 pcd->pcd_read_pmc = p6_read_pmc; 827 pcd->pcd_release_pmc = p6_release_pmc; 828 pcd->pcd_start_pmc = p6_start_pmc; 829 pcd->pcd_stop_pmc = p6_stop_pmc; 830 pcd->pcd_write_pmc = p6_write_pmc; 831 832 md->pmd_pcpu_fini = NULL; 833 md->pmd_pcpu_init = NULL; 834 md->pmd_intr = p6_intr; 835 836 md->pmd_npmc += P6_NPMCS; 837 838 break; 839 840 default: 841 KASSERT(0,("[p6,%d] Unknown CPU type", __LINE__)); 842 return ENOSYS; 843 } 844 845 return (0); 846} 847 848void 849pmc_p6_finalize(struct pmc_mdep *md) 850{ 851#if defined(INVARIANTS) 852 int i, ncpus; 853#endif 854 855 KASSERT(p6_pcpu != NULL, ("[p6,%d] NULL p6_pcpu", __LINE__)); 856 857#if defined(INVARIANTS) 858 ncpus = pmc_cpu_max(); 859 for (i = 0; i < ncpus; i++) 860 KASSERT(p6_pcpu[i] == NULL, ("[p6,%d] non-null pcpu %d", 861 __LINE__, i)); 862#endif 863 864 free(p6_pcpu, M_PMC); 865 p6_pcpu = NULL; 866} 867