xref: /illumos-gate/usr/src/uts/sun4v/pcbe/niagara2_pcbe.c (revision 968633ad8faee931821fd6b656eb0d96d4b186c0)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 /*
27  * This file contains preset event names from the Performance Application
28  * Programming Interface v3.5 which included the following notice:
29  *
30  *                             Copyright (c) 2005,6
31  *                           Innovative Computing Labs
32  *                         Computer Science Department,
33  *                            University of Tennessee,
34  *                                 Knoxville, TN.
35  *                              All Rights Reserved.
36  *
37  *
38  * Redistribution and use in source and binary forms, with or without
39  * modification, are permitted provided that the following conditions are met:
40  *
41  *    * Redistributions of source code must retain the above copyright notice,
42  *      this list of conditions and the following disclaimer.
43  *    * Redistributions in binary form must reproduce the above copyright
44  *      notice, this list of conditions and the following disclaimer in the
45  *      documentation and/or other materials provided with the distribution.
46  *    * Neither the name of the University of Tennessee nor the names of its
47  *      contributors may be used to endorse or promote products derived from
48  *      this software without specific prior written permission.
49  *
50  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
51  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
52  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
53  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
54  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
55  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
56  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
57  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
58  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
59  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
60  * POSSIBILITY OF SUCH DAMAGE.
61  *
62  *
63  * This open source software license conforms to the BSD License template.
64  */
65 
66 /*
67  * Niagara2 Performance Counter Backend
68  */
69 
70 #include <sys/cpuvar.h>
71 #include <sys/systm.h>
72 #include <sys/archsystm.h>
73 #include <sys/cmn_err.h>
74 #include <sys/cpc_impl.h>
75 #include <sys/cpc_pcbe.h>
76 #include <sys/modctl.h>
77 #include <sys/machsystm.h>
78 #include <sys/sdt.h>
79 #include <sys/niagara2regs.h>
80 #include <sys/hsvc.h>
81 #include <sys/hypervisor_api.h>
82 #include <sys/disp.h>
83 
84 /*LINTLIBRARY*/
85 static int ni2_pcbe_init(void);
86 static uint_t ni2_pcbe_ncounters(void);
87 static const char *ni2_pcbe_impl_name(void);
88 static const char *ni2_pcbe_cpuref(void);
89 static char *ni2_pcbe_list_events(uint_t picnum);
90 static char *ni2_pcbe_list_attrs(void);
91 static uint64_t ni2_pcbe_event_coverage(char *event);
92 static uint64_t ni2_pcbe_overflow_bitmap(void);
93 static int ni2_pcbe_configure(uint_t picnum, char *event, uint64_t preset,
94     uint32_t flags, uint_t nattrs, kcpc_attr_t *attrs, void **data,
95     void *token);
96 static void ni2_pcbe_program(void *token);
97 static void ni2_pcbe_allstop(void);
98 static void ni2_pcbe_sample(void *token);
99 static void ni2_pcbe_free(void *config);
100 
101 extern void ultra_setpcr(uint64_t);
102 extern uint64_t ultra_getpcr(void);
103 extern void ultra_setpic(uint64_t);
104 extern uint64_t ultra_getpic(void);
105 extern uint64_t ultra_gettick(void);
106 extern char cpu_module_name[];
107 
108 pcbe_ops_t ni2_pcbe_ops = {
109 	PCBE_VER_1,
110 	CPC_CAP_OVERFLOW_INTERRUPT | CPC_CAP_OVERFLOW_PRECISE,
111 	ni2_pcbe_ncounters,
112 	ni2_pcbe_impl_name,
113 	ni2_pcbe_cpuref,
114 	ni2_pcbe_list_events,
115 	ni2_pcbe_list_attrs,
116 	ni2_pcbe_event_coverage,
117 	ni2_pcbe_overflow_bitmap,
118 	ni2_pcbe_configure,
119 	ni2_pcbe_program,
120 	ni2_pcbe_allstop,
121 	ni2_pcbe_sample,
122 	ni2_pcbe_free
123 };
124 
125 typedef struct _ni2_pcbe_config {
126 	uint_t		pcbe_picno;	/* 0 for pic0 or 1 for pic1 */
127 	uint32_t	pcbe_evsel;	/* %pcr event code unshifted */
128 	uint32_t	pcbe_flags;	/* hpriv/user/system/priv */
129 	uint32_t	pcbe_pic;	/* unshifted raw %pic value */
130 } ni2_pcbe_config_t;
131 
132 typedef struct _ni2_event {
133 	const char	*name;
134 	const uint32_t	emask;
135 	const uint32_t	emask_valid;	/* Mask of unreserved MASK bits */
136 } ni2_event_t;
137 
138 typedef struct _ni2_generic_event {
139 	char *name;
140 	char *event;
141 } ni2_generic_event_t;
142 
143 #define	ULTRA_PCR_PRIVPIC	(UINT64_C(1) << CPC_NIAGARA2_PCR_PRIV_SHIFT)
144 #define	EV_END {NULL, 0, 0}
145 #define	GEN_EV_END {NULL, NULL}
146 
147 static const uint64_t	allstopped = (ULTRA_PCR_PRIVPIC |
148 	CPC_NIAGARA2_PCR_HOLDOV0 | CPC_NIAGARA2_PCR_HOLDOV1);
149 
150 /*
151  * We update this array in the program and allstop routine. The array
152  * is checked in the sample routine to allow us to only perform the
153  * PCR.ht bit check when counting is in progress.
154  */
155 static boolean_t ni2_cpc_counting[NCPU];
156 
157 static ni2_event_t ni2_events[] = {
158 	{ "Idle_strands",			0x000, 0x00 },
159 	{ "Br_completed",			0x201, 0xff },
160 	{ "Br_taken",				0x202, 0xff },
161 	{ "Instr_FGU_arithmetic",		0x204, 0xff },
162 	{ "Instr_ld",				0x208, 0xff },
163 	{ "Instr_st",				0x210, 0xff },
164 	{ "Instr_sw",				0x220, 0xff },
165 	{ "Instr_other",			0x240, 0xff },
166 	{ "Atomics",				0x280, 0xff },
167 	{ "Instr_cnt",				0x2fd, 0xff },
168 	{ "IC_miss",				0x301, 0x33 },
169 	{ "DC_miss",				0x302, 0x33 },
170 	{ "L2_imiss",				0x310, 0x33 },
171 	{ "L2_dmiss_ld",			0x320, 0x33 },
172 	{ "ITLB_HWTW_ref_L2",			0x404, 0x3c },
173 	{ "DTLB_HWTW_ref_L2",			0x408, 0x3c },
174 	{ "ITLB_HWTW_miss_L2",			0x410, 0x3c },
175 	{ "DTLB_HWTW_miss_L2",			0x420, 0x3c },
176 	{ "Stream_ld_to_PCX",			0x501, 0x3f },
177 	{ "Stream_st_to_PCX",			0x502, 0x3f },
178 	{ "CPU_ld_to_PCX",			0x504, 0x3f },
179 	{ "CPU_ifetch_to_PCX",			0x508, 0x3f },
180 	{ "CPU_st_to_PCX",			0x510, 0x3f },
181 	{ "MMU_ld_to_PCX",			0x520, 0x3f },
182 	{ "DES_3DES_op",			0x601, 0x3f },
183 	{ "AES_op",				0x602, 0x3f },
184 	{ "RC4_op",				0x604, 0x3f },
185 	{ "MD5_SHA-1_SHA-256_op",		0x608, 0x3f },
186 	{ "MA_op",				0x610, 0x3f },
187 	{ "CRC_TCPIP_cksum",			0x620, 0x3f },
188 	{ "DES_3DES_busy_cycle",		0x701, 0x3f },
189 	{ "AES_busy_cycle",			0x702, 0x3f },
190 	{ "RC4_busy_cycle",			0x704, 0x3f },
191 	{ "MD5_SHA-1_SHA-256_busy_cycle",	0x708, 0x3f },
192 	{ "MA_busy_cycle",			0x710, 0x3f },
193 	{ "CRC_MPA_cksum",			0x720, 0x3f },
194 	{ "ITLB_miss",				0xb04, 0x0c },
195 	{ "DTLB_miss",				0xb08, 0x0c },
196 	{ "TLB_miss",				0xb0c, 0x0c },
197 	EV_END
198 };
199 
200 static ni2_generic_event_t ni2_generic_events[] = {
201 	{ "PAPI_tot_ins",	"Instr_cnt" },
202 	{ "PAPI_l1_dcm",	"DC_miss" },
203 	{ "PAPI_l1_icm",	"IC_miss" },
204 	{ "PAPI_l2_icm",	"L2_imiss" },
205 	{ "PAPI_l2_ldm",	"L2_dmiss_ld" },
206 	{ "PAPI_tlb_dm",	"DTLB_miss" },
207 	{ "PAPI_tlb_im",	"ITLB_miss" },
208 	{ "PAPI_tlb_tm",	"TLB_miss" },
209 	{ "PAPI_br_tkn",	"Br_taken" },
210 	{ "PAPI_br_ins",	"Br_completed" },
211 	{ "PAPI_ld_ins",	"Instr_ld" },
212 	{ "PAPI_sr_ins",	"Instr_st" },
213 	GEN_EV_END
214 };
215 
216 static char		*evlist;
217 static size_t		evlist_sz;
218 static uint16_t 	pcr_pic0_mask;
219 static uint16_t 	pcr_pic1_mask;
220 
221 #define	CPU_REF_URL " Documentation for Sun processors can be found at: " \
222 			"http://www.sun.com/processors/manuals"
223 
224 #if defined(NIAGARA2_IMPL)
225 static const char	*cpu_impl_name = "UltraSPARC T2";
226 static const char *cpu_pcbe_ref = "See the \"UltraSPARC T2 User's Manual\" "
227 			"for descriptions of these events." CPU_REF_URL;
228 #elif defined(VFALLS_IMPL)
229 static const char	*cpu_impl_name = "UltraSPARC T2+";
230 static const char *cpu_pcbe_ref = "See the \"UltraSPARC T2+ User's Manual\" "
231 			"for descriptions of these events." CPU_REF_URL;
232 #endif
233 
234 static boolean_t cpu_hsvc_available = B_TRUE;
235 
236 static int
237 ni2_pcbe_init(void)
238 {
239 	ni2_event_t		*evp;
240 	ni2_generic_event_t	*gevp;
241 	int			status;
242 	uint64_t		cpu_hsvc_major;
243 	uint64_t		cpu_hsvc_minor;
244 #if defined(NIAGARA2_IMPL)
245 	uint64_t		hsvc_cpu_group = HSVC_GROUP_NIAGARA2_CPU;
246 	uint64_t		hsvc_cpu_major = NIAGARA2_HSVC_MAJOR;
247 #elif defined(VFALLS_IMPL)
248 	uint64_t		hsvc_cpu_group = HSVC_GROUP_VFALLS_CPU;
249 	uint64_t		hsvc_cpu_major = VFALLS_HSVC_MAJOR;
250 #endif
251 
252 	pcr_pic0_mask = CPC_NIAGARA2_PCR_PIC0_MASK;
253 	pcr_pic1_mask = CPC_NIAGARA2_PCR_PIC1_MASK;
254 
255 	/*
256 	 * Validate API version for Niagara2 specific hypervisor services
257 	 */
258 	status = hsvc_version(hsvc_cpu_group, &cpu_hsvc_major,
259 	    &cpu_hsvc_minor);
260 	if ((status != 0) || (cpu_hsvc_major != hsvc_cpu_major)) {
261 		cmn_err(CE_WARN, "hypervisor services not negotiated "
262 		    "or unsupported major number: group: 0x%lx major: 0x%lx "
263 		    "minor: 0x%lx errno: %d", hsvc_cpu_group,
264 		    cpu_hsvc_major, cpu_hsvc_minor, status);
265 		cpu_hsvc_available = B_FALSE;
266 	}
267 
268 	/*
269 	 * Construct event list.
270 	 *
271 	 * First pass:  Calculate size needed. We'll need an additional byte
272 	 *		for the NULL pointer during the last strcat.
273 	 *
274 	 * Second pass: Copy strings.
275 	 */
276 	for (evp = ni2_events; evp->name != NULL; evp++)
277 		evlist_sz += strlen(evp->name) + 1;
278 
279 	for (gevp = ni2_generic_events; gevp->name != NULL; gevp++)
280 		evlist_sz += strlen(gevp->name) + 1;
281 
282 	evlist = kmem_alloc(evlist_sz + 1, KM_SLEEP);
283 	evlist[0] = '\0';
284 
285 	for (evp = ni2_events; evp->name != NULL; evp++) {
286 		(void) strcat(evlist, evp->name);
287 		(void) strcat(evlist, ",");
288 	}
289 
290 	for (gevp = ni2_generic_events; gevp->name != NULL; gevp++) {
291 		(void) strcat(evlist, gevp->name);
292 		(void) strcat(evlist, ",");
293 	}
294 
295 	/*
296 	 * Remove trailing comma.
297 	 */
298 	evlist[evlist_sz - 1] = '\0';
299 
300 	return (0);
301 }
302 
303 static uint_t
304 ni2_pcbe_ncounters(void)
305 {
306 	return (2);
307 }
308 
309 static const char *
310 ni2_pcbe_impl_name(void)
311 {
312 	return (cpu_impl_name);
313 }
314 
315 static const char *
316 ni2_pcbe_cpuref(void)
317 {
318 	return (cpu_pcbe_ref);
319 }
320 
321 static char *
322 ni2_pcbe_list_events(uint_t picnum)
323 {
324 	ASSERT(picnum < cpc_ncounters);
325 
326 	return (evlist);
327 }
328 
329 static char *
330 ni2_pcbe_list_attrs(void)
331 {
332 	if (cpu_hsvc_available == B_TRUE)
333 #if defined(NIAGARA2_IMPL)
334 		return ("hpriv,emask");
335 #elif defined(VFALLS_IMPL)
336 		return ("hpriv,l2ctl,emask");
337 #endif
338 	else
339 		return ("emask");
340 }
341 
342 static ni2_generic_event_t *
343 find_generic_event(char *name)
344 {
345 	ni2_generic_event_t	*gevp;
346 
347 	for (gevp = ni2_generic_events; gevp->name != NULL; gevp++) {
348 		if (strcmp(name, gevp->name) == 0)
349 			return (gevp);
350 	}
351 
352 	return (NULL);
353 }
354 
355 static ni2_event_t *
356 find_event(char *name)
357 {
358 	ni2_event_t		*evp;
359 
360 	for (evp = ni2_events; evp->name != NULL; evp++)
361 		if (strcmp(name, evp->name) == 0)
362 			return (evp);
363 
364 	return (NULL);
365 }
366 
367 /*ARGSUSED*/
368 static uint64_t
369 ni2_pcbe_event_coverage(char *event)
370 {
371 	/*
372 	 * Fortunately, both pic0 and pic1 can count all events.
373 	 */
374 	return (0x3);
375 }
376 
377 static uint64_t
378 ni2_pcbe_overflow_bitmap(void)
379 {
380 	uint64_t	pcr, overflow;
381 	uint64_t	pic;
382 	uint32_t	pic0, pic1;
383 	boolean_t	update_pic = B_FALSE;
384 
385 	ASSERT(getpil() >= DISP_LEVEL);
386 	pcr = ultra_getpcr();
387 	DTRACE_PROBE1(niagara2__getpcr, uint64_t, pcr);
388 	overflow =  (pcr & CPC_NIAGARA2_PCR_OV0_MASK) >>
389 	    CPC_NIAGARA2_PCR_OV0_SHIFT;
390 	overflow |=  (pcr & CPC_NIAGARA2_PCR_OV1_MASK) >>
391 	    CPC_NIAGARA2_PCR_OV1_SHIFT;
392 
393 	pic = ultra_getpic();
394 	pic0 = (uint32_t)(pic & PIC0_MASK);
395 	pic1 = (uint32_t)((pic >> PIC1_SHIFT) & PIC0_MASK);
396 
397 	pcr |= (CPC_NIAGARA2_PCR_HOLDOV0 | CPC_NIAGARA2_PCR_HOLDOV1);
398 
399 	if (overflow & 0x1) {
400 		pcr &= ~(CPC_NIAGARA2_PCR_OV0_MASK |
401 		    CPC_NIAGARA2_PCR_HOLDOV0);
402 		if (PIC_IN_OV_RANGE(pic0)) {
403 			pic0 = 0;
404 			update_pic = B_TRUE;
405 		}
406 	}
407 
408 	if (overflow & 0x2) {
409 		pcr &= ~(CPC_NIAGARA2_PCR_OV1_MASK |
410 		    CPC_NIAGARA2_PCR_HOLDOV1);
411 		if (PIC_IN_OV_RANGE(pic1)) {
412 			pic1 = 0;
413 			update_pic = B_TRUE;
414 		}
415 	}
416 
417 	if (update_pic)
418 		ultra_setpic(((uint64_t)pic1 << PIC1_SHIFT) | pic0);
419 
420 	/*
421 	 * The HV interface does not need to be used here because we are
422 	 * only resetting the OV bits and do not need to set the HT bit.
423 	 */
424 	DTRACE_PROBE1(niagara2__setpcr, uint64_t, pcr);
425 	ultra_setpcr(pcr);
426 
427 	return (overflow);
428 }
429 
430 /*ARGSUSED*/
431 static int
432 ni2_pcbe_configure(uint_t picnum, char *event, uint64_t preset, uint32_t flags,
433     uint_t nattrs, kcpc_attr_t *attrs, void **data, void *token)
434 {
435 	ni2_pcbe_config_t	*cfg;
436 	ni2_pcbe_config_t	*other_config;
437 	ni2_event_t		*evp;
438 	ni2_generic_event_t	*gevp;
439 	int			i;
440 	uint32_t		evsel;
441 #if defined(VFALLS_IMPL)
442 	uint64_t		l2ctl = 0;
443 #endif
444 
445 	/*
446 	 * If we've been handed an existing configuration, we need only preset
447 	 * the counter value.
448 	 */
449 	if (*data != NULL) {
450 		cfg = *data;
451 		cfg->pcbe_pic = (uint32_t)preset;
452 		return (0);
453 	}
454 
455 	if (picnum > 1)
456 		return (CPC_INVALID_PICNUM);
457 
458 
459 	if ((evp = find_event(event)) == NULL) {
460 		if ((gevp = find_generic_event(event)) != NULL) {
461 			evp = find_event(gevp->event);
462 			ASSERT(evp != NULL);
463 
464 			if (nattrs > 0)
465 				return (CPC_ATTRIBUTE_OUT_OF_RANGE);
466 		} else {
467 			return (CPC_INVALID_EVENT);
468 		}
469 	}
470 
471 	evsel = evp->emask;
472 
473 	for (i = 0; i < nattrs; i++) {
474 		if (strcmp(attrs[i].ka_name, "hpriv") == 0) {
475 			if (attrs[i].ka_val != 0)
476 				flags |= CPC_COUNT_HV;
477 		} else if (strcmp(attrs[i].ka_name, "emask") == 0) {
478 			if ((attrs[i].ka_val | evp->emask_valid) !=
479 			    evp->emask_valid)
480 				return (CPC_ATTRIBUTE_OUT_OF_RANGE);
481 			evsel |= attrs[i].ka_val;
482 #if defined(VFALLS_IMPL)
483 		} else if (strcmp(attrs[i].ka_name, "l2ctl") == 0) {
484 			if ((attrs[i].ka_val | VFALLS_L2_CTL_MASK) !=
485 			    VFALLS_L2_CTL_MASK)
486 				return (CPC_ATTRIBUTE_OUT_OF_RANGE);
487 			else
488 				l2ctl = attrs[i].ka_val;
489 #endif
490 		} else
491 			return (CPC_INVALID_ATTRIBUTE);
492 	}
493 
494 #if defined(VFALLS_IMPL)
495 	/*
496 	 * Set PERF_CONTROL bits in L2_CONTROL_REG only when events have
497 	 * SL bits equal to 3.
498 	 */
499 	if ((evsel & VFALLS_SL3_MASK) == VFALLS_SL3_MASK) {
500 		if ((hv_niagara_setperf(HV_NIAGARA_L2_CTL, l2ctl)) != 0)
501 			return (CPC_HV_NO_ACCESS);
502 	}
503 #endif
504 
505 	/*
506 	 * Find other requests that will be programmed with this one, and ensure
507 	 * the flags don't conflict.
508 	 */
509 	if (((other_config = kcpc_next_config(token, NULL, NULL)) != NULL) &&
510 	    (other_config->pcbe_flags != flags))
511 		return (CPC_CONFLICTING_REQS);
512 
513 	/*
514 	 * If the hpriv attribute is present, make sure we have
515 	 * access to hyperprivileged events before continuing with
516 	 * this configuration. If we can set the ht bit in the PCR
517 	 * successfully, we must have access to hyperprivileged
518 	 * events.
519 	 *
520 	 * If this is a static per-CPU configuration, the CPC
521 	 * driver ensures there can not be more than one for this
522 	 * CPU. If this is a per-LWP configuration, the driver
523 	 * ensures no static per-CPU counting is ongoing and that
524 	 * the target LWP is not already being monitored.
525 	 */
526 	if (flags & CPC_COUNT_HV) {
527 		kpreempt_disable();
528 
529 		DTRACE_PROBE1(niagara2__setpcr, uint64_t,
530 		    allstopped | CPC_NIAGARA2_PCR_HT);
531 		if (hv_niagara_setperf(HV_NIAGARA_SPARC_CTL,
532 		    allstopped | CPC_NIAGARA2_PCR_HT) != H_EOK) {
533 			kpreempt_enable();
534 			return (CPC_HV_NO_ACCESS);
535 		}
536 
537 		DTRACE_PROBE1(niagara2__setpcr, uint64_t, allstopped);
538 		(void) hv_niagara_setperf(HV_NIAGARA_SPARC_CTL, allstopped);
539 
540 		kpreempt_enable();
541 	}
542 
543 	cfg = kmem_alloc(sizeof (*cfg), KM_SLEEP);
544 
545 	cfg->pcbe_picno = picnum;
546 	cfg->pcbe_evsel = evsel;
547 	cfg->pcbe_flags = flags;
548 	cfg->pcbe_pic = (uint32_t)preset;
549 
550 	*data = cfg;
551 	return (0);
552 }
553 
554 static void
555 ni2_pcbe_program(void *token)
556 {
557 	ni2_pcbe_config_t	*pic0;
558 	ni2_pcbe_config_t	*pic1;
559 	ni2_pcbe_config_t	*tmp;
560 	ni2_pcbe_config_t	nullcfg = { 1, 0, 0, 0 };
561 	uint64_t		pcr;
562 	uint64_t		curpic;
563 	uint64_t		toe;
564 
565 	/* enable trap-on-event for pic0 and pic1 */
566 	toe = (CPC_NIAGARA2_PCR_TOE0 | CPC_NIAGARA2_PCR_TOE1);
567 
568 	if ((pic0 = (ni2_pcbe_config_t *)kcpc_next_config(token, NULL, NULL)) ==
569 	    NULL)
570 		panic("ni2_pcbe: token %p has no configs", token);
571 
572 	if ((pic1 = kcpc_next_config(token, pic0, NULL)) == NULL) {
573 		pic1 = &nullcfg;
574 		nullcfg.pcbe_flags = pic0->pcbe_flags;
575 		toe = CPC_NIAGARA2_PCR_TOE0; /* enable trap-on-event for pic0 */
576 	}
577 
578 	if (pic0->pcbe_picno != 0) {
579 		/*
580 		 * pic0 is counter 1, so if we need the null config it should
581 		 * be counter 0.
582 		 */
583 		nullcfg.pcbe_picno = 0;
584 		tmp = pic0;
585 		pic0 = pic1;
586 		pic1 = tmp;
587 		toe = CPC_NIAGARA2_PCR_TOE1; /* enable trap-on-event for pic1 */
588 	}
589 
590 	if (pic0->pcbe_picno != 0 || pic1->pcbe_picno != 1)
591 		panic("%s: bad config on token %p\n", cpu_impl_name, token);
592 
593 	/*
594 	 * UltraSPARC does not allow pic0 to be configured differently
595 	 * from pic1. If the flags on these two configurations are
596 	 * different, they are incompatible. This condition should be
597 	 * caught at configure time.
598 	 */
599 	ASSERT(pic0->pcbe_flags == pic1->pcbe_flags);
600 
601 	ni2_pcbe_allstop();
602 
603 	ultra_setpic(((uint64_t)pic1->pcbe_pic << PIC1_SHIFT) |
604 	    (uint64_t)pic0->pcbe_pic);
605 
606 	pcr = (pic0->pcbe_evsel & pcr_pic0_mask) << CPC_NIAGARA2_PCR_PIC0_SHIFT;
607 	pcr |= (pic1->pcbe_evsel & pcr_pic1_mask) <<
608 	    CPC_NIAGARA2_PCR_PIC1_SHIFT;
609 
610 	if (pic0->pcbe_flags & CPC_COUNT_USER)
611 		pcr |= (1ull << CPC_NIAGARA2_PCR_UT_SHIFT);
612 	if (pic0->pcbe_flags & CPC_COUNT_SYSTEM)
613 		pcr |= (1ull << CPC_NIAGARA2_PCR_ST_SHIFT);
614 	if (pic0->pcbe_flags & CPC_COUNT_HV)
615 		pcr |= (1ull << CPC_NIAGARA2_PCR_HT_SHIFT);
616 	pcr |= toe;
617 
618 	DTRACE_PROBE1(niagara2__setpcr, uint64_t, pcr);
619 
620 	if (pic0->pcbe_flags & CPC_COUNT_HV) {
621 		/*
622 		 * The ht bit in the PCR is only writable in
623 		 * hyperprivileged mode. So if we are counting
624 		 * hpriv events, we must use the HV interface
625 		 * hv_niagara_setperf to set the PCR. If this
626 		 * fails, assume we no longer have access to
627 		 * hpriv events.
628 		 */
629 		if (hv_niagara_setperf(HV_NIAGARA_SPARC_CTL, pcr) != H_EOK) {
630 			kcpc_invalidate_config(token);
631 			return;
632 		}
633 	} else
634 		/* Set the PCR with no hpriv event counting enabled. */
635 		ultra_setpcr(pcr);
636 
637 	ni2_cpc_counting[CPU->cpu_id] = B_TRUE;
638 
639 	/*
640 	 * On UltraSPARC, only read-to-read counts are accurate. We cannot
641 	 * expect the value we wrote into the PIC, above, to be there after
642 	 * starting the counter. We must sample the counter value now and use
643 	 * that as the baseline for future samples.
644 	 */
645 	curpic = ultra_getpic();
646 	pic0->pcbe_pic = (uint32_t)(curpic & PIC0_MASK);
647 	pic1->pcbe_pic = (uint32_t)(curpic >> PIC1_SHIFT);
648 
649 	DTRACE_PROBE1(niagara2__newpic, uint64_t, curpic);
650 }
651 
652 static void
653 ni2_pcbe_allstop(void)
654 {
655 	/*
656 	 * We use the HV interface here because if we were counting
657 	 * hyperprivileged events, we must reset the PCR.ht bit to stop
658 	 * the counting. In the event that this HV call fails, we fall
659 	 * back on ultra_setpcr which does not have write access to the
660 	 * ht bit.
661 	 */
662 	if (hv_niagara_setperf(HV_NIAGARA_SPARC_CTL, allstopped) != H_EOK)
663 		ultra_setpcr(allstopped);
664 
665 	ni2_cpc_counting[CPU->cpu_id] = B_FALSE;
666 }
667 
668 static void
669 ni2_pcbe_sample(void *token)
670 {
671 	uint64_t		curpic;
672 	int64_t			diff;
673 	uint64_t		*pic0_data;
674 	uint64_t		*pic1_data;
675 	uint64_t		*dtmp;
676 	uint64_t		tmp;
677 	uint64_t		pcr;
678 	ni2_pcbe_config_t	*pic0;
679 	ni2_pcbe_config_t	*pic1;
680 	ni2_pcbe_config_t	nullcfg = { 1, 0, 0, 0 };
681 	ni2_pcbe_config_t	*ctmp;
682 
683 	curpic = ultra_getpic();
684 	DTRACE_PROBE1(niagara2__getpic, uint64_t, curpic);
685 
686 	if ((pic0 = kcpc_next_config(token, NULL, &pic0_data)) == NULL)
687 		panic("%s: token %p has no configs", cpu_impl_name, token);
688 
689 	if ((pic1 = kcpc_next_config(token, pic0, &pic1_data)) == NULL) {
690 		pic1 = &nullcfg;
691 		pic1_data = &tmp;
692 	}
693 
694 	if (pic0->pcbe_picno != 0) {
695 		nullcfg.pcbe_picno = 0;
696 		ctmp = pic0;
697 		pic0 = pic1;
698 		pic1 = ctmp;
699 		dtmp = pic0_data;
700 		pic0_data = pic1_data;
701 		pic1_data = dtmp;
702 	}
703 
704 	if (pic0->pcbe_picno != 0 || pic1->pcbe_picno != 1)
705 		panic("%s: bad config on token %p\n", cpu_impl_name, token);
706 
707 
708 	if (pic0->pcbe_flags & CPC_COUNT_HV) {
709 		/*
710 		 * If the hpriv attribute is present, but the HT bit
711 		 * is not set in the PCR, access to hyperprivileged
712 		 * events must have been revoked. Only perform this
713 		 * check if counting is not stopped.
714 		 */
715 		pcr = ultra_getpcr();
716 		DTRACE_PROBE1(niagara2__getpcr, uint64_t, pcr);
717 		if (ni2_cpc_counting[CPU->cpu_id] &&
718 		    !(pcr & CPC_NIAGARA2_PCR_HT)) {
719 			kcpc_invalidate_config(token);
720 			return;
721 		}
722 	}
723 
724 	diff = (curpic & PIC0_MASK) - (uint64_t)pic0->pcbe_pic;
725 	if (diff < 0)
726 		diff += (1ll << 32);
727 	*pic0_data += diff;
728 
729 	diff = (curpic >> 32) - (uint64_t)pic1->pcbe_pic;
730 	if (diff < 0)
731 		diff += (1ll << 32);
732 	*pic1_data += diff;
733 
734 	pic0->pcbe_pic = (uint32_t)(curpic & PIC0_MASK);
735 	pic1->pcbe_pic = (uint32_t)(curpic >> PIC1_SHIFT);
736 }
737 
738 static void
739 ni2_pcbe_free(void *config)
740 {
741 	kmem_free(config, sizeof (ni2_pcbe_config_t));
742 }
743 
744 
745 static struct modlpcbe modlpcbe = {
746 	&mod_pcbeops,
747 #if defined(NIAGARA2_IMPL)
748 	"UltraSPARC T2 Performance Counters",
749 #elif defined(VFALLS_IMPL)
750 	"UltraSPARC T2+ Performance Counters",
751 #endif
752 	&ni2_pcbe_ops
753 };
754 
755 static struct modlinkage modl = {
756 	MODREV_1,
757 	&modlpcbe,
758 };
759 
760 int
761 _init(void)
762 {
763 	if (ni2_pcbe_init() != 0)
764 		return (ENOTSUP);
765 	return (mod_install(&modl));
766 }
767 
768 int
769 _fini(void)
770 {
771 	return (mod_remove(&modl));
772 }
773 
774 int
775 _info(struct modinfo *mi)
776 {
777 	return (mod_info(&modl, mi));
778 }
779