xref: /titanic_50/usr/src/uts/intel/pcbe/p4_pcbe.c (revision 6a634c9dca3093f3922e4b7ab826d7bdf17bf78e)
17c478bd9Sstevel@tonic-gate /*
27c478bd9Sstevel@tonic-gate  * CDDL HEADER START
37c478bd9Sstevel@tonic-gate  *
47c478bd9Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
5fb2f18f8Sesaxe  * Common Development and Distribution License (the "License").
6fb2f18f8Sesaxe  * You may not use this file except in compliance with the License.
77c478bd9Sstevel@tonic-gate  *
87c478bd9Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
97c478bd9Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
107c478bd9Sstevel@tonic-gate  * See the License for the specific language governing permissions
117c478bd9Sstevel@tonic-gate  * and limitations under the License.
127c478bd9Sstevel@tonic-gate  *
137c478bd9Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
147c478bd9Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
157c478bd9Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
167c478bd9Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
177c478bd9Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
187c478bd9Sstevel@tonic-gate  *
197c478bd9Sstevel@tonic-gate  * CDDL HEADER END
207c478bd9Sstevel@tonic-gate  */
217c478bd9Sstevel@tonic-gate /*
22*7417cfdeSKuriakose Kuruvilla  * Copyright (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved.
237c478bd9Sstevel@tonic-gate  */
247c478bd9Sstevel@tonic-gate 
25c7a079a8SJonathan Haslam /*
26c7a079a8SJonathan Haslam  * This file contains preset event names from the Performance Application
27c7a079a8SJonathan Haslam  * Programming Interface v3.5 which included the following notice:
28c7a079a8SJonathan Haslam  *
29c7a079a8SJonathan Haslam  *                             Copyright (c) 2005,6
30c7a079a8SJonathan Haslam  *                           Innovative Computing Labs
31c7a079a8SJonathan Haslam  *                         Computer Science Department,
32c7a079a8SJonathan Haslam  *                            University of Tennessee,
33c7a079a8SJonathan Haslam  *                                 Knoxville, TN.
34c7a079a8SJonathan Haslam  *                              All Rights Reserved.
35c7a079a8SJonathan Haslam  *
36c7a079a8SJonathan Haslam  *
37c7a079a8SJonathan Haslam  * Redistribution and use in source and binary forms, with or without
38c7a079a8SJonathan Haslam  * modification, are permitted provided that the following conditions are met:
39c7a079a8SJonathan Haslam  *
40c7a079a8SJonathan Haslam  *    * Redistributions of source code must retain the above copyright notice,
41c7a079a8SJonathan Haslam  *      this list of conditions and the following disclaimer.
42c7a079a8SJonathan Haslam  *    * Redistributions in binary form must reproduce the above copyright
43c7a079a8SJonathan Haslam  *      notice, this list of conditions and the following disclaimer in the
44c7a079a8SJonathan Haslam  *      documentation and/or other materials provided with the distribution.
45c7a079a8SJonathan Haslam  *    * Neither the name of the University of Tennessee nor the names of its
46c7a079a8SJonathan Haslam  *      contributors may be used to endorse or promote products derived from
47c7a079a8SJonathan Haslam  *      this software without specific prior written permission.
48c7a079a8SJonathan Haslam  *
49c7a079a8SJonathan Haslam  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
50c7a079a8SJonathan Haslam  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
51c7a079a8SJonathan Haslam  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
52c7a079a8SJonathan Haslam  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
53c7a079a8SJonathan Haslam  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
54c7a079a8SJonathan Haslam  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
55c7a079a8SJonathan Haslam  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
56c7a079a8SJonathan Haslam  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
57c7a079a8SJonathan Haslam  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
58c7a079a8SJonathan Haslam  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
59c7a079a8SJonathan Haslam  * POSSIBILITY OF SUCH DAMAGE.
60c7a079a8SJonathan Haslam  *
61c7a079a8SJonathan Haslam  *
62c7a079a8SJonathan Haslam  * This open source software license conforms to the BSD License template.
63c7a079a8SJonathan Haslam  */
647c478bd9Sstevel@tonic-gate 
657c478bd9Sstevel@tonic-gate /*
667c478bd9Sstevel@tonic-gate  * Performance Counter Back-End for Pentium 4.
677c478bd9Sstevel@tonic-gate  */
687c478bd9Sstevel@tonic-gate 
697c478bd9Sstevel@tonic-gate #include <sys/cpuvar.h>
707c478bd9Sstevel@tonic-gate #include <sys/param.h>
717c478bd9Sstevel@tonic-gate #include <sys/cpc_impl.h>
727c478bd9Sstevel@tonic-gate #include <sys/cpc_pcbe.h>
737c478bd9Sstevel@tonic-gate #include <sys/inttypes.h>
747c478bd9Sstevel@tonic-gate #include <sys/errno.h>
757c478bd9Sstevel@tonic-gate #include <sys/systm.h>
767c478bd9Sstevel@tonic-gate #include <sys/archsystm.h>
777c478bd9Sstevel@tonic-gate #include <sys/x86_archext.h>
787c478bd9Sstevel@tonic-gate #include <sys/modctl.h>
797c478bd9Sstevel@tonic-gate #include <sys/sdt.h>
807c478bd9Sstevel@tonic-gate #include <sys/cred.h>
817c478bd9Sstevel@tonic-gate #include <sys/policy.h>
827c478bd9Sstevel@tonic-gate #include <sys/privregs.h>
837c478bd9Sstevel@tonic-gate 
847c478bd9Sstevel@tonic-gate static int p4_pcbe_init(void);
857c478bd9Sstevel@tonic-gate static uint_t p4_pcbe_ncounters(void);
867c478bd9Sstevel@tonic-gate static const char *p4_pcbe_impl_name(void);
877c478bd9Sstevel@tonic-gate static const char *p4_pcbe_cpuref(void);
887c478bd9Sstevel@tonic-gate static char *p4_pcbe_list_events(uint_t picnum);
897c478bd9Sstevel@tonic-gate static char *p4_pcbe_list_attrs(void);
907c478bd9Sstevel@tonic-gate static uint64_t p4_pcbe_event_coverage(char *event);
917c478bd9Sstevel@tonic-gate static uint64_t p4_pcbe_overflow_bitmap(void);
927c478bd9Sstevel@tonic-gate static int p4_pcbe_configure(uint_t picnum, char *event, uint64_t preset,
937c478bd9Sstevel@tonic-gate     uint32_t flags, uint_t nattrs, kcpc_attr_t *attrs, void **data,
947c478bd9Sstevel@tonic-gate     void *token);
957c478bd9Sstevel@tonic-gate static void p4_pcbe_program(void *token);
967c478bd9Sstevel@tonic-gate static void p4_pcbe_allstop(void);
977c478bd9Sstevel@tonic-gate static void p4_pcbe_sample(void *token);
987c478bd9Sstevel@tonic-gate static void p4_pcbe_free(void *config);
997c478bd9Sstevel@tonic-gate 
100fb2f18f8Sesaxe extern int cpuid_get_clogid(cpu_t *);
1017c478bd9Sstevel@tonic-gate 
1027c478bd9Sstevel@tonic-gate static pcbe_ops_t p4_pcbe_ops = {
1037c478bd9Sstevel@tonic-gate 	PCBE_VER_1,
1047c478bd9Sstevel@tonic-gate 	CPC_CAP_OVERFLOW_INTERRUPT | CPC_CAP_OVERFLOW_PRECISE,
1057c478bd9Sstevel@tonic-gate 	p4_pcbe_ncounters,
1067c478bd9Sstevel@tonic-gate 	p4_pcbe_impl_name,
1077c478bd9Sstevel@tonic-gate 	p4_pcbe_cpuref,
1087c478bd9Sstevel@tonic-gate 	p4_pcbe_list_events,
1097c478bd9Sstevel@tonic-gate 	p4_pcbe_list_attrs,
1107c478bd9Sstevel@tonic-gate 	p4_pcbe_event_coverage,
1117c478bd9Sstevel@tonic-gate 	p4_pcbe_overflow_bitmap,
1127c478bd9Sstevel@tonic-gate 	p4_pcbe_configure,
1137c478bd9Sstevel@tonic-gate 	p4_pcbe_program,
1147c478bd9Sstevel@tonic-gate 	p4_pcbe_allstop,
1157c478bd9Sstevel@tonic-gate 	p4_pcbe_sample,
1167c478bd9Sstevel@tonic-gate 	p4_pcbe_free
1177c478bd9Sstevel@tonic-gate };
1187c478bd9Sstevel@tonic-gate 
1197c478bd9Sstevel@tonic-gate /*
1207c478bd9Sstevel@tonic-gate  * P4 Configuration Flags.
1217c478bd9Sstevel@tonic-gate  */
1227c478bd9Sstevel@tonic-gate #define	P4_THIS_USR	0x1 /* HTT: Measure usr events on this logical CPU */
1237c478bd9Sstevel@tonic-gate #define	P4_THIS_SYS	0x2 /* HTT: Measure os events on this logical CPU */
1247c478bd9Sstevel@tonic-gate #define	P4_SIBLING_USR	0x4 /* HTT: Measure os events on other logical CPU */
1257c478bd9Sstevel@tonic-gate #define	P4_SIBLING_SYS	0x8 /* HTT: Measure usr events on other logical CPU */
1267c478bd9Sstevel@tonic-gate #define	P4_PMI		0x10 /* HTT: Set PMI bit for local logical CPU */
1277c478bd9Sstevel@tonic-gate 
1287c478bd9Sstevel@tonic-gate typedef struct _p4_pcbe_config {
1297c478bd9Sstevel@tonic-gate 	uint8_t		p4_flags;
1307c478bd9Sstevel@tonic-gate 	uint8_t		p4_picno;	/* From 0 to 18 */
1317c478bd9Sstevel@tonic-gate 	uint8_t		p4_escr_ndx;	/* Which ESCR to use */
1327c478bd9Sstevel@tonic-gate 	uint32_t	p4_escr;	/* Value to program in selected ESCR */
1337c478bd9Sstevel@tonic-gate 	uint32_t	p4_cccr;	/* Value to program in counter's CCCR */
1347c478bd9Sstevel@tonic-gate 	uint64_t	p4_rawpic;
1357c478bd9Sstevel@tonic-gate } p4_pcbe_config_t;
1367c478bd9Sstevel@tonic-gate 
1377c478bd9Sstevel@tonic-gate typedef uint32_t cntr_map_t;
1387c478bd9Sstevel@tonic-gate 
1397c478bd9Sstevel@tonic-gate typedef struct _p4_escr {
1407c478bd9Sstevel@tonic-gate 	int		pe_num;
1417c478bd9Sstevel@tonic-gate 	uint32_t	pe_addr;
1427c478bd9Sstevel@tonic-gate 	uint32_t	pe_map; /* bitmap of counters; bit 1 means ctr 0 */
1437c478bd9Sstevel@tonic-gate } p4_escr_t;
1447c478bd9Sstevel@tonic-gate 
1457c478bd9Sstevel@tonic-gate #define	MASK40			UINT64_C(0xffffffffff)
1467c478bd9Sstevel@tonic-gate 
1477c478bd9Sstevel@tonic-gate /*
1487c478bd9Sstevel@tonic-gate  * CCCR field definitions.
1497c478bd9Sstevel@tonic-gate  *
1507c478bd9Sstevel@tonic-gate  * Note that the Intel Developer's Manual states that the reserved field at
1517c478bd9Sstevel@tonic-gate  * bit location 16 and 17 must be set to 11. (??)
1527c478bd9Sstevel@tonic-gate  */
1537c478bd9Sstevel@tonic-gate #define	CCCR_ENABLE_SHIFT	12
1547c478bd9Sstevel@tonic-gate #define	CCCR_ESCR_SEL_SHIFT	13
1557c478bd9Sstevel@tonic-gate #define	CCCR_ACTV_THR_SHIFT	16
1567c478bd9Sstevel@tonic-gate #define	CCCR_COMPARE_SHIFT	18
1577c478bd9Sstevel@tonic-gate #define	CCCR_COMPLEMENT_SHIFT	19
1587c478bd9Sstevel@tonic-gate #define	CCCR_THRESHOLD_SHIFT	20
1597c478bd9Sstevel@tonic-gate #define	CCCR_EDGE_SHIFT		24
1607c478bd9Sstevel@tonic-gate #define	CCCR_OVF_PMI_SHIFT	26
1617c478bd9Sstevel@tonic-gate #define	CCCR_OVF_PMI_T0_SHIFT	26
1627c478bd9Sstevel@tonic-gate #define	CCCR_OVF_PMI_T1_SHIFT	27
1637c478bd9Sstevel@tonic-gate #define	CCCR_OVF_SHIFT		31
1647c478bd9Sstevel@tonic-gate #define	CCCR_ACTV_THR_MASK	0x3
1657c478bd9Sstevel@tonic-gate #define	CCCR_THRESHOLD_MAX	0xF
1667c478bd9Sstevel@tonic-gate #define	CCCR_ENABLE		(1U << CCCR_ENABLE_SHIFT)
1677c478bd9Sstevel@tonic-gate #define	CCCR_COMPARE		(1U << CCCR_COMPARE_SHIFT)
1687c478bd9Sstevel@tonic-gate #define	CCCR_COMPLEMENT		(1U << CCCR_COMPLEMENT_SHIFT)
1697c478bd9Sstevel@tonic-gate #define	CCCR_EDGE		(1U << CCCR_EDGE_SHIFT)
1707c478bd9Sstevel@tonic-gate #define	CCCR_OVF_PMI		(1U << CCCR_OVF_PMI_SHIFT)
1717c478bd9Sstevel@tonic-gate #define	CCCR_OVF_PMI_T0		(1U << CCCR_OVF_PMI_T0_SHIFT)
1727c478bd9Sstevel@tonic-gate #define	CCCR_OVF_PMI_T1		(1U << CCCR_OVF_PMI_T1_SHIFT)
1737c478bd9Sstevel@tonic-gate #define	CCCR_INIT		CCCR_ENABLE
1747c478bd9Sstevel@tonic-gate #define	CCCR_OVF		(1U << CCCR_OVF_SHIFT)
1757c478bd9Sstevel@tonic-gate 
1767c478bd9Sstevel@tonic-gate #define	ESCR_EVSEL_SHIFT	25
1777c478bd9Sstevel@tonic-gate #define	ESCR_EVMASK_SHIFT	9
1787c478bd9Sstevel@tonic-gate #define	ESCR_TAG_VALUE_SHIFT	5
1797c478bd9Sstevel@tonic-gate #define	ESCR_TAG_VALUE_MAX	0xF
1807c478bd9Sstevel@tonic-gate #define	ESCR_TAG_ENABLE_SHIFT	4
1817c478bd9Sstevel@tonic-gate #define	ESCR_USR_SHIFT		2
1827c478bd9Sstevel@tonic-gate #define	ESCR_OS_SHIFT		3
1837c478bd9Sstevel@tonic-gate #define	ESCR_USR		(1U << ESCR_USR_SHIFT)
1847c478bd9Sstevel@tonic-gate #define	ESCR_OS			(1U << ESCR_OS_SHIFT)
1857c478bd9Sstevel@tonic-gate #define	ESCR_TAG_ENABLE		(1U << ESCR_TAG_ENABLE_SHIFT)
1867c478bd9Sstevel@tonic-gate 
1877c478bd9Sstevel@tonic-gate /*
1887c478bd9Sstevel@tonic-gate  * HyperThreaded ESCR fields.
1897c478bd9Sstevel@tonic-gate  */
1907c478bd9Sstevel@tonic-gate #define	ESCR_T0_OS_SHIFT	3
1917c478bd9Sstevel@tonic-gate #define	ESCR_T0_USR_SHIFT	2
1927c478bd9Sstevel@tonic-gate #define	ESCR_T1_OS_SHIFT	1
1937c478bd9Sstevel@tonic-gate #define	ESCR_T1_USR_SHIFT	0
1947c478bd9Sstevel@tonic-gate #define	ESCR_T0_OS		(1U << ESCR_T0_OS_SHIFT)
1957c478bd9Sstevel@tonic-gate #define	ESCR_T0_USR		(1U << ESCR_T0_USR_SHIFT)
1967c478bd9Sstevel@tonic-gate #define	ESCR_T1_OS		(1U << ESCR_T1_OS_SHIFT)
1977c478bd9Sstevel@tonic-gate #define	ESCR_T1_USR		(1U << ESCR_T1_USR_SHIFT)
1987c478bd9Sstevel@tonic-gate 
1997c478bd9Sstevel@tonic-gate /*
2007c478bd9Sstevel@tonic-gate  * ESCRs are grouped by counter; each group of ESCRs is associated with a
2017c478bd9Sstevel@tonic-gate  * distinct group of counters. Use these macros to fill in the table below.
2027c478bd9Sstevel@tonic-gate  */
2037c478bd9Sstevel@tonic-gate #define	BPU0_map	(0x1 | 0x2)		/* Counters 0 and 1 */
2047c478bd9Sstevel@tonic-gate #define	BPU2_map	(0x4 | 0x8)		/* Counters 2 and 3 */
2057c478bd9Sstevel@tonic-gate #define	MS0_map		(0x10 | 0x20)		/* Counters 4 and 5 */
2067c478bd9Sstevel@tonic-gate #define	MS2_map		(0x40 | 0x80)		/* Counters 6 and 7 */
2077c478bd9Sstevel@tonic-gate #define	FLAME0_map	(0x100 | 0x200)		/* Counters 8 and 9 */
2087c478bd9Sstevel@tonic-gate #define	FLAME2_map	(0x400 | 0x800)		/* Counters 10 and 11 */
2097c478bd9Sstevel@tonic-gate #define	IQ0_map		(0x1000 | 0x2000 | 0x10000) /* Counters 12, 13, 16 */
2107c478bd9Sstevel@tonic-gate #define	IQ2_map		(0x4000 | 0x8000 | 0x20000) /* Counters 14, 15, 17 */
2117c478bd9Sstevel@tonic-gate 
2127c478bd9Sstevel@tonic-gate /*
2137c478bd9Sstevel@tonic-gate  * Table describing the 45 Event Selection and Control Registers (ESCRs).
2147c478bd9Sstevel@tonic-gate  */
2157c478bd9Sstevel@tonic-gate const p4_escr_t p4_escrs[] = {
2167c478bd9Sstevel@tonic-gate #define	BPU0 (1)
2177c478bd9Sstevel@tonic-gate 	{ 0, 0x3B2, BPU0_map },		/* 0 */
2187c478bd9Sstevel@tonic-gate #define	IS0 (1ULL << 1)
2197c478bd9Sstevel@tonic-gate 	{ 1, 0x3B4, BPU0_map },		/* 1 */
2207c478bd9Sstevel@tonic-gate #define	MOB0 (1ULL << 2)
2217c478bd9Sstevel@tonic-gate 	{ 2, 0x3AA, BPU0_map },		/* 2 */
2227c478bd9Sstevel@tonic-gate #define	ITLB0 (1ULL << 3)
2237c478bd9Sstevel@tonic-gate 	{ 3, 0x3B6, BPU0_map },		/* 3 */
2247c478bd9Sstevel@tonic-gate #define	PMH0 (1ULL << 4)
2257c478bd9Sstevel@tonic-gate 	{ 4, 0x3AC, BPU0_map },		/* 4 */
2267c478bd9Sstevel@tonic-gate #define	IX0 (1ULL << 5)
2277c478bd9Sstevel@tonic-gate 	{ 5, 0x3C8, BPU0_map },		/* 5 */
2287c478bd9Sstevel@tonic-gate #define	FSB0 (1ULL << 6)
2297c478bd9Sstevel@tonic-gate 	{ 6, 0x3A2, BPU0_map },		/* 6 */
2307c478bd9Sstevel@tonic-gate #define	BSU0 (1ULL << 7)
2317c478bd9Sstevel@tonic-gate 	{ 7, 0x3A0, BPU0_map },		/* 7 */
2327c478bd9Sstevel@tonic-gate #define	BPU1 (1ULL << 8)
2337c478bd9Sstevel@tonic-gate 	{ 0, 0x3B3, BPU2_map },		/* 8 */
2347c478bd9Sstevel@tonic-gate #define	IS1 (1ULL << 9)
2357c478bd9Sstevel@tonic-gate 	{ 1, 0x3B5, BPU2_map },		/* 9 */
2367c478bd9Sstevel@tonic-gate #define	MOB1 (1ULL << 10)
2377c478bd9Sstevel@tonic-gate 	{ 2, 0x3AB, BPU2_map },		/* 10 */
2387c478bd9Sstevel@tonic-gate #define	ITLB1 (1ULL << 11)
2397c478bd9Sstevel@tonic-gate 	{ 3, 0x3B7, BPU2_map },		/* 11 */
2407c478bd9Sstevel@tonic-gate #define	PMH1 (1ULL << 12)
2417c478bd9Sstevel@tonic-gate 	{ 4, 0x3AD, BPU2_map },		/* 12 */
2427c478bd9Sstevel@tonic-gate #define	IX1 (1ULL << 13)
2437c478bd9Sstevel@tonic-gate 	{ 5, 0x3C9, BPU2_map },		/* 13 */
2447c478bd9Sstevel@tonic-gate #define	FSB1 (1ULL << 14)
2457c478bd9Sstevel@tonic-gate 	{ 6, 0x3A3, BPU2_map },		/* 14 */
2467c478bd9Sstevel@tonic-gate #define	BSU1 (1ULL << 15)
2477c478bd9Sstevel@tonic-gate 	{ 7, 0x3A1, BPU2_map },		/* 15 */
2487c478bd9Sstevel@tonic-gate #define	MS0 (1ULL << 16)
2497c478bd9Sstevel@tonic-gate 	{ 0, 0x3C0, MS0_map },		/* 16 */
2507c478bd9Sstevel@tonic-gate #define	TC0 (1ULL << 17)
2517c478bd9Sstevel@tonic-gate 	{ 1, 0x3C4, MS0_map },		/* 17 */
2527c478bd9Sstevel@tonic-gate #define	TBPU0 (1ULL << 18)
2537c478bd9Sstevel@tonic-gate 	{ 2, 0x3C2, MS0_map },		/* 18 */
2547c478bd9Sstevel@tonic-gate #define	MS1 (1ULL << 19)
2557c478bd9Sstevel@tonic-gate 	{ 0, 0x3C1, MS2_map },		/* 19 */
2567c478bd9Sstevel@tonic-gate #define	TC1 (1ULL << 20)
2577c478bd9Sstevel@tonic-gate 	{ 1, 0x3C5, MS2_map },		/* 20 */
2587c478bd9Sstevel@tonic-gate #define	TBPU1 (1ULL << 21)
2597c478bd9Sstevel@tonic-gate 	{ 2, 0x3C3, MS2_map },		/* 21 */
2607c478bd9Sstevel@tonic-gate #define	FLAME0 (1ULL << 22)
2617c478bd9Sstevel@tonic-gate 	{ 0, 0x3A6, FLAME0_map },	/* 22 */
2627c478bd9Sstevel@tonic-gate #define	FIRM0 (1ULL << 23)
2637c478bd9Sstevel@tonic-gate 	{ 1, 0x3A4, FLAME0_map },	/* 23 */
2647c478bd9Sstevel@tonic-gate #define	SAAT0 (1ULL << 24)
2657c478bd9Sstevel@tonic-gate 	{ 2, 0x3AE, FLAME0_map },	/* 24 */
2667c478bd9Sstevel@tonic-gate #define	U2L0 (1ULL << 25)
2677c478bd9Sstevel@tonic-gate 	{ 3, 0x3B0, FLAME0_map },	/* 25 */
2687c478bd9Sstevel@tonic-gate #define	DAC0 (1ULL << 26)
2697c478bd9Sstevel@tonic-gate 	{ 5, 0x3A8, FLAME0_map },	/* 26 */
2707c478bd9Sstevel@tonic-gate #define	FLAME1 (1ULL << 27)
2717c478bd9Sstevel@tonic-gate 	{ 0, 0x3A7, FLAME2_map },	/* 27 */
2727c478bd9Sstevel@tonic-gate #define	FIRM1 (1ULL << 28)
2737c478bd9Sstevel@tonic-gate 	{ 1, 0x3A5, FLAME2_map },	/* 28 */
2747c478bd9Sstevel@tonic-gate #define	SAAT1 (1ULL << 29)
2757c478bd9Sstevel@tonic-gate 	{ 2, 0x3AF, FLAME2_map },	/* 29 */
2767c478bd9Sstevel@tonic-gate #define	U2L1 (1ULL << 30)
2777c478bd9Sstevel@tonic-gate 	{ 3, 0x3B1, FLAME2_map },	/* 30 */
2787c478bd9Sstevel@tonic-gate #define	DAC1 (1ULL << 31)
2797c478bd9Sstevel@tonic-gate 	{ 5, 0x3A9, FLAME2_map },	/* 31 */
2807c478bd9Sstevel@tonic-gate #define	IQ0 (1ULL << 32)
2817c478bd9Sstevel@tonic-gate 	{ 0, 0x3BA, IQ0_map },		/* 32 */
2827c478bd9Sstevel@tonic-gate #define	ALF0 (1ULL << 33)
2837c478bd9Sstevel@tonic-gate 	{ 1, 0x3CA, IQ0_map },		/* 33 */
2847c478bd9Sstevel@tonic-gate #define	RAT0 (1ULL << 34)
2857c478bd9Sstevel@tonic-gate 	{ 2, 0x3BC, IQ0_map },		/* 34 */
2867c478bd9Sstevel@tonic-gate #define	SSU0 (1ULL << 35)
2877c478bd9Sstevel@tonic-gate 	{ 3, 0x3BE, IQ0_map },		/* 35 */
2887c478bd9Sstevel@tonic-gate #define	CRU0 (1ULL << 36)
2897c478bd9Sstevel@tonic-gate 	{ 4, 0x3B8, IQ0_map },		/* 36 */
2907c478bd9Sstevel@tonic-gate #define	CRU2 (1ULL << 37)
2917c478bd9Sstevel@tonic-gate 	{ 5, 0x3CC, IQ0_map },		/* 37 */
2927c478bd9Sstevel@tonic-gate #define	CRU4 (1ULL << 38)
2937c478bd9Sstevel@tonic-gate 	{ 6, 0x3E0, IQ0_map },		/* 38 */
2947c478bd9Sstevel@tonic-gate #define	IQ1 (1ULL << 39)
2957c478bd9Sstevel@tonic-gate 	{ 0, 0x3BB, IQ2_map },		/* 39 */
2967c478bd9Sstevel@tonic-gate #define	ALF1 (1ULL << 40)
2977c478bd9Sstevel@tonic-gate 	{ 1, 0x3CB, IQ2_map },		/* 40 */
2987c478bd9Sstevel@tonic-gate #define	RAT1 (1ULL << 41)
2997c478bd9Sstevel@tonic-gate 	{ 2, 0x3BD, IQ2_map },		/* 41 */
3007c478bd9Sstevel@tonic-gate #define	CRU1 (1ULL << 42)
3017c478bd9Sstevel@tonic-gate 	{ 4, 0x3B9, IQ2_map },		/* 42 */
3027c478bd9Sstevel@tonic-gate #define	CRU3 (1ULL << 43)
3037c478bd9Sstevel@tonic-gate 	{ 5, 0x3CD, IQ2_map },		/* 43 */
3047c478bd9Sstevel@tonic-gate #define	CRU5 (1ULL << 44)
3057c478bd9Sstevel@tonic-gate 	{ 6, 0x3E1, IQ2_map }		/* 44 */
3067c478bd9Sstevel@tonic-gate };
3077c478bd9Sstevel@tonic-gate 
3087c478bd9Sstevel@tonic-gate #define	ESCR_MAX_INDEX 44
3097c478bd9Sstevel@tonic-gate 
3107c478bd9Sstevel@tonic-gate typedef struct _p4_ctr {
3117c478bd9Sstevel@tonic-gate 	uint32_t	pc_caddr;	/* counter MSR address */
3127c478bd9Sstevel@tonic-gate 	uint32_t	pc_ctladdr;	/* counter's CCCR MSR address */
3137c478bd9Sstevel@tonic-gate 	uint64_t	pc_map;		/* bitmap of ESCRs controlling ctr */
3147c478bd9Sstevel@tonic-gate } p4_ctr_t;
3157c478bd9Sstevel@tonic-gate 
3167c478bd9Sstevel@tonic-gate const p4_ctr_t p4_ctrs[18] = {
3177c478bd9Sstevel@tonic-gate { /* BPU_COUNTER0 */ 0x300, 0x360, BSU0|FSB0|MOB0|PMH0|BPU0|IS0|ITLB0|IX0},
3187c478bd9Sstevel@tonic-gate { /* BPU_COUNTER1 */ 0x301, 0x361, BSU0|FSB0|MOB0|PMH0|BPU0|IS0|ITLB0|IX0},
3197c478bd9Sstevel@tonic-gate { /* BPU_COUNTER2 */ 0x302, 0x362, BSU1|FSB1|MOB1|PMH1|BPU1|IS1|ITLB1|IX1},
3207c478bd9Sstevel@tonic-gate { /* BPU_COUNTER3 */ 0x303, 0x363, BSU1|FSB1|MOB1|PMH1|BPU1|IS1|ITLB1|IX1},
3217c478bd9Sstevel@tonic-gate { /* MS_COUNTER0 */  0x304, 0x364, MS0|TBPU0|TC0 },
3227c478bd9Sstevel@tonic-gate { /* MS_COUNTER1 */  0x305, 0x365, MS0|TBPU0|TC0 },
3237c478bd9Sstevel@tonic-gate { /* MS_COUNTER2 */  0x306, 0x366, MS1|TBPU1|TC1 },
3247c478bd9Sstevel@tonic-gate { /* MS_COUNTER3 */  0x307, 0x367, MS1|TBPU1|TC1 },
3257c478bd9Sstevel@tonic-gate { /* FLAME_COUNTER0 */ 0x308, 0x368, FIRM0|FLAME0|DAC0|SAAT0|U2L0 },
3267c478bd9Sstevel@tonic-gate { /* FLAME_COUNTER1 */ 0x309, 0x369, FIRM0|FLAME0|DAC0|SAAT0|U2L0 },
3277c478bd9Sstevel@tonic-gate { /* FLAME_COUNTER2 */ 0x30A, 0x36A, FIRM1|FLAME1|DAC1|SAAT1|U2L1 },
3287c478bd9Sstevel@tonic-gate { /* FLAME_COUNTER3 */ 0x30B, 0x36B, FIRM1|FLAME1|DAC1|SAAT1|U2L1 },
3297c478bd9Sstevel@tonic-gate { /* IQ_COUNTER0 */  0x30C, 0x36C, CRU0|CRU2|CRU4|IQ0|RAT0|SSU0|ALF0 },
3307c478bd9Sstevel@tonic-gate { /* IQ_COUNTER1 */  0x30D, 0x36D, CRU0|CRU2|CRU4|IQ0|RAT0|SSU0|ALF0 },
3317c478bd9Sstevel@tonic-gate { /* IQ_COUNTER2 */  0x30E, 0x36E, CRU1|CRU3|CRU5|IQ1|RAT1|ALF1 },
3327c478bd9Sstevel@tonic-gate { /* IQ_COUNTER3 */  0x30F, 0x36F, CRU1|CRU3|CRU5|IQ1|RAT1|ALF1 },
3337c478bd9Sstevel@tonic-gate { /* IQ_COUNTER4 */  0x310, 0x370, CRU0|CRU2|CRU4|IQ0|RAT0|SSU0|ALF0 },
3347c478bd9Sstevel@tonic-gate { /* IQ_COUNTER5 */  0x311, 0x371, CRU1|CRU3|CRU5|IQ1|RAT1|ALF1 }
3357c478bd9Sstevel@tonic-gate };
3367c478bd9Sstevel@tonic-gate 
3377c478bd9Sstevel@tonic-gate typedef struct _p4_event {
3387c478bd9Sstevel@tonic-gate 	char		*pe_name;	/* Name of event according to docs */
3397c478bd9Sstevel@tonic-gate 	uint64_t	pe_escr_map;	/* Bitmap of ESCRs capable of event */
3407c478bd9Sstevel@tonic-gate 	uint32_t	pe_escr_mask;	/* permissible ESCR event mask */
3417c478bd9Sstevel@tonic-gate 	uint8_t		pe_ev;		/* ESCR event select value */
3427c478bd9Sstevel@tonic-gate 	uint16_t	pe_cccr;	/* CCCR select value */
3437c478bd9Sstevel@tonic-gate 	uint32_t	pe_ctr_mask;	/* Bitmap of capable counters */
3447c478bd9Sstevel@tonic-gate } p4_event_t;
3457c478bd9Sstevel@tonic-gate 
346c7a079a8SJonathan Haslam typedef struct _p4_generic_event {
347c7a079a8SJonathan Haslam 	char		*name;
348c7a079a8SJonathan Haslam 	char		*event;
349c7a079a8SJonathan Haslam 	uint16_t	emask;
350c7a079a8SJonathan Haslam 	uint32_t	ctr_mask;
351c7a079a8SJonathan Haslam } p4_generic_event_t;
352c7a079a8SJonathan Haslam 
3537c478bd9Sstevel@tonic-gate #define	C(n) (1 << n)
354c7a079a8SJonathan Haslam #define	GEN_EVT_END { NULL, NULL, 0x0, 0x0 }
3557c478bd9Sstevel@tonic-gate 
3567c478bd9Sstevel@tonic-gate p4_event_t p4_events[] = {
3577c478bd9Sstevel@tonic-gate { "branch_retired", CRU2|CRU3, 0xF, 0x6, 0x5, C(12)|C(13)|C(14)|C(15)|C(16) },
3587c478bd9Sstevel@tonic-gate { "mispred_branch_retired", CRU0|CRU1, 0x1, 0x3, 0x4,
3597c478bd9Sstevel@tonic-gate 	C(12)|C(13)|C(14)|C(15)|C(16) },
3607c478bd9Sstevel@tonic-gate { "TC_deliver_mode", TC0|TC1, 0xFF, 0x1, 0x1, C(4)|C(5)|C(6)|C(7) },
3617c478bd9Sstevel@tonic-gate { "BPU_fetch_request", BPU0|BPU1, 0x1, 0x3, 0x0, C(0)|C(1)|C(2)|C(3) },
3627c478bd9Sstevel@tonic-gate { "ITLB_reference", ITLB0|ITLB1, 0x7, 0x18, 0x3, C(0)|C(1)|C(2)|C(3) },
3637c478bd9Sstevel@tonic-gate { "memory_cancel", DAC0|DAC1, 0x6, 0x2, 0x5, C(8)|C(9)|C(10)|C(11) },
3647c478bd9Sstevel@tonic-gate { "memory_complete", SAAT0|SAAT1, 0x3, 0x8, 0x2, C(8)|C(9)|C(10)|C(11) },
3657c478bd9Sstevel@tonic-gate { "load_port_replay", SAAT0|SAAT1, 0x1, 0x4, 0x2, C(8)|C(9)|C(10)|C(11) },
3667c478bd9Sstevel@tonic-gate { "store_port_replay", SAAT0|SAAT1, 0x1, 0x5, 0x2, C(8)|C(9)|C(10)|C(11) },
3677c478bd9Sstevel@tonic-gate { "MOB_load_replay", MOB0|MOB1, 0x35, 0x3, 0x2, C(0)|C(1)|C(2)|C(3) },
3687c478bd9Sstevel@tonic-gate { "page_walk_type", PMH0|PMH1, 0x3, 0x1, 0x4, C(0)|C(1)|C(2)|C(3) },
3697c478bd9Sstevel@tonic-gate { "BSQ_cache_reference", BSU0|BSU1, 0x73F, 0xC, 0x7, C(0)|C(1)|C(2)|C(3) },
3707c478bd9Sstevel@tonic-gate { "IOQ_allocation", FSB0, 0xEFFF, 0x3, 0x6, C(0)|C(1) },
3717c478bd9Sstevel@tonic-gate { "IOQ_active_entries", FSB1, 0xEFFF, 0x1A, 0x6, C(2)|C(3) },
3727c478bd9Sstevel@tonic-gate { "FSB_data_activity", FSB0|FSB1, 0x3F, 0x17, 0x6, C(0)|C(1)|C(2)|C(3) },
3737c478bd9Sstevel@tonic-gate { "BSQ_allocation", BSU0, 0x3FEF, 0x5, 0x7, C(0)|C(1) },
3747c478bd9Sstevel@tonic-gate { "bsq_active_entries", BSU1, 0x3FEF, 0x6, 0x7, C(2)|C(3) },
3757c478bd9Sstevel@tonic-gate { "x87_assist", CRU2|CRU3, 0x1F, 0x3, 0x5, C(12)|C(13)|C(14)|C(15)|C(16)|C(17)},
3767c478bd9Sstevel@tonic-gate { "SSE_input_assist", FIRM0|FIRM1, 0x8000, 0x34, 0x1, C(8)|C(9)|C(10)|C(11) },
3777c478bd9Sstevel@tonic-gate { "packed_SP_uop", FIRM0|FIRM1, 0x8000, 0x8, 0x1, C(8)|C(9)|C(10)|C(11) },
3787c478bd9Sstevel@tonic-gate { "packed_DP_uop", FIRM0|FIRM1, 0x8000, 0xC, 0x1, C(8)|C(9)|C(10)|C(11) },
3797c478bd9Sstevel@tonic-gate { "scalar_SP_uop", FIRM0|FIRM1, 0x8000, 0xA, 0x1, C(8)|C(9)|C(10)|C(11) },
3807c478bd9Sstevel@tonic-gate { "scalar_DP_uop", FIRM0|FIRM1, 0x8000, 0xE, 0x1, C(8)|C(9)|C(10)|C(11) },
3817c478bd9Sstevel@tonic-gate { "64bit_MMX_uop", FIRM0|FIRM1, 0x8000, 0x2, 0x1, C(8)|C(9)|C(10)|C(11) },
3827c478bd9Sstevel@tonic-gate { "128bit_MMX_uop", FIRM0|FIRM1, 0x8000, 0x1A, 0x1, C(8)|C(9)|C(10)|C(11) },
3837c478bd9Sstevel@tonic-gate { "x87_FP_uop", FIRM0|FIRM1, 0x8000, 0x4, 0x1, C(8)|C(9)|C(10)|C(11) },
3847c478bd9Sstevel@tonic-gate { "x87_SIMD_moves_uop", FIRM0|FIRM1, 0x18, 0x2E, 0x1, C(8)|C(9)|C(10)|C(11) },
3857c478bd9Sstevel@tonic-gate { "machine_clear", CRU2|CRU3, 0xD, 0x2, 0x5,
3867c478bd9Sstevel@tonic-gate 	C(12)|C(13)|C(14)|C(15)|C(16)|C(17)},
387c7a079a8SJonathan Haslam { "global_power_events", FSB0|FSB1, 0x1, 0x13, 0x6, C(0)|C(1)|C(2)|C(3) },
3887c478bd9Sstevel@tonic-gate { "tc_ms_xfer", MS0|MS1, 0x1, 0x5, 0x0, C(4)|C(5)|C(6)|C(7) },
3897c478bd9Sstevel@tonic-gate { "uop_queue_writes", MS0|MS1, 0x7, 0x9, 0x0, C(4)|C(5)|C(6)|C(7) },
3907c478bd9Sstevel@tonic-gate { "front_end_event", CRU2|CRU3, 0x3, 0x8, 0x5,
3917c478bd9Sstevel@tonic-gate 	C(12)|C(13)|C(14)|C(15)|C(16)|C(17)},
3927c478bd9Sstevel@tonic-gate { "execution_event", CRU2|CRU3, 0xFF, 0xC, 0x5,
3937c478bd9Sstevel@tonic-gate 	C(12)|C(13)|C(14)|C(15)|C(16)|C(17)},
3947c478bd9Sstevel@tonic-gate { "replay_event", CRU2|CRU3, 0x3, 0x9, 0x5,
3957c478bd9Sstevel@tonic-gate 	C(12)|C(13)|C(14)|C(15)|C(16)|C(17)},
3967c478bd9Sstevel@tonic-gate { "instr_retired", CRU0|CRU1, 0xF, 0x2, 0x4,
3977c478bd9Sstevel@tonic-gate 	C(12)|C(13)|C(14)|C(15)|C(16)|C(17)},
3987c478bd9Sstevel@tonic-gate { "uops_retired", CRU0|CRU1, 0x3, 0x1, 0x4,
3997c478bd9Sstevel@tonic-gate 	C(12)|C(13)|C(14)|C(15)|C(16)|C(17)},
4007c478bd9Sstevel@tonic-gate { "uop_type", RAT0|RAT1, 0x3, 0x2, 0x2, C(12)|C(13)|C(14)|C(15)|C(16)|C(17)},
4017c478bd9Sstevel@tonic-gate { "retired_mispred_branch_type", TBPU0|TBPU1, 0x1F, 0x5, 0x2,
4027c478bd9Sstevel@tonic-gate 	C(4)|C(5)|C(6)|C(7)},
4037c478bd9Sstevel@tonic-gate { "retired_branch_type", TBPU0|TBPU1, 0x1F, 0x4, 0x2, C(4)|C(5)|C(6)|C(7) },
4047c478bd9Sstevel@tonic-gate { NULL, 0, 0, 0, 0 }
4057c478bd9Sstevel@tonic-gate };
4067c478bd9Sstevel@tonic-gate 
407c7a079a8SJonathan Haslam static p4_generic_event_t p4_generic_events[] = {
408c7a079a8SJonathan Haslam { "PAPI_br_msp", "branch_retired", 0xa, C(12)|C(13)|C(14)|C(15)|C(16) },
409c7a079a8SJonathan Haslam { "PAPI_br_ins", "branch_retired", 0xf, C(12)|C(13)|C(14)|C(15)|C(16) },
410c7a079a8SJonathan Haslam { "PAPI_br_tkn", "branch_retired", 0xc, C(12)|C(13)|C(14)|C(15)|C(16) },
411c7a079a8SJonathan Haslam { "PAPI_br_ntk", "branch_retired", 0x3, C(12)|C(13)|C(14)|C(15)|C(16) },
412c7a079a8SJonathan Haslam { "PAPI_br_prc", "branch_retired", 0x5, C(12)|C(13)|C(14)|C(15)|C(16) },
413c7a079a8SJonathan Haslam { "PAPI_tot_ins", "instr_retired", 0x3, C(12)|C(13)|C(14)|C(15)|C(16)|C(17) },
414c7a079a8SJonathan Haslam { "PAPI_tot_cyc", "global_power_events", 0x1, C(0)|C(1)|C(2)|C(3) },
415c7a079a8SJonathan Haslam { "PAPI_tlb_dm", "page_walk_type", 0x1, C(0)|C(1)|C(2)|C(3) },
416c7a079a8SJonathan Haslam { "PAPI_tlb_im", "page_walk_type", 0x2, C(0)|C(1)|C(2)|C(3) },
417c7a079a8SJonathan Haslam { "PAPI_tlb_tm", "page_walk_type", 0x3, C(0)|C(1)|C(2)|C(3) },
418c7a079a8SJonathan Haslam { "PAPI_l1_icm", "BPU_fetch_request", 0x1, C(0)|C(1)|C(2)|C(3) },
419c7a079a8SJonathan Haslam { "PAPI_l2_ldm", "BSQ_cache_reference", 0x100, C(0)|C(1)|C(2)|C(3) },
420c7a079a8SJonathan Haslam { "PAPI_l2_stm", "BSQ_cache_reference", 0x400, C(0)|C(1)|C(2)|C(3) },
421c7a079a8SJonathan Haslam { "PAPI_l2_tcm", "BSQ_cache_reference", 0x500, C(0)|C(1)|C(2)|C(3) },
422c7a079a8SJonathan Haslam GEN_EVT_END
423c7a079a8SJonathan Haslam };
424c7a079a8SJonathan Haslam 
4257c478bd9Sstevel@tonic-gate /*
4267c478bd9Sstevel@tonic-gate  * Indicates whether the "rdpmc" instruction is available on this processor.
4277c478bd9Sstevel@tonic-gate  */
4287c478bd9Sstevel@tonic-gate static int p4_rdpmc_avail = 0;
4297c478bd9Sstevel@tonic-gate 
4307c478bd9Sstevel@tonic-gate static const uint64_t p4_cccrstop = 0;
4317c478bd9Sstevel@tonic-gate 
4327c478bd9Sstevel@tonic-gate static char *p4_eventlist[18];
4337c478bd9Sstevel@tonic-gate 
4347c478bd9Sstevel@tonic-gate /*
4357c478bd9Sstevel@tonic-gate  * If set, this processor has HyperThreading.
4367c478bd9Sstevel@tonic-gate  */
4377c478bd9Sstevel@tonic-gate static int p4_htt = 0;
4387c478bd9Sstevel@tonic-gate 
4397c478bd9Sstevel@tonic-gate #define	P4_FAMILY	0xF
4407c478bd9Sstevel@tonic-gate 
4417c478bd9Sstevel@tonic-gate static int
p4_pcbe_init(void)4427c478bd9Sstevel@tonic-gate p4_pcbe_init(void)
4437c478bd9Sstevel@tonic-gate {
4447c478bd9Sstevel@tonic-gate 	int			i;
4457c478bd9Sstevel@tonic-gate 	size_t			size;
4467c478bd9Sstevel@tonic-gate 	p4_event_t		*ev;
447c7a079a8SJonathan Haslam 	p4_generic_event_t	*gevp;
4487c478bd9Sstevel@tonic-gate 
4497c478bd9Sstevel@tonic-gate 	/*
4507c478bd9Sstevel@tonic-gate 	 * If we're not running on a P4, refuse to load.
4517c478bd9Sstevel@tonic-gate 	 */
4527c478bd9Sstevel@tonic-gate 	if (cpuid_getvendor(CPU) != X86_VENDOR_Intel ||
4537c478bd9Sstevel@tonic-gate 	    cpuid_getfamily(CPU) != P4_FAMILY)
4547c478bd9Sstevel@tonic-gate 		return (-1);
4557c478bd9Sstevel@tonic-gate 
4567c478bd9Sstevel@tonic-gate 	/*
4577c478bd9Sstevel@tonic-gate 	 * Set up the event lists for each counter.
4587c478bd9Sstevel@tonic-gate 	 *
4597c478bd9Sstevel@tonic-gate 	 * First pass calculates the size of the event list, and the second
4607c478bd9Sstevel@tonic-gate 	 * pass copies each event name into the event list.
4617c478bd9Sstevel@tonic-gate 	 */
4627c478bd9Sstevel@tonic-gate 	for (i = 0; i < 18; i++) {
4637c478bd9Sstevel@tonic-gate 		size = 0;
464c7a079a8SJonathan Haslam 
4657c478bd9Sstevel@tonic-gate 		for (ev = p4_events; ev->pe_name != NULL; ev++) {
4667c478bd9Sstevel@tonic-gate 			if (ev->pe_ctr_mask & C(i))
4677c478bd9Sstevel@tonic-gate 				size += strlen(ev->pe_name) + 1;
4687c478bd9Sstevel@tonic-gate 		}
4697c478bd9Sstevel@tonic-gate 
470c7a079a8SJonathan Haslam 		for (gevp = p4_generic_events; gevp->name != NULL; gevp++) {
471c7a079a8SJonathan Haslam 			if (gevp->ctr_mask & C(i))
472c7a079a8SJonathan Haslam 				size += strlen(gevp->name) + 1;
473c7a079a8SJonathan Haslam 		}
474c7a079a8SJonathan Haslam 
4757c478bd9Sstevel@tonic-gate 		/*
4767c478bd9Sstevel@tonic-gate 		 * We use 'size + 1' here to ensure room for the final
4777c478bd9Sstevel@tonic-gate 		 * strcat when it terminates the string.
4787c478bd9Sstevel@tonic-gate 		 */
4797c478bd9Sstevel@tonic-gate 		p4_eventlist[i] = (char *)kmem_alloc(size + 1, KM_SLEEP);
4807c478bd9Sstevel@tonic-gate 		*p4_eventlist[i] = '\0';
4817c478bd9Sstevel@tonic-gate 
4827c478bd9Sstevel@tonic-gate 		for (ev = p4_events; ev->pe_name != NULL; ev++) {
4837c478bd9Sstevel@tonic-gate 			if (ev->pe_ctr_mask & C(i)) {
4847c478bd9Sstevel@tonic-gate 				(void) strcat(p4_eventlist[i], ev->pe_name);
4857c478bd9Sstevel@tonic-gate 				(void) strcat(p4_eventlist[i], ",");
4867c478bd9Sstevel@tonic-gate 			}
4877c478bd9Sstevel@tonic-gate 		}
488c7a079a8SJonathan Haslam 
489c7a079a8SJonathan Haslam 		for (gevp = p4_generic_events; gevp->name != NULL; gevp++) {
490c7a079a8SJonathan Haslam 			if (gevp->ctr_mask & C(i)) {
491c7a079a8SJonathan Haslam 				(void) strcat(p4_eventlist[i], gevp->name);
492c7a079a8SJonathan Haslam 				(void) strcat(p4_eventlist[i], ",");
493c7a079a8SJonathan Haslam 			}
494c7a079a8SJonathan Haslam 		}
495c7a079a8SJonathan Haslam 
4967c478bd9Sstevel@tonic-gate 		/*
4977c478bd9Sstevel@tonic-gate 		 * Remove trailing ','
4987c478bd9Sstevel@tonic-gate 		 */
4997c478bd9Sstevel@tonic-gate 		p4_eventlist[i][size - 1] = '\0';
5007c478bd9Sstevel@tonic-gate 	}
5017c478bd9Sstevel@tonic-gate 
502*7417cfdeSKuriakose Kuruvilla 	if (is_x86_feature(x86_featureset, X86FSET_MMX))
5037c478bd9Sstevel@tonic-gate 		p4_rdpmc_avail = 1;
5047c478bd9Sstevel@tonic-gate 	/*
5057c478bd9Sstevel@tonic-gate 	 * The X86_HTT flag may disappear soon, so we'll isolate the impact of
5067c478bd9Sstevel@tonic-gate 	 * its demise to the following if().
5077c478bd9Sstevel@tonic-gate 	 */
508*7417cfdeSKuriakose Kuruvilla 	if (is_x86_feature(x86_featureset, X86FSET_HTT))
5097c478bd9Sstevel@tonic-gate 		p4_htt = 1;
5107c478bd9Sstevel@tonic-gate 
5117c478bd9Sstevel@tonic-gate 	return (0);
5127c478bd9Sstevel@tonic-gate }
5137c478bd9Sstevel@tonic-gate 
5147c478bd9Sstevel@tonic-gate static uint_t
p4_pcbe_ncounters(void)5157c478bd9Sstevel@tonic-gate p4_pcbe_ncounters(void)
5167c478bd9Sstevel@tonic-gate {
5177c478bd9Sstevel@tonic-gate 	return (18);
5187c478bd9Sstevel@tonic-gate }
5197c478bd9Sstevel@tonic-gate 
5207c478bd9Sstevel@tonic-gate static const char *
p4_pcbe_impl_name(void)5217c478bd9Sstevel@tonic-gate p4_pcbe_impl_name(void)
5227c478bd9Sstevel@tonic-gate {
5237c478bd9Sstevel@tonic-gate 	if (p4_htt)
524b885580bSAlexander Kolbasov 		return (PCBE_IMPL_NAME_P4HT);
5257c478bd9Sstevel@tonic-gate 	return ("Pentium 4");
5267c478bd9Sstevel@tonic-gate }
5277c478bd9Sstevel@tonic-gate 
5287c478bd9Sstevel@tonic-gate static const char *
p4_pcbe_cpuref(void)5297c478bd9Sstevel@tonic-gate p4_pcbe_cpuref(void)
5307c478bd9Sstevel@tonic-gate {
5317c478bd9Sstevel@tonic-gate 	return ("See Appendix A.1 of the \"IA-32 Intel Architecture Software " \
5327c478bd9Sstevel@tonic-gate 	    "Developer's Manual Volume 3: System Programming Guide,\" "	       \
5337c478bd9Sstevel@tonic-gate 	    "Order # 245472-012, 2003");
5347c478bd9Sstevel@tonic-gate }
5357c478bd9Sstevel@tonic-gate 
5367c478bd9Sstevel@tonic-gate static char *
p4_pcbe_list_events(uint_t picnum)5377c478bd9Sstevel@tonic-gate p4_pcbe_list_events(uint_t picnum)
5387c478bd9Sstevel@tonic-gate {
5397c478bd9Sstevel@tonic-gate 	ASSERT(picnum >= 0 && picnum < 18);
5407c478bd9Sstevel@tonic-gate 
5417c478bd9Sstevel@tonic-gate 	return (p4_eventlist[picnum]);
5427c478bd9Sstevel@tonic-gate }
5437c478bd9Sstevel@tonic-gate 
5447c478bd9Sstevel@tonic-gate #define	P4_ATTRS "emask,tag,compare,complement,threshold,edge"
5457c478bd9Sstevel@tonic-gate 
5467c478bd9Sstevel@tonic-gate static char *
p4_pcbe_list_attrs(void)5477c478bd9Sstevel@tonic-gate p4_pcbe_list_attrs(void)
5487c478bd9Sstevel@tonic-gate {
5497c478bd9Sstevel@tonic-gate 	if (p4_htt)
5507c478bd9Sstevel@tonic-gate 		return (P4_ATTRS ",active_thread,count_sibling_usr,"
5517c478bd9Sstevel@tonic-gate 		    "count_sibling_sys");
5527c478bd9Sstevel@tonic-gate 	return (P4_ATTRS);
5537c478bd9Sstevel@tonic-gate }
5547c478bd9Sstevel@tonic-gate 
555c7a079a8SJonathan Haslam static p4_generic_event_t *
find_generic_event(char * name)556c7a079a8SJonathan Haslam find_generic_event(char *name)
557c7a079a8SJonathan Haslam {
558c7a079a8SJonathan Haslam 	p4_generic_event_t	*gevp;
559c7a079a8SJonathan Haslam 
56049f52d51SKuriakose Kuruvilla 	for (gevp = p4_generic_events; gevp->name != NULL; gevp++)
561c7a079a8SJonathan Haslam 		if (strcmp(name, gevp->name) == 0)
562c7a079a8SJonathan Haslam 			return (gevp);
563c7a079a8SJonathan Haslam 
564c7a079a8SJonathan Haslam 	return (NULL);
565c7a079a8SJonathan Haslam }
566c7a079a8SJonathan Haslam 
567c7a079a8SJonathan Haslam static p4_event_t *
find_event(char * name)568c7a079a8SJonathan Haslam find_event(char *name)
569c7a079a8SJonathan Haslam {
570c7a079a8SJonathan Haslam 	p4_event_t		*evp;
571c7a079a8SJonathan Haslam 
572c7a079a8SJonathan Haslam 	for (evp = p4_events; evp->pe_name != NULL; evp++)
573c7a079a8SJonathan Haslam 		if (strcmp(name, evp->pe_name) == 0)
574c7a079a8SJonathan Haslam 			return (evp);
575c7a079a8SJonathan Haslam 
576c7a079a8SJonathan Haslam 	return (NULL);
577c7a079a8SJonathan Haslam }
578c7a079a8SJonathan Haslam 
5797c478bd9Sstevel@tonic-gate static uint64_t
p4_pcbe_event_coverage(char * event)5807c478bd9Sstevel@tonic-gate p4_pcbe_event_coverage(char *event)
5817c478bd9Sstevel@tonic-gate {
5827c478bd9Sstevel@tonic-gate 	p4_event_t		*ev;
583c7a079a8SJonathan Haslam 	p4_generic_event_t	*gevp;
5847c478bd9Sstevel@tonic-gate 
585c7a079a8SJonathan Haslam 	if ((ev = find_event(event)) == NULL) {
586c7a079a8SJonathan Haslam 		if ((gevp = find_generic_event(event)) != NULL)
587c7a079a8SJonathan Haslam 			return (gevp->ctr_mask);
588c7a079a8SJonathan Haslam 		else
589c7a079a8SJonathan Haslam 			return (0);
5907c478bd9Sstevel@tonic-gate 	}
5917c478bd9Sstevel@tonic-gate 
5927c478bd9Sstevel@tonic-gate 	return (ev->pe_ctr_mask);
5937c478bd9Sstevel@tonic-gate }
5947c478bd9Sstevel@tonic-gate 
5957c478bd9Sstevel@tonic-gate static uint64_t
p4_pcbe_overflow_bitmap(void)5967c478bd9Sstevel@tonic-gate p4_pcbe_overflow_bitmap(void)
5977c478bd9Sstevel@tonic-gate {
5987c478bd9Sstevel@tonic-gate 	extern int	kcpc_hw_overflow_intr_installed;
5997c478bd9Sstevel@tonic-gate 	uint64_t	ret = 0;
6007c478bd9Sstevel@tonic-gate 	int		i;
6017c478bd9Sstevel@tonic-gate 
6027c478bd9Sstevel@tonic-gate 	/*
6037c478bd9Sstevel@tonic-gate 	 * The CCCR's OVF bit indicates that the corresponding counter has
6047c478bd9Sstevel@tonic-gate 	 * overflowed. It must be explicitly cleared by software, so it is
6057c478bd9Sstevel@tonic-gate 	 * safe to read the CCCR values here.
6067c478bd9Sstevel@tonic-gate 	 */
6077c478bd9Sstevel@tonic-gate 	for (i = 0; i < 18; i++) {
6080ac7d7d8Skucharsk 		if (rdmsr(p4_ctrs[i].pc_ctladdr) & CCCR_OVF)
6097c478bd9Sstevel@tonic-gate 			ret |= (1 << i);
6107c478bd9Sstevel@tonic-gate 	}
6117c478bd9Sstevel@tonic-gate 
6127c478bd9Sstevel@tonic-gate 	/*
6137c478bd9Sstevel@tonic-gate 	 * Pentium 4 and Xeon turn off the CPC interrupt mask bit in the LVT at
6147c478bd9Sstevel@tonic-gate 	 * every overflow. Turn it back on here.
6157c478bd9Sstevel@tonic-gate 	 */
6167c478bd9Sstevel@tonic-gate 	ASSERT(kcpc_hw_overflow_intr_installed);
6177c478bd9Sstevel@tonic-gate 	(*kcpc_hw_enable_cpc_intr)();
6187c478bd9Sstevel@tonic-gate 
6197c478bd9Sstevel@tonic-gate 	return (ret);
6207c478bd9Sstevel@tonic-gate }
6217c478bd9Sstevel@tonic-gate 
6227c478bd9Sstevel@tonic-gate static int
p4_escr_inuse(p4_pcbe_config_t ** cfgs,int escr_ndx)6237c478bd9Sstevel@tonic-gate p4_escr_inuse(p4_pcbe_config_t **cfgs, int escr_ndx)
6247c478bd9Sstevel@tonic-gate {
6257c478bd9Sstevel@tonic-gate 	int i;
6267c478bd9Sstevel@tonic-gate 
6277c478bd9Sstevel@tonic-gate 	for (i = 0; i < 18; i++) {
6287c478bd9Sstevel@tonic-gate 		if (cfgs[i] == NULL)
6297c478bd9Sstevel@tonic-gate 			continue;
6307c478bd9Sstevel@tonic-gate 		if (cfgs[i]->p4_escr_ndx == escr_ndx)
6317c478bd9Sstevel@tonic-gate 			return (1);
6327c478bd9Sstevel@tonic-gate 	}
6337c478bd9Sstevel@tonic-gate 
6347c478bd9Sstevel@tonic-gate 	return (0);
6357c478bd9Sstevel@tonic-gate }
6367c478bd9Sstevel@tonic-gate 
6377c478bd9Sstevel@tonic-gate static void
build_cfgs(p4_pcbe_config_t * cfgs[18],uint64_t * data[18],void * token)6387c478bd9Sstevel@tonic-gate build_cfgs(p4_pcbe_config_t *cfgs[18], uint64_t *data[18], void *token)
6397c478bd9Sstevel@tonic-gate {
6407c478bd9Sstevel@tonic-gate 	p4_pcbe_config_t	*cfg = NULL;
6417c478bd9Sstevel@tonic-gate 	uint64_t		*daddr;
6427c478bd9Sstevel@tonic-gate 
6437c478bd9Sstevel@tonic-gate 	bzero(cfgs, 18 * sizeof (p4_pcbe_config_t *));
6447c478bd9Sstevel@tonic-gate 
6457c478bd9Sstevel@tonic-gate 	do {
6467c478bd9Sstevel@tonic-gate 		cfg = (p4_pcbe_config_t *)kcpc_next_config(token, cfg, &daddr);
6477c478bd9Sstevel@tonic-gate 
6487c478bd9Sstevel@tonic-gate 		if (cfg != NULL) {
6497c478bd9Sstevel@tonic-gate 			ASSERT(cfg->p4_picno < 18);
6507c478bd9Sstevel@tonic-gate 			cfgs[cfg->p4_picno] = cfg;
6517c478bd9Sstevel@tonic-gate 			if (data != NULL) {
6527c478bd9Sstevel@tonic-gate 				ASSERT(daddr != NULL);
6537c478bd9Sstevel@tonic-gate 				data[cfg->p4_picno] = daddr;
6547c478bd9Sstevel@tonic-gate 			}
6557c478bd9Sstevel@tonic-gate 		}
6567c478bd9Sstevel@tonic-gate 	} while (cfg != NULL);
6577c478bd9Sstevel@tonic-gate }
6587c478bd9Sstevel@tonic-gate 
6597c478bd9Sstevel@tonic-gate /*
6607c478bd9Sstevel@tonic-gate  * Programming a counter:
6617c478bd9Sstevel@tonic-gate  *
6627c478bd9Sstevel@tonic-gate  * Select event.
6637c478bd9Sstevel@tonic-gate  * Choose an ESCR capable of counting that event.
6647c478bd9Sstevel@tonic-gate  * Set up the ESCR with the desired parameters (usr, sys, tag).
6657c478bd9Sstevel@tonic-gate  * Set up the CCCR to point to the selected ESCR.
6667c478bd9Sstevel@tonic-gate  * Set the CCCR parameters (overflow, cascade, edge, etc).
6677c478bd9Sstevel@tonic-gate  */
6687c478bd9Sstevel@tonic-gate static int
p4_pcbe_configure(uint_t picnum,char * eventname,uint64_t preset,uint32_t flags,uint_t nattrs,kcpc_attr_t * attrs,void ** data,void * token)6697c478bd9Sstevel@tonic-gate p4_pcbe_configure(uint_t picnum, char *eventname, uint64_t preset,
6707c478bd9Sstevel@tonic-gate     uint32_t flags, uint_t nattrs, kcpc_attr_t *attrs, void **data,
6717c478bd9Sstevel@tonic-gate     void *token)
6727c478bd9Sstevel@tonic-gate {
6737c478bd9Sstevel@tonic-gate 	p4_pcbe_config_t	*cfgs[18];
6747c478bd9Sstevel@tonic-gate 	p4_pcbe_config_t	*cfg;
6757c478bd9Sstevel@tonic-gate 	p4_event_t		*ev;
676c7a079a8SJonathan Haslam 	p4_generic_event_t	*gevp;
6777c478bd9Sstevel@tonic-gate 	int			escr_ndx;
6787c478bd9Sstevel@tonic-gate 	int			i;
6797c478bd9Sstevel@tonic-gate 	uint16_t		emask = 0;
6807c478bd9Sstevel@tonic-gate 	uint8_t			tag;
6817c478bd9Sstevel@tonic-gate 	int			use_tag = 0;
6827c478bd9Sstevel@tonic-gate 	int			active_thread = 0x3; /* default is "any" */
6837c478bd9Sstevel@tonic-gate 	int			compare = 0;
6847c478bd9Sstevel@tonic-gate 	int			complement = 0;
6857c478bd9Sstevel@tonic-gate 	int			threshold = 0;
6867c478bd9Sstevel@tonic-gate 	int			edge = 0;
6877c478bd9Sstevel@tonic-gate 	int			sibling_usr = 0; /* count usr on other cpu */
6887c478bd9Sstevel@tonic-gate 	int			sibling_sys = 0; /* count sys on other cpu */
689c7a079a8SJonathan Haslam 	int			invalid_attr = 0;
6907c478bd9Sstevel@tonic-gate 
6917c478bd9Sstevel@tonic-gate 	/*
6927c478bd9Sstevel@tonic-gate 	 * If we've been handed an existing configuration, we need only preset
6937c478bd9Sstevel@tonic-gate 	 * the counter value.
6947c478bd9Sstevel@tonic-gate 	 */
6957c478bd9Sstevel@tonic-gate 	if (*data != NULL) {
6967c478bd9Sstevel@tonic-gate 		cfg = *data;
6977c478bd9Sstevel@tonic-gate 		cfg->p4_rawpic = preset & MASK40;
6987c478bd9Sstevel@tonic-gate 		return (0);
6997c478bd9Sstevel@tonic-gate 	}
7007c478bd9Sstevel@tonic-gate 
7017c478bd9Sstevel@tonic-gate 	if (picnum < 0 || picnum >= 18)
7027c478bd9Sstevel@tonic-gate 		return (CPC_INVALID_PICNUM);
7037c478bd9Sstevel@tonic-gate 
704c7a079a8SJonathan Haslam 	if ((ev	= find_event(eventname)) == NULL) {
705c7a079a8SJonathan Haslam 		if ((gevp = find_generic_event(eventname)) != NULL) {
706c7a079a8SJonathan Haslam 			ev = find_event(gevp->event);
707c7a079a8SJonathan Haslam 			ASSERT(ev != NULL);
708c7a079a8SJonathan Haslam 
709c7a079a8SJonathan Haslam 			/*
710c7a079a8SJonathan Haslam 			 * For generic events a HTT processor is only allowed
711c7a079a8SJonathan Haslam 			 * to specify the 'active_thread', 'count_sibling_usr'
712c7a079a8SJonathan Haslam 			 * and 'count_sibling_sys' attributes.
713c7a079a8SJonathan Haslam 			 */
714c7a079a8SJonathan Haslam 			if (p4_htt)
715c7a079a8SJonathan Haslam 				for (i = 0; i < nattrs; i++)
716c7a079a8SJonathan Haslam 					if (strstr(P4_ATTRS,
717c7a079a8SJonathan Haslam 					    attrs[i].ka_name) != NULL)
718c7a079a8SJonathan Haslam 						invalid_attr = 1;
719c7a079a8SJonathan Haslam 
720c7a079a8SJonathan Haslam 			if ((p4_htt && invalid_attr) ||
721c7a079a8SJonathan Haslam 			    (!p4_htt && nattrs > 0))
722c7a079a8SJonathan Haslam 				return (CPC_ATTRIBUTE_OUT_OF_RANGE);
723c7a079a8SJonathan Haslam 
724c7a079a8SJonathan Haslam 			emask = gevp->emask;
725c7a079a8SJonathan Haslam 		} else {
7267c478bd9Sstevel@tonic-gate 			return (CPC_INVALID_EVENT);
727c7a079a8SJonathan Haslam 		}
728c7a079a8SJonathan Haslam 	}
7297c478bd9Sstevel@tonic-gate 
7307c478bd9Sstevel@tonic-gate 	build_cfgs(cfgs, NULL, token);
7317c478bd9Sstevel@tonic-gate 
7327c478bd9Sstevel@tonic-gate 	/*
7337c478bd9Sstevel@tonic-gate 	 * Find an ESCR capable of counting this event.
7347c478bd9Sstevel@tonic-gate 	 */
7357c478bd9Sstevel@tonic-gate 	for (escr_ndx = 0; escr_ndx < ESCR_MAX_INDEX; escr_ndx++) {
7367c478bd9Sstevel@tonic-gate 		if ((ev->pe_escr_map & (1ULL << escr_ndx)) &&
7377c478bd9Sstevel@tonic-gate 		    p4_escr_inuse(cfgs, escr_ndx) == 0)
7387c478bd9Sstevel@tonic-gate 			break;
7397c478bd9Sstevel@tonic-gate 	}
7407c478bd9Sstevel@tonic-gate 
7417c478bd9Sstevel@tonic-gate 	/*
7427c478bd9Sstevel@tonic-gate 	 * All ESCRs capable of counting this event are already being
7437c478bd9Sstevel@tonic-gate 	 * used.
7447c478bd9Sstevel@tonic-gate 	 */
7457c478bd9Sstevel@tonic-gate 	if (escr_ndx == ESCR_MAX_INDEX)
7467c478bd9Sstevel@tonic-gate 		return (CPC_RESOURCE_UNAVAIL);
7477c478bd9Sstevel@tonic-gate 
7487c478bd9Sstevel@tonic-gate 	/*
7497c478bd9Sstevel@tonic-gate 	 * At this point, ev points to the desired event and escr is the index
7507c478bd9Sstevel@tonic-gate 	 * of a capable and available ESCR.
7517c478bd9Sstevel@tonic-gate 	 *
7527c478bd9Sstevel@tonic-gate 	 * Now process and verify the attributes.
7537c478bd9Sstevel@tonic-gate 	 */
7547c478bd9Sstevel@tonic-gate 	for (i = 0; i < nattrs; i++) {
7557c478bd9Sstevel@tonic-gate 		if (strcmp("emask", attrs[i].ka_name) == 0) {
7567c478bd9Sstevel@tonic-gate 			if ((attrs[i].ka_val | ev->pe_escr_mask)
7577c478bd9Sstevel@tonic-gate 			    != ev->pe_escr_mask)
7587c478bd9Sstevel@tonic-gate 				return (CPC_ATTRIBUTE_OUT_OF_RANGE);
7597c478bd9Sstevel@tonic-gate 			emask = attrs[i].ka_val;
7607c478bd9Sstevel@tonic-gate 			continue;
7617c478bd9Sstevel@tonic-gate 		} else if (strcmp("tag", attrs[i].ka_name) == 0) {
7627c478bd9Sstevel@tonic-gate 			if (attrs[i].ka_val > ESCR_TAG_VALUE_MAX)
7637c478bd9Sstevel@tonic-gate 				return (CPC_ATTRIBUTE_OUT_OF_RANGE);
7647c478bd9Sstevel@tonic-gate 			tag = attrs[i].ka_val;
7657c478bd9Sstevel@tonic-gate 			use_tag = 1;
7667c478bd9Sstevel@tonic-gate 			continue;
7677c478bd9Sstevel@tonic-gate 		} else if (strcmp("compare", attrs[i].ka_name) == 0) {
7687c478bd9Sstevel@tonic-gate 			if (attrs[i].ka_val != 0)
7697c478bd9Sstevel@tonic-gate 				compare = 1;
7707c478bd9Sstevel@tonic-gate 			continue;
7717c478bd9Sstevel@tonic-gate 		} else if (strcmp("complement", attrs[i].ka_name) == 0) {
7727c478bd9Sstevel@tonic-gate 			if (attrs[i].ka_val != 0)
7737c478bd9Sstevel@tonic-gate 				complement = 1;
7747c478bd9Sstevel@tonic-gate 			continue;
7757c478bd9Sstevel@tonic-gate 		} else if (strcmp("threshold", attrs[i].ka_name) == 0) {
7767c478bd9Sstevel@tonic-gate 			if (attrs[i].ka_val > CCCR_THRESHOLD_MAX)
7777c478bd9Sstevel@tonic-gate 				return (CPC_ATTRIBUTE_OUT_OF_RANGE);
7787c478bd9Sstevel@tonic-gate 			threshold = attrs[i].ka_val;
7797c478bd9Sstevel@tonic-gate 			continue;
7807c478bd9Sstevel@tonic-gate 		} else if (strcmp("edge", attrs[i].ka_name) == 0) {
7817c478bd9Sstevel@tonic-gate 			if (attrs[i].ka_val != 0)
7827c478bd9Sstevel@tonic-gate 				edge = 1;
7837c478bd9Sstevel@tonic-gate 			continue;
7847c478bd9Sstevel@tonic-gate 		}
7857c478bd9Sstevel@tonic-gate 
7867c478bd9Sstevel@tonic-gate 		/*
7877c478bd9Sstevel@tonic-gate 		 * The remaining attributes are valid only on HyperThreaded P4s
7887c478bd9Sstevel@tonic-gate 		 * for processes with the "cpc_cpu" privilege.
7897c478bd9Sstevel@tonic-gate 		 */
7907c478bd9Sstevel@tonic-gate 		if (p4_htt == 0)
7917c478bd9Sstevel@tonic-gate 			return (CPC_INVALID_ATTRIBUTE);
7927c478bd9Sstevel@tonic-gate 
7937c478bd9Sstevel@tonic-gate 		if (secpolicy_cpc_cpu(crgetcred()) != 0)
7947c478bd9Sstevel@tonic-gate 			return (CPC_ATTR_REQUIRES_PRIVILEGE);
7957c478bd9Sstevel@tonic-gate 
7967c478bd9Sstevel@tonic-gate 		if (strcmp("active_thread", attrs[i].ka_name) == 0) {
7977c478bd9Sstevel@tonic-gate 			if ((attrs[i].ka_val | CCCR_ACTV_THR_MASK) !=
7987c478bd9Sstevel@tonic-gate 			    CCCR_ACTV_THR_MASK)
7997c478bd9Sstevel@tonic-gate 				return (CPC_ATTRIBUTE_OUT_OF_RANGE);
8007c478bd9Sstevel@tonic-gate 			active_thread = (int)attrs[i].ka_val;
8017c478bd9Sstevel@tonic-gate 		} else if (strcmp("count_sibling_usr", attrs[i].ka_name) == 0) {
8027c478bd9Sstevel@tonic-gate 			if (attrs[i].ka_val != 0)
8037c478bd9Sstevel@tonic-gate 				sibling_usr = 1;
8047c478bd9Sstevel@tonic-gate 		} else if (strcmp("count_sibling_sys", attrs[i].ka_name) == 0) {
8057c478bd9Sstevel@tonic-gate 			if (attrs[i].ka_val != 0)
8067c478bd9Sstevel@tonic-gate 				sibling_sys = 1;
8077c478bd9Sstevel@tonic-gate 		} else
8087c478bd9Sstevel@tonic-gate 			return (CPC_INVALID_ATTRIBUTE);
8097c478bd9Sstevel@tonic-gate 	}
8107c478bd9Sstevel@tonic-gate 
8117c478bd9Sstevel@tonic-gate 	/*
8127c478bd9Sstevel@tonic-gate 	 * Make sure the counter can count this event
8137c478bd9Sstevel@tonic-gate 	 */
8147c478bd9Sstevel@tonic-gate 	if ((ev->pe_ctr_mask & C(picnum)) == 0)
8157c478bd9Sstevel@tonic-gate 		return (CPC_PIC_NOT_CAPABLE);
8167c478bd9Sstevel@tonic-gate 
8177c478bd9Sstevel@tonic-gate 	/*
8187c478bd9Sstevel@tonic-gate 	 * Find an ESCR that lines up with the event _and_ the counter.
8197c478bd9Sstevel@tonic-gate 	 */
8207c478bd9Sstevel@tonic-gate 	for (escr_ndx = 0; escr_ndx < ESCR_MAX_INDEX; escr_ndx++) {
8217c478bd9Sstevel@tonic-gate 		if ((ev->pe_escr_map & (1ULL << escr_ndx)) &&
8227c478bd9Sstevel@tonic-gate 		    (p4_escrs[escr_ndx].pe_map & (1 << picnum)) &&
8237c478bd9Sstevel@tonic-gate 		    p4_escr_inuse(cfgs, escr_ndx) == 0)
8247c478bd9Sstevel@tonic-gate 			break;
8257c478bd9Sstevel@tonic-gate 	}
8267c478bd9Sstevel@tonic-gate 	if (escr_ndx == ESCR_MAX_INDEX)
8277c478bd9Sstevel@tonic-gate 		return (CPC_RESOURCE_UNAVAIL);
8287c478bd9Sstevel@tonic-gate 
8297c478bd9Sstevel@tonic-gate 	cfg = (p4_pcbe_config_t *)kmem_alloc(sizeof (p4_pcbe_config_t),
8307c478bd9Sstevel@tonic-gate 	    KM_SLEEP);
8317c478bd9Sstevel@tonic-gate 
8327c478bd9Sstevel@tonic-gate 	cfg->p4_flags = 0;
8337c478bd9Sstevel@tonic-gate 	cfg->p4_picno = picnum;
8347c478bd9Sstevel@tonic-gate 	cfg->p4_escr_ndx = escr_ndx;
8357c478bd9Sstevel@tonic-gate 	cfg->p4_escr = (ev->pe_ev << ESCR_EVSEL_SHIFT) |
8367c478bd9Sstevel@tonic-gate 	    (emask << ESCR_EVMASK_SHIFT);
8377c478bd9Sstevel@tonic-gate 
8387c478bd9Sstevel@tonic-gate 	if (use_tag == 1) {
8397c478bd9Sstevel@tonic-gate 		cfg->p4_escr |= tag << ESCR_TAG_VALUE_SHIFT;
8407c478bd9Sstevel@tonic-gate 		cfg->p4_escr |= ESCR_TAG_ENABLE;
8417c478bd9Sstevel@tonic-gate 	}
8427c478bd9Sstevel@tonic-gate 
8437c478bd9Sstevel@tonic-gate 	if (p4_htt) {
8447c478bd9Sstevel@tonic-gate 		/*
8457c478bd9Sstevel@tonic-gate 		 * This is a HyperThreaded P4.  Since we don't know which
8467c478bd9Sstevel@tonic-gate 		 * logical CPU this configuration will eventually be programmed
8477c478bd9Sstevel@tonic-gate 		 * on, we can't yet decide which fields of the ESCR to select.
8487c478bd9Sstevel@tonic-gate 		 *
8497c478bd9Sstevel@tonic-gate 		 * Record the necessary information in the flags for later.
8507c478bd9Sstevel@tonic-gate 		 */
8517c478bd9Sstevel@tonic-gate 		if (flags & CPC_COUNT_USER)
8527c478bd9Sstevel@tonic-gate 			cfg->p4_flags |= P4_THIS_USR;
8537c478bd9Sstevel@tonic-gate 		if (flags & CPC_COUNT_SYSTEM)
8547c478bd9Sstevel@tonic-gate 			cfg->p4_flags |= P4_THIS_SYS;
8557c478bd9Sstevel@tonic-gate 		if (p4_htt && sibling_usr)
8567c478bd9Sstevel@tonic-gate 			cfg->p4_flags |= P4_SIBLING_USR;
8577c478bd9Sstevel@tonic-gate 		if (p4_htt && sibling_sys)
8587c478bd9Sstevel@tonic-gate 			cfg->p4_flags |= P4_SIBLING_SYS;
8597c478bd9Sstevel@tonic-gate 	} else {
8607c478bd9Sstevel@tonic-gate 		/*
8617c478bd9Sstevel@tonic-gate 		 * This is not HyperThreaded, so we can determine the exact
8627c478bd9Sstevel@tonic-gate 		 * ESCR value necessary now.
8637c478bd9Sstevel@tonic-gate 		 */
8647c478bd9Sstevel@tonic-gate 		if (flags & CPC_COUNT_USER)
8657c478bd9Sstevel@tonic-gate 			cfg->p4_escr |= ESCR_USR;
8667c478bd9Sstevel@tonic-gate 		if (flags & CPC_COUNT_SYSTEM)
8677c478bd9Sstevel@tonic-gate 			cfg->p4_escr |= ESCR_OS;
8687c478bd9Sstevel@tonic-gate 	}
8697c478bd9Sstevel@tonic-gate 
8707c478bd9Sstevel@tonic-gate 	cfg->p4_rawpic = preset & MASK40;
8717c478bd9Sstevel@tonic-gate 
8727c478bd9Sstevel@tonic-gate 	/*
8737c478bd9Sstevel@tonic-gate 	 * Even on non-HT P4s, Intel states the active_thread field (marked as
8747c478bd9Sstevel@tonic-gate 	 * "reserved" for the non-HT chips) must be set to all 1s.
8757c478bd9Sstevel@tonic-gate 	 */
8767c478bd9Sstevel@tonic-gate 	cfg->p4_cccr = CCCR_INIT | (active_thread << CCCR_ACTV_THR_SHIFT);
8777c478bd9Sstevel@tonic-gate 	if (compare)
8787c478bd9Sstevel@tonic-gate 		cfg->p4_cccr |= CCCR_COMPARE;
8797c478bd9Sstevel@tonic-gate 	if (complement)
8807c478bd9Sstevel@tonic-gate 		cfg->p4_cccr |= CCCR_COMPLEMENT;
8817c478bd9Sstevel@tonic-gate 	cfg->p4_cccr |= threshold << CCCR_THRESHOLD_SHIFT;
8827c478bd9Sstevel@tonic-gate 	if (edge)
8837c478bd9Sstevel@tonic-gate 		cfg->p4_cccr |= CCCR_EDGE;
8847c478bd9Sstevel@tonic-gate 	cfg->p4_cccr |= p4_escrs[cfg->p4_escr_ndx].pe_num
8857c478bd9Sstevel@tonic-gate 	    << CCCR_ESCR_SEL_SHIFT;
8867c478bd9Sstevel@tonic-gate 	if (flags & CPC_OVF_NOTIFY_EMT) {
8877c478bd9Sstevel@tonic-gate 		if (p4_htt)
8887c478bd9Sstevel@tonic-gate 			cfg->p4_flags |= P4_PMI;
8897c478bd9Sstevel@tonic-gate 		else {
8907c478bd9Sstevel@tonic-gate 			/*
8917c478bd9Sstevel@tonic-gate 			 * If the user has asked for notification of overflows,
8927c478bd9Sstevel@tonic-gate 			 * we automatically program the hardware to generate an
8937c478bd9Sstevel@tonic-gate 			 * interrupt on overflow.
8947c478bd9Sstevel@tonic-gate 			 *
8957c478bd9Sstevel@tonic-gate 			 * This can only be programmed now if this P4 doesn't
8967c478bd9Sstevel@tonic-gate 			 * have HyperThreading. If it does, we must wait until
8977c478bd9Sstevel@tonic-gate 			 * we know which logical CPU we'll be programming.
8987c478bd9Sstevel@tonic-gate 			 */
8997c478bd9Sstevel@tonic-gate 			cfg->p4_cccr |= CCCR_OVF_PMI;
9007c478bd9Sstevel@tonic-gate 		}
9017c478bd9Sstevel@tonic-gate 	}
9027c478bd9Sstevel@tonic-gate 
9037c478bd9Sstevel@tonic-gate 	*data = cfg;
9047c478bd9Sstevel@tonic-gate 
9057c478bd9Sstevel@tonic-gate 	return (0);
9067c478bd9Sstevel@tonic-gate }
9077c478bd9Sstevel@tonic-gate 
9087c478bd9Sstevel@tonic-gate static void
p4_pcbe_program(void * token)9097c478bd9Sstevel@tonic-gate p4_pcbe_program(void *token)
9107c478bd9Sstevel@tonic-gate {
9117c478bd9Sstevel@tonic-gate 	int			i;
9127c478bd9Sstevel@tonic-gate 	uint64_t		cccr;
9137c478bd9Sstevel@tonic-gate 	p4_pcbe_config_t	*cfgs[18];
9147c478bd9Sstevel@tonic-gate 
9157c478bd9Sstevel@tonic-gate 	p4_pcbe_allstop();
9167c478bd9Sstevel@tonic-gate 
9177c478bd9Sstevel@tonic-gate 	build_cfgs(cfgs, NULL, token);
9187c478bd9Sstevel@tonic-gate 
9197c478bd9Sstevel@tonic-gate 	if (p4_rdpmc_avail) {
920843e1988Sjohnlev 		ulong_t curcr4 = getcr4();
9217c478bd9Sstevel@tonic-gate 		if (kcpc_allow_nonpriv(token))
9227c478bd9Sstevel@tonic-gate 			setcr4(curcr4 | CR4_PCE);
9237c478bd9Sstevel@tonic-gate 		else
9247c478bd9Sstevel@tonic-gate 			setcr4(curcr4 & ~CR4_PCE);
9257c478bd9Sstevel@tonic-gate 	}
9267c478bd9Sstevel@tonic-gate 
9277c478bd9Sstevel@tonic-gate 	/*
9287c478bd9Sstevel@tonic-gate 	 * Ideally we would start all counters with a single operation, but in
9297c478bd9Sstevel@tonic-gate 	 * P4 each counter is enabled individually via its CCCR. To minimize the
9307c478bd9Sstevel@tonic-gate 	 * probe effect of enabling the counters, we do it in two passes: the
9317c478bd9Sstevel@tonic-gate 	 * first programs the counter and ESCR, and the second programs the
9327c478bd9Sstevel@tonic-gate 	 * CCCR (and thus enables the counter).
9337c478bd9Sstevel@tonic-gate 	 */
9347c478bd9Sstevel@tonic-gate 	if (p4_htt) {
935fb2f18f8Sesaxe 		int	lid = cpuid_get_clogid(CPU); /* Logical ID of CPU */
9367c478bd9Sstevel@tonic-gate 
9377c478bd9Sstevel@tonic-gate 		for (i = 0; i < 18; i++) {
9380ac7d7d8Skucharsk 			uint64_t escr;
9390ac7d7d8Skucharsk 
9407c478bd9Sstevel@tonic-gate 			if (cfgs[i] == NULL)
9417c478bd9Sstevel@tonic-gate 				continue;
9427c478bd9Sstevel@tonic-gate 			escr = (uint64_t)cfgs[i]->p4_escr;
9437c478bd9Sstevel@tonic-gate 
9447c478bd9Sstevel@tonic-gate 			if (cfgs[i]->p4_flags & P4_THIS_USR)
9457c478bd9Sstevel@tonic-gate 				escr |= (lid == 0) ? ESCR_T0_USR : ESCR_T1_USR;
9467c478bd9Sstevel@tonic-gate 			if (cfgs[i]->p4_flags & P4_THIS_SYS)
9477c478bd9Sstevel@tonic-gate 				escr |= (lid == 0) ? ESCR_T0_OS : ESCR_T1_OS;
9487c478bd9Sstevel@tonic-gate 			if (cfgs[i]->p4_flags & P4_SIBLING_USR)
9497c478bd9Sstevel@tonic-gate 				escr |= (lid == 0) ? ESCR_T1_USR : ESCR_T0_USR;
9507c478bd9Sstevel@tonic-gate 			if (cfgs[i]->p4_flags & P4_SIBLING_SYS)
9517c478bd9Sstevel@tonic-gate 				escr |= (lid == 0) ? ESCR_T1_OS : ESCR_T0_OS;
9527c478bd9Sstevel@tonic-gate 
9530ac7d7d8Skucharsk 			wrmsr(p4_ctrs[i].pc_caddr, cfgs[i]->p4_rawpic);
9540ac7d7d8Skucharsk 			wrmsr(p4_escrs[cfgs[i]->p4_escr_ndx].pe_addr, escr);
9557c478bd9Sstevel@tonic-gate 		}
9567c478bd9Sstevel@tonic-gate 
9577c478bd9Sstevel@tonic-gate 		for (i = 0; i < 18; i++) {
9587c478bd9Sstevel@tonic-gate 			if (cfgs[i] == NULL)
9597c478bd9Sstevel@tonic-gate 				continue;
9607c478bd9Sstevel@tonic-gate 			cccr = (uint64_t)cfgs[i]->p4_cccr;
9617c478bd9Sstevel@tonic-gate 			/*
9627c478bd9Sstevel@tonic-gate 			 * We always target the overflow interrupt at the
9637c478bd9Sstevel@tonic-gate 			 * logical CPU which is doing the counting.
9647c478bd9Sstevel@tonic-gate 			 */
9657c478bd9Sstevel@tonic-gate 			if (cfgs[i]->p4_flags & P4_PMI)
9667c478bd9Sstevel@tonic-gate 				cccr |= (lid == 0) ?
9677c478bd9Sstevel@tonic-gate 				    CCCR_OVF_PMI_T0 : CCCR_OVF_PMI_T1;
9680ac7d7d8Skucharsk 			wrmsr(p4_ctrs[i].pc_ctladdr, cccr);
9697c478bd9Sstevel@tonic-gate 		}
9707c478bd9Sstevel@tonic-gate 	} else {
9717c478bd9Sstevel@tonic-gate 		for (i = 0; i < 18; i++) {
9727c478bd9Sstevel@tonic-gate 			if (cfgs[i] == NULL)
9737c478bd9Sstevel@tonic-gate 				continue;
9740ac7d7d8Skucharsk 			wrmsr(p4_ctrs[i].pc_caddr, cfgs[i]->p4_rawpic);
9750ac7d7d8Skucharsk 			wrmsr(p4_escrs[cfgs[i]->p4_escr_ndx].pe_addr,
9760ac7d7d8Skucharsk 			    (uint64_t)cfgs[i]->p4_escr);
9777c478bd9Sstevel@tonic-gate 		}
9787c478bd9Sstevel@tonic-gate 
9797c478bd9Sstevel@tonic-gate 		for (i = 0; i < 18; i++) {
9807c478bd9Sstevel@tonic-gate 			if (cfgs[i] == NULL)
9817c478bd9Sstevel@tonic-gate 				continue;
9820ac7d7d8Skucharsk 			wrmsr(p4_ctrs[i].pc_ctladdr,
9830ac7d7d8Skucharsk 			    (uint64_t)cfgs[i]->p4_cccr);
9847c478bd9Sstevel@tonic-gate 		}
9857c478bd9Sstevel@tonic-gate 	}
9867c478bd9Sstevel@tonic-gate }
9877c478bd9Sstevel@tonic-gate 
9887c478bd9Sstevel@tonic-gate static void
p4_pcbe_allstop(void)9897c478bd9Sstevel@tonic-gate p4_pcbe_allstop(void)
9907c478bd9Sstevel@tonic-gate {
9917c478bd9Sstevel@tonic-gate 	int		i;
9927c478bd9Sstevel@tonic-gate 
9937c478bd9Sstevel@tonic-gate 	for (i = 0; i < 18; i++)
9940ac7d7d8Skucharsk 		wrmsr(p4_ctrs[i].pc_ctladdr, 0ULL);
9957c478bd9Sstevel@tonic-gate 
9967c478bd9Sstevel@tonic-gate 	setcr4(getcr4() & ~CR4_PCE);
9977c478bd9Sstevel@tonic-gate }
9987c478bd9Sstevel@tonic-gate 
9997c478bd9Sstevel@tonic-gate 
10007c478bd9Sstevel@tonic-gate static void
p4_pcbe_sample(void * token)10017c478bd9Sstevel@tonic-gate p4_pcbe_sample(void *token)
10027c478bd9Sstevel@tonic-gate {
10037c478bd9Sstevel@tonic-gate 	p4_pcbe_config_t	*cfgs[18];
10047c478bd9Sstevel@tonic-gate 	uint64_t		*addrs[18];
10057c478bd9Sstevel@tonic-gate 	uint64_t		curpic[18];
10067c478bd9Sstevel@tonic-gate 	int64_t			diff;
10077c478bd9Sstevel@tonic-gate 	int			i;
10087c478bd9Sstevel@tonic-gate 
10097c478bd9Sstevel@tonic-gate 	for (i = 0; i < 18; i++)
10100ac7d7d8Skucharsk 		curpic[i] = rdmsr(p4_ctrs[i].pc_caddr);
10117c478bd9Sstevel@tonic-gate 
10127c478bd9Sstevel@tonic-gate 	build_cfgs(cfgs, addrs, token);
10137c478bd9Sstevel@tonic-gate 
10147c478bd9Sstevel@tonic-gate 	for (i = 0; i < 18; i++) {
10157c478bd9Sstevel@tonic-gate 		if (cfgs[i] == NULL)
10167c478bd9Sstevel@tonic-gate 			continue;
10177c478bd9Sstevel@tonic-gate 		diff = curpic[i] - cfgs[i]->p4_rawpic;
10187c478bd9Sstevel@tonic-gate 		if (diff < 0)
10197c478bd9Sstevel@tonic-gate 			diff += (1ll << 40);
10207c478bd9Sstevel@tonic-gate 		*addrs[i] += diff;
10217c478bd9Sstevel@tonic-gate 		DTRACE_PROBE4(p4__pcbe__sample, int, i, uint64_t, *addrs[i],
10227c478bd9Sstevel@tonic-gate 		    uint64_t, curpic[i], uint64_t, cfgs[i]->p4_rawpic);
10237c478bd9Sstevel@tonic-gate 		cfgs[i]->p4_rawpic = *addrs[i] & MASK40;
10247c478bd9Sstevel@tonic-gate 	}
10257c478bd9Sstevel@tonic-gate }
10267c478bd9Sstevel@tonic-gate 
10277c478bd9Sstevel@tonic-gate static void
p4_pcbe_free(void * config)10287c478bd9Sstevel@tonic-gate p4_pcbe_free(void *config)
10297c478bd9Sstevel@tonic-gate {
10307c478bd9Sstevel@tonic-gate 	kmem_free(config, sizeof (p4_pcbe_config_t));
10317c478bd9Sstevel@tonic-gate }
10327c478bd9Sstevel@tonic-gate 
10337c478bd9Sstevel@tonic-gate static struct modlpcbe modlpcbe = {
10347c478bd9Sstevel@tonic-gate 	&mod_pcbeops,
1035820c9f58Skk208521 	"Pentium 4 Performance Counters",
10367c478bd9Sstevel@tonic-gate 	&p4_pcbe_ops
10377c478bd9Sstevel@tonic-gate };
10387c478bd9Sstevel@tonic-gate 
10397c478bd9Sstevel@tonic-gate static struct modlinkage modl = {
10407c478bd9Sstevel@tonic-gate 	MODREV_1,
10417c478bd9Sstevel@tonic-gate 	&modlpcbe,
10427c478bd9Sstevel@tonic-gate };
10437c478bd9Sstevel@tonic-gate 
10447c478bd9Sstevel@tonic-gate int
_init(void)10457c478bd9Sstevel@tonic-gate _init(void)
10467c478bd9Sstevel@tonic-gate {
10477c478bd9Sstevel@tonic-gate 	if (p4_pcbe_init() != 0)
10487c478bd9Sstevel@tonic-gate 		return (ENOTSUP);
10497c478bd9Sstevel@tonic-gate 	return (mod_install(&modl));
10507c478bd9Sstevel@tonic-gate }
10517c478bd9Sstevel@tonic-gate 
10527c478bd9Sstevel@tonic-gate int
_fini(void)10537c478bd9Sstevel@tonic-gate _fini(void)
10547c478bd9Sstevel@tonic-gate {
10557c478bd9Sstevel@tonic-gate 	return (mod_remove(&modl));
10567c478bd9Sstevel@tonic-gate }
10577c478bd9Sstevel@tonic-gate 
10587c478bd9Sstevel@tonic-gate int
_info(struct modinfo * mi)10597c478bd9Sstevel@tonic-gate _info(struct modinfo *mi)
10607c478bd9Sstevel@tonic-gate {
10617c478bd9Sstevel@tonic-gate 	return (mod_info(&modl, mi));
10627c478bd9Sstevel@tonic-gate }
1063