1145256Sjkoshy/*- 2183266Sjkoshy * Copyright (c) 2003-2005,2008 Joseph Koshy 3174395Sjkoshy * Copyright (c) 2007 The FreeBSD Foundation 4145256Sjkoshy * All rights reserved. 5145256Sjkoshy * 6174395Sjkoshy * Portions of this software were developed by A. Joseph Koshy under 7174395Sjkoshy * sponsorship from the FreeBSD Foundation and Google, Inc. 8174395Sjkoshy * 9145256Sjkoshy * Redistribution and use in source and binary forms, with or without 10145256Sjkoshy * modification, are permitted provided that the following conditions 11145256Sjkoshy * are met: 12145256Sjkoshy * 1. Redistributions of source code must retain the above copyright 13145256Sjkoshy * notice, this list of conditions and the following disclaimer. 14145256Sjkoshy * 2. Redistributions in binary form must reproduce the above copyright 15145256Sjkoshy * notice, this list of conditions and the following disclaimer in the 16145256Sjkoshy * documentation and/or other materials provided with the distribution. 17145256Sjkoshy * 18145256Sjkoshy * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 19145256Sjkoshy * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20145256Sjkoshy * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21145256Sjkoshy * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 22145256Sjkoshy * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23145256Sjkoshy * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 24145256Sjkoshy * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 25145256Sjkoshy * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 26145256Sjkoshy * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 27145256Sjkoshy * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 28145256Sjkoshy * SUCH DAMAGE. 29145256Sjkoshy */ 30145256Sjkoshy 31145256Sjkoshy#include <sys/cdefs.h> 32145256Sjkoshy__FBSDID("$FreeBSD$"); 33145256Sjkoshy 34145256Sjkoshy#include <sys/param.h> 35196224Sjhb#include <sys/bus.h> 36145256Sjkoshy#include <sys/lock.h> 37145256Sjkoshy#include <sys/mutex.h> 38145338Smarcel#include <sys/pmc.h> 39145256Sjkoshy#include <sys/pmckern.h> 40145256Sjkoshy#include <sys/smp.h> 41145256Sjkoshy#include <sys/systm.h> 42145256Sjkoshy 43196224Sjhb#include <machine/intr_machdep.h> 44196224Sjhb#include <machine/apicvar.h> 45174395Sjkoshy#include <machine/cpu.h> 46147191Sjkoshy#include <machine/cpufunc.h> 47185341Sjkim#include <machine/cputypes.h> 48145256Sjkoshy#include <machine/md_var.h> 49147191Sjkoshy#include <machine/pmc_mdep.h> 50147191Sjkoshy#include <machine/specialreg.h> 51145256Sjkoshy 52145256Sjkoshy/* 53145256Sjkoshy * PENTIUM PRO SUPPORT 54147191Sjkoshy * 55147191Sjkoshy * Quirks: 56147191Sjkoshy * 57147191Sjkoshy * - Both PMCs are enabled by a single bit P6_EVSEL_EN in performance 58147191Sjkoshy * counter '0'. This bit needs to be '1' if any of the two 59147191Sjkoshy * performance counters are in use. Perf counters can also be 60147191Sjkoshy * switched off by writing zeros to their EVSEL register. 61147191Sjkoshy * 62147191Sjkoshy * - While the width of these counters is 40 bits, we do not appear to 63147191Sjkoshy * have a way of writing 40 bits to the counter MSRs. A WRMSR 64147191Sjkoshy * instruction will sign extend bit 31 of the value being written to 65147191Sjkoshy * the perf counter -- a value of 0x80000000 written to an perf 66147191Sjkoshy * counter register will be sign extended to 0xFF80000000. 67147191Sjkoshy * 68147191Sjkoshy * This quirk primarily affects thread-mode PMCs in counting mode, as 69147191Sjkoshy * these PMCs read and write PMC registers at every context switch. 70145256Sjkoshy */ 71145256Sjkoshy 72145256Sjkoshystruct p6pmc_descr { 73145256Sjkoshy struct pmc_descr pm_descr; /* common information */ 74145256Sjkoshy uint32_t pm_pmc_msr; 75145256Sjkoshy uint32_t pm_evsel_msr; 76145256Sjkoshy}; 77145256Sjkoshy 78145256Sjkoshystatic struct p6pmc_descr p6_pmcdesc[P6_NPMCS] = { 79145256Sjkoshy 80145256Sjkoshy#define P6_PMC_CAPS (PMC_CAP_INTERRUPT | PMC_CAP_USER | PMC_CAP_SYSTEM | \ 81145256Sjkoshy PMC_CAP_EDGE | PMC_CAP_THRESHOLD | PMC_CAP_READ | PMC_CAP_WRITE | \ 82145256Sjkoshy PMC_CAP_INVERT | PMC_CAP_QUALIFIER) 83145256Sjkoshy 84145256Sjkoshy /* PMC 0 */ 85145256Sjkoshy { 86145256Sjkoshy .pm_descr = 87145256Sjkoshy { 88145256Sjkoshy .pd_name ="P6-0", 89145256Sjkoshy .pd_class = PMC_CLASS_P6, 90145256Sjkoshy .pd_caps = P6_PMC_CAPS, 91145256Sjkoshy .pd_width = 40 92145256Sjkoshy }, 93145256Sjkoshy .pm_pmc_msr = P6_MSR_PERFCTR0, 94145256Sjkoshy .pm_evsel_msr = P6_MSR_EVSEL0 95145256Sjkoshy }, 96145256Sjkoshy 97145256Sjkoshy /* PMC 1 */ 98145256Sjkoshy { 99145256Sjkoshy .pm_descr = 100145256Sjkoshy { 101145256Sjkoshy .pd_name ="P6-1", 102145256Sjkoshy .pd_class = PMC_CLASS_P6, 103145256Sjkoshy .pd_caps = P6_PMC_CAPS, 104145256Sjkoshy .pd_width = 40 105145256Sjkoshy }, 106145256Sjkoshy .pm_pmc_msr = P6_MSR_PERFCTR1, 107145256Sjkoshy .pm_evsel_msr = P6_MSR_EVSEL1 108145256Sjkoshy } 109145256Sjkoshy}; 110145256Sjkoshy 111145256Sjkoshystatic enum pmc_cputype p6_cputype; 112145256Sjkoshy 113145256Sjkoshy/* 114145256Sjkoshy * P6 Event descriptor 115183717Sjkoshy * 116183717Sjkoshy * The 'pm_flags' field has the following structure: 117183717Sjkoshy * - The upper 4 bits are used to track which counter an event is valid on. 118183717Sjkoshy * - The lower bits form a bitmask of flags indicating support for the event 119183717Sjkoshy * on a given CPU. 120145256Sjkoshy */ 121145256Sjkoshy 122145256Sjkoshystruct p6_event_descr { 123145256Sjkoshy const enum pmc_event pm_event; 124145256Sjkoshy uint32_t pm_evsel; 125145256Sjkoshy uint32_t pm_flags; 126145256Sjkoshy uint32_t pm_unitmask; 127145256Sjkoshy}; 128145256Sjkoshy 129183717Sjkoshy#define P6F_CTR(C) (1 << (28 + (C))) 130183717Sjkoshy#define P6F_CTR0 P6F_CTR(0) 131183717Sjkoshy#define P6F_CTR1 P6F_CTR(1) 132183717Sjkoshy#define P6F(CPU) (1 << ((CPU) - PMC_CPU_INTEL_P6)) 133183717Sjkoshy#define _P6F(C) P6F(PMC_CPU_INTEL_##C) 134183717Sjkoshy#define P6F_P6 _P6F(P6) 135183717Sjkoshy#define P6F_CL _P6F(CL) 136183717Sjkoshy#define P6F_PII _P6F(PII) 137183717Sjkoshy#define P6F_PIII _P6F(PIII) 138183717Sjkoshy#define P6F_PM _P6F(PM) 139183717Sjkoshy#define P6F_ALL_CPUS (P6F_P6 | P6F_PII | P6F_CL | P6F_PIII | P6F_PM) 140183717Sjkoshy#define P6F_ALL_CTRS (P6F_CTR0 | P6F_CTR1) 141183717Sjkoshy#define P6F_ALL (P6F_ALL_CPUS | P6F_ALL_CTRS) 142183717Sjkoshy 143183717Sjkoshy#define P6_EVENT_VALID_FOR_CPU(P,CPU) ((P)->pm_flags & P6F(CPU)) 144183717Sjkoshy#define P6_EVENT_VALID_FOR_CTR(P,CTR) ((P)->pm_flags & P6F_CTR(CTR)) 145183717Sjkoshy 146145256Sjkoshystatic const struct p6_event_descr p6_events[] = { 147145256Sjkoshy 148145256Sjkoshy#define P6_EVDESCR(NAME, EVSEL, FLAGS, UMASK) \ 149145256Sjkoshy { \ 150145256Sjkoshy .pm_event = PMC_EV_P6_##NAME, \ 151145256Sjkoshy .pm_evsel = (EVSEL), \ 152145256Sjkoshy .pm_flags = (FLAGS), \ 153145256Sjkoshy .pm_unitmask = (UMASK) \ 154145256Sjkoshy } 155145256Sjkoshy 156145256SjkoshyP6_EVDESCR(DATA_MEM_REFS, 0x43, P6F_ALL, 0x00), 157145256SjkoshyP6_EVDESCR(DCU_LINES_IN, 0x45, P6F_ALL, 0x00), 158145256SjkoshyP6_EVDESCR(DCU_M_LINES_IN, 0x46, P6F_ALL, 0x00), 159145256SjkoshyP6_EVDESCR(DCU_M_LINES_OUT, 0x47, P6F_ALL, 0x00), 160145256SjkoshyP6_EVDESCR(DCU_MISS_OUTSTANDING, 0x47, P6F_ALL, 0x00), 161145256SjkoshyP6_EVDESCR(IFU_FETCH, 0x80, P6F_ALL, 0x00), 162145256SjkoshyP6_EVDESCR(IFU_FETCH_MISS, 0x81, P6F_ALL, 0x00), 163145256SjkoshyP6_EVDESCR(ITLB_MISS, 0x85, P6F_ALL, 0x00), 164145256SjkoshyP6_EVDESCR(IFU_MEM_STALL, 0x86, P6F_ALL, 0x00), 165145256SjkoshyP6_EVDESCR(ILD_STALL, 0x87, P6F_ALL, 0x00), 166145256SjkoshyP6_EVDESCR(L2_IFETCH, 0x28, P6F_ALL, 0x0F), 167145256SjkoshyP6_EVDESCR(L2_LD, 0x29, P6F_ALL, 0x0F), 168145256SjkoshyP6_EVDESCR(L2_ST, 0x2A, P6F_ALL, 0x0F), 169145256SjkoshyP6_EVDESCR(L2_LINES_IN, 0x24, P6F_ALL, 0x0F), 170145256SjkoshyP6_EVDESCR(L2_LINES_OUT, 0x26, P6F_ALL, 0x0F), 171145256SjkoshyP6_EVDESCR(L2_M_LINES_INM, 0x25, P6F_ALL, 0x00), 172145256SjkoshyP6_EVDESCR(L2_M_LINES_OUTM, 0x27, P6F_ALL, 0x0F), 173145256SjkoshyP6_EVDESCR(L2_RQSTS, 0x2E, P6F_ALL, 0x0F), 174145256SjkoshyP6_EVDESCR(L2_ADS, 0x21, P6F_ALL, 0x00), 175145256SjkoshyP6_EVDESCR(L2_DBUS_BUSY, 0x22, P6F_ALL, 0x00), 176145256SjkoshyP6_EVDESCR(L2_DBUS_BUSY_RD, 0x23, P6F_ALL, 0x00), 177145256SjkoshyP6_EVDESCR(BUS_DRDY_CLOCKS, 0x62, P6F_ALL, 0x20), 178145256SjkoshyP6_EVDESCR(BUS_LOCK_CLOCKS, 0x63, P6F_ALL, 0x20), 179145256SjkoshyP6_EVDESCR(BUS_REQ_OUTSTANDING, 0x60, P6F_ALL, 0x00), 180145256SjkoshyP6_EVDESCR(BUS_TRAN_BRD, 0x65, P6F_ALL, 0x20), 181145256SjkoshyP6_EVDESCR(BUS_TRAN_RFO, 0x66, P6F_ALL, 0x20), 182145256SjkoshyP6_EVDESCR(BUS_TRANS_WB, 0x67, P6F_ALL, 0x20), 183145256SjkoshyP6_EVDESCR(BUS_TRAN_IFETCH, 0x68, P6F_ALL, 0x20), 184145256SjkoshyP6_EVDESCR(BUS_TRAN_INVAL, 0x69, P6F_ALL, 0x20), 185145256SjkoshyP6_EVDESCR(BUS_TRAN_PWR, 0x6A, P6F_ALL, 0x20), 186145256SjkoshyP6_EVDESCR(BUS_TRANS_P, 0x6B, P6F_ALL, 0x20), 187145256SjkoshyP6_EVDESCR(BUS_TRANS_IO, 0x6C, P6F_ALL, 0x20), 188145256SjkoshyP6_EVDESCR(BUS_TRAN_DEF, 0x6D, P6F_ALL, 0x20), 189145256SjkoshyP6_EVDESCR(BUS_TRAN_BURST, 0x6E, P6F_ALL, 0x20), 190145256SjkoshyP6_EVDESCR(BUS_TRAN_ANY, 0x70, P6F_ALL, 0x20), 191145256SjkoshyP6_EVDESCR(BUS_TRAN_MEM, 0x6F, P6F_ALL, 0x20), 192145256SjkoshyP6_EVDESCR(BUS_DATA_RCV, 0x64, P6F_ALL, 0x00), 193145256SjkoshyP6_EVDESCR(BUS_BNR_DRV, 0x61, P6F_ALL, 0x00), 194145256SjkoshyP6_EVDESCR(BUS_HIT_DRV, 0x7A, P6F_ALL, 0x00), 195145256SjkoshyP6_EVDESCR(BUS_HITM_DRV, 0x7B, P6F_ALL, 0x00), 196145256SjkoshyP6_EVDESCR(BUS_SNOOP_STALL, 0x7E, P6F_ALL, 0x00), 197145256SjkoshyP6_EVDESCR(FLOPS, 0xC1, P6F_ALL_CPUS | P6F_CTR0, 0x00), 198145256SjkoshyP6_EVDESCR(FP_COMPS_OPS_EXE, 0x10, P6F_ALL_CPUS | P6F_CTR0, 0x00), 199145256SjkoshyP6_EVDESCR(FP_ASSIST, 0x11, P6F_ALL_CPUS | P6F_CTR1, 0x00), 200145256SjkoshyP6_EVDESCR(MUL, 0x12, P6F_ALL_CPUS | P6F_CTR1, 0x00), 201145256SjkoshyP6_EVDESCR(DIV, 0x13, P6F_ALL_CPUS | P6F_CTR1, 0x00), 202145256SjkoshyP6_EVDESCR(CYCLES_DIV_BUSY, 0x14, P6F_ALL_CPUS | P6F_CTR0, 0x00), 203145256SjkoshyP6_EVDESCR(LD_BLOCKS, 0x03, P6F_ALL, 0x00), 204145256SjkoshyP6_EVDESCR(SB_DRAINS, 0x04, P6F_ALL, 0x00), 205145256SjkoshyP6_EVDESCR(MISALIGN_MEM_REF, 0x05, P6F_ALL, 0x00), 206145256SjkoshyP6_EVDESCR(EMON_KNI_PREF_DISPATCHED, 0x07, P6F_PIII | P6F_ALL_CTRS, 0x03), 207145256SjkoshyP6_EVDESCR(EMON_KNI_PREF_MISS, 0x4B, P6F_PIII | P6F_ALL_CTRS, 0x03), 208145256SjkoshyP6_EVDESCR(INST_RETIRED, 0xC0, P6F_ALL, 0x00), 209145256SjkoshyP6_EVDESCR(UOPS_RETIRED, 0xC2, P6F_ALL, 0x00), 210145256SjkoshyP6_EVDESCR(INST_DECODED, 0xD0, P6F_ALL, 0x00), 211145256SjkoshyP6_EVDESCR(EMON_KNI_INST_RETIRED, 0xD8, P6F_PIII | P6F_ALL_CTRS, 0x01), 212145256SjkoshyP6_EVDESCR(EMON_KNI_COMP_INST_RET, 0xD9, P6F_PIII | P6F_ALL_CTRS, 0x01), 213145256SjkoshyP6_EVDESCR(HW_INT_RX, 0xC8, P6F_ALL, 0x00), 214145256SjkoshyP6_EVDESCR(CYCLES_INT_MASKED, 0xC6, P6F_ALL, 0x00), 215145256SjkoshyP6_EVDESCR(CYCLES_INT_PENDING_AND_MASKED, 0xC7, P6F_ALL, 0x00), 216145256SjkoshyP6_EVDESCR(BR_INST_RETIRED, 0xC4, P6F_ALL, 0x00), 217145256SjkoshyP6_EVDESCR(BR_MISS_PRED_RETIRED, 0xC5, P6F_ALL, 0x00), 218145256SjkoshyP6_EVDESCR(BR_TAKEN_RETIRED, 0xC9, P6F_ALL, 0x00), 219145256SjkoshyP6_EVDESCR(BR_MISS_PRED_TAKEN_RET, 0xCA, P6F_ALL, 0x00), 220145256SjkoshyP6_EVDESCR(BR_INST_DECODED, 0xE0, P6F_ALL, 0x00), 221145256SjkoshyP6_EVDESCR(BTB_MISSES, 0xE2, P6F_ALL, 0x00), 222145256SjkoshyP6_EVDESCR(BR_BOGUS, 0xE4, P6F_ALL, 0x00), 223145256SjkoshyP6_EVDESCR(BACLEARS, 0xE6, P6F_ALL, 0x00), 224145256SjkoshyP6_EVDESCR(RESOURCE_STALLS, 0xA2, P6F_ALL, 0x00), 225145256SjkoshyP6_EVDESCR(PARTIAL_RAT_STALLS, 0xD2, P6F_ALL, 0x00), 226145256SjkoshyP6_EVDESCR(SEGMENT_REG_LOADS, 0x06, P6F_ALL, 0x00), 227145256SjkoshyP6_EVDESCR(CPU_CLK_UNHALTED, 0x79, P6F_ALL, 0x00), 228145256SjkoshyP6_EVDESCR(MMX_INSTR_EXEC, 0xB0, 229145256Sjkoshy P6F_ALL_CTRS | P6F_CL | P6F_PII, 0x00), 230145256SjkoshyP6_EVDESCR(MMX_SAT_INSTR_EXEC, 0xB1, 231145256Sjkoshy P6F_ALL_CTRS | P6F_PII | P6F_PIII, 0x00), 232145256SjkoshyP6_EVDESCR(MMX_UOPS_EXEC, 0xB2, 233145256Sjkoshy P6F_ALL_CTRS | P6F_PII | P6F_PIII, 0x0F), 234145256SjkoshyP6_EVDESCR(MMX_INSTR_TYPE_EXEC, 0xB3, 235145256Sjkoshy P6F_ALL_CTRS | P6F_PII | P6F_PIII, 0x3F), 236145256SjkoshyP6_EVDESCR(FP_MMX_TRANS, 0xCC, 237145256Sjkoshy P6F_ALL_CTRS | P6F_PII | P6F_PIII, 0x01), 238145256SjkoshyP6_EVDESCR(MMX_ASSIST, 0xCD, 239145256Sjkoshy P6F_ALL_CTRS | P6F_PII | P6F_PIII, 0x00), 240145256SjkoshyP6_EVDESCR(MMX_INSTR_RET, 0xCE, P6F_ALL_CTRS | P6F_PII, 0x00), 241145256SjkoshyP6_EVDESCR(SEG_RENAME_STALLS, 0xD4, 242145256Sjkoshy P6F_ALL_CTRS | P6F_PII | P6F_PIII, 0x0F), 243145256SjkoshyP6_EVDESCR(SEG_REG_RENAMES, 0xD5, 244145256Sjkoshy P6F_ALL_CTRS | P6F_PII | P6F_PIII, 0x0F), 245145256SjkoshyP6_EVDESCR(RET_SEG_RENAMES, 0xD6, 246145256Sjkoshy P6F_ALL_CTRS | P6F_PII | P6F_PIII, 0x00), 247145256SjkoshyP6_EVDESCR(EMON_EST_TRANS, 0x58, P6F_ALL_CTRS | P6F_PM, 0x02), 248145256SjkoshyP6_EVDESCR(EMON_THERMAL_TRIP, 0x59, P6F_ALL_CTRS | P6F_PM, 0x00), 249145256SjkoshyP6_EVDESCR(BR_INST_EXEC, 0x88, P6F_ALL_CTRS | P6F_PM, 0x00), 250145256SjkoshyP6_EVDESCR(BR_MISSP_EXEC, 0x89, P6F_ALL_CTRS | P6F_PM, 0x00), 251145256SjkoshyP6_EVDESCR(BR_BAC_MISSP_EXEC, 0x8A, P6F_ALL_CTRS | P6F_PM, 0x00), 252145256SjkoshyP6_EVDESCR(BR_CND_EXEC, 0x8B, P6F_ALL_CTRS | P6F_PM, 0x00), 253145256SjkoshyP6_EVDESCR(BR_CND_MISSP_EXEC, 0x8C, P6F_ALL_CTRS | P6F_PM, 0x00), 254145256SjkoshyP6_EVDESCR(BR_IND_EXEC, 0x8D, P6F_ALL_CTRS | P6F_PM, 0x00), 255145256SjkoshyP6_EVDESCR(BR_IND_MISSP_EXEC, 0x8E, P6F_ALL_CTRS | P6F_PM, 0x00), 256145256SjkoshyP6_EVDESCR(BR_RET_EXEC, 0x8F, P6F_ALL_CTRS | P6F_PM, 0x00), 257145256SjkoshyP6_EVDESCR(BR_RET_MISSP_EXEC, 0x90, P6F_ALL_CTRS | P6F_PM, 0x00), 258145256SjkoshyP6_EVDESCR(BR_RET_BAC_MISSP_EXEC, 0x91, P6F_ALL_CTRS | P6F_PM, 0x00), 259145256SjkoshyP6_EVDESCR(BR_CALL_EXEC, 0x92, P6F_ALL_CTRS | P6F_PM, 0x00), 260145256SjkoshyP6_EVDESCR(BR_CALL_MISSP_EXEC, 0x93, P6F_ALL_CTRS | P6F_PM, 0x00), 261145256SjkoshyP6_EVDESCR(BR_IND_CALL_EXEC, 0x94, P6F_ALL_CTRS | P6F_PM, 0x00), 262145256SjkoshyP6_EVDESCR(EMON_SIMD_INSTR_RETIRED, 0xCE, P6F_ALL_CTRS | P6F_PM, 0x00), 263145256SjkoshyP6_EVDESCR(EMON_SYNCH_UOPS, 0xD3, P6F_ALL_CTRS | P6F_PM, 0x00), 264145256SjkoshyP6_EVDESCR(EMON_ESP_UOPS, 0xD7, P6F_ALL_CTRS | P6F_PM, 0x00), 265145256SjkoshyP6_EVDESCR(EMON_FUSED_UOPS_RET, 0xDA, P6F_ALL_CTRS | P6F_PM, 0x03), 266145256SjkoshyP6_EVDESCR(EMON_UNFUSION, 0xDB, P6F_ALL_CTRS | P6F_PM, 0x00), 267145256SjkoshyP6_EVDESCR(EMON_PREF_RQSTS_UP, 0xF0, P6F_ALL_CTRS | P6F_PM, 0x00), 268145256SjkoshyP6_EVDESCR(EMON_PREF_RQSTS_DN, 0xD8, P6F_ALL_CTRS | P6F_PM, 0x00), 269145256SjkoshyP6_EVDESCR(EMON_SSE_SSE2_INST_RETIRED, 0xD8, P6F_ALL_CTRS | P6F_PM, 0x03), 270145256SjkoshyP6_EVDESCR(EMON_SSE_SSE2_COMP_INST_RETIRED, 0xD9, P6F_ALL_CTRS | P6F_PM, 0x03) 271145256Sjkoshy 272145256Sjkoshy#undef P6_EVDESCR 273145256Sjkoshy}; 274145256Sjkoshy 275145256Sjkoshy#define P6_NEVENTS (PMC_EV_P6_LAST - PMC_EV_P6_FIRST + 1) 276145256Sjkoshy 277145256Sjkoshystatic const struct p6_event_descr * 278145256Sjkoshyp6_find_event(enum pmc_event ev) 279145256Sjkoshy{ 280145256Sjkoshy int n; 281145256Sjkoshy 282145256Sjkoshy for (n = 0; n < P6_NEVENTS; n++) 283145256Sjkoshy if (p6_events[n].pm_event == ev) 284145256Sjkoshy break; 285145256Sjkoshy if (n == P6_NEVENTS) 286145256Sjkoshy return NULL; 287145256Sjkoshy return &p6_events[n]; 288145256Sjkoshy} 289145256Sjkoshy 290145256Sjkoshy/* 291145256Sjkoshy * Per-CPU data structure for P6 class CPUs 292145256Sjkoshy * 293145256Sjkoshy * [common stuff] 294147191Sjkoshy * [flags for maintaining PMC start/stop state] 295145256Sjkoshy * [3 struct pmc_hw pointers] 296145256Sjkoshy * [3 struct pmc_hw structures] 297145256Sjkoshy */ 298145256Sjkoshy 299145256Sjkoshystruct p6_cpu { 300145256Sjkoshy struct pmc_hw pc_p6pmcs[P6_NPMCS]; 301147989Sjkoshy uint32_t pc_state; 302145256Sjkoshy}; 303145256Sjkoshy 304184802Sjkoshystatic struct p6_cpu **p6_pcpu; 305184802Sjkoshy 306147191Sjkoshy/* 307147191Sjkoshy * If CTR1 is active, we need to keep the 'EN' bit if CTR0 set, 308147191Sjkoshy * with the rest of CTR0 being zero'ed out. 309147191Sjkoshy */ 310147191Sjkoshy#define P6_SYNC_CTR_STATE(PC) do { \ 311147191Sjkoshy uint32_t _config, _enable; \ 312147191Sjkoshy _enable = 0; \ 313147191Sjkoshy if ((PC)->pc_state & 0x02) \ 314147191Sjkoshy _enable |= P6_EVSEL_EN; \ 315147191Sjkoshy if ((PC)->pc_state & 0x01) \ 316147191Sjkoshy _config = rdmsr(P6_MSR_EVSEL0) | \ 317147191Sjkoshy P6_EVSEL_EN; \ 318147191Sjkoshy else \ 319147191Sjkoshy _config = 0; \ 320147191Sjkoshy wrmsr(P6_MSR_EVSEL0, _config | _enable); \ 321147191Sjkoshy } while (0) 322147191Sjkoshy 323147191Sjkoshy#define P6_MARK_STARTED(PC,RI) do { \ 324147191Sjkoshy (PC)->pc_state |= (1 << ((RI)-1)); \ 325147191Sjkoshy } while (0) 326147191Sjkoshy 327147191Sjkoshy#define P6_MARK_STOPPED(PC,RI) do { \ 328147191Sjkoshy (PC)->pc_state &= ~(1<< ((RI)-1)); \ 329147191Sjkoshy } while (0) 330147191Sjkoshy 331145256Sjkoshystatic int 332184802Sjkoshyp6_pcpu_init(struct pmc_mdep *md, int cpu) 333145256Sjkoshy{ 334184802Sjkoshy int first_ri, n; 335184802Sjkoshy struct p6_cpu *p6c; 336184802Sjkoshy struct pmc_cpu *pc; 337145256Sjkoshy struct pmc_hw *phw; 338145256Sjkoshy 339183266Sjkoshy KASSERT(cpu >= 0 && cpu < pmc_cpu_max(), 340145256Sjkoshy ("[p6,%d] bad cpu %d", __LINE__, cpu)); 341145256Sjkoshy 342145256Sjkoshy PMCDBG(MDP,INI,0,"p6-init cpu=%d", cpu); 343145256Sjkoshy 344184802Sjkoshy p6c = malloc(sizeof (struct p6_cpu), M_PMC, M_WAITOK|M_ZERO); 345184802Sjkoshy pc = pmc_pcpu[cpu]; 346145256Sjkoshy 347184802Sjkoshy KASSERT(pc != NULL, ("[p6,%d] cpu %d null per-cpu", __LINE__, cpu)); 348145256Sjkoshy 349184802Sjkoshy phw = p6c->pc_p6pmcs; 350184802Sjkoshy p6_pcpu[cpu] = p6c; 351184802Sjkoshy 352184802Sjkoshy first_ri = md->pmd_classdep[PMC_MDEP_CLASS_INDEX_P6].pcd_ri; 353184802Sjkoshy 354145256Sjkoshy for (n = 0; n < P6_NPMCS; n++, phw++) { 355145256Sjkoshy phw->phw_state = PMC_PHW_FLAG_IS_ENABLED | 356145256Sjkoshy PMC_PHW_CPU_TO_STATE(cpu) | PMC_PHW_INDEX_TO_STATE(n); 357145256Sjkoshy phw->phw_pmc = NULL; 358184802Sjkoshy pc->pc_hwpmcs[n + first_ri] = phw; 359145256Sjkoshy } 360145256Sjkoshy 361184802Sjkoshy return (0); 362145256Sjkoshy} 363145256Sjkoshy 364145256Sjkoshystatic int 365184802Sjkoshyp6_pcpu_fini(struct pmc_mdep *md, int cpu) 366145256Sjkoshy{ 367184802Sjkoshy int first_ri, n; 368184802Sjkoshy struct p6_cpu *p6c; 369184802Sjkoshy struct pmc_cpu *pc; 370145256Sjkoshy 371183266Sjkoshy KASSERT(cpu >= 0 && cpu < pmc_cpu_max(), 372145256Sjkoshy ("[p6,%d] bad cpu %d", __LINE__, cpu)); 373145256Sjkoshy 374145256Sjkoshy PMCDBG(MDP,INI,0,"p6-cleanup cpu=%d", cpu); 375145256Sjkoshy 376184802Sjkoshy p6c = p6_pcpu[cpu]; 377184802Sjkoshy p6_pcpu[cpu] = NULL; 378145256Sjkoshy 379184802Sjkoshy KASSERT(p6c != NULL, ("[p6,%d] null pcpu", __LINE__)); 380145256Sjkoshy 381184802Sjkoshy free(p6c, M_PMC); 382145615Sjkoshy 383184802Sjkoshy first_ri = md->pmd_classdep[PMC_MDEP_CLASS_INDEX_P6].pcd_ri; 384184802Sjkoshy pc = pmc_pcpu[cpu]; 385184802Sjkoshy for (n = 0; n < P6_NPMCS; n++) 386184802Sjkoshy pc->pc_hwpmcs[n + first_ri] = NULL; 387145774Sjkoshy 388184802Sjkoshy return (0); 389145256Sjkoshy} 390145256Sjkoshy 391145256Sjkoshystatic int 392145256Sjkoshyp6_read_pmc(int cpu, int ri, pmc_value_t *v) 393145256Sjkoshy{ 394145256Sjkoshy struct pmc *pm; 395145256Sjkoshy struct p6pmc_descr *pd; 396145256Sjkoshy pmc_value_t tmp; 397145256Sjkoshy 398184802Sjkoshy KASSERT(cpu >= 0 && cpu < pmc_cpu_max(), 399184802Sjkoshy ("[p6,%d] illegal cpu value %d", __LINE__, cpu)); 400184802Sjkoshy KASSERT(ri >= 0 && ri < P6_NPMCS, 401184802Sjkoshy ("[p6,%d] illegal row-index %d", __LINE__, ri)); 402145256Sjkoshy 403184802Sjkoshy pm = p6_pcpu[cpu]->pc_p6pmcs[ri].phw_pmc; 404184802Sjkoshy pd = &p6_pmcdesc[ri]; 405184802Sjkoshy 406145256Sjkoshy KASSERT(pm, 407145256Sjkoshy ("[p6,%d] cpu %d ri %d pmc not configured", __LINE__, cpu, ri)); 408145256Sjkoshy 409147191Sjkoshy tmp = rdmsr(pd->pm_pmc_msr) & P6_PERFCTR_READ_MASK; 410145774Sjkoshy if (PMC_IS_SAMPLING_MODE(PMC_TO_MODE(pm))) 411147191Sjkoshy *v = P6_PERFCTR_VALUE_TO_RELOAD_COUNT(tmp); 412145256Sjkoshy else 413145256Sjkoshy *v = tmp; 414145256Sjkoshy 415145256Sjkoshy PMCDBG(MDP,REA,1, "p6-read cpu=%d ri=%d msr=0x%x -> v=%jx", cpu, ri, 416145256Sjkoshy pd->pm_pmc_msr, *v); 417145256Sjkoshy 418184802Sjkoshy return (0); 419145256Sjkoshy} 420145256Sjkoshy 421145256Sjkoshystatic int 422145256Sjkoshyp6_write_pmc(int cpu, int ri, pmc_value_t v) 423145256Sjkoshy{ 424145256Sjkoshy struct pmc *pm; 425145256Sjkoshy struct p6pmc_descr *pd; 426145256Sjkoshy 427184802Sjkoshy KASSERT(cpu >= 0 && cpu < pmc_cpu_max(), 428184802Sjkoshy ("[p6,%d] illegal cpu value %d", __LINE__, cpu)); 429184802Sjkoshy KASSERT(ri >= 0 && ri < P6_NPMCS, 430184802Sjkoshy ("[p6,%d] illegal row-index %d", __LINE__, ri)); 431145256Sjkoshy 432184802Sjkoshy pm = p6_pcpu[cpu]->pc_p6pmcs[ri].phw_pmc; 433184802Sjkoshy pd = &p6_pmcdesc[ri]; 434184802Sjkoshy 435145256Sjkoshy KASSERT(pm, 436145256Sjkoshy ("[p6,%d] cpu %d ri %d pmc not configured", __LINE__, cpu, ri)); 437145256Sjkoshy 438145256Sjkoshy PMCDBG(MDP,WRI,1, "p6-write cpu=%d ri=%d msr=0x%x v=%jx", cpu, ri, 439145256Sjkoshy pd->pm_pmc_msr, v); 440145256Sjkoshy 441145774Sjkoshy if (PMC_IS_SAMPLING_MODE(PMC_TO_MODE(pm))) 442147191Sjkoshy v = P6_RELOAD_COUNT_TO_PERFCTR_VALUE(v); 443145256Sjkoshy 444147191Sjkoshy wrmsr(pd->pm_pmc_msr, v & P6_PERFCTR_WRITE_MASK); 445145256Sjkoshy 446184802Sjkoshy return (0); 447145256Sjkoshy} 448145256Sjkoshy 449145256Sjkoshystatic int 450145256Sjkoshyp6_config_pmc(int cpu, int ri, struct pmc *pm) 451145256Sjkoshy{ 452184802Sjkoshy KASSERT(cpu >= 0 && cpu < pmc_cpu_max(), 453184802Sjkoshy ("[p6,%d] illegal CPU %d", __LINE__, cpu)); 454145256Sjkoshy 455184802Sjkoshy KASSERT(ri >= 0 && ri < P6_NPMCS, 456184802Sjkoshy ("[p6,%d] illegal row-index %d", __LINE__, ri)); 457184802Sjkoshy 458145256Sjkoshy PMCDBG(MDP,CFG,1, "p6-config cpu=%d ri=%d pm=%p", cpu, ri, pm); 459145256Sjkoshy 460184802Sjkoshy KASSERT(p6_pcpu[cpu] != NULL, ("[p6,%d] null per-cpu %d", __LINE__, 461184802Sjkoshy cpu)); 462145256Sjkoshy 463184802Sjkoshy p6_pcpu[cpu]->pc_p6pmcs[ri].phw_pmc = pm; 464184802Sjkoshy 465184802Sjkoshy return (0); 466145256Sjkoshy} 467145256Sjkoshy 468145256Sjkoshy/* 469145774Sjkoshy * Retrieve a configured PMC pointer from hardware state. 470145774Sjkoshy */ 471145774Sjkoshy 472145774Sjkoshystatic int 473145774Sjkoshyp6_get_config(int cpu, int ri, struct pmc **ppm) 474145774Sjkoshy{ 475145774Sjkoshy 476184802Sjkoshy KASSERT(cpu >= 0 && cpu < pmc_cpu_max(), 477184802Sjkoshy ("[p6,%d] illegal CPU %d", __LINE__, cpu)); 478184802Sjkoshy KASSERT(ri >= 0 && ri < P6_NPMCS, 479184802Sjkoshy ("[p6,%d] illegal row-index %d", __LINE__, ri)); 480184802Sjkoshy 481184802Sjkoshy *ppm = p6_pcpu[cpu]->pc_p6pmcs[ri].phw_pmc; 482184802Sjkoshy 483184802Sjkoshy return (0); 484145774Sjkoshy} 485145774Sjkoshy 486145774Sjkoshy 487145774Sjkoshy/* 488145256Sjkoshy * A pmc may be allocated to a given row index if: 489145256Sjkoshy * - the event is valid for this CPU 490145256Sjkoshy * - the event is valid for this counter index 491145256Sjkoshy */ 492145256Sjkoshy 493145256Sjkoshystatic int 494145256Sjkoshyp6_allocate_pmc(int cpu, int ri, struct pmc *pm, 495145256Sjkoshy const struct pmc_op_pmcallocate *a) 496145256Sjkoshy{ 497145256Sjkoshy uint32_t allowed_unitmask, caps, config, unitmask; 498145256Sjkoshy const struct p6pmc_descr *pd; 499145256Sjkoshy const struct p6_event_descr *pevent; 500145256Sjkoshy enum pmc_event ev; 501145256Sjkoshy 502145256Sjkoshy (void) cpu; 503145256Sjkoshy 504183266Sjkoshy KASSERT(cpu >= 0 && cpu < pmc_cpu_max(), 505184802Sjkoshy ("[p6,%d] illegal CPU %d", __LINE__, cpu)); 506145256Sjkoshy KASSERT(ri >= 0 && ri < P6_NPMCS, 507184802Sjkoshy ("[p6,%d] illegal row-index value %d", __LINE__, ri)); 508145256Sjkoshy 509145256Sjkoshy pd = &p6_pmcdesc[ri]; 510145256Sjkoshy 511145256Sjkoshy PMCDBG(MDP,ALL,1, "p6-allocate ri=%d class=%d pmccaps=0x%x " 512145256Sjkoshy "reqcaps=0x%x", ri, pd->pm_descr.pd_class, pd->pm_descr.pd_caps, 513145256Sjkoshy pm->pm_caps); 514145256Sjkoshy 515145256Sjkoshy /* check class */ 516145774Sjkoshy if (pd->pm_descr.pd_class != a->pm_class) 517184802Sjkoshy return (EINVAL); 518145256Sjkoshy 519145256Sjkoshy /* check requested capabilities */ 520145256Sjkoshy caps = a->pm_caps; 521145256Sjkoshy if ((pd->pm_descr.pd_caps & caps) != caps) 522184802Sjkoshy return (EPERM); 523145256Sjkoshy 524145256Sjkoshy ev = pm->pm_event; 525145256Sjkoshy 526145256Sjkoshy if (ev < PMC_EV_P6_FIRST || ev > PMC_EV_P6_LAST) 527184802Sjkoshy return (EINVAL); 528145256Sjkoshy 529145256Sjkoshy if ((pevent = p6_find_event(ev)) == NULL) 530184802Sjkoshy return (ESRCH); 531145256Sjkoshy 532145256Sjkoshy if (!P6_EVENT_VALID_FOR_CPU(pevent, p6_cputype) || 533145256Sjkoshy !P6_EVENT_VALID_FOR_CTR(pevent, (ri-1))) 534184802Sjkoshy return (EINVAL); 535145256Sjkoshy 536145256Sjkoshy /* For certain events, Pentium M differs from the stock P6 */ 537145256Sjkoshy allowed_unitmask = 0; 538145256Sjkoshy if (p6_cputype == PMC_CPU_INTEL_PM) { 539145256Sjkoshy if (ev == PMC_EV_P6_L2_LD || ev == PMC_EV_P6_L2_LINES_IN || 540145256Sjkoshy ev == PMC_EV_P6_L2_LINES_OUT) 541145256Sjkoshy allowed_unitmask = P6_EVSEL_TO_UMASK(0x3F); 542145256Sjkoshy else if (ev == PMC_EV_P6_L2_M_LINES_OUTM) 543145256Sjkoshy allowed_unitmask = P6_EVSEL_TO_UMASK(0x30); 544145256Sjkoshy } else 545145256Sjkoshy allowed_unitmask = P6_EVSEL_TO_UMASK(pevent->pm_unitmask); 546145256Sjkoshy 547147191Sjkoshy unitmask = a->pm_md.pm_ppro.pm_ppro_config & P6_EVSEL_UMASK_MASK; 548145256Sjkoshy if (unitmask & ~allowed_unitmask) /* disallow reserved bits */ 549184802Sjkoshy return (EINVAL); 550145256Sjkoshy 551145256Sjkoshy if (ev == PMC_EV_P6_MMX_UOPS_EXEC) /* hardcoded mask */ 552145256Sjkoshy unitmask = P6_EVSEL_TO_UMASK(0x0F); 553145256Sjkoshy 554145256Sjkoshy config = 0; 555145256Sjkoshy 556145256Sjkoshy config |= P6_EVSEL_EVENT_SELECT(pevent->pm_evsel); 557145256Sjkoshy 558145256Sjkoshy if (unitmask & (caps & PMC_CAP_QUALIFIER)) 559145256Sjkoshy config |= unitmask; 560145256Sjkoshy 561145256Sjkoshy if (caps & PMC_CAP_THRESHOLD) 562147191Sjkoshy config |= a->pm_md.pm_ppro.pm_ppro_config & 563147191Sjkoshy P6_EVSEL_CMASK_MASK; 564145256Sjkoshy 565145256Sjkoshy /* set at least one of the 'usr' or 'os' caps */ 566145256Sjkoshy if (caps & PMC_CAP_USER) 567145256Sjkoshy config |= P6_EVSEL_USR; 568145256Sjkoshy if (caps & PMC_CAP_SYSTEM) 569145256Sjkoshy config |= P6_EVSEL_OS; 570145256Sjkoshy if ((caps & (PMC_CAP_USER|PMC_CAP_SYSTEM)) == 0) 571145256Sjkoshy config |= (P6_EVSEL_USR|P6_EVSEL_OS); 572145256Sjkoshy 573145256Sjkoshy if (caps & PMC_CAP_EDGE) 574145256Sjkoshy config |= P6_EVSEL_E; 575145256Sjkoshy if (caps & PMC_CAP_INVERT) 576145256Sjkoshy config |= P6_EVSEL_INV; 577145256Sjkoshy if (caps & PMC_CAP_INTERRUPT) 578145256Sjkoshy config |= P6_EVSEL_INT; 579145256Sjkoshy 580147191Sjkoshy pm->pm_md.pm_ppro.pm_ppro_evsel = config; 581145256Sjkoshy 582145256Sjkoshy PMCDBG(MDP,ALL,2, "p6-allocate config=0x%x", config); 583145256Sjkoshy 584184802Sjkoshy return (0); 585145256Sjkoshy} 586145256Sjkoshy 587145256Sjkoshystatic int 588145256Sjkoshyp6_release_pmc(int cpu, int ri, struct pmc *pm) 589145256Sjkoshy{ 590145256Sjkoshy (void) pm; 591145256Sjkoshy 592145256Sjkoshy PMCDBG(MDP,REL,1, "p6-release cpu=%d ri=%d pm=%p", cpu, ri, pm); 593145256Sjkoshy 594183266Sjkoshy KASSERT(cpu >= 0 && cpu < pmc_cpu_max(), 595145256Sjkoshy ("[p6,%d] illegal CPU value %d", __LINE__, cpu)); 596145256Sjkoshy KASSERT(ri >= 0 && ri < P6_NPMCS, 597145256Sjkoshy ("[p6,%d] illegal row-index %d", __LINE__, ri)); 598145256Sjkoshy 599184802Sjkoshy KASSERT(p6_pcpu[cpu]->pc_p6pmcs[ri].phw_pmc == NULL, 600184802Sjkoshy ("[p6,%d] PHW pmc non-NULL", __LINE__)); 601145256Sjkoshy 602184802Sjkoshy return (0); 603145256Sjkoshy} 604145256Sjkoshy 605145256Sjkoshystatic int 606145256Sjkoshyp6_start_pmc(int cpu, int ri) 607145256Sjkoshy{ 608145256Sjkoshy uint32_t config; 609145256Sjkoshy struct pmc *pm; 610147191Sjkoshy struct p6_cpu *pc; 611145256Sjkoshy const struct p6pmc_descr *pd; 612145256Sjkoshy 613183266Sjkoshy KASSERT(cpu >= 0 && cpu < pmc_cpu_max(), 614145256Sjkoshy ("[p6,%d] illegal CPU value %d", __LINE__, cpu)); 615145256Sjkoshy KASSERT(ri >= 0 && ri < P6_NPMCS, 616145256Sjkoshy ("[p6,%d] illegal row-index %d", __LINE__, ri)); 617145256Sjkoshy 618184802Sjkoshy pc = p6_pcpu[cpu]; 619184802Sjkoshy pm = pc->pc_p6pmcs[ri].phw_pmc; 620184802Sjkoshy pd = &p6_pmcdesc[ri]; 621145256Sjkoshy 622145256Sjkoshy KASSERT(pm, 623145256Sjkoshy ("[p6,%d] starting cpu%d,ri%d with no pmc configured", 624145256Sjkoshy __LINE__, cpu, ri)); 625145256Sjkoshy 626145256Sjkoshy PMCDBG(MDP,STA,1, "p6-start cpu=%d ri=%d", cpu, ri); 627145256Sjkoshy 628147191Sjkoshy config = pm->pm_md.pm_ppro.pm_ppro_evsel; 629145256Sjkoshy 630145256Sjkoshy PMCDBG(MDP,STA,2, "p6-start/2 cpu=%d ri=%d evselmsr=0x%x config=0x%x", 631145256Sjkoshy cpu, ri, pd->pm_evsel_msr, config); 632145256Sjkoshy 633147191Sjkoshy P6_MARK_STARTED(pc, ri); 634147191Sjkoshy wrmsr(pd->pm_evsel_msr, config); 635147191Sjkoshy 636147191Sjkoshy P6_SYNC_CTR_STATE(pc); 637147191Sjkoshy 638184802Sjkoshy return (0); 639145256Sjkoshy} 640145256Sjkoshy 641145256Sjkoshystatic int 642145256Sjkoshyp6_stop_pmc(int cpu, int ri) 643145256Sjkoshy{ 644145256Sjkoshy struct pmc *pm; 645147191Sjkoshy struct p6_cpu *pc; 646145256Sjkoshy struct p6pmc_descr *pd; 647145256Sjkoshy 648183266Sjkoshy KASSERT(cpu >= 0 && cpu < pmc_cpu_max(), 649145256Sjkoshy ("[p6,%d] illegal cpu value %d", __LINE__, cpu)); 650145256Sjkoshy KASSERT(ri >= 0 && ri < P6_NPMCS, 651145256Sjkoshy ("[p6,%d] illegal row index %d", __LINE__, ri)); 652145256Sjkoshy 653184802Sjkoshy pc = p6_pcpu[cpu]; 654184802Sjkoshy pm = pc->pc_p6pmcs[ri].phw_pmc; 655184802Sjkoshy pd = &p6_pmcdesc[ri]; 656145256Sjkoshy 657145256Sjkoshy KASSERT(pm, 658145256Sjkoshy ("[p6,%d] cpu%d ri%d no configured PMC to stop", __LINE__, 659145256Sjkoshy cpu, ri)); 660145256Sjkoshy 661145256Sjkoshy PMCDBG(MDP,STO,1, "p6-stop cpu=%d ri=%d", cpu, ri); 662145256Sjkoshy 663147191Sjkoshy wrmsr(pd->pm_evsel_msr, 0); /* stop hw */ 664147191Sjkoshy P6_MARK_STOPPED(pc, ri); /* update software state */ 665145256Sjkoshy 666147191Sjkoshy P6_SYNC_CTR_STATE(pc); /* restart CTR1 if need be */ 667145256Sjkoshy 668147191Sjkoshy PMCDBG(MDP,STO,2, "p6-stop/2 cpu=%d ri=%d", cpu, ri); 669184802Sjkoshy 670184802Sjkoshy return (0); 671145256Sjkoshy} 672145256Sjkoshy 673145256Sjkoshystatic int 674174395Sjkoshyp6_intr(int cpu, struct trapframe *tf) 675145256Sjkoshy{ 676184802Sjkoshy int error, retval, ri; 677147191Sjkoshy uint32_t perf0cfg; 678147191Sjkoshy struct pmc *pm; 679147191Sjkoshy struct p6_cpu *pc; 680147191Sjkoshy pmc_value_t v; 681147191Sjkoshy 682183266Sjkoshy KASSERT(cpu >= 0 && cpu < pmc_cpu_max(), 683147191Sjkoshy ("[p6,%d] CPU %d out of range", __LINE__, cpu)); 684147191Sjkoshy 685147191Sjkoshy retval = 0; 686184802Sjkoshy pc = p6_pcpu[cpu]; 687147191Sjkoshy 688147191Sjkoshy /* stop both PMCs */ 689147191Sjkoshy perf0cfg = rdmsr(P6_MSR_EVSEL0); 690147191Sjkoshy wrmsr(P6_MSR_EVSEL0, perf0cfg & ~P6_EVSEL_EN); 691147191Sjkoshy 692184802Sjkoshy for (ri = 0; ri < P6_NPMCS; ri++) { 693147191Sjkoshy 694184802Sjkoshy if ((pm = pc->pc_p6pmcs[ri].phw_pmc) == NULL || 695147191Sjkoshy !PMC_IS_SAMPLING_MODE(PMC_TO_MODE(pm))) { 696147191Sjkoshy continue; 697147191Sjkoshy } 698147191Sjkoshy 699185555Sjkoshy if (!P6_PMC_HAS_OVERFLOWED(ri)) 700185555Sjkoshy continue; 701185555Sjkoshy 702147191Sjkoshy retval = 1; 703147191Sjkoshy 704185555Sjkoshy if (pm->pm_state != PMC_STATE_RUNNING) 705185555Sjkoshy continue; 706185555Sjkoshy 707236238Sfabient error = pmc_process_interrupt(cpu, PMC_HR, pm, tf, 708174395Sjkoshy TRAPF_USERMODE(tf)); 709147191Sjkoshy if (error) 710147191Sjkoshy P6_MARK_STOPPED(pc,ri); 711147191Sjkoshy 712147191Sjkoshy /* reload sampling count */ 713147191Sjkoshy v = pm->pm_sc.pm_reloadcount; 714184802Sjkoshy wrmsr(P6_MSR_PERFCTR0 + ri, 715147191Sjkoshy P6_RELOAD_COUNT_TO_PERFCTR_VALUE(v)); 716147191Sjkoshy 717147191Sjkoshy } 718147191Sjkoshy 719147191Sjkoshy /* 720147191Sjkoshy * On P6 processors, the LAPIC needs to have its PMC interrupt 721147191Sjkoshy * unmasked after a PMC interrupt. 722147191Sjkoshy */ 723147191Sjkoshy if (retval) 724196224Sjhb lapic_reenable_pmc(); 725147191Sjkoshy 726147867Sjkoshy atomic_add_int(retval ? &pmc_stats.pm_intr_processed : 727147867Sjkoshy &pmc_stats.pm_intr_ignored, 1); 728147867Sjkoshy 729147191Sjkoshy /* restart counters that can be restarted */ 730147191Sjkoshy P6_SYNC_CTR_STATE(pc); 731147191Sjkoshy 732184802Sjkoshy return (retval); 733145256Sjkoshy} 734145256Sjkoshy 735145256Sjkoshystatic int 736145256Sjkoshyp6_describe(int cpu, int ri, struct pmc_info *pi, 737145256Sjkoshy struct pmc **ppmc) 738145256Sjkoshy{ 739145256Sjkoshy int error; 740145256Sjkoshy size_t copied; 741145256Sjkoshy struct pmc_hw *phw; 742145256Sjkoshy struct p6pmc_descr *pd; 743145256Sjkoshy 744184802Sjkoshy KASSERT(cpu >= 0 && cpu < pmc_cpu_max(), 745184802Sjkoshy ("[p6,%d] illegal CPU %d", __LINE__, cpu)); 746184802Sjkoshy KASSERT(ri >= 0 && ri < P6_NPMCS, 747184802Sjkoshy ("[p6,%d] row-index %d out of range", __LINE__, ri)); 748184802Sjkoshy 749145256Sjkoshy phw = pmc_pcpu[cpu]->pc_hwpmcs[ri]; 750145256Sjkoshy pd = &p6_pmcdesc[ri]; 751145256Sjkoshy 752184802Sjkoshy KASSERT(phw == &p6_pcpu[cpu]->pc_p6pmcs[ri], 753184802Sjkoshy ("[p6,%d] phw mismatch", __LINE__)); 754184802Sjkoshy 755145256Sjkoshy if ((error = copystr(pd->pm_descr.pd_name, pi->pm_name, 756145256Sjkoshy PMC_NAME_MAX, &copied)) != 0) 757184802Sjkoshy return (error); 758145256Sjkoshy 759145256Sjkoshy pi->pm_class = pd->pm_descr.pd_class; 760145256Sjkoshy 761145256Sjkoshy if (phw->phw_state & PMC_PHW_FLAG_IS_ENABLED) { 762145256Sjkoshy pi->pm_enabled = TRUE; 763145256Sjkoshy *ppmc = phw->phw_pmc; 764145256Sjkoshy } else { 765145256Sjkoshy pi->pm_enabled = FALSE; 766145256Sjkoshy *ppmc = NULL; 767145256Sjkoshy } 768145256Sjkoshy 769184802Sjkoshy return (0); 770145256Sjkoshy} 771145256Sjkoshy 772145256Sjkoshystatic int 773145256Sjkoshyp6_get_msr(int ri, uint32_t *msr) 774145256Sjkoshy{ 775145256Sjkoshy KASSERT(ri >= 0 && ri < P6_NPMCS, 776145256Sjkoshy ("[p6,%d ri %d out of range", __LINE__, ri)); 777145256Sjkoshy 778145774Sjkoshy *msr = p6_pmcdesc[ri].pm_pmc_msr - P6_MSR_PERFCTR0; 779184802Sjkoshy 780184802Sjkoshy return (0); 781145256Sjkoshy} 782145256Sjkoshy 783145256Sjkoshyint 784184802Sjkoshypmc_p6_initialize(struct pmc_mdep *md, int ncpus) 785145256Sjkoshy{ 786184802Sjkoshy struct pmc_classdep *pcd; 787184802Sjkoshy 788185341Sjkim KASSERT(cpu_vendor_id == CPU_VENDOR_INTEL, 789145256Sjkoshy ("[p6,%d] Initializing non-intel processor", __LINE__)); 790145256Sjkoshy 791145256Sjkoshy PMCDBG(MDP,INI,1, "%s", "p6-initialize"); 792145256Sjkoshy 793184802Sjkoshy /* Allocate space for pointers to per-cpu descriptors. */ 794184802Sjkoshy p6_pcpu = malloc(sizeof(struct p6_cpu **) * ncpus, M_PMC, 795184802Sjkoshy M_ZERO|M_WAITOK); 796145256Sjkoshy 797184802Sjkoshy /* Fill in the class dependent descriptor. */ 798184802Sjkoshy pcd = &md->pmd_classdep[PMC_MDEP_CLASS_INDEX_P6]; 799184802Sjkoshy 800184802Sjkoshy switch (md->pmd_cputype) { 801184802Sjkoshy 802145256Sjkoshy /* 803145256Sjkoshy * P6 Family Processors 804145256Sjkoshy */ 805145256Sjkoshy case PMC_CPU_INTEL_P6: 806145256Sjkoshy case PMC_CPU_INTEL_CL: 807145256Sjkoshy case PMC_CPU_INTEL_PII: 808145256Sjkoshy case PMC_CPU_INTEL_PIII: 809145256Sjkoshy case PMC_CPU_INTEL_PM: 810145256Sjkoshy 811184802Sjkoshy p6_cputype = md->pmd_cputype; 812145256Sjkoshy 813184802Sjkoshy pcd->pcd_caps = P6_PMC_CAPS; 814184802Sjkoshy pcd->pcd_class = PMC_CLASS_P6; 815184802Sjkoshy pcd->pcd_num = P6_NPMCS; 816184802Sjkoshy pcd->pcd_ri = md->pmd_npmc; 817184802Sjkoshy pcd->pcd_width = 40; 818145256Sjkoshy 819184802Sjkoshy pcd->pcd_allocate_pmc = p6_allocate_pmc; 820184802Sjkoshy pcd->pcd_config_pmc = p6_config_pmc; 821184802Sjkoshy pcd->pcd_describe = p6_describe; 822184802Sjkoshy pcd->pcd_get_config = p6_get_config; 823184802Sjkoshy pcd->pcd_get_msr = p6_get_msr; 824184802Sjkoshy pcd->pcd_pcpu_fini = p6_pcpu_fini; 825184802Sjkoshy pcd->pcd_pcpu_init = p6_pcpu_init; 826184802Sjkoshy pcd->pcd_read_pmc = p6_read_pmc; 827184802Sjkoshy pcd->pcd_release_pmc = p6_release_pmc; 828184802Sjkoshy pcd->pcd_start_pmc = p6_start_pmc; 829184802Sjkoshy pcd->pcd_stop_pmc = p6_stop_pmc; 830184802Sjkoshy pcd->pcd_write_pmc = p6_write_pmc; 831145256Sjkoshy 832184802Sjkoshy md->pmd_pcpu_fini = NULL; 833184802Sjkoshy md->pmd_pcpu_init = NULL; 834184802Sjkoshy md->pmd_intr = p6_intr; 835184802Sjkoshy 836184802Sjkoshy md->pmd_npmc += P6_NPMCS; 837184802Sjkoshy 838145256Sjkoshy break; 839184802Sjkoshy 840145256Sjkoshy default: 841145256Sjkoshy KASSERT(0,("[p6,%d] Unknown CPU type", __LINE__)); 842145256Sjkoshy return ENOSYS; 843145256Sjkoshy } 844145256Sjkoshy 845184802Sjkoshy return (0); 846145256Sjkoshy} 847184802Sjkoshy 848184802Sjkoshyvoid 849184802Sjkoshypmc_p6_finalize(struct pmc_mdep *md) 850184802Sjkoshy{ 851184802Sjkoshy#if defined(INVARIANTS) 852184802Sjkoshy int i, ncpus; 853184802Sjkoshy#endif 854184802Sjkoshy 855184802Sjkoshy KASSERT(p6_pcpu != NULL, ("[p6,%d] NULL p6_pcpu", __LINE__)); 856184802Sjkoshy 857184802Sjkoshy#if defined(INVARIANTS) 858184802Sjkoshy ncpus = pmc_cpu_max(); 859184802Sjkoshy for (i = 0; i < ncpus; i++) 860184802Sjkoshy KASSERT(p6_pcpu[i] == NULL, ("[p6,%d] non-null pcpu %d", 861184802Sjkoshy __LINE__, i)); 862184802Sjkoshy#endif 863184802Sjkoshy 864184802Sjkoshy free(p6_pcpu, M_PMC); 865184802Sjkoshy p6_pcpu = NULL; 866184802Sjkoshy} 867