xref: /illumos-gate/usr/src/uts/intel/pcbe/core_pcbe.c (revision a1e3874e)
17bd4a6f5Skk /*
27bd4a6f5Skk  * CDDL HEADER START
37bd4a6f5Skk  *
47bd4a6f5Skk  * The contents of this file are subject to the terms of the
57bd4a6f5Skk  * Common Development and Distribution License (the "License").
67bd4a6f5Skk  * You may not use this file except in compliance with the License.
77bd4a6f5Skk  *
87bd4a6f5Skk  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
97bd4a6f5Skk  * or http://www.opensolaris.org/os/licensing.
107bd4a6f5Skk  * See the License for the specific language governing permissions
117bd4a6f5Skk  * and limitations under the License.
127bd4a6f5Skk  *
137bd4a6f5Skk  * When distributing Covered Code, include this CDDL HEADER in each
147bd4a6f5Skk  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
157bd4a6f5Skk  * If applicable, add the following below this CDDL HEADER, with the
167bd4a6f5Skk  * fields enclosed by brackets "[]" replaced with your own identifying
177bd4a6f5Skk  * information: Portions Copyright [yyyy] [name of copyright owner]
187bd4a6f5Skk  *
197bd4a6f5Skk  * CDDL HEADER END
207bd4a6f5Skk  */
217bd4a6f5Skk /*
22ac13ce24SAdrian Frost  * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
23a00b240dSJohn Levon  * Copyright 2019 Joyent, Inc.
247bd4a6f5Skk  */
257bd4a6f5Skk 
26560e0ee2SKuriakose Kuruvilla /*
27560e0ee2SKuriakose Kuruvilla  * This file contains preset event names from the Performance Application
28560e0ee2SKuriakose Kuruvilla  * Programming Interface v3.5 which included the following notice:
29560e0ee2SKuriakose Kuruvilla  *
30560e0ee2SKuriakose Kuruvilla  *                             Copyright (c) 2005,6
31560e0ee2SKuriakose Kuruvilla  *                           Innovative Computing Labs
32560e0ee2SKuriakose Kuruvilla  *                         Computer Science Department,
33560e0ee2SKuriakose Kuruvilla  *                            University of Tennessee,
34560e0ee2SKuriakose Kuruvilla  *                                 Knoxville, TN.
35560e0ee2SKuriakose Kuruvilla  *                              All Rights Reserved.
36560e0ee2SKuriakose Kuruvilla  *
37560e0ee2SKuriakose Kuruvilla  *
38560e0ee2SKuriakose Kuruvilla  * Redistribution and use in source and binary forms, with or without
39560e0ee2SKuriakose Kuruvilla  * modification, are permitted provided that the following conditions are met:
40560e0ee2SKuriakose Kuruvilla  *
41560e0ee2SKuriakose Kuruvilla  *    * Redistributions of source code must retain the above copyright notice,
42560e0ee2SKuriakose Kuruvilla  *      this list of conditions and the following disclaimer.
43560e0ee2SKuriakose Kuruvilla  *    * Redistributions in binary form must reproduce the above copyright
44560e0ee2SKuriakose Kuruvilla  *      notice, this list of conditions and the following disclaimer in the
45560e0ee2SKuriakose Kuruvilla  *      documentation and/or other materials provided with the distribution.
46560e0ee2SKuriakose Kuruvilla  *    * Neither the name of the University of Tennessee nor the names of its
47560e0ee2SKuriakose Kuruvilla  *      contributors may be used to endorse or promote products derived from
48560e0ee2SKuriakose Kuruvilla  *      this software without specific prior written permission.
49560e0ee2SKuriakose Kuruvilla  *
50560e0ee2SKuriakose Kuruvilla  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
51560e0ee2SKuriakose Kuruvilla  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
52560e0ee2SKuriakose Kuruvilla  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
53560e0ee2SKuriakose Kuruvilla  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
54560e0ee2SKuriakose Kuruvilla  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
55560e0ee2SKuriakose Kuruvilla  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
56560e0ee2SKuriakose Kuruvilla  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
57560e0ee2SKuriakose Kuruvilla  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
58560e0ee2SKuriakose Kuruvilla  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
59560e0ee2SKuriakose Kuruvilla  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
60560e0ee2SKuriakose Kuruvilla  * POSSIBILITY OF SUCH DAMAGE.
61560e0ee2SKuriakose Kuruvilla  *
62560e0ee2SKuriakose Kuruvilla  *
63560e0ee2SKuriakose Kuruvilla  * This open source software license conforms to the BSD License template.
64560e0ee2SKuriakose Kuruvilla  */
65560e0ee2SKuriakose Kuruvilla 
66560e0ee2SKuriakose Kuruvilla 
677bd4a6f5Skk /*
68afb806e6SKuriakose Kuruvilla  * Performance Counter Back-End for Intel processors supporting Architectural
69afb806e6SKuriakose Kuruvilla  * Performance Monitoring.
707bd4a6f5Skk  */
717bd4a6f5Skk 
727bd4a6f5Skk #include <sys/cpuvar.h>
737bd4a6f5Skk #include <sys/param.h>
747bd4a6f5Skk #include <sys/cpc_impl.h>
757bd4a6f5Skk #include <sys/cpc_pcbe.h>
767bd4a6f5Skk #include <sys/modctl.h>
777bd4a6f5Skk #include <sys/inttypes.h>
787bd4a6f5Skk #include <sys/systm.h>
797bd4a6f5Skk #include <sys/cmn_err.h>
807bd4a6f5Skk #include <sys/x86_archext.h>
817bd4a6f5Skk #include <sys/sdt.h>
827bd4a6f5Skk #include <sys/archsystm.h>
837bd4a6f5Skk #include <sys/privregs.h>
847bd4a6f5Skk #include <sys/ddi.h>
857bd4a6f5Skk #include <sys/sunddi.h>
867bd4a6f5Skk #include <sys/cred.h>
877bd4a6f5Skk #include <sys/policy.h>
887bd4a6f5Skk 
897e3dbbacSRobert Mustacchi #include "core_pcbe_table.h"
907e3dbbacSRobert Mustacchi #include <core_pcbe_cpcgen.h>
917e3dbbacSRobert Mustacchi 
927bd4a6f5Skk static int core_pcbe_init(void);
937bd4a6f5Skk static uint_t core_pcbe_ncounters(void);
947bd4a6f5Skk static const char *core_pcbe_impl_name(void);
957bd4a6f5Skk static const char *core_pcbe_cpuref(void);
967bd4a6f5Skk static char *core_pcbe_list_events(uint_t picnum);
977bd4a6f5Skk static char *core_pcbe_list_attrs(void);
987bd4a6f5Skk static uint64_t core_pcbe_event_coverage(char *event);
997bd4a6f5Skk static uint64_t core_pcbe_overflow_bitmap(void);
1007bd4a6f5Skk static int core_pcbe_configure(uint_t picnum, char *event, uint64_t preset,
1017bd4a6f5Skk     uint32_t flags, uint_t nattrs, kcpc_attr_t *attrs, void **data,
1027bd4a6f5Skk     void *token);
1037bd4a6f5Skk static void core_pcbe_program(void *token);
1047bd4a6f5Skk static void core_pcbe_allstop(void);
1057bd4a6f5Skk static void core_pcbe_sample(void *token);
1067bd4a6f5Skk static void core_pcbe_free(void *config);
1077bd4a6f5Skk 
1087bd4a6f5Skk #define	FALSE	0
1097bd4a6f5Skk #define	TRUE	1
1107bd4a6f5Skk 
1117bd4a6f5Skk /* Counter Type */
1127bd4a6f5Skk #define	CORE_GPC	0	/* General-Purpose Counter (GPC) */
1137bd4a6f5Skk #define	CORE_FFC	1	/* Fixed-Function Counter (FFC) */
1147bd4a6f5Skk 
1157bd4a6f5Skk /* MSR Addresses */
1167bd4a6f5Skk #define	GPC_BASE_PMC		0x00c1	/* First GPC */
1177bd4a6f5Skk #define	GPC_BASE_PES		0x0186	/* First GPC Event Select register */
1187bd4a6f5Skk #define	FFC_BASE_PMC		0x0309	/* First FFC */
1197bd4a6f5Skk #define	PERF_FIXED_CTR_CTRL	0x038d	/* Used to enable/disable FFCs */
1207bd4a6f5Skk #define	PERF_GLOBAL_STATUS	0x038e	/* Overflow status register */
1217bd4a6f5Skk #define	PERF_GLOBAL_CTRL	0x038f	/* Used to enable/disable counting */
1227bd4a6f5Skk #define	PERF_GLOBAL_OVF_CTRL	0x0390	/* Used to clear overflow status */
1237bd4a6f5Skk 
1247bd4a6f5Skk /*
1257bd4a6f5Skk  * Processor Event Select register fields
1267bd4a6f5Skk  */
1277bd4a6f5Skk #define	CORE_USR	(1ULL << 16)	/* Count while not in ring 0 */
1287bd4a6f5Skk #define	CORE_OS		(1ULL << 17)	/* Count while in ring 0 */
1297bd4a6f5Skk #define	CORE_EDGE	(1ULL << 18)	/* Enable edge detection */
1307bd4a6f5Skk #define	CORE_PC		(1ULL << 19)	/* Enable pin control */
1317bd4a6f5Skk #define	CORE_INT	(1ULL << 20)	/* Enable interrupt on overflow */
1327bd4a6f5Skk #define	CORE_EN		(1ULL << 22)	/* Enable counting */
1337bd4a6f5Skk #define	CORE_INV	(1ULL << 23)	/* Invert the CMASK */
134a18ddb3cSKuriakose Kuruvilla #define	CORE_ANYTHR	(1ULL << 21)	/* Count event for any thread on core */
1357bd4a6f5Skk 
1367bd4a6f5Skk #define	CORE_UMASK_SHIFT	8
1377bd4a6f5Skk #define	CORE_UMASK_MASK		0xffu
1387bd4a6f5Skk #define	CORE_CMASK_SHIFT	24
1397bd4a6f5Skk #define	CORE_CMASK_MASK		0xffu
1407bd4a6f5Skk 
1417bd4a6f5Skk /*
1427bd4a6f5Skk  * Fixed-function counter attributes
1437bd4a6f5Skk  */
1447bd4a6f5Skk #define	CORE_FFC_OS_EN	(1ULL << 0)	/* Count while not in ring 0 */
1457bd4a6f5Skk #define	CORE_FFC_USR_EN	(1ULL << 1)	/* Count while in ring 1 */
146a18ddb3cSKuriakose Kuruvilla #define	CORE_FFC_ANYTHR	(1ULL << 2)	/* Count event for any thread on core */
1477bd4a6f5Skk #define	CORE_FFC_PMI	(1ULL << 3)	/* Enable interrupt on overflow */
1487bd4a6f5Skk 
1497bd4a6f5Skk /*
1507bd4a6f5Skk  * Number of bits for specifying each FFC's attributes in the control register
1517bd4a6f5Skk  */
1527bd4a6f5Skk #define	CORE_FFC_ATTR_SIZE	4
1537bd4a6f5Skk 
1547bd4a6f5Skk /*
1557bd4a6f5Skk  * CondChgd and OvfBuffer fields of global status and overflow control registers
1567bd4a6f5Skk  */
1577bd4a6f5Skk #define	CONDCHGD	(1ULL << 63)
1587bd4a6f5Skk #define	OVFBUFFER	(1ULL << 62)
1597bd4a6f5Skk #define	MASK_CONDCHGD_OVFBUFFER	(CONDCHGD | OVFBUFFER)
1607bd4a6f5Skk 
1617bd4a6f5Skk #define	ALL_STOPPED	0ULL
1627bd4a6f5Skk 
1637bd4a6f5Skk #define	BITMASK_XBITS(x)	((1ull << (x)) - 1ull)
1647bd4a6f5Skk 
1657bd4a6f5Skk /*
1667bd4a6f5Skk  * Only the lower 32-bits can be written to in the general-purpose
1677bd4a6f5Skk  * counters.  The higher bits are extended from bit 31; all ones if
1687bd4a6f5Skk  * bit 31 is one and all zeros otherwise.
1697bd4a6f5Skk  *
1707bd4a6f5Skk  * The fixed-function counters do not have this restriction.
1717bd4a6f5Skk  */
1727bd4a6f5Skk #define	BITS_EXTENDED_FROM_31	(BITMASK_XBITS(width_gpc) & ~BITMASK_XBITS(31))
1737bd4a6f5Skk 
1747bd4a6f5Skk #define	WRMSR(msr, value)						\
1757bd4a6f5Skk 	wrmsr((msr), (value));						\
1767bd4a6f5Skk 	DTRACE_PROBE2(wrmsr, uint64_t, (msr), uint64_t, (value));
1777bd4a6f5Skk 
1787bd4a6f5Skk #define	RDMSR(msr, value)						\
1797bd4a6f5Skk 	(value) = rdmsr((msr));						\
1807bd4a6f5Skk 	DTRACE_PROBE2(rdmsr, uint64_t, (msr), uint64_t, (value));
1817bd4a6f5Skk 
1827bd4a6f5Skk typedef struct core_pcbe_config {
1837bd4a6f5Skk 	uint64_t	core_rawpic;
1847bd4a6f5Skk 	uint64_t	core_ctl;	/* Event Select bits */
1857bd4a6f5Skk 	uint64_t	core_pmc;	/* Counter register address */
1867bd4a6f5Skk 	uint64_t	core_pes;	/* Event Select register address */
1877bd4a6f5Skk 	uint_t		core_picno;
1887bd4a6f5Skk 	uint8_t		core_pictype;	/* CORE_GPC or CORE_FFC */
1897bd4a6f5Skk } core_pcbe_config_t;
1907bd4a6f5Skk 
1917bd4a6f5Skk pcbe_ops_t core_pcbe_ops = {
1927bd4a6f5Skk 	PCBE_VER_1,			/* pcbe_ver */
1937bd4a6f5Skk 	CPC_CAP_OVERFLOW_INTERRUPT | CPC_CAP_OVERFLOW_PRECISE,	/* pcbe_caps */
1947bd4a6f5Skk 	core_pcbe_ncounters,		/* pcbe_ncounters */
1957bd4a6f5Skk 	core_pcbe_impl_name,		/* pcbe_impl_name */
1967bd4a6f5Skk 	core_pcbe_cpuref,		/* pcbe_cpuref */
1977bd4a6f5Skk 	core_pcbe_list_events,		/* pcbe_list_events */
1987bd4a6f5Skk 	core_pcbe_list_attrs,		/* pcbe_list_attrs */
1997bd4a6f5Skk 	core_pcbe_event_coverage,	/* pcbe_event_coverage */
2007bd4a6f5Skk 	core_pcbe_overflow_bitmap,	/* pcbe_overflow_bitmap */
2017bd4a6f5Skk 	core_pcbe_configure,		/* pcbe_configure */
2027bd4a6f5Skk 	core_pcbe_program,		/* pcbe_program */
2037bd4a6f5Skk 	core_pcbe_allstop,		/* pcbe_allstop */
2047bd4a6f5Skk 	core_pcbe_sample,		/* pcbe_sample */
2057bd4a6f5Skk 	core_pcbe_free			/* pcbe_free */
2067bd4a6f5Skk };
2077bd4a6f5Skk 
208afb806e6SKuriakose Kuruvilla struct nametable_core_uarch {
2097bd4a6f5Skk 	const char	*name;
2107bd4a6f5Skk 	uint64_t	restricted_bits;
2117bd4a6f5Skk 	uint8_t		event_num;
2127bd4a6f5Skk };
2137bd4a6f5Skk 
2147bd4a6f5Skk /*
2157bd4a6f5Skk  * Counting an event for all cores or all bus agents requires cpc_cpu privileges
2167bd4a6f5Skk  */
2177bd4a6f5Skk #define	ALL_CORES	(1ULL << 15)
2187bd4a6f5Skk #define	ALL_AGENTS	(1ULL << 13)
2197bd4a6f5Skk 
220560e0ee2SKuriakose Kuruvilla struct generic_events {
221560e0ee2SKuriakose Kuruvilla 	const char	*name;
222560e0ee2SKuriakose Kuruvilla 	uint8_t		event_num;
223560e0ee2SKuriakose Kuruvilla 	uint8_t		umask;
224560e0ee2SKuriakose Kuruvilla };
225560e0ee2SKuriakose Kuruvilla 
226560e0ee2SKuriakose Kuruvilla static const struct generic_events cmn_generic_events[] = {
227560e0ee2SKuriakose Kuruvilla 	{ "PAPI_tot_cyc", 0x3c, 0x00 }, /* cpu_clk_unhalted.thread_p/core */
228560e0ee2SKuriakose Kuruvilla 	{ "PAPI_tot_ins", 0xc0, 0x00 }, /* inst_retired.any_p		  */
229560e0ee2SKuriakose Kuruvilla 	{ "PAPI_br_ins",  0xc4, 0x0c }, /* br_inst_retired.taken	  */
230560e0ee2SKuriakose Kuruvilla 	{ "PAPI_br_msp",  0xc5, 0x00 }, /* br_inst_retired.mispred	  */
231560e0ee2SKuriakose Kuruvilla 	{ "PAPI_br_ntk",  0xc4, 0x03 },
232560e0ee2SKuriakose Kuruvilla 				/* br_inst_retired.pred_not_taken|pred_taken */
233560e0ee2SKuriakose Kuruvilla 	{ "PAPI_br_prc",  0xc4, 0x05 },
234560e0ee2SKuriakose Kuruvilla 				/* br_inst_retired.pred_not_taken|pred_taken */
235560e0ee2SKuriakose Kuruvilla 	{ "PAPI_hw_int",  0xc8, 0x00 }, /* hw_int_rvc			  */
236560e0ee2SKuriakose Kuruvilla 	{ "PAPI_tot_iis", 0xaa, 0x01 }, /* macro_insts.decoded		  */
237560e0ee2SKuriakose Kuruvilla 	{ "PAPI_l1_dca",  0x43, 0x01 }, /* l1d_all_ref			  */
238560e0ee2SKuriakose Kuruvilla 	{ "PAPI_l1_icm",  0x81, 0x00 }, /* l1i_misses			  */
239560e0ee2SKuriakose Kuruvilla 	{ "PAPI_l1_icr",  0x80, 0x00 }, /* l1i_reads			  */
240560e0ee2SKuriakose Kuruvilla 	{ "PAPI_l1_tcw",  0x41, 0x0f }, /* l1d_cache_st.mesi		  */
241560e0ee2SKuriakose Kuruvilla 	{ "PAPI_l2_stm",  0x2a, 0x41 }, /* l2_st.self.i_state		  */
242560e0ee2SKuriakose Kuruvilla 	{ "PAPI_l2_tca",  0x2e, 0x4f }, /* l2_rqsts.self.demand.mesi	  */
243560e0ee2SKuriakose Kuruvilla 	{ "PAPI_l2_tch",  0x2e, 0x4e }, /* l2_rqsts.mes			  */
244560e0ee2SKuriakose Kuruvilla 	{ "PAPI_l2_tcm",  0x2e, 0x41 }, /* l2_rqsts.self.demand.i_state   */
245560e0ee2SKuriakose Kuruvilla 	{ "PAPI_l2_tcw",  0x2a, 0x4f }, /* l2_st.self.mesi		  */
246560e0ee2SKuriakose Kuruvilla 	{ "PAPI_ld_ins",  0xc0, 0x01 }, /* inst_retired.loads		  */
247560e0ee2SKuriakose Kuruvilla 	{ "PAPI_lst_ins", 0xc0, 0x03 }, /* inst_retired.loads|stores	  */
248560e0ee2SKuriakose Kuruvilla 	{ "PAPI_sr_ins",  0xc0, 0x02 }, /* inst_retired.stores		  */
249560e0ee2SKuriakose Kuruvilla 	{ "PAPI_tlb_dm",  0x08, 0x01 }, /* dtlb_misses.any		  */
250560e0ee2SKuriakose Kuruvilla 	{ "PAPI_tlb_im",  0x82, 0x12 }, /* itlb.small_miss|large_miss	  */
251560e0ee2SKuriakose Kuruvilla 	{ "PAPI_tlb_tl",  0x0c, 0x03 }, /* page_walks			  */
252560e0ee2SKuriakose Kuruvilla 	{ "",		  NT_END, 0  }
253560e0ee2SKuriakose Kuruvilla };
254560e0ee2SKuriakose Kuruvilla 
255560e0ee2SKuriakose Kuruvilla static const struct generic_events generic_events_pic0[] = {
256560e0ee2SKuriakose Kuruvilla 	{ "PAPI_l1_dcm",  0xcb, 0x01 }, /* mem_load_retired.l1d_miss */
257560e0ee2SKuriakose Kuruvilla 	{ "",		  NT_END, 0  }
258560e0ee2SKuriakose Kuruvilla };
259560e0ee2SKuriakose Kuruvilla 
260a18ddb3cSKuriakose Kuruvilla /*
261a18ddb3cSKuriakose Kuruvilla  * The events listed in the following table can be counted on all
262afb806e6SKuriakose Kuruvilla  * general-purpose counters on processors that are of Penryn and Merom Family
263a18ddb3cSKuriakose Kuruvilla  */
264afb806e6SKuriakose Kuruvilla static const struct nametable_core_uarch cmn_gpc_events_core_uarch[] = {
2657bd4a6f5Skk 	/* Alphabetical order of event name */
2667bd4a6f5Skk 
2677bd4a6f5Skk 	{ "baclears",			0x0,	0xe6 },
2687bd4a6f5Skk 	{ "bogus_br",			0x0,	0xe4 },
2697bd4a6f5Skk 	{ "br_bac_missp_exec",		0x0,	0x8a },
2707bd4a6f5Skk 
2717bd4a6f5Skk 	{ "br_call_exec",		0x0,	0x92 },
2727bd4a6f5Skk 	{ "br_call_missp_exec",		0x0,	0x93 },
2737bd4a6f5Skk 	{ "br_cnd_exec",		0x0,	0x8b },
2747bd4a6f5Skk 
2757bd4a6f5Skk 	{ "br_cnd_missp_exec",		0x0,	0x8c },
2767bd4a6f5Skk 	{ "br_ind_call_exec",		0x0,	0x94 },
2777bd4a6f5Skk 	{ "br_ind_exec",		0x0,	0x8d },
2787bd4a6f5Skk 
2797bd4a6f5Skk 	{ "br_ind_missp_exec",		0x0,	0x8e },
2807bd4a6f5Skk 	{ "br_inst_decoded",		0x0,	0xe0 },
2817bd4a6f5Skk 	{ "br_inst_exec",		0x0,	0x88 },
2827bd4a6f5Skk 
2837bd4a6f5Skk 	{ "br_inst_retired",		0x0,	0xc4 },
2847bd4a6f5Skk 	{ "br_inst_retired_mispred",	0x0,	0xc5 },
2857bd4a6f5Skk 	{ "br_missp_exec",		0x0,	0x89 },
2867bd4a6f5Skk 
2877bd4a6f5Skk 	{ "br_ret_bac_missp_exec",	0x0,	0x91 },
2887bd4a6f5Skk 	{ "br_ret_exec",		0x0,	0x8f },
2897bd4a6f5Skk 	{ "br_ret_missp_exec",		0x0,	0x90 },
2907bd4a6f5Skk 
2917bd4a6f5Skk 	{ "br_tkn_bubble_1",		0x0,	0x97 },
2927bd4a6f5Skk 	{ "br_tkn_bubble_2",		0x0,	0x98 },
2937bd4a6f5Skk 	{ "bus_bnr_drv",		ALL_AGENTS,	0x61 },
2947bd4a6f5Skk 
2957bd4a6f5Skk 	{ "bus_data_rcv",		ALL_CORES,	0x64 },
2967bd4a6f5Skk 	{ "bus_drdy_clocks",		ALL_AGENTS,	0x62 },
2977bd4a6f5Skk 	{ "bus_hit_drv",		ALL_AGENTS,	0x7a },
2987bd4a6f5Skk 
2997bd4a6f5Skk 	{ "bus_hitm_drv",		ALL_AGENTS,	0x7b },
3007bd4a6f5Skk 	{ "bus_io_wait",		ALL_CORES,	0x7f },
3017bd4a6f5Skk 	{ "bus_lock_clocks",		ALL_CORES | ALL_AGENTS,	0x63 },
3027bd4a6f5Skk 
3037bd4a6f5Skk 	{ "bus_request_outstanding",	ALL_CORES | ALL_AGENTS,	0x60 },
3047bd4a6f5Skk 	{ "bus_trans_any",		ALL_CORES | ALL_AGENTS,	0x70 },
3057bd4a6f5Skk 	{ "bus_trans_brd",		ALL_CORES | ALL_AGENTS,	0x65 },
3067bd4a6f5Skk 
3077bd4a6f5Skk 	{ "bus_trans_burst",		ALL_CORES | ALL_AGENTS,	0x6e },
3087bd4a6f5Skk 	{ "bus_trans_def",		ALL_CORES | ALL_AGENTS,	0x6d },
3097bd4a6f5Skk 	{ "bus_trans_ifetch",		ALL_CORES | ALL_AGENTS,	0x68 },
3107bd4a6f5Skk 
3117bd4a6f5Skk 	{ "bus_trans_inval",		ALL_CORES | ALL_AGENTS,	0x69 },
3127bd4a6f5Skk 	{ "bus_trans_io",		ALL_CORES | ALL_AGENTS,	0x6c },
3137bd4a6f5Skk 	{ "bus_trans_mem",		ALL_CORES | ALL_AGENTS,	0x6f },
3147bd4a6f5Skk 
3157bd4a6f5Skk 	{ "bus_trans_p",		ALL_CORES | ALL_AGENTS,	0x6b },
3167bd4a6f5Skk 	{ "bus_trans_pwr",		ALL_CORES | ALL_AGENTS,	0x6a },
3177bd4a6f5Skk 	{ "bus_trans_rfo",		ALL_CORES | ALL_AGENTS,	0x66 },
3187bd4a6f5Skk 
3197bd4a6f5Skk 	{ "bus_trans_wb",		ALL_CORES | ALL_AGENTS,	0x67 },
3207bd4a6f5Skk 	{ "busq_empty",			ALL_CORES,	0x7d },
3217bd4a6f5Skk 	{ "cmp_snoop",			ALL_CORES,	0x78 },
3227bd4a6f5Skk 
3237bd4a6f5Skk 	{ "cpu_clk_unhalted",		0x0,	0x3c },
3247bd4a6f5Skk 	{ "cycles_int",			0x0,	0xc6 },
3257bd4a6f5Skk 	{ "cycles_l1i_mem_stalled",	0x0,	0x86 },
3267bd4a6f5Skk 
3277bd4a6f5Skk 	{ "dtlb_misses",		0x0,	0x08 },
3287bd4a6f5Skk 	{ "eist_trans",			0x0,	0x3a },
3297bd4a6f5Skk 	{ "esp",			0x0,	0xab },
3307bd4a6f5Skk 
3317bd4a6f5Skk 	{ "ext_snoop",			ALL_AGENTS,	0x77 },
3327bd4a6f5Skk 	{ "fp_mmx_trans",		0x0,	0xcc },
3337bd4a6f5Skk 	{ "hw_int_rcv",			0x0,	0xc8 },
3347bd4a6f5Skk 
3357bd4a6f5Skk 	{ "ild_stall",			0x0,	0x87 },
3367bd4a6f5Skk 	{ "inst_queue",			0x0,	0x83 },
3377bd4a6f5Skk 	{ "inst_retired",		0x0,	0xc0 },
3387bd4a6f5Skk 
3397bd4a6f5Skk 	{ "itlb",			0x0,	0x82 },
3407bd4a6f5Skk 	{ "itlb_miss_retired",		0x0,	0xc9 },
3417bd4a6f5Skk 	{ "l1d_all_ref",		0x0,	0x43 },
3427bd4a6f5Skk 
3437bd4a6f5Skk 	{ "l1d_cache_ld",		0x0,	0x40 },
3447bd4a6f5Skk 	{ "l1d_cache_lock",		0x0,	0x42 },
3457bd4a6f5Skk 	{ "l1d_cache_st",		0x0,	0x41 },
3467bd4a6f5Skk 
3477bd4a6f5Skk 	{ "l1d_m_evict",		0x0,	0x47 },
3487bd4a6f5Skk 	{ "l1d_m_repl",			0x0,	0x46 },
3497bd4a6f5Skk 	{ "l1d_pend_miss",		0x0,	0x48 },
3507bd4a6f5Skk 
3517bd4a6f5Skk 	{ "l1d_prefetch",		0x0,	0x4e },
3527bd4a6f5Skk 	{ "l1d_repl",			0x0,	0x45 },
3537bd4a6f5Skk 	{ "l1d_split",			0x0,	0x49 },
3547bd4a6f5Skk 
3557bd4a6f5Skk 	{ "l1i_misses",			0x0,	0x81 },
3567bd4a6f5Skk 	{ "l1i_reads",			0x0,	0x80 },
3577bd4a6f5Skk 	{ "l2_ads",			ALL_CORES,	0x21 },
3587bd4a6f5Skk 
3597bd4a6f5Skk 	{ "l2_dbus_busy_rd",		ALL_CORES,	0x23 },
3607bd4a6f5Skk 	{ "l2_ifetch",			ALL_CORES,	0x28 },
3617bd4a6f5Skk 	{ "l2_ld",			ALL_CORES,	0x29 },
3627bd4a6f5Skk 
3637bd4a6f5Skk 	{ "l2_lines_in",		ALL_CORES,	0x24 },
3647bd4a6f5Skk 	{ "l2_lines_out",		ALL_CORES,	0x26 },
3657bd4a6f5Skk 	{ "l2_lock",			ALL_CORES,	0x2b },
3667bd4a6f5Skk 
3677bd4a6f5Skk 	{ "l2_m_lines_in",		ALL_CORES,	0x25 },
3687bd4a6f5Skk 	{ "l2_m_lines_out",		ALL_CORES,	0x27 },
3697bd4a6f5Skk 	{ "l2_no_req",			ALL_CORES,	0x32 },
3707bd4a6f5Skk 
3717bd4a6f5Skk 	{ "l2_reject_busq",		ALL_CORES,	0x30 },
3727bd4a6f5Skk 	{ "l2_rqsts",			ALL_CORES,	0x2e },
3737bd4a6f5Skk 	{ "l2_st",			ALL_CORES,	0x2a },
3747bd4a6f5Skk 
3757bd4a6f5Skk 	{ "load_block",			0x0,	0x03 },
3767bd4a6f5Skk 	{ "load_hit_pre",		0x0,	0x4c },
3777bd4a6f5Skk 	{ "machine_nukes",		0x0,	0xc3 },
3787bd4a6f5Skk 
3797bd4a6f5Skk 	{ "macro_insts",		0x0,	0xaa },
3807bd4a6f5Skk 	{ "memory_disambiguation",	0x0,	0x09 },
381a18ddb3cSKuriakose Kuruvilla 	{ "misalign_mem_ref",		0x0,	0x05 },
3827bd4a6f5Skk 	{ "page_walks",			0x0,	0x0c },
3837bd4a6f5Skk 
3847bd4a6f5Skk 	{ "pref_rqsts_dn",		0x0,	0xf8 },
3857bd4a6f5Skk 	{ "pref_rqsts_up",		0x0,	0xf0 },
3867bd4a6f5Skk 	{ "rat_stalls",			0x0,	0xd2 },
3877bd4a6f5Skk 
3887bd4a6f5Skk 	{ "resource_stalls",		0x0,	0xdc },
3897bd4a6f5Skk 	{ "rs_uops_dispatched",		0x0,	0xa0 },
3907bd4a6f5Skk 	{ "seg_reg_renames",		0x0,	0xd5 },
3917bd4a6f5Skk 
3927bd4a6f5Skk 	{ "seg_rename_stalls",		0x0,	0xd4 },
3937bd4a6f5Skk 	{ "segment_reg_loads",		0x0,	0x06 },
3947bd4a6f5Skk 	{ "simd_assist",		0x0,	0xcd },
3957bd4a6f5Skk 
3967bd4a6f5Skk 	{ "simd_comp_inst_retired",	0x0,	0xca },
3977bd4a6f5Skk 	{ "simd_inst_retired",		0x0,	0xc7 },
3987bd4a6f5Skk 	{ "simd_instr_retired",		0x0,	0xce },
3997bd4a6f5Skk 
4007bd4a6f5Skk 	{ "simd_sat_instr_retired",	0x0,	0xcf },
4017bd4a6f5Skk 	{ "simd_sat_uop_exec",		0x0,	0xb1 },
4027bd4a6f5Skk 	{ "simd_uop_type_exec",		0x0,	0xb3 },
4037bd4a6f5Skk 
4047bd4a6f5Skk 	{ "simd_uops_exec",		0x0,	0xb0 },
4057bd4a6f5Skk 	{ "snoop_stall_drv",		ALL_CORES | ALL_AGENTS,	0x7e },
4067bd4a6f5Skk 	{ "sse_pre_exec",		0x0,	0x07 },
4077bd4a6f5Skk 
4087bd4a6f5Skk 	{ "sse_pre_miss",		0x0,	0x4b },
4097bd4a6f5Skk 	{ "store_block",		0x0,	0x04 },
4107bd4a6f5Skk 	{ "thermal_trip",		0x0,	0x3b },
4117bd4a6f5Skk 
4127bd4a6f5Skk 	{ "uops_retired",		0x0,	0xc2 },
4137bd4a6f5Skk 	{ "x87_ops_retired",		0x0,	0xc1 },
4147bd4a6f5Skk 	{ "",				0x0,	NT_END }
4157bd4a6f5Skk };
4167bd4a6f5Skk 
4177bd4a6f5Skk /*
4187bd4a6f5Skk  * If any of the pic specific events require privileges, make sure to add a
4197bd4a6f5Skk  * check in configure_gpc() to find whether an event hard-coded as a number by
4207bd4a6f5Skk  * the user has any privilege requirements
4217bd4a6f5Skk  */
422afb806e6SKuriakose Kuruvilla static const struct nametable_core_uarch pic0_events[] = {
4237bd4a6f5Skk 	/* Alphabetical order of event name */
4247bd4a6f5Skk 
4257bd4a6f5Skk 	{ "cycles_div_busy",		0x0,	0x14 },
4267bd4a6f5Skk 	{ "fp_comp_ops_exe",		0x0,	0x10 },
4277bd4a6f5Skk 	{ "idle_during_div",		0x0,	0x18 },
4287bd4a6f5Skk 
4297bd4a6f5Skk 	{ "mem_load_retired",		0x0,	0xcb },
4307bd4a6f5Skk 	{ "rs_uops_dispatched_port",	0x0,	0xa1 },
4317bd4a6f5Skk 	{ "",				0x0,	NT_END }
4327bd4a6f5Skk };
4337bd4a6f5Skk 
434afb806e6SKuriakose Kuruvilla static const struct nametable_core_uarch pic1_events[] = {
4357bd4a6f5Skk 	/* Alphabetical order of event name */
4367bd4a6f5Skk 
4377bd4a6f5Skk 	{ "delayed_bypass",	0x0,	0x19 },
4387bd4a6f5Skk 	{ "div",		0x0,	0x13 },
4397bd4a6f5Skk 	{ "fp_assist",		0x0,	0x11 },
4407bd4a6f5Skk 
4417bd4a6f5Skk 	{ "mul",		0x0,	0x12 },
4427bd4a6f5Skk 	{ "",			0x0,	NT_END }
4437bd4a6f5Skk };
4447bd4a6f5Skk 
4456323db19SKrishnendu Sadhukhan - Sun Microsystems /* FFC entries must be in order */
446560e0ee2SKuriakose Kuruvilla static char *ffc_names_non_htt[] = {
4476323db19SKrishnendu Sadhukhan - Sun Microsystems 	"instr_retired.any",
4486323db19SKrishnendu Sadhukhan - Sun Microsystems 	"cpu_clk_unhalted.core",
4496323db19SKrishnendu Sadhukhan - Sun Microsystems 	"cpu_clk_unhalted.ref",
4506323db19SKrishnendu Sadhukhan - Sun Microsystems 	NULL
4516323db19SKrishnendu Sadhukhan - Sun Microsystems };
4526323db19SKrishnendu Sadhukhan - Sun Microsystems 
453560e0ee2SKuriakose Kuruvilla static char *ffc_names_htt[] = {
4546323db19SKrishnendu Sadhukhan - Sun Microsystems 	"instr_retired.any",
455a18ddb3cSKuriakose Kuruvilla 	"cpu_clk_unhalted.thread",
4567bd4a6f5Skk 	"cpu_clk_unhalted.ref",
4577bd4a6f5Skk 	NULL
4587bd4a6f5Skk };
4597bd4a6f5Skk 
460560e0ee2SKuriakose Kuruvilla static char *ffc_genericnames[] = {
461560e0ee2SKuriakose Kuruvilla 	"PAPI_tot_ins",
462560e0ee2SKuriakose Kuruvilla 	"PAPI_tot_cyc",
463560e0ee2SKuriakose Kuruvilla 	"",
464560e0ee2SKuriakose Kuruvilla 	NULL
465560e0ee2SKuriakose Kuruvilla };
4666323db19SKrishnendu Sadhukhan - Sun Microsystems 
467560e0ee2SKuriakose Kuruvilla static char	**ffc_names = NULL;
468560e0ee2SKuriakose Kuruvilla static char	**ffc_allnames = NULL;
469e7334537SKrishnendu Sadhukhan - Sun Microsystems static char	**gpc_names = NULL;
470a18ddb3cSKuriakose Kuruvilla static uint32_t	versionid;
4717bd4a6f5Skk static uint64_t	num_gpc;
4727bd4a6f5Skk static uint64_t	width_gpc;
4737bd4a6f5Skk static uint64_t	mask_gpc;
4747bd4a6f5Skk static uint64_t	num_ffc;
4757bd4a6f5Skk static uint64_t	width_ffc;
4767bd4a6f5Skk static uint64_t	mask_ffc;
4777bd4a6f5Skk static uint_t	total_pmc;
4787bd4a6f5Skk static uint64_t	control_ffc;
4797bd4a6f5Skk static uint64_t	control_gpc;
4807bd4a6f5Skk static uint64_t	control_mask;
481a18ddb3cSKuriakose Kuruvilla static uint32_t	arch_events_vector;
4827bd4a6f5Skk 
483a18ddb3cSKuriakose Kuruvilla #define	IMPL_NAME_LEN 100
484a18ddb3cSKuriakose Kuruvilla static char core_impl_name[IMPL_NAME_LEN];
4857bd4a6f5Skk 
4867bd4a6f5Skk static const char *core_cpuref =
4877e3dbbacSRobert Mustacchi 	"See https://download.01.org/perfmon/index/ or Chapers 18 and 19 " \
4887e3dbbacSRobert Mustacchi 	"of the \"Intel 64 and IA-32 Architectures Software Developer's " \
4897e3dbbacSRobert Mustacchi 	"Manual Volume 3: System Programming Guide\" Order Number: " \
4907e3dbbacSRobert Mustacchi 	"325384-062US, March 2017.";
491a18ddb3cSKuriakose Kuruvilla 
492a18ddb3cSKuriakose Kuruvilla 
4936323db19SKrishnendu Sadhukhan - Sun Microsystems /* Architectural events */
4946323db19SKrishnendu Sadhukhan - Sun Microsystems #define	ARCH_EVENTS_COMMON					\
4956323db19SKrishnendu Sadhukhan - Sun Microsystems 	{ 0xc0, 0x00, C_ALL, "inst_retired.any_p" },		\
4966323db19SKrishnendu Sadhukhan - Sun Microsystems 	{ 0x3c, 0x01, C_ALL, "cpu_clk_unhalted.ref_p" },	\
4976323db19SKrishnendu Sadhukhan - Sun Microsystems 	{ 0x2e, 0x4f, C_ALL, "longest_lat_cache.reference" },	\
4986323db19SKrishnendu Sadhukhan - Sun Microsystems 	{ 0x2e, 0x41, C_ALL, "longest_lat_cache.miss" },	\
4996323db19SKrishnendu Sadhukhan - Sun Microsystems 	{ 0xc4, 0x00, C_ALL, "br_inst_retired.all_branches" },	\
5006323db19SKrishnendu Sadhukhan - Sun Microsystems 	{ 0xc5, 0x00, C_ALL, "br_misp_retired.all_branches" }
5016323db19SKrishnendu Sadhukhan - Sun Microsystems 
502560e0ee2SKuriakose Kuruvilla static const struct events_table_t arch_events_table_non_htt[] = {
5036323db19SKrishnendu Sadhukhan - Sun Microsystems 	{ 0x3c, 0x00, C_ALL, "cpu_clk_unhalted.core" },
5046323db19SKrishnendu Sadhukhan - Sun Microsystems 	ARCH_EVENTS_COMMON
5056323db19SKrishnendu Sadhukhan - Sun Microsystems };
506a18ddb3cSKuriakose Kuruvilla 
507560e0ee2SKuriakose Kuruvilla static const struct events_table_t arch_events_table_htt[] = {
5086323db19SKrishnendu Sadhukhan - Sun Microsystems 	{ 0x3c, 0x00, C_ALL, "cpu_clk_unhalted.thread_p" },
5096323db19SKrishnendu Sadhukhan - Sun Microsystems 	ARCH_EVENTS_COMMON
510a18ddb3cSKuriakose Kuruvilla };
511a18ddb3cSKuriakose Kuruvilla 
512560e0ee2SKuriakose Kuruvilla static char *arch_genevents_table[] = {
513560e0ee2SKuriakose Kuruvilla 	"PAPI_tot_cyc", /* cpu_clk_unhalted.thread_p/core */
514560e0ee2SKuriakose Kuruvilla 	"PAPI_tot_ins", /* inst_retired.any_p		  */
515560e0ee2SKuriakose Kuruvilla 	"",		/* cpu_clk_unhalted.ref_p	  */
516560e0ee2SKuriakose Kuruvilla 	"",		/* longest_lat_cache.reference	  */
517560e0ee2SKuriakose Kuruvilla 	"",		/* longest_lat_cache.miss	  */
518560e0ee2SKuriakose Kuruvilla 	"",		/* br_inst_retired.all_branches	  */
519560e0ee2SKuriakose Kuruvilla 	"",		/* br_misp_retired.all_branches	  */
520560e0ee2SKuriakose Kuruvilla };
521560e0ee2SKuriakose Kuruvilla 
522560e0ee2SKuriakose Kuruvilla static const struct events_table_t *arch_events_table = NULL;
5236323db19SKrishnendu Sadhukhan - Sun Microsystems static uint64_t known_arch_events;
5246323db19SKrishnendu Sadhukhan - Sun Microsystems static uint64_t known_ffc_num;
525a18ddb3cSKuriakose Kuruvilla static const struct events_table_t *events_table = NULL;
526a18ddb3cSKuriakose Kuruvilla 
527a18ddb3cSKuriakose Kuruvilla /*
528a18ddb3cSKuriakose Kuruvilla  * Initialize string containing list of supported general-purpose counter
529afb806e6SKuriakose Kuruvilla  * events for processors of Penryn and Merom Family
530a18ddb3cSKuriakose Kuruvilla  */
531a18ddb3cSKuriakose Kuruvilla static void
pcbe_init_core_uarch()532afb806e6SKuriakose Kuruvilla pcbe_init_core_uarch()
533a18ddb3cSKuriakose Kuruvilla {
534afb806e6SKuriakose Kuruvilla 	const struct nametable_core_uarch	*n;
535560e0ee2SKuriakose Kuruvilla 	const struct generic_events		*k;
536afb806e6SKuriakose Kuruvilla 	const struct nametable_core_uarch	*picspecific_events;
537560e0ee2SKuriakose Kuruvilla 	const struct generic_events		*picspecific_genericevents;
538a18ddb3cSKuriakose Kuruvilla 	size_t			common_size;
539a18ddb3cSKuriakose Kuruvilla 	size_t			size;
540a18ddb3cSKuriakose Kuruvilla 	uint64_t		i;
541a18ddb3cSKuriakose Kuruvilla 
542a18ddb3cSKuriakose Kuruvilla 	gpc_names = kmem_alloc(num_gpc * sizeof (char *), KM_SLEEP);
543a18ddb3cSKuriakose Kuruvilla 
544a18ddb3cSKuriakose Kuruvilla 	/* Calculate space needed to save all the common event names */
545a18ddb3cSKuriakose Kuruvilla 	common_size = 0;
546afb806e6SKuriakose Kuruvilla 	for (n = cmn_gpc_events_core_uarch; n->event_num != NT_END; n++) {
547a18ddb3cSKuriakose Kuruvilla 		common_size += strlen(n->name) + 1;
548a18ddb3cSKuriakose Kuruvilla 	}
549a18ddb3cSKuriakose Kuruvilla 
550560e0ee2SKuriakose Kuruvilla 	for (k = cmn_generic_events; k->event_num != NT_END; k++) {
551560e0ee2SKuriakose Kuruvilla 		common_size += strlen(k->name) + 1;
552560e0ee2SKuriakose Kuruvilla 	}
553560e0ee2SKuriakose Kuruvilla 
554a18ddb3cSKuriakose Kuruvilla 	for (i = 0; i < num_gpc; i++) {
555a18ddb3cSKuriakose Kuruvilla 		size = 0;
556560e0ee2SKuriakose Kuruvilla 		picspecific_genericevents = NULL;
557560e0ee2SKuriakose Kuruvilla 
558a18ddb3cSKuriakose Kuruvilla 		switch (i) {
559a18ddb3cSKuriakose Kuruvilla 			case 0:
560a18ddb3cSKuriakose Kuruvilla 				picspecific_events = pic0_events;
561560e0ee2SKuriakose Kuruvilla 				picspecific_genericevents = generic_events_pic0;
562a18ddb3cSKuriakose Kuruvilla 				break;
563a18ddb3cSKuriakose Kuruvilla 			case 1:
564a18ddb3cSKuriakose Kuruvilla 				picspecific_events = pic1_events;
565a18ddb3cSKuriakose Kuruvilla 				break;
566a18ddb3cSKuriakose Kuruvilla 			default:
567a18ddb3cSKuriakose Kuruvilla 				picspecific_events = NULL;
568a18ddb3cSKuriakose Kuruvilla 				break;
569a18ddb3cSKuriakose Kuruvilla 		}
570a18ddb3cSKuriakose Kuruvilla 		if (picspecific_events != NULL) {
571a18ddb3cSKuriakose Kuruvilla 			for (n = picspecific_events;
572a18ddb3cSKuriakose Kuruvilla 			    n->event_num != NT_END;
573a18ddb3cSKuriakose Kuruvilla 			    n++) {
574a18ddb3cSKuriakose Kuruvilla 				size += strlen(n->name) + 1;
575a18ddb3cSKuriakose Kuruvilla 			}
576a18ddb3cSKuriakose Kuruvilla 		}
577560e0ee2SKuriakose Kuruvilla 		if (picspecific_genericevents != NULL) {
578560e0ee2SKuriakose Kuruvilla 			for (k = picspecific_genericevents;
579560e0ee2SKuriakose Kuruvilla 			    k->event_num != NT_END; k++) {
580560e0ee2SKuriakose Kuruvilla 				size += strlen(k->name) + 1;
581560e0ee2SKuriakose Kuruvilla 			}
582560e0ee2SKuriakose Kuruvilla 		}
583a18ddb3cSKuriakose Kuruvilla 
584a18ddb3cSKuriakose Kuruvilla 		gpc_names[i] =
585a18ddb3cSKuriakose Kuruvilla 		    kmem_alloc(size + common_size + 1, KM_SLEEP);
586a18ddb3cSKuriakose Kuruvilla 
587a18ddb3cSKuriakose Kuruvilla 		gpc_names[i][0] = '\0';
588a18ddb3cSKuriakose Kuruvilla 		if (picspecific_events != NULL) {
589a18ddb3cSKuriakose Kuruvilla 			for (n = picspecific_events;
590560e0ee2SKuriakose Kuruvilla 			    n->event_num != NT_END; n++) {
591a18ddb3cSKuriakose Kuruvilla 				(void) strcat(gpc_names[i], n->name);
592a18ddb3cSKuriakose Kuruvilla 				(void) strcat(gpc_names[i], ",");
593a18ddb3cSKuriakose Kuruvilla 			}
594a18ddb3cSKuriakose Kuruvilla 		}
595560e0ee2SKuriakose Kuruvilla 		if (picspecific_genericevents != NULL) {
596560e0ee2SKuriakose Kuruvilla 			for (k = picspecific_genericevents;
597560e0ee2SKuriakose Kuruvilla 			    k->event_num != NT_END; k++) {
598560e0ee2SKuriakose Kuruvilla 				(void) strcat(gpc_names[i], k->name);
599560e0ee2SKuriakose Kuruvilla 				(void) strcat(gpc_names[i], ",");
600560e0ee2SKuriakose Kuruvilla 			}
601560e0ee2SKuriakose Kuruvilla 		}
602afb806e6SKuriakose Kuruvilla 		for (n = cmn_gpc_events_core_uarch; n->event_num != NT_END;
603a18ddb3cSKuriakose Kuruvilla 		    n++) {
604a18ddb3cSKuriakose Kuruvilla 			(void) strcat(gpc_names[i], n->name);
605a18ddb3cSKuriakose Kuruvilla 			(void) strcat(gpc_names[i], ",");
606a18ddb3cSKuriakose Kuruvilla 		}
607560e0ee2SKuriakose Kuruvilla 		for (k = cmn_generic_events; k->event_num != NT_END; k++) {
608560e0ee2SKuriakose Kuruvilla 			(void) strcat(gpc_names[i], k->name);
609560e0ee2SKuriakose Kuruvilla 			(void) strcat(gpc_names[i], ",");
610560e0ee2SKuriakose Kuruvilla 		}
611560e0ee2SKuriakose Kuruvilla 
612a18ddb3cSKuriakose Kuruvilla 		/*
613a18ddb3cSKuriakose Kuruvilla 		 * Remove trailing comma.
614a18ddb3cSKuriakose Kuruvilla 		 */
615a18ddb3cSKuriakose Kuruvilla 		gpc_names[i][common_size + size - 1] = '\0';
616a18ddb3cSKuriakose Kuruvilla 	}
617a18ddb3cSKuriakose Kuruvilla }
618a18ddb3cSKuriakose Kuruvilla 
6197bd4a6f5Skk static int
core_pcbe_init(void)6207bd4a6f5Skk core_pcbe_init(void)
6217bd4a6f5Skk {
6227bd4a6f5Skk 	struct cpuid_regs	cp;
6237bd4a6f5Skk 	size_t			size;
6247bd4a6f5Skk 	uint64_t		i;
625a18ddb3cSKuriakose Kuruvilla 	uint64_t		j;
626a18ddb3cSKuriakose Kuruvilla 	uint64_t		arch_events_vector_length;
627a18ddb3cSKuriakose Kuruvilla 	size_t			arch_events_string_length;
628c18e9bc3SRobert Mustacchi 	uint_t			model, stepping;
629a18ddb3cSKuriakose Kuruvilla 
630a18ddb3cSKuriakose Kuruvilla 	if (cpuid_getvendor(CPU) != X86_VENDOR_Intel)
631a18ddb3cSKuriakose Kuruvilla 		return (-1);
632a18ddb3cSKuriakose Kuruvilla 
633a18ddb3cSKuriakose Kuruvilla 	/* Obtain Basic CPUID information */
634a18ddb3cSKuriakose Kuruvilla 	cp.cp_eax = 0x0;
635a18ddb3cSKuriakose Kuruvilla 	(void) __cpuid_insn(&cp);
6367bd4a6f5Skk 
637a18ddb3cSKuriakose Kuruvilla 	/* No Architectural Performance Monitoring Leaf returned by CPUID */
638a18ddb3cSKuriakose Kuruvilla 	if (cp.cp_eax < 0xa) {
6397bd4a6f5Skk 		return (-1);
640a18ddb3cSKuriakose Kuruvilla 	}
6417bd4a6f5Skk 
6427bd4a6f5Skk 	/* Obtain the Architectural Performance Monitoring Leaf */
6437bd4a6f5Skk 	cp.cp_eax = 0xa;
6447bd4a6f5Skk 	(void) __cpuid_insn(&cp);
6457bd4a6f5Skk 
6467bd4a6f5Skk 	versionid = cp.cp_eax & 0xFF;
6477bd4a6f5Skk 
6487bd4a6f5Skk 	/*
649e7334537SKrishnendu Sadhukhan - Sun Microsystems 	 * Fixed-Function Counters (FFC)
650e7334537SKrishnendu Sadhukhan - Sun Microsystems 	 *
6517bd4a6f5Skk 	 * All Family 6 Model 15 and Model 23 processors have fixed-function
6527bd4a6f5Skk 	 * counters.  These counters were made Architectural with
653a18ddb3cSKuriakose Kuruvilla 	 * Family 6 Model 15 Stepping 9.
6547bd4a6f5Skk 	 */
6557bd4a6f5Skk 	switch (versionid) {
6567bd4a6f5Skk 
6577bd4a6f5Skk 		case 0:
6587bd4a6f5Skk 			return (-1);
6597bd4a6f5Skk 
660a18ddb3cSKuriakose Kuruvilla 		case 2:
6617bd4a6f5Skk 			num_ffc = cp.cp_edx & 0x1F;
6627bd4a6f5Skk 			width_ffc = (cp.cp_edx >> 5) & 0xFF;
6637bd4a6f5Skk 
6647bd4a6f5Skk 			/*
665a18ddb3cSKuriakose Kuruvilla 			 * Some processors have an errata (AW34) where
666a18ddb3cSKuriakose Kuruvilla 			 * versionid is reported as 2 when actually 1.
667a18ddb3cSKuriakose Kuruvilla 			 * In this case, fixed-function counters are
668a18ddb3cSKuriakose Kuruvilla 			 * model-specific as in Version 1.
6697bd4a6f5Skk 			 */
670a18ddb3cSKuriakose Kuruvilla 			if (num_ffc != 0) {
671a18ddb3cSKuriakose Kuruvilla 				break;
672a18ddb3cSKuriakose Kuruvilla 			}
673a18ddb3cSKuriakose Kuruvilla 			/* FALLTHROUGH */
674a18ddb3cSKuriakose Kuruvilla 		case 1:
6757bd4a6f5Skk 			num_ffc = 3;
6767bd4a6f5Skk 			width_ffc = 40;
677a18ddb3cSKuriakose Kuruvilla 			versionid = 1;
678a18ddb3cSKuriakose Kuruvilla 			break;
679a18ddb3cSKuriakose Kuruvilla 
680a18ddb3cSKuriakose Kuruvilla 		default:
681a18ddb3cSKuriakose Kuruvilla 			num_ffc = cp.cp_edx & 0x1F;
682a18ddb3cSKuriakose Kuruvilla 			width_ffc = (cp.cp_edx >> 5) & 0xFF;
6837bd4a6f5Skk 			break;
6847bd4a6f5Skk 	}
6857bd4a6f5Skk 
686a18ddb3cSKuriakose Kuruvilla 
6877bd4a6f5Skk 	if (num_ffc >= 64)
6887bd4a6f5Skk 		return (-1);
6897bd4a6f5Skk 
6906323db19SKrishnendu Sadhukhan - Sun Microsystems 	/* Set HTT-specific names of architectural & FFC events */
6917417cfdeSKuriakose Kuruvilla 	if (is_x86_feature(x86_featureset, X86FSET_HTT)) {
6926323db19SKrishnendu Sadhukhan - Sun Microsystems 		ffc_names = ffc_names_htt;
6936323db19SKrishnendu Sadhukhan - Sun Microsystems 		arch_events_table = arch_events_table_htt;
6946323db19SKrishnendu Sadhukhan - Sun Microsystems 		known_arch_events =
6956323db19SKrishnendu Sadhukhan - Sun Microsystems 		    sizeof (arch_events_table_htt) /
6966323db19SKrishnendu Sadhukhan - Sun Microsystems 		    sizeof (struct events_table_t);
6976323db19SKrishnendu Sadhukhan - Sun Microsystems 		known_ffc_num =
6986323db19SKrishnendu Sadhukhan - Sun Microsystems 		    sizeof (ffc_names_htt) / sizeof (char *);
6996323db19SKrishnendu Sadhukhan - Sun Microsystems 	} else {
7006323db19SKrishnendu Sadhukhan - Sun Microsystems 		ffc_names = ffc_names_non_htt;
7016323db19SKrishnendu Sadhukhan - Sun Microsystems 		arch_events_table = arch_events_table_non_htt;
7026323db19SKrishnendu Sadhukhan - Sun Microsystems 		known_arch_events =
7036323db19SKrishnendu Sadhukhan - Sun Microsystems 		    sizeof (arch_events_table_non_htt) /
7046323db19SKrishnendu Sadhukhan - Sun Microsystems 		    sizeof (struct events_table_t);
7056323db19SKrishnendu Sadhukhan - Sun Microsystems 		known_ffc_num =
7066323db19SKrishnendu Sadhukhan - Sun Microsystems 		    sizeof (ffc_names_non_htt) / sizeof (char *);
7076323db19SKrishnendu Sadhukhan - Sun Microsystems 	}
7086323db19SKrishnendu Sadhukhan - Sun Microsystems 
7096323db19SKrishnendu Sadhukhan - Sun Microsystems 	if (num_ffc >= known_ffc_num) {
7107bd4a6f5Skk 		/*
7117bd4a6f5Skk 		 * The system seems to have more fixed-function counters than
7127bd4a6f5Skk 		 * what this PCBE is able to handle correctly.  Default to the
7137bd4a6f5Skk 		 * maximum number of fixed-function counters that this driver
7147bd4a6f5Skk 		 * is aware of.
7157bd4a6f5Skk 		 */
7166323db19SKrishnendu Sadhukhan - Sun Microsystems 		num_ffc = known_ffc_num - 1;
7177bd4a6f5Skk 	}
7187bd4a6f5Skk 
7197bd4a6f5Skk 	mask_ffc = BITMASK_XBITS(width_ffc);
720e7334537SKrishnendu Sadhukhan - Sun Microsystems 	control_ffc = BITMASK_XBITS(num_ffc);
7217bd4a6f5Skk 
722e7334537SKrishnendu Sadhukhan - Sun Microsystems 	/*
723e7334537SKrishnendu Sadhukhan - Sun Microsystems 	 * General Purpose Counters (GPC)
724e7334537SKrishnendu Sadhukhan - Sun Microsystems 	 */
7257bd4a6f5Skk 	num_gpc = (cp.cp_eax >> 8) & 0xFF;
7267bd4a6f5Skk 	width_gpc = (cp.cp_eax >> 16) & 0xFF;
7277bd4a6f5Skk 
7287bd4a6f5Skk 	if (num_gpc >= 64)
7297bd4a6f5Skk 		return (-1);
7307bd4a6f5Skk 
7317bd4a6f5Skk 	mask_gpc = BITMASK_XBITS(width_gpc);
7327bd4a6f5Skk 
7337bd4a6f5Skk 	control_gpc = BITMASK_XBITS(num_gpc);
7347bd4a6f5Skk 
7357bd4a6f5Skk 	control_mask = (control_ffc << 32) | control_gpc;
7367bd4a6f5Skk 
737e7334537SKrishnendu Sadhukhan - Sun Microsystems 	total_pmc = num_gpc + num_ffc;
7387bd4a6f5Skk 	if (total_pmc > 64) {
7397bd4a6f5Skk 		/* Too wide for the overflow bitmap */
7407bd4a6f5Skk 		return (-1);
7417bd4a6f5Skk 	}
7427bd4a6f5Skk 
743560e0ee2SKuriakose Kuruvilla 	/* FFC names */
744560e0ee2SKuriakose Kuruvilla 	ffc_allnames = kmem_alloc(num_ffc * sizeof (char *), KM_SLEEP);
745560e0ee2SKuriakose Kuruvilla 	for (i = 0; i < num_ffc; i++) {
746560e0ee2SKuriakose Kuruvilla 		ffc_allnames[i] = kmem_alloc(
747560e0ee2SKuriakose Kuruvilla 		    strlen(ffc_names[i]) + strlen(ffc_genericnames[i]) + 2,
748560e0ee2SKuriakose Kuruvilla 		    KM_SLEEP);
749560e0ee2SKuriakose Kuruvilla 
750560e0ee2SKuriakose Kuruvilla 		ffc_allnames[i][0] = '\0';
751560e0ee2SKuriakose Kuruvilla 		(void) strcat(ffc_allnames[i], ffc_names[i]);
752560e0ee2SKuriakose Kuruvilla 
753560e0ee2SKuriakose Kuruvilla 		/* Check if this ffc has a generic name */
754560e0ee2SKuriakose Kuruvilla 		if (strcmp(ffc_genericnames[i], "") != 0) {
755560e0ee2SKuriakose Kuruvilla 			(void) strcat(ffc_allnames[i], ",");
756560e0ee2SKuriakose Kuruvilla 			(void) strcat(ffc_allnames[i], ffc_genericnames[i]);
757560e0ee2SKuriakose Kuruvilla 		}
758560e0ee2SKuriakose Kuruvilla 	}
759560e0ee2SKuriakose Kuruvilla 
760560e0ee2SKuriakose Kuruvilla 	/* GPC events for Family 6 Models 15, 23 and 29 only */
761a18ddb3cSKuriakose Kuruvilla 	if ((cpuid_getfamily(CPU) == 6) &&
762afb806e6SKuriakose Kuruvilla 	    ((cpuid_getmodel(CPU) == 15) || (cpuid_getmodel(CPU) == 23) ||
763afb806e6SKuriakose Kuruvilla 	    (cpuid_getmodel(CPU) == 29))) {
764a18ddb3cSKuriakose Kuruvilla 		(void) snprintf(core_impl_name, IMPL_NAME_LEN,
765a18ddb3cSKuriakose Kuruvilla 		    "Core Microarchitecture");
766afb806e6SKuriakose Kuruvilla 		pcbe_init_core_uarch();
767a18ddb3cSKuriakose Kuruvilla 		return (0);
768a18ddb3cSKuriakose Kuruvilla 	}
769a18ddb3cSKuriakose Kuruvilla 
770a18ddb3cSKuriakose Kuruvilla 	(void) snprintf(core_impl_name, IMPL_NAME_LEN,
771a18ddb3cSKuriakose Kuruvilla 	    "Intel Arch PerfMon v%d on Family %d Model %d",
772a18ddb3cSKuriakose Kuruvilla 	    versionid, cpuid_getfamily(CPU), cpuid_getmodel(CPU));
773a18ddb3cSKuriakose Kuruvilla 
774e7334537SKrishnendu Sadhukhan - Sun Microsystems 	/*
775e7334537SKrishnendu Sadhukhan - Sun Microsystems 	 * Architectural events
776e7334537SKrishnendu Sadhukhan - Sun Microsystems 	 */
777a18ddb3cSKuriakose Kuruvilla 	arch_events_vector_length = (cp.cp_eax >> 24) & 0xFF;
778a18ddb3cSKuriakose Kuruvilla 
779a18ddb3cSKuriakose Kuruvilla 	ASSERT(known_arch_events == arch_events_vector_length);
780a18ddb3cSKuriakose Kuruvilla 
781a18ddb3cSKuriakose Kuruvilla 	/*
782a18ddb3cSKuriakose Kuruvilla 	 * To handle the case where a new performance monitoring setup is run
783a18ddb3cSKuriakose Kuruvilla 	 * on a non-debug kernel
784a18ddb3cSKuriakose Kuruvilla 	 */
785a18ddb3cSKuriakose Kuruvilla 	if (known_arch_events > arch_events_vector_length) {
786a18ddb3cSKuriakose Kuruvilla 		known_arch_events = arch_events_vector_length;
787a18ddb3cSKuriakose Kuruvilla 	} else {
788a18ddb3cSKuriakose Kuruvilla 		arch_events_vector_length = known_arch_events;
789a18ddb3cSKuriakose Kuruvilla 	}
790a18ddb3cSKuriakose Kuruvilla 
791a18ddb3cSKuriakose Kuruvilla 	arch_events_vector = cp.cp_ebx &
792a18ddb3cSKuriakose Kuruvilla 	    BITMASK_XBITS(arch_events_vector_length);
793a18ddb3cSKuriakose Kuruvilla 
794e7334537SKrishnendu Sadhukhan - Sun Microsystems 	/*
795e7334537SKrishnendu Sadhukhan - Sun Microsystems 	 * Process architectural and non-architectural events using GPC
796e7334537SKrishnendu Sadhukhan - Sun Microsystems 	 */
7977bd4a6f5Skk 	if (num_gpc > 0) {
798a18ddb3cSKuriakose Kuruvilla 
7997bd4a6f5Skk 		gpc_names = kmem_alloc(num_gpc * sizeof (char *), KM_SLEEP);
8007bd4a6f5Skk 
801a18ddb3cSKuriakose Kuruvilla 		/* Calculate space required for the architectural gpc events */
802a18ddb3cSKuriakose Kuruvilla 		arch_events_string_length = 0;
803a18ddb3cSKuriakose Kuruvilla 		for (i = 0; i < known_arch_events; i++) {
804a18ddb3cSKuriakose Kuruvilla 			if (((1U << i) & arch_events_vector) == 0) {
805a18ddb3cSKuriakose Kuruvilla 				arch_events_string_length +=
806a18ddb3cSKuriakose Kuruvilla 				    strlen(arch_events_table[i].name) + 1;
807560e0ee2SKuriakose Kuruvilla 				if (strcmp(arch_genevents_table[i], "") != 0) {
808560e0ee2SKuriakose Kuruvilla 					arch_events_string_length +=
809560e0ee2SKuriakose Kuruvilla 					    strlen(arch_genevents_table[i]) + 1;
810560e0ee2SKuriakose Kuruvilla 				}
811a18ddb3cSKuriakose Kuruvilla 			}
812a18ddb3cSKuriakose Kuruvilla 		}
813a18ddb3cSKuriakose Kuruvilla 
814e7334537SKrishnendu Sadhukhan - Sun Microsystems 		/* Non-architectural events list */
81503dbaac7SKuriakose Kuruvilla 		model = cpuid_getmodel(CPU);
816c18e9bc3SRobert Mustacchi 		stepping = cpuid_getstep(CPU);
817c18e9bc3SRobert Mustacchi 		events_table = core_cpcgen_table(model, stepping);
8187bd4a6f5Skk 
8197bd4a6f5Skk 		for (i = 0; i < num_gpc; i++) {
820a18ddb3cSKuriakose Kuruvilla 
821e7334537SKrishnendu Sadhukhan - Sun Microsystems 			/*
822e7334537SKrishnendu Sadhukhan - Sun Microsystems 			 * Determine length of all supported event names
823e7334537SKrishnendu Sadhukhan - Sun Microsystems 			 * (architectural + non-architectural)
824e7334537SKrishnendu Sadhukhan - Sun Microsystems 			 */
825a18ddb3cSKuriakose Kuruvilla 			size = arch_events_string_length;
826a18ddb3cSKuriakose Kuruvilla 			for (j = 0; events_table != NULL &&
827a18ddb3cSKuriakose Kuruvilla 			    events_table[j].eventselect != NT_END;
828a18ddb3cSKuriakose Kuruvilla 			    j++) {
829a18ddb3cSKuriakose Kuruvilla 				if (C(i) & events_table[j].supported_counters) {
830a18ddb3cSKuriakose Kuruvilla 					size += strlen(events_table[j].name) +
831a18ddb3cSKuriakose Kuruvilla 					    1;
8327bd4a6f5Skk 				}
8337bd4a6f5Skk 			}
8347bd4a6f5Skk 
835a18ddb3cSKuriakose Kuruvilla 			/* Allocate memory for this pics list */
836a18ddb3cSKuriakose Kuruvilla 			gpc_names[i] = kmem_alloc(size + 1, KM_SLEEP);
8377bd4a6f5Skk 			gpc_names[i][0] = '\0';
838a18ddb3cSKuriakose Kuruvilla 			if (size == 0) {
839a18ddb3cSKuriakose Kuruvilla 				continue;
840a18ddb3cSKuriakose Kuruvilla 			}
841a18ddb3cSKuriakose Kuruvilla 
842e7334537SKrishnendu Sadhukhan - Sun Microsystems 			/*
843e7334537SKrishnendu Sadhukhan - Sun Microsystems 			 * Create the list of all supported events
844e7334537SKrishnendu Sadhukhan - Sun Microsystems 			 * (architectural + non-architectural)
845e7334537SKrishnendu Sadhukhan - Sun Microsystems 			 */
846a18ddb3cSKuriakose Kuruvilla 			for (j = 0; j < known_arch_events; j++) {
847a18ddb3cSKuriakose Kuruvilla 				if (((1U << j) & arch_events_vector) == 0) {
848a18ddb3cSKuriakose Kuruvilla 					(void) strcat(gpc_names[i],
849a18ddb3cSKuriakose Kuruvilla 					    arch_events_table[j].name);
8507bd4a6f5Skk 					(void) strcat(gpc_names[i], ",");
851560e0ee2SKuriakose Kuruvilla 					if (strcmp(
852560e0ee2SKuriakose Kuruvilla 					    arch_genevents_table[j], "")
853560e0ee2SKuriakose Kuruvilla 					    != 0) {
854560e0ee2SKuriakose Kuruvilla 						(void) strcat(gpc_names[i],
855560e0ee2SKuriakose Kuruvilla 						    arch_genevents_table[j]);
856560e0ee2SKuriakose Kuruvilla 						(void) strcat(gpc_names[i],
857560e0ee2SKuriakose Kuruvilla 						    ",");
858560e0ee2SKuriakose Kuruvilla 					}
8597bd4a6f5Skk 				}
8607bd4a6f5Skk 			}
861a18ddb3cSKuriakose Kuruvilla 
862a18ddb3cSKuriakose Kuruvilla 			for (j = 0; events_table != NULL &&
863a18ddb3cSKuriakose Kuruvilla 			    events_table[j].eventselect != NT_END;
864a18ddb3cSKuriakose Kuruvilla 			    j++) {
865a18ddb3cSKuriakose Kuruvilla 				if (C(i) & events_table[j].supported_counters) {
866a18ddb3cSKuriakose Kuruvilla 					(void) strcat(gpc_names[i],
867a18ddb3cSKuriakose Kuruvilla 					    events_table[j].name);
868a18ddb3cSKuriakose Kuruvilla 					(void) strcat(gpc_names[i], ",");
869a18ddb3cSKuriakose Kuruvilla 				}
8707bd4a6f5Skk 			}
871e7334537SKrishnendu Sadhukhan - Sun Microsystems 
872e7334537SKrishnendu Sadhukhan - Sun Microsystems 			/* Remove trailing comma */
873a18ddb3cSKuriakose Kuruvilla 			gpc_names[i][size - 1] = '\0';
8747bd4a6f5Skk 		}
8757bd4a6f5Skk 	}
876560e0ee2SKuriakose Kuruvilla 
8777bd4a6f5Skk 	return (0);
8787bd4a6f5Skk }
8797bd4a6f5Skk 
core_pcbe_ncounters()8807bd4a6f5Skk static uint_t core_pcbe_ncounters()
8817bd4a6f5Skk {
8827bd4a6f5Skk 	return (total_pmc);
8837bd4a6f5Skk }
8847bd4a6f5Skk 
core_pcbe_impl_name(void)8857bd4a6f5Skk static const char *core_pcbe_impl_name(void)
8867bd4a6f5Skk {
8877bd4a6f5Skk 	return (core_impl_name);
8887bd4a6f5Skk }
8897bd4a6f5Skk 
core_pcbe_cpuref(void)8907bd4a6f5Skk static const char *core_pcbe_cpuref(void)
8917bd4a6f5Skk {
8927bd4a6f5Skk 	return (core_cpuref);
8937bd4a6f5Skk }
8947bd4a6f5Skk 
core_pcbe_list_events(uint_t picnum)8957bd4a6f5Skk static char *core_pcbe_list_events(uint_t picnum)
8967bd4a6f5Skk {
8977bd4a6f5Skk 	ASSERT(picnum < cpc_ncounters);
8987bd4a6f5Skk 
8997bd4a6f5Skk 	if (picnum < num_gpc) {
9007bd4a6f5Skk 		return (gpc_names[picnum]);
9017bd4a6f5Skk 	} else {
902560e0ee2SKuriakose Kuruvilla 		return (ffc_allnames[picnum - num_gpc]);
9037bd4a6f5Skk 	}
9047bd4a6f5Skk }
9057bd4a6f5Skk 
core_pcbe_list_attrs(void)9067bd4a6f5Skk static char *core_pcbe_list_attrs(void)
9077bd4a6f5Skk {
908a18ddb3cSKuriakose Kuruvilla 	if (versionid >= 3) {
909a18ddb3cSKuriakose Kuruvilla 		return ("edge,inv,umask,cmask,anythr");
910a18ddb3cSKuriakose Kuruvilla 	} else {
911a18ddb3cSKuriakose Kuruvilla 		return ("edge,pc,inv,umask,cmask");
912a18ddb3cSKuriakose Kuruvilla 	}
9137bd4a6f5Skk }
9147bd4a6f5Skk 
915afb806e6SKuriakose Kuruvilla static const struct nametable_core_uarch *
find_gpcevent_core_uarch(char * name,const struct nametable_core_uarch * nametable)916afb806e6SKuriakose Kuruvilla find_gpcevent_core_uarch(char *name,
917afb806e6SKuriakose Kuruvilla     const struct nametable_core_uarch *nametable)
9187bd4a6f5Skk {
919afb806e6SKuriakose Kuruvilla 	const struct nametable_core_uarch *n;
920e7334537SKrishnendu Sadhukhan - Sun Microsystems 	int compare_result = -1;
9217bd4a6f5Skk 
9227bd4a6f5Skk 	for (n = nametable; n->event_num != NT_END; n++) {
9237bd4a6f5Skk 		compare_result = strcmp(name, n->name);
9247bd4a6f5Skk 		if (compare_result <= 0) {
9257bd4a6f5Skk 			break;
9267bd4a6f5Skk 		}
9277bd4a6f5Skk 	}
9287bd4a6f5Skk 
9297bd4a6f5Skk 	if (compare_result == 0) {
9307bd4a6f5Skk 		return (n);
9317bd4a6f5Skk 	}
9327bd4a6f5Skk 
9337bd4a6f5Skk 	return (NULL);
9347bd4a6f5Skk }
9357bd4a6f5Skk 
936560e0ee2SKuriakose Kuruvilla static const struct generic_events *
find_generic_events(char * name,const struct generic_events * table)937560e0ee2SKuriakose Kuruvilla find_generic_events(char *name, const struct generic_events *table)
938560e0ee2SKuriakose Kuruvilla {
939560e0ee2SKuriakose Kuruvilla 	const struct generic_events *n;
940560e0ee2SKuriakose Kuruvilla 
941560e0ee2SKuriakose Kuruvilla 	for (n = table; n->event_num != NT_END; n++) {
942560e0ee2SKuriakose Kuruvilla 		if (strcmp(name, n->name) == 0) {
943560e0ee2SKuriakose Kuruvilla 			return (n);
944560e0ee2SKuriakose Kuruvilla 		};
945560e0ee2SKuriakose Kuruvilla 	}
946560e0ee2SKuriakose Kuruvilla 
947560e0ee2SKuriakose Kuruvilla 	return (NULL);
948560e0ee2SKuriakose Kuruvilla }
949560e0ee2SKuriakose Kuruvilla 
950a18ddb3cSKuriakose Kuruvilla static const struct events_table_t *
find_gpcevent(char * name)951a18ddb3cSKuriakose Kuruvilla find_gpcevent(char *name)
952a18ddb3cSKuriakose Kuruvilla {
953a18ddb3cSKuriakose Kuruvilla 	int i;
954a18ddb3cSKuriakose Kuruvilla 
955e7334537SKrishnendu Sadhukhan - Sun Microsystems 	/* Search architectural events */
956a18ddb3cSKuriakose Kuruvilla 	for (i = 0; i < known_arch_events; i++) {
957560e0ee2SKuriakose Kuruvilla 		if (strcmp(name, arch_events_table[i].name) == 0 ||
958560e0ee2SKuriakose Kuruvilla 		    strcmp(name, arch_genevents_table[i]) == 0) {
959a18ddb3cSKuriakose Kuruvilla 			if (((1U << i) & arch_events_vector) == 0) {
960a18ddb3cSKuriakose Kuruvilla 				return (&arch_events_table[i]);
961a18ddb3cSKuriakose Kuruvilla 			}
962a18ddb3cSKuriakose Kuruvilla 		}
963a18ddb3cSKuriakose Kuruvilla 	}
964a18ddb3cSKuriakose Kuruvilla 
965e7334537SKrishnendu Sadhukhan - Sun Microsystems 	/* Search non-architectural events */
966e7334537SKrishnendu Sadhukhan - Sun Microsystems 	if (events_table != NULL) {
967e7334537SKrishnendu Sadhukhan - Sun Microsystems 		for (i = 0; events_table[i].eventselect != NT_END; i++) {
968e7334537SKrishnendu Sadhukhan - Sun Microsystems 			if (strcmp(name, events_table[i].name) == 0) {
969e7334537SKrishnendu Sadhukhan - Sun Microsystems 				return (&events_table[i]);
970e7334537SKrishnendu Sadhukhan - Sun Microsystems 			}
971a18ddb3cSKuriakose Kuruvilla 		}
972a18ddb3cSKuriakose Kuruvilla 	}
973a18ddb3cSKuriakose Kuruvilla 
974a18ddb3cSKuriakose Kuruvilla 	return (NULL);
975a18ddb3cSKuriakose Kuruvilla }
976560e0ee2SKuriakose Kuruvilla 
9777bd4a6f5Skk static uint64_t
core_pcbe_event_coverage(char * event)9787bd4a6f5Skk core_pcbe_event_coverage(char *event)
9797bd4a6f5Skk {
9807bd4a6f5Skk 	uint64_t bitmap;
9817bd4a6f5Skk 	uint64_t bitmask;
982a18ddb3cSKuriakose Kuruvilla 	const struct events_table_t *n;
9837bd4a6f5Skk 	int i;
9847bd4a6f5Skk 
9857bd4a6f5Skk 	bitmap = 0;
9867bd4a6f5Skk 
9877bd4a6f5Skk 	/* Is it an event that a GPC can track? */
988a18ddb3cSKuriakose Kuruvilla 	if (versionid >= 3) {
989a18ddb3cSKuriakose Kuruvilla 		n = find_gpcevent(event);
990a18ddb3cSKuriakose Kuruvilla 		if (n != NULL) {
991a18ddb3cSKuriakose Kuruvilla 			bitmap |= (n->supported_counters &
992a18ddb3cSKuriakose Kuruvilla 			    BITMASK_XBITS(num_gpc));
993a18ddb3cSKuriakose Kuruvilla 		}
994a18ddb3cSKuriakose Kuruvilla 	} else {
995560e0ee2SKuriakose Kuruvilla 		if (find_generic_events(event, cmn_generic_events) != NULL) {
996560e0ee2SKuriakose Kuruvilla 			bitmap |= BITMASK_XBITS(num_gpc);
997*a1e3874eSJohn Levon 		} else if (find_generic_events(event,
998*a1e3874eSJohn Levon 		    generic_events_pic0) != NULL) {
999560e0ee2SKuriakose Kuruvilla 			bitmap |= 1ULL;
1000560e0ee2SKuriakose Kuruvilla 		} else if (find_gpcevent_core_uarch(event,
1001560e0ee2SKuriakose Kuruvilla 		    cmn_gpc_events_core_uarch) != NULL) {
1002a18ddb3cSKuriakose Kuruvilla 			bitmap |= BITMASK_XBITS(num_gpc);
1003afb806e6SKuriakose Kuruvilla 		} else if (find_gpcevent_core_uarch(event, pic0_events) !=
1004afb806e6SKuriakose Kuruvilla 		    NULL) {
1005a18ddb3cSKuriakose Kuruvilla 			bitmap |= 1ULL;
1006afb806e6SKuriakose Kuruvilla 		} else if (find_gpcevent_core_uarch(event, pic1_events) !=
1007afb806e6SKuriakose Kuruvilla 		    NULL) {
1008a18ddb3cSKuriakose Kuruvilla 			bitmap |= 1ULL << 1;
1009a18ddb3cSKuriakose Kuruvilla 		}
10107bd4a6f5Skk 	}
10117bd4a6f5Skk 
10127bd4a6f5Skk 	/* Check if the event can be counted in the fixed-function counters */
10137bd4a6f5Skk 	if (num_ffc > 0) {
10147bd4a6f5Skk 		bitmask = 1ULL << num_gpc;
10157bd4a6f5Skk 		for (i = 0; i < num_ffc; i++) {
10167bd4a6f5Skk 			if (strcmp(event, ffc_names[i]) == 0) {
10177bd4a6f5Skk 				bitmap |= bitmask;
1018560e0ee2SKuriakose Kuruvilla 			} else if (strcmp(event, ffc_genericnames[i]) == 0) {
1019560e0ee2SKuriakose Kuruvilla 				bitmap |= bitmask;
10207bd4a6f5Skk 			}
10217bd4a6f5Skk 			bitmask = bitmask << 1;
10227bd4a6f5Skk 		}
10237bd4a6f5Skk 	}
10247bd4a6f5Skk 
10257bd4a6f5Skk 	return (bitmap);
10267bd4a6f5Skk }
10277bd4a6f5Skk 
10287bd4a6f5Skk static uint64_t
core_pcbe_overflow_bitmap(void)10297bd4a6f5Skk core_pcbe_overflow_bitmap(void)
10307bd4a6f5Skk {
10317bd4a6f5Skk 	uint64_t interrupt_status;
10327bd4a6f5Skk 	uint64_t intrbits_ffc;
10337bd4a6f5Skk 	uint64_t intrbits_gpc;
10347bd4a6f5Skk 	extern int kcpc_hw_overflow_intr_installed;
10357bd4a6f5Skk 	uint64_t overflow_bitmap;
10367bd4a6f5Skk 
10377bd4a6f5Skk 	RDMSR(PERF_GLOBAL_STATUS, interrupt_status);
10387bd4a6f5Skk 	WRMSR(PERF_GLOBAL_OVF_CTRL, interrupt_status);
10397bd4a6f5Skk 
10407bd4a6f5Skk 	interrupt_status = interrupt_status & control_mask;
10417bd4a6f5Skk 	intrbits_ffc = (interrupt_status >> 32) & control_ffc;
10427bd4a6f5Skk 	intrbits_gpc = interrupt_status & control_gpc;
10437bd4a6f5Skk 	overflow_bitmap = (intrbits_ffc << num_gpc) | intrbits_gpc;
10447bd4a6f5Skk 
10457bd4a6f5Skk 	ASSERT(kcpc_hw_overflow_intr_installed);
10467bd4a6f5Skk 	(*kcpc_hw_enable_cpc_intr)();
10477bd4a6f5Skk 
10487bd4a6f5Skk 	return (overflow_bitmap);
10497bd4a6f5Skk }
10507bd4a6f5Skk 
10517bd4a6f5Skk static int
check_cpc_securitypolicy(core_pcbe_config_t * conf,const struct nametable_core_uarch * n)1052a18ddb3cSKuriakose Kuruvilla check_cpc_securitypolicy(core_pcbe_config_t *conf,
1053afb806e6SKuriakose Kuruvilla     const struct nametable_core_uarch *n)
10547bd4a6f5Skk {
10557bd4a6f5Skk 	if (conf->core_ctl & n->restricted_bits) {
10567bd4a6f5Skk 		if (secpolicy_cpc_cpu(crgetcred()) != 0) {
10577bd4a6f5Skk 			return (CPC_ATTR_REQUIRES_PRIVILEGE);
10587bd4a6f5Skk 		}
10597bd4a6f5Skk 	}
10607bd4a6f5Skk 	return (0);
10617bd4a6f5Skk }
10627bd4a6f5Skk 
10637bd4a6f5Skk static int
configure_gpc(uint_t picnum,char * event,uint64_t preset,uint32_t flags,uint_t nattrs,kcpc_attr_t * attrs,void ** data)10647bd4a6f5Skk configure_gpc(uint_t picnum, char *event, uint64_t preset, uint32_t flags,
10657bd4a6f5Skk     uint_t nattrs, kcpc_attr_t *attrs, void **data)
10667bd4a6f5Skk {
10677bd4a6f5Skk 	core_pcbe_config_t	conf;
1068afb806e6SKuriakose Kuruvilla 	const struct nametable_core_uarch	*n;
1069560e0ee2SKuriakose Kuruvilla 	const struct generic_events *k = NULL;
1070afb806e6SKuriakose Kuruvilla 	const struct nametable_core_uarch	*m;
1071afb806e6SKuriakose Kuruvilla 	const struct nametable_core_uarch	*picspecific_events;
1072afb806e6SKuriakose Kuruvilla 	struct nametable_core_uarch	nt_raw = { "", 0x0, 0x0 };
10737bd4a6f5Skk 	uint_t			i;
10747bd4a6f5Skk 	long			event_num;
1075a18ddb3cSKuriakose Kuruvilla 	const struct events_table_t *eventcode;
10767bd4a6f5Skk 
10777bd4a6f5Skk 	if (((preset & BITS_EXTENDED_FROM_31) != 0) &&
10787bd4a6f5Skk 	    ((preset & BITS_EXTENDED_FROM_31) !=
10797bd4a6f5Skk 	    BITS_EXTENDED_FROM_31)) {
10807bd4a6f5Skk 
10817bd4a6f5Skk 		/*
10827bd4a6f5Skk 		 * Bits beyond bit-31 in the general-purpose counters can only
10837bd4a6f5Skk 		 * be written to by extension of bit 31.  We cannot preset
10847bd4a6f5Skk 		 * these bits to any value other than all 1s or all 0s.
10857bd4a6f5Skk 		 */
10867bd4a6f5Skk 		return (CPC_ATTRIBUTE_OUT_OF_RANGE);
10877bd4a6f5Skk 	}
10887bd4a6f5Skk 
1089a18ddb3cSKuriakose Kuruvilla 	if (versionid >= 3) {
1090a18ddb3cSKuriakose Kuruvilla 		eventcode = find_gpcevent(event);
1091a18ddb3cSKuriakose Kuruvilla 		if (eventcode != NULL) {
1092a18ddb3cSKuriakose Kuruvilla 			if ((C(picnum) & eventcode->supported_counters) == 0) {
1093a18ddb3cSKuriakose Kuruvilla 				return (CPC_PIC_NOT_CAPABLE);
1094a18ddb3cSKuriakose Kuruvilla 			}
1095560e0ee2SKuriakose Kuruvilla 			if (nattrs > 0 &&
1096560e0ee2SKuriakose Kuruvilla 			    (strncmp("PAPI_", event, 5) == 0)) {
1097560e0ee2SKuriakose Kuruvilla 				return (CPC_ATTRIBUTE_OUT_OF_RANGE);
1098560e0ee2SKuriakose Kuruvilla 			}
1099a18ddb3cSKuriakose Kuruvilla 			conf.core_ctl = eventcode->eventselect;
1100a18ddb3cSKuriakose Kuruvilla 			conf.core_ctl |= eventcode->unitmask <<
1101a18ddb3cSKuriakose Kuruvilla 			    CORE_UMASK_SHIFT;
1102a18ddb3cSKuriakose Kuruvilla 		} else {
1103a18ddb3cSKuriakose Kuruvilla 			/* Event specified as raw event code */
1104a18ddb3cSKuriakose Kuruvilla 			if (ddi_strtol(event, NULL, 0, &event_num) != 0) {
1105a18ddb3cSKuriakose Kuruvilla 				return (CPC_INVALID_EVENT);
1106a18ddb3cSKuriakose Kuruvilla 			}
1107a18ddb3cSKuriakose Kuruvilla 			conf.core_ctl = event_num & 0xFF;
11087bd4a6f5Skk 		}
1109a18ddb3cSKuriakose Kuruvilla 	} else {
1110560e0ee2SKuriakose Kuruvilla 		if ((k = find_generic_events(event, cmn_generic_events)) !=
1111560e0ee2SKuriakose Kuruvilla 		    NULL ||
1112560e0ee2SKuriakose Kuruvilla 		    (picnum == 0 &&
1113560e0ee2SKuriakose Kuruvilla 		    (k = find_generic_events(event, generic_events_pic0)) !=
1114560e0ee2SKuriakose Kuruvilla 		    NULL)) {
1115560e0ee2SKuriakose Kuruvilla 			if (nattrs > 0) {
1116560e0ee2SKuriakose Kuruvilla 				return (CPC_ATTRIBUTE_OUT_OF_RANGE);
1117a18ddb3cSKuriakose Kuruvilla 			}
1118560e0ee2SKuriakose Kuruvilla 			conf.core_ctl = k->event_num;
1119560e0ee2SKuriakose Kuruvilla 			conf.core_ctl |= k->umask << CORE_UMASK_SHIFT;
1120560e0ee2SKuriakose Kuruvilla 		} else {
1121560e0ee2SKuriakose Kuruvilla 			/* Not a generic event */
1122560e0ee2SKuriakose Kuruvilla 
1123560e0ee2SKuriakose Kuruvilla 			n = find_gpcevent_core_uarch(event,
1124560e0ee2SKuriakose Kuruvilla 			    cmn_gpc_events_core_uarch);
1125560e0ee2SKuriakose Kuruvilla 			if (n == NULL) {
1126560e0ee2SKuriakose Kuruvilla 				switch (picnum) {
1127560e0ee2SKuriakose Kuruvilla 					case 0:
1128560e0ee2SKuriakose Kuruvilla 						picspecific_events =
1129560e0ee2SKuriakose Kuruvilla 						    pic0_events;
1130560e0ee2SKuriakose Kuruvilla 						break;
1131560e0ee2SKuriakose Kuruvilla 					case 1:
1132560e0ee2SKuriakose Kuruvilla 						picspecific_events =
1133560e0ee2SKuriakose Kuruvilla 						    pic1_events;
1134560e0ee2SKuriakose Kuruvilla 						break;
1135560e0ee2SKuriakose Kuruvilla 					default:
1136560e0ee2SKuriakose Kuruvilla 						picspecific_events = NULL;
1137560e0ee2SKuriakose Kuruvilla 						break;
1138560e0ee2SKuriakose Kuruvilla 				}
1139560e0ee2SKuriakose Kuruvilla 				if (picspecific_events != NULL) {
1140560e0ee2SKuriakose Kuruvilla 					n = find_gpcevent_core_uarch(event,
1141560e0ee2SKuriakose Kuruvilla 					    picspecific_events);
1142560e0ee2SKuriakose Kuruvilla 				}
1143a18ddb3cSKuriakose Kuruvilla 			}
1144560e0ee2SKuriakose Kuruvilla 			if (n == NULL) {
1145560e0ee2SKuriakose Kuruvilla 
1146560e0ee2SKuriakose Kuruvilla 				/*
1147560e0ee2SKuriakose Kuruvilla 				 * Check if this is a case where the event was
1148560e0ee2SKuriakose Kuruvilla 				 * specified directly by its event number
1149560e0ee2SKuriakose Kuruvilla 				 * instead of its name string.
1150560e0ee2SKuriakose Kuruvilla 				 */
1151560e0ee2SKuriakose Kuruvilla 				if (ddi_strtol(event, NULL, 0, &event_num) !=
1152560e0ee2SKuriakose Kuruvilla 				    0) {
1153560e0ee2SKuriakose Kuruvilla 					return (CPC_INVALID_EVENT);
1154560e0ee2SKuriakose Kuruvilla 				}
11557bd4a6f5Skk 
1156560e0ee2SKuriakose Kuruvilla 				event_num = event_num & 0xFF;
1157560e0ee2SKuriakose Kuruvilla 
1158560e0ee2SKuriakose Kuruvilla 				/*
1159560e0ee2SKuriakose Kuruvilla 				 * Search the event table to find out if the
1160560e0ee2SKuriakose Kuruvilla 				 * event specified has an privilege
1161560e0ee2SKuriakose Kuruvilla 				 * requirements.  Currently none of the
1162560e0ee2SKuriakose Kuruvilla 				 * pic-specific counters have any privilege
1163560e0ee2SKuriakose Kuruvilla 				 * requirements.  Hence only the table
1164560e0ee2SKuriakose Kuruvilla 				 * cmn_gpc_events_core_uarch is searched.
1165560e0ee2SKuriakose Kuruvilla 				 */
1166560e0ee2SKuriakose Kuruvilla 				for (m = cmn_gpc_events_core_uarch;
1167560e0ee2SKuriakose Kuruvilla 				    m->event_num != NT_END;
1168560e0ee2SKuriakose Kuruvilla 				    m++) {
1169560e0ee2SKuriakose Kuruvilla 					if (event_num == m->event_num) {
1170560e0ee2SKuriakose Kuruvilla 						break;
1171560e0ee2SKuriakose Kuruvilla 					}
1172560e0ee2SKuriakose Kuruvilla 				}
1173560e0ee2SKuriakose Kuruvilla 				if (m->event_num == NT_END) {
1174560e0ee2SKuriakose Kuruvilla 					nt_raw.event_num = (uint8_t)event_num;
1175560e0ee2SKuriakose Kuruvilla 					n = &nt_raw;
1176560e0ee2SKuriakose Kuruvilla 				} else {
1177560e0ee2SKuriakose Kuruvilla 					n = m;
11787bd4a6f5Skk 				}
11797bd4a6f5Skk 			}
1180560e0ee2SKuriakose Kuruvilla 			conf.core_ctl = n->event_num; /* Event Select */
11817bd4a6f5Skk 		}
11827bd4a6f5Skk 	}
11837bd4a6f5Skk 
1184a18ddb3cSKuriakose Kuruvilla 
11857bd4a6f5Skk 	conf.core_picno = picnum;
11867bd4a6f5Skk 	conf.core_pictype = CORE_GPC;
11877bd4a6f5Skk 	conf.core_rawpic = preset & mask_gpc;
11887bd4a6f5Skk 
11897bd4a6f5Skk 	conf.core_pes = GPC_BASE_PES + picnum;
11907bd4a6f5Skk 	conf.core_pmc = GPC_BASE_PMC + picnum;
11917bd4a6f5Skk 
11927bd4a6f5Skk 	for (i = 0; i < nattrs; i++) {
11937bd4a6f5Skk 		if (strncmp(attrs[i].ka_name, "umask", 6) == 0) {
119434fdb700SKuriakose Kuruvilla 			if ((attrs[i].ka_val | CORE_UMASK_MASK) !=
119534fdb700SKuriakose Kuruvilla 			    CORE_UMASK_MASK) {
11967bd4a6f5Skk 				return (CPC_ATTRIBUTE_OUT_OF_RANGE);
11977bd4a6f5Skk 			}
119834fdb700SKuriakose Kuruvilla 			/* Clear out the default umask */
119934fdb700SKuriakose Kuruvilla 			conf.core_ctl &= ~ (CORE_UMASK_MASK <<
120034fdb700SKuriakose Kuruvilla 			    CORE_UMASK_SHIFT);
120134fdb700SKuriakose Kuruvilla 			/* Use the user provided umask */
12027bd4a6f5Skk 			conf.core_ctl |= attrs[i].ka_val <<
12037bd4a6f5Skk 			    CORE_UMASK_SHIFT;
1204a18ddb3cSKuriakose Kuruvilla 		} else  if (strncmp(attrs[i].ka_name, "edge", 6) == 0) {
12057bd4a6f5Skk 			if (attrs[i].ka_val != 0)
12067bd4a6f5Skk 				conf.core_ctl |= CORE_EDGE;
12077bd4a6f5Skk 		} else if (strncmp(attrs[i].ka_name, "inv", 4) == 0) {
12087bd4a6f5Skk 			if (attrs[i].ka_val != 0)
12097bd4a6f5Skk 				conf.core_ctl |= CORE_INV;
12107bd4a6f5Skk 		} else if (strncmp(attrs[i].ka_name, "cmask", 6) == 0) {
12117bd4a6f5Skk 			if ((attrs[i].ka_val | CORE_CMASK_MASK) !=
12127bd4a6f5Skk 			    CORE_CMASK_MASK) {
12137bd4a6f5Skk 				return (CPC_ATTRIBUTE_OUT_OF_RANGE);
12147bd4a6f5Skk 			}
1215a18ddb3cSKuriakose Kuruvilla 			conf.core_ctl |= attrs[i].ka_val <<
1216a18ddb3cSKuriakose Kuruvilla 			    CORE_CMASK_SHIFT;
1217a18ddb3cSKuriakose Kuruvilla 		} else if (strncmp(attrs[i].ka_name, "anythr", 7) ==
1218a18ddb3cSKuriakose Kuruvilla 		    0) {
1219a18ddb3cSKuriakose Kuruvilla 			if (versionid < 3)
1220a18ddb3cSKuriakose Kuruvilla 				return (CPC_INVALID_ATTRIBUTE);
1221a18ddb3cSKuriakose Kuruvilla 			if (secpolicy_cpc_cpu(crgetcred()) != 0) {
1222a18ddb3cSKuriakose Kuruvilla 				return (CPC_ATTR_REQUIRES_PRIVILEGE);
1223a18ddb3cSKuriakose Kuruvilla 			}
1224a18ddb3cSKuriakose Kuruvilla 			if (attrs[i].ka_val != 0)
1225a18ddb3cSKuriakose Kuruvilla 				conf.core_ctl |= CORE_ANYTHR;
12267bd4a6f5Skk 		} else {
12277bd4a6f5Skk 			return (CPC_INVALID_ATTRIBUTE);
12287bd4a6f5Skk 		}
12297bd4a6f5Skk 	}
12307bd4a6f5Skk 
12317bd4a6f5Skk 	if (flags & CPC_COUNT_USER)
12327bd4a6f5Skk 		conf.core_ctl |= CORE_USR;
12337bd4a6f5Skk 	if (flags & CPC_COUNT_SYSTEM)
12347bd4a6f5Skk 		conf.core_ctl |= CORE_OS;
12357bd4a6f5Skk 	if (flags & CPC_OVF_NOTIFY_EMT)
12367bd4a6f5Skk 		conf.core_ctl |= CORE_INT;
12377bd4a6f5Skk 	conf.core_ctl |= CORE_EN;
12387bd4a6f5Skk 
1239560e0ee2SKuriakose Kuruvilla 	if (versionid < 3 && k == NULL) {
1240a18ddb3cSKuriakose Kuruvilla 		if (check_cpc_securitypolicy(&conf, n) != 0) {
1241a18ddb3cSKuriakose Kuruvilla 			return (CPC_ATTR_REQUIRES_PRIVILEGE);
1242a18ddb3cSKuriakose Kuruvilla 		}
12437bd4a6f5Skk 	}
12447bd4a6f5Skk 
12457bd4a6f5Skk 	*data = kmem_alloc(sizeof (core_pcbe_config_t), KM_SLEEP);
12467bd4a6f5Skk 	*((core_pcbe_config_t *)*data) = conf;
12477bd4a6f5Skk 
12487bd4a6f5Skk 	return (0);
12497bd4a6f5Skk }
12507bd4a6f5Skk 
12517bd4a6f5Skk static int
configure_ffc(uint_t picnum,char * event,uint64_t preset,uint32_t flags,uint_t nattrs,kcpc_attr_t * attrs,void ** data)12527bd4a6f5Skk configure_ffc(uint_t picnum, char *event, uint64_t preset, uint32_t flags,
1253a18ddb3cSKuriakose Kuruvilla     uint_t nattrs, kcpc_attr_t *attrs, void **data)
12547bd4a6f5Skk {
12557bd4a6f5Skk 	core_pcbe_config_t	*conf;
1256a18ddb3cSKuriakose Kuruvilla 	uint_t			i;
12577bd4a6f5Skk 
12587bd4a6f5Skk 	if (picnum - num_gpc >= num_ffc) {
12597bd4a6f5Skk 		return (CPC_INVALID_PICNUM);
12607bd4a6f5Skk 	}
1261a18ddb3cSKuriakose Kuruvilla 
1262560e0ee2SKuriakose Kuruvilla 	if ((strcmp(ffc_names[picnum-num_gpc], event) != 0) &&
1263560e0ee2SKuriakose Kuruvilla 	    (strcmp(ffc_genericnames[picnum-num_gpc], event) != 0)) {
12647bd4a6f5Skk 		return (CPC_INVALID_EVENT);
12657bd4a6f5Skk 	}
12667bd4a6f5Skk 
1267a18ddb3cSKuriakose Kuruvilla 	if ((versionid < 3) && (nattrs != 0)) {
12687bd4a6f5Skk 		return (CPC_INVALID_ATTRIBUTE);
12697bd4a6f5Skk 	}
12707bd4a6f5Skk 
12717bd4a6f5Skk 	conf = kmem_alloc(sizeof (core_pcbe_config_t), KM_SLEEP);
1272a18ddb3cSKuriakose Kuruvilla 	conf->core_ctl = 0;
1273a18ddb3cSKuriakose Kuruvilla 
1274a18ddb3cSKuriakose Kuruvilla 	for (i = 0; i < nattrs; i++) {
1275a18ddb3cSKuriakose Kuruvilla 		if (strncmp(attrs[i].ka_name, "anythr", 7) == 0) {
1276a18ddb3cSKuriakose Kuruvilla 			if (secpolicy_cpc_cpu(crgetcred()) != 0) {
127796992ee7SEthindra Ramamurthy 				kmem_free(conf, sizeof (core_pcbe_config_t));
1278a18ddb3cSKuriakose Kuruvilla 				return (CPC_ATTR_REQUIRES_PRIVILEGE);
1279a18ddb3cSKuriakose Kuruvilla 			}
1280a18ddb3cSKuriakose Kuruvilla 			if (attrs[i].ka_val != 0) {
1281a18ddb3cSKuriakose Kuruvilla 				conf->core_ctl |= CORE_FFC_ANYTHR;
1282a18ddb3cSKuriakose Kuruvilla 			}
1283a18ddb3cSKuriakose Kuruvilla 		} else {
1284a18ddb3cSKuriakose Kuruvilla 			kmem_free(conf, sizeof (core_pcbe_config_t));
1285a18ddb3cSKuriakose Kuruvilla 			return (CPC_INVALID_ATTRIBUTE);
1286a18ddb3cSKuriakose Kuruvilla 		}
1287a18ddb3cSKuriakose Kuruvilla 	}
12887bd4a6f5Skk 
12897bd4a6f5Skk 	conf->core_picno = picnum;
12907bd4a6f5Skk 	conf->core_pictype = CORE_FFC;
12917bd4a6f5Skk 	conf->core_rawpic = preset & mask_ffc;
12927bd4a6f5Skk 	conf->core_pmc = FFC_BASE_PMC + (picnum - num_gpc);
12937bd4a6f5Skk 
12947bd4a6f5Skk 	/* All fixed-function counters have the same control register */
12957bd4a6f5Skk 	conf->core_pes = PERF_FIXED_CTR_CTRL;
12967bd4a6f5Skk 
12977bd4a6f5Skk 	if (flags & CPC_COUNT_USER)
12987bd4a6f5Skk 		conf->core_ctl |= CORE_FFC_USR_EN;
12997bd4a6f5Skk 	if (flags & CPC_COUNT_SYSTEM)
13007bd4a6f5Skk 		conf->core_ctl |= CORE_FFC_OS_EN;
13017bd4a6f5Skk 	if (flags & CPC_OVF_NOTIFY_EMT)
13027bd4a6f5Skk 		conf->core_ctl |= CORE_FFC_PMI;
13037bd4a6f5Skk 
13047bd4a6f5Skk 	*data = conf;
13057bd4a6f5Skk 	return (0);
13067bd4a6f5Skk }
13077bd4a6f5Skk 
13087bd4a6f5Skk /*ARGSUSED*/
13097bd4a6f5Skk static int
core_pcbe_configure(uint_t picnum,char * event,uint64_t preset,uint32_t flags,uint_t nattrs,kcpc_attr_t * attrs,void ** data,void * token)13107bd4a6f5Skk core_pcbe_configure(uint_t picnum, char *event, uint64_t preset,
13117bd4a6f5Skk     uint32_t flags, uint_t nattrs, kcpc_attr_t *attrs, void **data,
13127bd4a6f5Skk     void *token)
13137bd4a6f5Skk {
13147bd4a6f5Skk 	int			ret;
13157bd4a6f5Skk 	core_pcbe_config_t	*conf;
13167bd4a6f5Skk 
13177bd4a6f5Skk 	/*
13187bd4a6f5Skk 	 * If we've been handed an existing configuration, we need only preset
13197bd4a6f5Skk 	 * the counter value.
13207bd4a6f5Skk 	 */
13217bd4a6f5Skk 	if (*data != NULL) {
13227bd4a6f5Skk 		conf = *data;
13237bd4a6f5Skk 		ASSERT(conf->core_pictype == CORE_GPC ||
13247bd4a6f5Skk 		    conf->core_pictype == CORE_FFC);
13257bd4a6f5Skk 		if (conf->core_pictype == CORE_GPC)
13267bd4a6f5Skk 			conf->core_rawpic = preset & mask_gpc;
13277bd4a6f5Skk 		else /* CORE_FFC */
13287bd4a6f5Skk 			conf->core_rawpic = preset & mask_ffc;
13297bd4a6f5Skk 		return (0);
13307bd4a6f5Skk 	}
13317bd4a6f5Skk 
13327bd4a6f5Skk 	if (picnum >= total_pmc) {
13337bd4a6f5Skk 		return (CPC_INVALID_PICNUM);
13347bd4a6f5Skk 	}
13357bd4a6f5Skk 
13367bd4a6f5Skk 	if (picnum < num_gpc) {
13377bd4a6f5Skk 		ret = configure_gpc(picnum, event, preset, flags,
13387bd4a6f5Skk 		    nattrs, attrs, data);
13397bd4a6f5Skk 	} else {
13407bd4a6f5Skk 		ret = configure_ffc(picnum, event, preset, flags,
1341a18ddb3cSKuriakose Kuruvilla 		    nattrs, attrs, data);
13427bd4a6f5Skk 	}
13437bd4a6f5Skk 	return (ret);
13447bd4a6f5Skk }
13457bd4a6f5Skk 
13467bd4a6f5Skk static void
core_pcbe_program(void * token)13477bd4a6f5Skk core_pcbe_program(void *token)
13487bd4a6f5Skk {
13497bd4a6f5Skk 	core_pcbe_config_t	*cfg;
13507bd4a6f5Skk 	uint64_t		perf_global_ctrl;
13517bd4a6f5Skk 	uint64_t		perf_fixed_ctr_ctrl;
13527bd4a6f5Skk 	uint64_t		curcr4;
13537bd4a6f5Skk 
13547bd4a6f5Skk 	core_pcbe_allstop();
13557bd4a6f5Skk 
13567bd4a6f5Skk 	curcr4 = getcr4();
13577bd4a6f5Skk 	if (kcpc_allow_nonpriv(token))
13587bd4a6f5Skk 		/* Allow RDPMC at any ring level */
13597bd4a6f5Skk 		setcr4(curcr4 | CR4_PCE);
13607bd4a6f5Skk 	else
13617bd4a6f5Skk 		/* Allow RDPMC only at ring 0 */
13627bd4a6f5Skk 		setcr4(curcr4 & ~CR4_PCE);
13637bd4a6f5Skk 
13647bd4a6f5Skk 	/* Clear any overflow indicators before programming the counters */
13657bd4a6f5Skk 	WRMSR(PERF_GLOBAL_OVF_CTRL, MASK_CONDCHGD_OVFBUFFER | control_mask);
13667bd4a6f5Skk 
13677bd4a6f5Skk 	cfg = NULL;
13687bd4a6f5Skk 	perf_global_ctrl = 0;
13697bd4a6f5Skk 	perf_fixed_ctr_ctrl = 0;
13707bd4a6f5Skk 	cfg = (core_pcbe_config_t *)kcpc_next_config(token, cfg, NULL);
13717bd4a6f5Skk 	while (cfg != NULL) {
13727bd4a6f5Skk 		ASSERT(cfg->core_pictype == CORE_GPC ||
13737bd4a6f5Skk 		    cfg->core_pictype == CORE_FFC);
13747bd4a6f5Skk 
13757bd4a6f5Skk 		if (cfg->core_pictype == CORE_GPC) {
13767bd4a6f5Skk 			/*
13777bd4a6f5Skk 			 * General-purpose counter registers have write
13787bd4a6f5Skk 			 * restrictions where only the lower 32-bits can be
13797bd4a6f5Skk 			 * written to.  The rest of the relevant bits are
13807bd4a6f5Skk 			 * written to by extension from bit 31 (all ZEROS if
13817bd4a6f5Skk 			 * bit-31 is ZERO and all ONE if bit-31 is ONE).  This
13827bd4a6f5Skk 			 * makes it possible to write to the counter register
13837bd4a6f5Skk 			 * only values that have all ONEs or all ZEROs in the
13847bd4a6f5Skk 			 * higher bits.
13857bd4a6f5Skk 			 */
13867bd4a6f5Skk 			if (((cfg->core_rawpic & BITS_EXTENDED_FROM_31) == 0) ||
13877bd4a6f5Skk 			    ((cfg->core_rawpic & BITS_EXTENDED_FROM_31) ==
13887bd4a6f5Skk 			    BITS_EXTENDED_FROM_31)) {
13897bd4a6f5Skk 				/*
13907bd4a6f5Skk 				 * Straighforward case where the higher bits
13917bd4a6f5Skk 				 * are all ZEROs or all ONEs.
13927bd4a6f5Skk 				 */
13937bd4a6f5Skk 				WRMSR(cfg->core_pmc,
13947bd4a6f5Skk 				    (cfg->core_rawpic & mask_gpc));
13957bd4a6f5Skk 			} else {
13967bd4a6f5Skk 				/*
13977bd4a6f5Skk 				 * The high order bits are not all the same.
13987bd4a6f5Skk 				 * We save what is currently in the registers
13997bd4a6f5Skk 				 * and do not write to it.  When we want to do
14007bd4a6f5Skk 				 * a read from this register later (in
14017bd4a6f5Skk 				 * core_pcbe_sample()), we subtract the value
14027bd4a6f5Skk 				 * we save here to get the actual event count.
14037bd4a6f5Skk 				 *
14047bd4a6f5Skk 				 * NOTE: As a result, we will not get overflow
14057bd4a6f5Skk 				 * interrupts as expected.
14067bd4a6f5Skk 				 */
14077bd4a6f5Skk 				RDMSR(cfg->core_pmc, cfg->core_rawpic);
14087bd4a6f5Skk 				cfg->core_rawpic = cfg->core_rawpic & mask_gpc;
14097bd4a6f5Skk 			}
14107bd4a6f5Skk 			WRMSR(cfg->core_pes, cfg->core_ctl);
14117bd4a6f5Skk 			perf_global_ctrl |= 1ull << cfg->core_picno;
14127bd4a6f5Skk 		} else {
14137bd4a6f5Skk 			/*
14147bd4a6f5Skk 			 * Unlike the general-purpose counters, all relevant
14157bd4a6f5Skk 			 * bits of fixed-function counters can be written to.
14167bd4a6f5Skk 			 */
14177bd4a6f5Skk 			WRMSR(cfg->core_pmc, cfg->core_rawpic & mask_ffc);
14187bd4a6f5Skk 
14197bd4a6f5Skk 			/*
14207bd4a6f5Skk 			 * Collect the control bits for all the
14217bd4a6f5Skk 			 * fixed-function counters and write it at one shot
14227bd4a6f5Skk 			 * later in this function
14237bd4a6f5Skk 			 */
14247bd4a6f5Skk 			perf_fixed_ctr_ctrl |= cfg->core_ctl <<
14257bd4a6f5Skk 			    ((cfg->core_picno - num_gpc) * CORE_FFC_ATTR_SIZE);
14267bd4a6f5Skk 			perf_global_ctrl |=
14277bd4a6f5Skk 			    1ull << (cfg->core_picno - num_gpc + 32);
14287bd4a6f5Skk 		}
14297bd4a6f5Skk 
14307bd4a6f5Skk 		cfg = (core_pcbe_config_t *)
14317bd4a6f5Skk 		    kcpc_next_config(token, cfg, NULL);
14327bd4a6f5Skk 	}
14337bd4a6f5Skk 
14347bd4a6f5Skk 	/* Enable all the counters */
14357bd4a6f5Skk 	WRMSR(PERF_FIXED_CTR_CTRL, perf_fixed_ctr_ctrl);
14367bd4a6f5Skk 	WRMSR(PERF_GLOBAL_CTRL, perf_global_ctrl);
14377bd4a6f5Skk }
14387bd4a6f5Skk 
14397bd4a6f5Skk static void
core_pcbe_allstop(void)14407bd4a6f5Skk core_pcbe_allstop(void)
14417bd4a6f5Skk {
14427bd4a6f5Skk 	/* Disable all the counters together */
14437bd4a6f5Skk 	WRMSR(PERF_GLOBAL_CTRL, ALL_STOPPED);
14447bd4a6f5Skk 
14457bd4a6f5Skk 	setcr4(getcr4() & ~CR4_PCE);
14467bd4a6f5Skk }
14477bd4a6f5Skk 
14487bd4a6f5Skk static void
core_pcbe_sample(void * token)14497bd4a6f5Skk core_pcbe_sample(void *token)
14507bd4a6f5Skk {
14517bd4a6f5Skk 	uint64_t		*daddr;
14527bd4a6f5Skk 	uint64_t		curpic;
14537bd4a6f5Skk 	core_pcbe_config_t	*cfg;
14547bd4a6f5Skk 	uint64_t			counter_mask;
14557bd4a6f5Skk 
14567bd4a6f5Skk 	cfg = (core_pcbe_config_t *)kcpc_next_config(token, NULL, &daddr);
14577bd4a6f5Skk 	while (cfg != NULL) {
14587bd4a6f5Skk 		ASSERT(cfg->core_pictype == CORE_GPC ||
14597bd4a6f5Skk 		    cfg->core_pictype == CORE_FFC);
14607bd4a6f5Skk 
14617bd4a6f5Skk 		curpic = rdmsr(cfg->core_pmc);
14627bd4a6f5Skk 
14637bd4a6f5Skk 		DTRACE_PROBE4(core__pcbe__sample,
14647bd4a6f5Skk 		    uint64_t, cfg->core_pmc,
14657bd4a6f5Skk 		    uint64_t, curpic,
14667bd4a6f5Skk 		    uint64_t, cfg->core_rawpic,
14677bd4a6f5Skk 		    uint64_t, *daddr);
14687bd4a6f5Skk 
14697bd4a6f5Skk 		if (cfg->core_pictype == CORE_GPC) {
14707bd4a6f5Skk 			counter_mask = mask_gpc;
14717bd4a6f5Skk 		} else {
14727bd4a6f5Skk 			counter_mask = mask_ffc;
14737bd4a6f5Skk 		}
14747bd4a6f5Skk 		curpic = curpic & counter_mask;
14757bd4a6f5Skk 		if (curpic >= cfg->core_rawpic) {
14767bd4a6f5Skk 			*daddr += curpic - cfg->core_rawpic;
14777bd4a6f5Skk 		} else {
14787bd4a6f5Skk 			/* Counter overflowed since our last sample */
14797bd4a6f5Skk 			*daddr += counter_mask - (cfg->core_rawpic - curpic) +
14807bd4a6f5Skk 			    1;
14817bd4a6f5Skk 		}
14827bd4a6f5Skk 		cfg->core_rawpic = *daddr & counter_mask;
14837bd4a6f5Skk 
14847bd4a6f5Skk 		cfg =
14857bd4a6f5Skk 		    (core_pcbe_config_t *)kcpc_next_config(token, cfg, &daddr);
14867bd4a6f5Skk 	}
14877bd4a6f5Skk }
14887bd4a6f5Skk 
14897bd4a6f5Skk static void
core_pcbe_free(void * config)14907bd4a6f5Skk core_pcbe_free(void *config)
14917bd4a6f5Skk {
14927bd4a6f5Skk 	kmem_free(config, sizeof (core_pcbe_config_t));
14937bd4a6f5Skk }
14947bd4a6f5Skk 
14957bd4a6f5Skk static struct modlpcbe core_modlpcbe = {
14967bd4a6f5Skk 	&mod_pcbeops,
1497820c9f58Skk 	"Core Performance Counters",
14987bd4a6f5Skk 	&core_pcbe_ops
14997bd4a6f5Skk };
15007bd4a6f5Skk 
15017bd4a6f5Skk static struct modlinkage core_modl = {
15027bd4a6f5Skk 	MODREV_1,
15037bd4a6f5Skk 	&core_modlpcbe,
15047bd4a6f5Skk };
15057bd4a6f5Skk 
15067bd4a6f5Skk int
_init(void)15077bd4a6f5Skk _init(void)
15087bd4a6f5Skk {
15097bd4a6f5Skk 	if (core_pcbe_init() != 0) {
15107bd4a6f5Skk 		return (ENOTSUP);
15117bd4a6f5Skk 	}
15127bd4a6f5Skk 	return (mod_install(&core_modl));
15137bd4a6f5Skk }
15147bd4a6f5Skk 
15157bd4a6f5Skk int
_fini(void)15167bd4a6f5Skk _fini(void)
15177bd4a6f5Skk {
15187bd4a6f5Skk 	return (mod_remove(&core_modl));
15197bd4a6f5Skk }
15207bd4a6f5Skk 
15217bd4a6f5Skk int
_info(struct modinfo * mi)15227bd4a6f5Skk _info(struct modinfo *mi)
15237bd4a6f5Skk {
15247bd4a6f5Skk 	return (mod_info(&core_modl, mi));
15257bd4a6f5Skk }
1526