xref: /illumos-gate/usr/src/uts/sun4v/pcbe/niagara2_pcbe.c (revision 46b592853d0f4f11781b6b0a7533f267c6aee132)
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 2009 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 	{ "PAPI_fp_ops",	"Instr_FGU_arithmetic" },
214 	{ "PAPI_fp_ins",	"Instr_FGU_arithmetic" },
215 	GEN_EV_END
216 };
217 
218 static char		*evlist;
219 static size_t		evlist_sz;
220 static uint16_t 	pcr_pic0_mask;
221 static uint16_t 	pcr_pic1_mask;
222 
223 #define	CPU_REF_URL " Documentation for Sun processors can be found at: " \
224 			"http://www.sun.com/processors/manuals"
225 
226 #if defined(NIAGARA2_IMPL)
227 static const char	*cpu_impl_name = "UltraSPARC T2";
228 static const char *cpu_pcbe_ref = "See the \"UltraSPARC T2 User's Manual\" "
229 			"for descriptions of these events." CPU_REF_URL;
230 #elif defined(VFALLS_IMPL)
231 static const char	*cpu_impl_name = "UltraSPARC T2+";
232 static const char *cpu_pcbe_ref = "See the \"UltraSPARC T2+ User's Manual\" "
233 			"for descriptions of these events." CPU_REF_URL;
234 #endif
235 
236 static boolean_t cpu_hsvc_available = B_TRUE;
237 
238 static int
239 ni2_pcbe_init(void)
240 {
241 	ni2_event_t		*evp;
242 	ni2_generic_event_t	*gevp;
243 	int			status;
244 	uint64_t		cpu_hsvc_major;
245 	uint64_t		cpu_hsvc_minor;
246 #if defined(NIAGARA2_IMPL)
247 	uint64_t		hsvc_cpu_group = HSVC_GROUP_NIAGARA2_CPU;
248 	uint64_t		hsvc_cpu_major = NIAGARA2_HSVC_MAJOR;
249 #elif defined(VFALLS_IMPL)
250 	uint64_t		hsvc_cpu_group = HSVC_GROUP_VFALLS_CPU;
251 	uint64_t		hsvc_cpu_major = VFALLS_HSVC_MAJOR;
252 #endif
253 
254 	pcr_pic0_mask = CPC_NIAGARA2_PCR_PIC0_MASK;
255 	pcr_pic1_mask = CPC_NIAGARA2_PCR_PIC1_MASK;
256 
257 	/*
258 	 * Validate API version for Niagara2 specific hypervisor services
259 	 */
260 	status = hsvc_version(hsvc_cpu_group, &cpu_hsvc_major,
261 	    &cpu_hsvc_minor);
262 	if ((status != 0) || (cpu_hsvc_major != hsvc_cpu_major)) {
263 		cmn_err(CE_WARN, "hypervisor services not negotiated "
264 		    "or unsupported major number: group: 0x%lx major: 0x%lx "
265 		    "minor: 0x%lx errno: %d", hsvc_cpu_group,
266 		    cpu_hsvc_major, cpu_hsvc_minor, status);
267 		cpu_hsvc_available = B_FALSE;
268 	}
269 
270 	/*
271 	 * Construct event list.
272 	 *
273 	 * First pass:  Calculate size needed. We'll need an additional byte
274 	 *		for the NULL pointer during the last strcat.
275 	 *
276 	 * Second pass: Copy strings.
277 	 */
278 	for (evp = ni2_events; evp->name != NULL; evp++)
279 		evlist_sz += strlen(evp->name) + 1;
280 
281 	for (gevp = ni2_generic_events; gevp->name != NULL; gevp++)
282 		evlist_sz += strlen(gevp->name) + 1;
283 
284 	evlist = kmem_alloc(evlist_sz + 1, KM_SLEEP);
285 	evlist[0] = '\0';
286 
287 	for (evp = ni2_events; evp->name != NULL; evp++) {
288 		(void) strcat(evlist, evp->name);
289 		(void) strcat(evlist, ",");
290 	}
291 
292 	for (gevp = ni2_generic_events; gevp->name != NULL; gevp++) {
293 		(void) strcat(evlist, gevp->name);
294 		(void) strcat(evlist, ",");
295 	}
296 
297 	/*
298 	 * Remove trailing comma.
299 	 */
300 	evlist[evlist_sz - 1] = '\0';
301 
302 	return (0);
303 }
304 
305 static uint_t
306 ni2_pcbe_ncounters(void)
307 {
308 	return (2);
309 }
310 
311 static const char *
312 ni2_pcbe_impl_name(void)
313 {
314 	return (cpu_impl_name);
315 }
316 
317 static const char *
318 ni2_pcbe_cpuref(void)
319 {
320 	return (cpu_pcbe_ref);
321 }
322 
323 static char *
324 ni2_pcbe_list_events(uint_t picnum)
325 {
326 	ASSERT(picnum < cpc_ncounters);
327 
328 	return (evlist);
329 }
330 
331 static char *
332 ni2_pcbe_list_attrs(void)
333 {
334 	if (cpu_hsvc_available == B_TRUE)
335 #if defined(NIAGARA2_IMPL)
336 		return ("hpriv,emask");
337 #elif defined(VFALLS_IMPL)
338 		return ("hpriv,l2ctl,emask");
339 #endif
340 	else
341 		return ("emask");
342 }
343 
344 static ni2_generic_event_t *
345 find_generic_event(char *name)
346 {
347 	ni2_generic_event_t	*gevp;
348 
349 	for (gevp = ni2_generic_events; gevp->name != NULL; gevp++) {
350 		if (strcmp(name, gevp->name) == 0)
351 			return (gevp);
352 	}
353 
354 	return (NULL);
355 }
356 
357 static ni2_event_t *
358 find_event(char *name)
359 {
360 	ni2_event_t		*evp;
361 
362 	for (evp = ni2_events; evp->name != NULL; evp++)
363 		if (strcmp(name, evp->name) == 0)
364 			return (evp);
365 
366 	return (NULL);
367 }
368 
369 /*ARGSUSED*/
370 static uint64_t
371 ni2_pcbe_event_coverage(char *event)
372 {
373 	/*
374 	 * Fortunately, both pic0 and pic1 can count all events.
375 	 */
376 	return (0x3);
377 }
378 
379 static uint64_t
380 ni2_pcbe_overflow_bitmap(void)
381 {
382 	uint64_t	pcr, overflow;
383 	uint64_t	pic;
384 	uint32_t	pic0, pic1;
385 	boolean_t	update_pic = B_FALSE;
386 
387 	ASSERT(getpil() >= DISP_LEVEL);
388 	pcr = ultra_getpcr();
389 	DTRACE_PROBE1(niagara2__getpcr, uint64_t, pcr);
390 	overflow =  (pcr & CPC_NIAGARA2_PCR_OV0_MASK) >>
391 	    CPC_NIAGARA2_PCR_OV0_SHIFT;
392 	overflow |=  (pcr & CPC_NIAGARA2_PCR_OV1_MASK) >>
393 	    CPC_NIAGARA2_PCR_OV1_SHIFT;
394 
395 	pic = ultra_getpic();
396 	pic0 = (uint32_t)(pic & PIC0_MASK);
397 	pic1 = (uint32_t)((pic >> PIC1_SHIFT) & PIC0_MASK);
398 
399 	pcr |= (CPC_NIAGARA2_PCR_HOLDOV0 | CPC_NIAGARA2_PCR_HOLDOV1);
400 
401 	if (overflow & 0x1) {
402 		pcr &= ~(CPC_NIAGARA2_PCR_OV0_MASK |
403 		    CPC_NIAGARA2_PCR_HOLDOV0);
404 		if (PIC_IN_OV_RANGE(pic0)) {
405 			pic0 = 0;
406 			update_pic = B_TRUE;
407 		}
408 	}
409 
410 	if (overflow & 0x2) {
411 		pcr &= ~(CPC_NIAGARA2_PCR_OV1_MASK |
412 		    CPC_NIAGARA2_PCR_HOLDOV1);
413 		if (PIC_IN_OV_RANGE(pic1)) {
414 			pic1 = 0;
415 			update_pic = B_TRUE;
416 		}
417 	}
418 
419 	if (update_pic)
420 		ultra_setpic(((uint64_t)pic1 << PIC1_SHIFT) | pic0);
421 
422 	/*
423 	 * The HV interface does not need to be used here because we are
424 	 * only resetting the OV bits and do not need to set the HT bit.
425 	 */
426 	DTRACE_PROBE1(niagara2__setpcr, uint64_t, pcr);
427 	ultra_setpcr(pcr);
428 
429 	return (overflow);
430 }
431 
432 /*ARGSUSED*/
433 static int
434 ni2_pcbe_configure(uint_t picnum, char *event, uint64_t preset, uint32_t flags,
435     uint_t nattrs, kcpc_attr_t *attrs, void **data, void *token)
436 {
437 	ni2_pcbe_config_t	*cfg;
438 	ni2_pcbe_config_t	*other_config;
439 	ni2_event_t		*evp;
440 	ni2_generic_event_t	*gevp;
441 	int			i;
442 	uint32_t		evsel;
443 #if defined(VFALLS_IMPL)
444 	uint64_t		l2ctl = 0;
445 #endif
446 
447 	/*
448 	 * If we've been handed an existing configuration, we need only preset
449 	 * the counter value.
450 	 */
451 	if (*data != NULL) {
452 		cfg = *data;
453 		cfg->pcbe_pic = (uint32_t)preset;
454 		return (0);
455 	}
456 
457 	if (picnum > 1)
458 		return (CPC_INVALID_PICNUM);
459 
460 
461 	if ((evp = find_event(event)) == NULL) {
462 		if ((gevp = find_generic_event(event)) != NULL) {
463 			evp = find_event(gevp->event);
464 			ASSERT(evp != NULL);
465 
466 			if (nattrs > 0)
467 				return (CPC_ATTRIBUTE_OUT_OF_RANGE);
468 		} else {
469 			return (CPC_INVALID_EVENT);
470 		}
471 	}
472 
473 	evsel = evp->emask;
474 
475 	for (i = 0; i < nattrs; i++) {
476 		if (strcmp(attrs[i].ka_name, "hpriv") == 0) {
477 			if (attrs[i].ka_val != 0)
478 				flags |= CPC_COUNT_HV;
479 		} else if (strcmp(attrs[i].ka_name, "emask") == 0) {
480 			if ((attrs[i].ka_val | evp->emask_valid) !=
481 			    evp->emask_valid)
482 				return (CPC_ATTRIBUTE_OUT_OF_RANGE);
483 			evsel |= attrs[i].ka_val;
484 #if defined(VFALLS_IMPL)
485 		} else if (strcmp(attrs[i].ka_name, "l2ctl") == 0) {
486 			if ((attrs[i].ka_val | VFALLS_L2_CTL_MASK) !=
487 			    VFALLS_L2_CTL_MASK)
488 				return (CPC_ATTRIBUTE_OUT_OF_RANGE);
489 			else
490 				l2ctl = attrs[i].ka_val;
491 #endif
492 		} else
493 			return (CPC_INVALID_ATTRIBUTE);
494 	}
495 
496 #if defined(VFALLS_IMPL)
497 	/*
498 	 * Set PERF_CONTROL bits in L2_CONTROL_REG only when events have
499 	 * SL bits equal to 3.
500 	 */
501 	if ((evsel & VFALLS_SL3_MASK) == VFALLS_SL3_MASK) {
502 		if ((hv_niagara_setperf(HV_NIAGARA_L2_CTL, l2ctl)) != 0)
503 			return (CPC_HV_NO_ACCESS);
504 	}
505 #endif
506 
507 	/*
508 	 * Find other requests that will be programmed with this one, and ensure
509 	 * the flags don't conflict.
510 	 */
511 	if (((other_config = kcpc_next_config(token, NULL, NULL)) != NULL) &&
512 	    (other_config->pcbe_flags != flags))
513 		return (CPC_CONFLICTING_REQS);
514 
515 	/*
516 	 * If the hpriv attribute is present, make sure we have
517 	 * access to hyperprivileged events before continuing with
518 	 * this configuration. If we can set the ht bit in the PCR
519 	 * successfully, we must have access to hyperprivileged
520 	 * events.
521 	 *
522 	 * If this is a static per-CPU configuration, the CPC
523 	 * driver ensures there can not be more than one for this
524 	 * CPU. If this is a per-LWP configuration, the driver
525 	 * ensures no static per-CPU counting is ongoing and that
526 	 * the target LWP is not already being monitored.
527 	 */
528 	if (flags & CPC_COUNT_HV) {
529 		kpreempt_disable();
530 
531 		DTRACE_PROBE1(niagara2__setpcr, uint64_t,
532 		    allstopped | CPC_NIAGARA2_PCR_HT);
533 		if (hv_niagara_setperf(HV_NIAGARA_SPARC_CTL,
534 		    allstopped | CPC_NIAGARA2_PCR_HT) != H_EOK) {
535 			kpreempt_enable();
536 			return (CPC_HV_NO_ACCESS);
537 		}
538 
539 		DTRACE_PROBE1(niagara2__setpcr, uint64_t, allstopped);
540 		(void) hv_niagara_setperf(HV_NIAGARA_SPARC_CTL, allstopped);
541 
542 		kpreempt_enable();
543 	}
544 
545 	cfg = kmem_alloc(sizeof (*cfg), KM_SLEEP);
546 
547 	cfg->pcbe_picno = picnum;
548 	cfg->pcbe_evsel = evsel;
549 	cfg->pcbe_flags = flags;
550 	cfg->pcbe_pic = (uint32_t)preset;
551 
552 	*data = cfg;
553 	return (0);
554 }
555 
556 static void
557 ni2_pcbe_program(void *token)
558 {
559 	ni2_pcbe_config_t	*pic0;
560 	ni2_pcbe_config_t	*pic1;
561 	ni2_pcbe_config_t	*tmp;
562 	ni2_pcbe_config_t	nullcfg = { 1, 0, 0, 0 };
563 	uint64_t		pcr;
564 	uint64_t		curpic;
565 	uint64_t		toe;
566 
567 	/* enable trap-on-event for pic0 and pic1 */
568 	toe = (CPC_NIAGARA2_PCR_TOE0 | CPC_NIAGARA2_PCR_TOE1);
569 
570 	if ((pic0 = (ni2_pcbe_config_t *)kcpc_next_config(token, NULL, NULL)) ==
571 	    NULL)
572 		panic("ni2_pcbe: token %p has no configs", token);
573 
574 	if ((pic1 = kcpc_next_config(token, pic0, NULL)) == NULL) {
575 		pic1 = &nullcfg;
576 		nullcfg.pcbe_flags = pic0->pcbe_flags;
577 		toe = CPC_NIAGARA2_PCR_TOE0; /* enable trap-on-event for pic0 */
578 	}
579 
580 	if (pic0->pcbe_picno != 0) {
581 		/*
582 		 * pic0 is counter 1, so if we need the null config it should
583 		 * be counter 0.
584 		 */
585 		nullcfg.pcbe_picno = 0;
586 		tmp = pic0;
587 		pic0 = pic1;
588 		pic1 = tmp;
589 		toe = CPC_NIAGARA2_PCR_TOE1; /* enable trap-on-event for pic1 */
590 	}
591 
592 	if (pic0->pcbe_picno != 0 || pic1->pcbe_picno != 1)
593 		panic("%s: bad config on token %p\n", cpu_impl_name, token);
594 
595 	/*
596 	 * UltraSPARC does not allow pic0 to be configured differently
597 	 * from pic1. If the flags on these two configurations are
598 	 * different, they are incompatible. This condition should be
599 	 * caught at configure time.
600 	 */
601 	ASSERT(pic0->pcbe_flags == pic1->pcbe_flags);
602 
603 	ni2_pcbe_allstop();
604 
605 	ultra_setpic(((uint64_t)pic1->pcbe_pic << PIC1_SHIFT) |
606 	    (uint64_t)pic0->pcbe_pic);
607 
608 	pcr = (pic0->pcbe_evsel & pcr_pic0_mask) << CPC_NIAGARA2_PCR_PIC0_SHIFT;
609 	pcr |= (pic1->pcbe_evsel & pcr_pic1_mask) <<
610 	    CPC_NIAGARA2_PCR_PIC1_SHIFT;
611 
612 	if (pic0->pcbe_flags & CPC_COUNT_USER)
613 		pcr |= (1ull << CPC_NIAGARA2_PCR_UT_SHIFT);
614 	if (pic0->pcbe_flags & CPC_COUNT_SYSTEM)
615 		pcr |= (1ull << CPC_NIAGARA2_PCR_ST_SHIFT);
616 	if (pic0->pcbe_flags & CPC_COUNT_HV)
617 		pcr |= (1ull << CPC_NIAGARA2_PCR_HT_SHIFT);
618 	pcr |= toe;
619 
620 	DTRACE_PROBE1(niagara2__setpcr, uint64_t, pcr);
621 
622 	if (pic0->pcbe_flags & CPC_COUNT_HV) {
623 		/*
624 		 * The ht bit in the PCR is only writable in
625 		 * hyperprivileged mode. So if we are counting
626 		 * hpriv events, we must use the HV interface
627 		 * hv_niagara_setperf to set the PCR. If this
628 		 * fails, assume we no longer have access to
629 		 * hpriv events.
630 		 */
631 		if (hv_niagara_setperf(HV_NIAGARA_SPARC_CTL, pcr) != H_EOK) {
632 			kcpc_invalidate_config(token);
633 			return;
634 		}
635 	} else
636 		/* Set the PCR with no hpriv event counting enabled. */
637 		ultra_setpcr(pcr);
638 
639 	ni2_cpc_counting[CPU->cpu_id] = B_TRUE;
640 
641 	/*
642 	 * On UltraSPARC, only read-to-read counts are accurate. We cannot
643 	 * expect the value we wrote into the PIC, above, to be there after
644 	 * starting the counter. We must sample the counter value now and use
645 	 * that as the baseline for future samples.
646 	 */
647 	curpic = ultra_getpic();
648 	pic0->pcbe_pic = (uint32_t)(curpic & PIC0_MASK);
649 	pic1->pcbe_pic = (uint32_t)(curpic >> PIC1_SHIFT);
650 
651 	DTRACE_PROBE1(niagara2__newpic, uint64_t, curpic);
652 }
653 
654 static void
655 ni2_pcbe_allstop(void)
656 {
657 	/*
658 	 * We use the HV interface here because if we were counting
659 	 * hyperprivileged events, we must reset the PCR.ht bit to stop
660 	 * the counting. In the event that this HV call fails, we fall
661 	 * back on ultra_setpcr which does not have write access to the
662 	 * ht bit.
663 	 */
664 	if (hv_niagara_setperf(HV_NIAGARA_SPARC_CTL, allstopped) != H_EOK)
665 		ultra_setpcr(allstopped);
666 
667 	ni2_cpc_counting[CPU->cpu_id] = B_FALSE;
668 }
669 
670 static void
671 ni2_pcbe_sample(void *token)
672 {
673 	uint64_t		curpic;
674 	int64_t			diff;
675 	uint64_t		*pic0_data;
676 	uint64_t		*pic1_data;
677 	uint64_t		*dtmp;
678 	uint64_t		tmp;
679 	uint64_t		pcr;
680 	ni2_pcbe_config_t	*pic0;
681 	ni2_pcbe_config_t	*pic1;
682 	ni2_pcbe_config_t	nullcfg = { 1, 0, 0, 0 };
683 	ni2_pcbe_config_t	*ctmp;
684 
685 	curpic = ultra_getpic();
686 	DTRACE_PROBE1(niagara2__getpic, uint64_t, curpic);
687 
688 	if ((pic0 = kcpc_next_config(token, NULL, &pic0_data)) == NULL)
689 		panic("%s: token %p has no configs", cpu_impl_name, token);
690 
691 	if ((pic1 = kcpc_next_config(token, pic0, &pic1_data)) == NULL) {
692 		pic1 = &nullcfg;
693 		pic1_data = &tmp;
694 	}
695 
696 	if (pic0->pcbe_picno != 0) {
697 		nullcfg.pcbe_picno = 0;
698 		ctmp = pic0;
699 		pic0 = pic1;
700 		pic1 = ctmp;
701 		dtmp = pic0_data;
702 		pic0_data = pic1_data;
703 		pic1_data = dtmp;
704 	}
705 
706 	if (pic0->pcbe_picno != 0 || pic1->pcbe_picno != 1)
707 		panic("%s: bad config on token %p\n", cpu_impl_name, token);
708 
709 
710 	if (pic0->pcbe_flags & CPC_COUNT_HV) {
711 		/*
712 		 * If the hpriv attribute is present, but the HT bit
713 		 * is not set in the PCR, access to hyperprivileged
714 		 * events must have been revoked. Only perform this
715 		 * check if counting is not stopped.
716 		 */
717 		pcr = ultra_getpcr();
718 		DTRACE_PROBE1(niagara2__getpcr, uint64_t, pcr);
719 		if (ni2_cpc_counting[CPU->cpu_id] &&
720 		    !(pcr & CPC_NIAGARA2_PCR_HT)) {
721 			kcpc_invalidate_config(token);
722 			return;
723 		}
724 	}
725 
726 	diff = (curpic & PIC0_MASK) - (uint64_t)pic0->pcbe_pic;
727 	if (diff < 0)
728 		diff += (1ll << 32);
729 	*pic0_data += diff;
730 
731 	diff = (curpic >> 32) - (uint64_t)pic1->pcbe_pic;
732 	if (diff < 0)
733 		diff += (1ll << 32);
734 	*pic1_data += diff;
735 
736 	pic0->pcbe_pic = (uint32_t)(curpic & PIC0_MASK);
737 	pic1->pcbe_pic = (uint32_t)(curpic >> PIC1_SHIFT);
738 }
739 
740 static void
741 ni2_pcbe_free(void *config)
742 {
743 	kmem_free(config, sizeof (ni2_pcbe_config_t));
744 }
745 
746 
747 static struct modlpcbe modlpcbe = {
748 	&mod_pcbeops,
749 #if defined(NIAGARA2_IMPL)
750 	"UltraSPARC T2 Performance Counters",
751 #elif defined(VFALLS_IMPL)
752 	"UltraSPARC T2+ Performance Counters",
753 #endif
754 	&ni2_pcbe_ops
755 };
756 
757 static struct modlinkage modl = {
758 	MODREV_1,
759 	&modlpcbe,
760 };
761 
762 int
763 _init(void)
764 {
765 	if (ni2_pcbe_init() != 0)
766 		return (ENOTSUP);
767 	return (mod_install(&modl));
768 }
769 
770 int
771 _fini(void)
772 {
773 	return (mod_remove(&modl));
774 }
775 
776 int
777 _info(struct modinfo *mi)
778 {
779 	return (mod_info(&modl, mi));
780 }
781