xref: /titanic_51/usr/src/uts/common/io/cpc.c (revision 7c478bd95313f5f23a4c958a745db2134aa03244)
1*7c478bd9Sstevel@tonic-gate /*
2*7c478bd9Sstevel@tonic-gate  * CDDL HEADER START
3*7c478bd9Sstevel@tonic-gate  *
4*7c478bd9Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
5*7c478bd9Sstevel@tonic-gate  * Common Development and Distribution License, Version 1.0 only
6*7c478bd9Sstevel@tonic-gate  * (the "License").  You may not use this file except in compliance
7*7c478bd9Sstevel@tonic-gate  * with the License.
8*7c478bd9Sstevel@tonic-gate  *
9*7c478bd9Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10*7c478bd9Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
11*7c478bd9Sstevel@tonic-gate  * See the License for the specific language governing permissions
12*7c478bd9Sstevel@tonic-gate  * and limitations under the License.
13*7c478bd9Sstevel@tonic-gate  *
14*7c478bd9Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
15*7c478bd9Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16*7c478bd9Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
17*7c478bd9Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
18*7c478bd9Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
19*7c478bd9Sstevel@tonic-gate  *
20*7c478bd9Sstevel@tonic-gate  * CDDL HEADER END
21*7c478bd9Sstevel@tonic-gate  */
22*7c478bd9Sstevel@tonic-gate /*
23*7c478bd9Sstevel@tonic-gate  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
24*7c478bd9Sstevel@tonic-gate  * Use is subject to license terms.
25*7c478bd9Sstevel@tonic-gate  */
26*7c478bd9Sstevel@tonic-gate 
27*7c478bd9Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
28*7c478bd9Sstevel@tonic-gate 
29*7c478bd9Sstevel@tonic-gate /*
30*7c478bd9Sstevel@tonic-gate  * CPU Performance Counter system calls and device driver.
31*7c478bd9Sstevel@tonic-gate  *
32*7c478bd9Sstevel@tonic-gate  * This module uses a combination of thread context operators, and
33*7c478bd9Sstevel@tonic-gate  * thread-specific data to export CPU performance counters
34*7c478bd9Sstevel@tonic-gate  * via both a system call and a driver interface.
35*7c478bd9Sstevel@tonic-gate  *
36*7c478bd9Sstevel@tonic-gate  * There are three access methods exported - the 'shared' device
37*7c478bd9Sstevel@tonic-gate  * and the 'private' and 'agent' variants of the system call.
38*7c478bd9Sstevel@tonic-gate  *
39*7c478bd9Sstevel@tonic-gate  * The shared device treats the performance counter registers as
40*7c478bd9Sstevel@tonic-gate  * a processor metric, regardless of the work scheduled on them.
41*7c478bd9Sstevel@tonic-gate  * The private system call treats the performance counter registers
42*7c478bd9Sstevel@tonic-gate  * as a property of a single lwp.  This is achieved by using the
43*7c478bd9Sstevel@tonic-gate  * thread context operators to virtualize the contents of the
44*7c478bd9Sstevel@tonic-gate  * performance counter registers between lwps.
45*7c478bd9Sstevel@tonic-gate  *
46*7c478bd9Sstevel@tonic-gate  * The agent method is like the private method, except that it must
47*7c478bd9Sstevel@tonic-gate  * be accessed via /proc's agent lwp to allow the counter context of
48*7c478bd9Sstevel@tonic-gate  * other threads to be examined safely.
49*7c478bd9Sstevel@tonic-gate  *
50*7c478bd9Sstevel@tonic-gate  * The shared usage fundamentally conflicts with the agent and private usage;
51*7c478bd9Sstevel@tonic-gate  * almost all of the complexity of the module is needed to allow these two
52*7c478bd9Sstevel@tonic-gate  * models to co-exist in a reasonable way.
53*7c478bd9Sstevel@tonic-gate  */
54*7c478bd9Sstevel@tonic-gate 
55*7c478bd9Sstevel@tonic-gate #include <sys/types.h>
56*7c478bd9Sstevel@tonic-gate #include <sys/file.h>
57*7c478bd9Sstevel@tonic-gate #include <sys/errno.h>
58*7c478bd9Sstevel@tonic-gate #include <sys/open.h>
59*7c478bd9Sstevel@tonic-gate #include <sys/cred.h>
60*7c478bd9Sstevel@tonic-gate #include <sys/conf.h>
61*7c478bd9Sstevel@tonic-gate #include <sys/stat.h>
62*7c478bd9Sstevel@tonic-gate #include <sys/processor.h>
63*7c478bd9Sstevel@tonic-gate #include <sys/cpuvar.h>
64*7c478bd9Sstevel@tonic-gate #include <sys/disp.h>
65*7c478bd9Sstevel@tonic-gate #include <sys/kmem.h>
66*7c478bd9Sstevel@tonic-gate #include <sys/modctl.h>
67*7c478bd9Sstevel@tonic-gate #include <sys/ddi.h>
68*7c478bd9Sstevel@tonic-gate #include <sys/sunddi.h>
69*7c478bd9Sstevel@tonic-gate #include <sys/nvpair.h>
70*7c478bd9Sstevel@tonic-gate #include <sys/policy.h>
71*7c478bd9Sstevel@tonic-gate #include <sys/machsystm.h>
72*7c478bd9Sstevel@tonic-gate #include <sys/cpc_impl.h>
73*7c478bd9Sstevel@tonic-gate #include <sys/cpc_pcbe.h>
74*7c478bd9Sstevel@tonic-gate #include <sys/kcpc.h>
75*7c478bd9Sstevel@tonic-gate 
76*7c478bd9Sstevel@tonic-gate static int kcpc_copyin_set(kcpc_set_t **set, void *ubuf, size_t len);
77*7c478bd9Sstevel@tonic-gate static int kcpc_verify_set(kcpc_set_t *set);
78*7c478bd9Sstevel@tonic-gate static uint32_t kcpc_nvlist_npairs(nvlist_t *list);
79*7c478bd9Sstevel@tonic-gate 
80*7c478bd9Sstevel@tonic-gate /*
81*7c478bd9Sstevel@tonic-gate  * Generic attributes supported regardless of processor.
82*7c478bd9Sstevel@tonic-gate  */
83*7c478bd9Sstevel@tonic-gate 
84*7c478bd9Sstevel@tonic-gate #define	ATTRLIST "picnum"
85*7c478bd9Sstevel@tonic-gate #define	SEPARATOR ","
86*7c478bd9Sstevel@tonic-gate 
87*7c478bd9Sstevel@tonic-gate /*
88*7c478bd9Sstevel@tonic-gate  * System call to access CPU performance counters.
89*7c478bd9Sstevel@tonic-gate  */
90*7c478bd9Sstevel@tonic-gate static int
91*7c478bd9Sstevel@tonic-gate cpc(int cmd, id_t lwpid, void *udata1, void *udata2, void *udata3)
92*7c478bd9Sstevel@tonic-gate {
93*7c478bd9Sstevel@tonic-gate 	kthread_t	*t;
94*7c478bd9Sstevel@tonic-gate 	int		error;
95*7c478bd9Sstevel@tonic-gate 	int		size;
96*7c478bd9Sstevel@tonic-gate 	const char	*str;
97*7c478bd9Sstevel@tonic-gate 	int		code;
98*7c478bd9Sstevel@tonic-gate 
99*7c478bd9Sstevel@tonic-gate 	/*
100*7c478bd9Sstevel@tonic-gate 	 * This CPC syscall should only be loaded if it found a PCBE to use.
101*7c478bd9Sstevel@tonic-gate 	 */
102*7c478bd9Sstevel@tonic-gate 	ASSERT(pcbe_ops != NULL);
103*7c478bd9Sstevel@tonic-gate 
104*7c478bd9Sstevel@tonic-gate 	if (curproc->p_agenttp == curthread) {
105*7c478bd9Sstevel@tonic-gate 		/*
106*7c478bd9Sstevel@tonic-gate 		 * Only if /proc is invoking this system call from
107*7c478bd9Sstevel@tonic-gate 		 * the agent thread do we allow the caller to examine
108*7c478bd9Sstevel@tonic-gate 		 * the contexts of other lwps in the process.  And
109*7c478bd9Sstevel@tonic-gate 		 * because we know we're the agent, we know we don't
110*7c478bd9Sstevel@tonic-gate 		 * have to grab p_lock because no-one else can change
111*7c478bd9Sstevel@tonic-gate 		 * the state of the process.
112*7c478bd9Sstevel@tonic-gate 		 */
113*7c478bd9Sstevel@tonic-gate 		if ((t = idtot(curproc, lwpid)) == NULL || t == curthread)
114*7c478bd9Sstevel@tonic-gate 			return (set_errno(ESRCH));
115*7c478bd9Sstevel@tonic-gate 		ASSERT(t->t_tid == lwpid && ttolwp(t) != NULL);
116*7c478bd9Sstevel@tonic-gate 	} else
117*7c478bd9Sstevel@tonic-gate 		t = curthread;
118*7c478bd9Sstevel@tonic-gate 
119*7c478bd9Sstevel@tonic-gate 	if (t->t_cpc_set == NULL && (cmd == CPC_SAMPLE || cmd == CPC_RELE))
120*7c478bd9Sstevel@tonic-gate 		return (set_errno(EINVAL));
121*7c478bd9Sstevel@tonic-gate 
122*7c478bd9Sstevel@tonic-gate 	switch (cmd) {
123*7c478bd9Sstevel@tonic-gate 	case CPC_BIND:
124*7c478bd9Sstevel@tonic-gate 		/*
125*7c478bd9Sstevel@tonic-gate 		 * udata1 = pointer to packed nvlist buffer
126*7c478bd9Sstevel@tonic-gate 		 * udata2 = size of packed nvlist buffer
127*7c478bd9Sstevel@tonic-gate 		 * udata3 = User addr to return error subcode in.
128*7c478bd9Sstevel@tonic-gate 		 */
129*7c478bd9Sstevel@tonic-gate 
130*7c478bd9Sstevel@tonic-gate 		rw_enter(&kcpc_cpuctx_lock, RW_READER);
131*7c478bd9Sstevel@tonic-gate 		if (kcpc_cpuctx) {
132*7c478bd9Sstevel@tonic-gate 			rw_exit(&kcpc_cpuctx_lock);
133*7c478bd9Sstevel@tonic-gate 			return (set_errno(EAGAIN));
134*7c478bd9Sstevel@tonic-gate 		}
135*7c478bd9Sstevel@tonic-gate 
136*7c478bd9Sstevel@tonic-gate 		if (kcpc_hw_lwp_hook() != 0) {
137*7c478bd9Sstevel@tonic-gate 			rw_exit(&kcpc_cpuctx_lock);
138*7c478bd9Sstevel@tonic-gate 			return (set_errno(EACCES));
139*7c478bd9Sstevel@tonic-gate 		}
140*7c478bd9Sstevel@tonic-gate 
141*7c478bd9Sstevel@tonic-gate 		/*
142*7c478bd9Sstevel@tonic-gate 		 * An LWP may only have one set bound to it at a time; if there
143*7c478bd9Sstevel@tonic-gate 		 * is a set bound to this LWP already, we unbind it here.
144*7c478bd9Sstevel@tonic-gate 		 */
145*7c478bd9Sstevel@tonic-gate 		if (t->t_cpc_set != NULL)
146*7c478bd9Sstevel@tonic-gate 			(void) kcpc_unbind(t->t_cpc_set);
147*7c478bd9Sstevel@tonic-gate 		ASSERT(t->t_cpc_set == NULL);
148*7c478bd9Sstevel@tonic-gate 
149*7c478bd9Sstevel@tonic-gate 		if ((error = kcpc_copyin_set(&t->t_cpc_set, udata1,
150*7c478bd9Sstevel@tonic-gate 		    (size_t)udata2)) != 0) {
151*7c478bd9Sstevel@tonic-gate 			rw_exit(&kcpc_cpuctx_lock);
152*7c478bd9Sstevel@tonic-gate 			return (set_errno(error));
153*7c478bd9Sstevel@tonic-gate 		}
154*7c478bd9Sstevel@tonic-gate 
155*7c478bd9Sstevel@tonic-gate 		if ((error = kcpc_verify_set(t->t_cpc_set)) != 0) {
156*7c478bd9Sstevel@tonic-gate 			rw_exit(&kcpc_cpuctx_lock);
157*7c478bd9Sstevel@tonic-gate 			kcpc_free_set(t->t_cpc_set);
158*7c478bd9Sstevel@tonic-gate 			t->t_cpc_set = NULL;
159*7c478bd9Sstevel@tonic-gate 			if (copyout(&error, udata3, sizeof (error)) == -1)
160*7c478bd9Sstevel@tonic-gate 				return (set_errno(EFAULT));
161*7c478bd9Sstevel@tonic-gate 			return (set_errno(EINVAL));
162*7c478bd9Sstevel@tonic-gate 		}
163*7c478bd9Sstevel@tonic-gate 
164*7c478bd9Sstevel@tonic-gate 		if ((error = kcpc_bind_thread(t->t_cpc_set, t, &code)) != 0) {
165*7c478bd9Sstevel@tonic-gate 			rw_exit(&kcpc_cpuctx_lock);
166*7c478bd9Sstevel@tonic-gate 			kcpc_free_set(t->t_cpc_set);
167*7c478bd9Sstevel@tonic-gate 			t->t_cpc_set = NULL;
168*7c478bd9Sstevel@tonic-gate 			/*
169*7c478bd9Sstevel@tonic-gate 			 * EINVAL and EACCES are the only errors with more
170*7c478bd9Sstevel@tonic-gate 			 * specific subcodes.
171*7c478bd9Sstevel@tonic-gate 			 */
172*7c478bd9Sstevel@tonic-gate 			if ((error == EINVAL || error == EACCES) &&
173*7c478bd9Sstevel@tonic-gate 			    copyout(&code, udata3, sizeof (code)) == -1)
174*7c478bd9Sstevel@tonic-gate 				return (set_errno(EFAULT));
175*7c478bd9Sstevel@tonic-gate 			return (set_errno(error));
176*7c478bd9Sstevel@tonic-gate 		}
177*7c478bd9Sstevel@tonic-gate 
178*7c478bd9Sstevel@tonic-gate 		rw_exit(&kcpc_cpuctx_lock);
179*7c478bd9Sstevel@tonic-gate 		return (0);
180*7c478bd9Sstevel@tonic-gate 	case CPC_SAMPLE:
181*7c478bd9Sstevel@tonic-gate 		/*
182*7c478bd9Sstevel@tonic-gate 		 * udata1 = pointer to user's buffer
183*7c478bd9Sstevel@tonic-gate 		 * udata2 = pointer to user's hrtime
184*7c478bd9Sstevel@tonic-gate 		 * udata3 = pointer to user's tick
185*7c478bd9Sstevel@tonic-gate 		 */
186*7c478bd9Sstevel@tonic-gate 		/*
187*7c478bd9Sstevel@tonic-gate 		 * We only allow thread-bound sets to be sampled via the
188*7c478bd9Sstevel@tonic-gate 		 * syscall, so if this set has a CPU-bound context, return an
189*7c478bd9Sstevel@tonic-gate 		 * error.
190*7c478bd9Sstevel@tonic-gate 		 */
191*7c478bd9Sstevel@tonic-gate 		if (t->t_cpc_set->ks_ctx->kc_cpuid != -1)
192*7c478bd9Sstevel@tonic-gate 			return (set_errno(EINVAL));
193*7c478bd9Sstevel@tonic-gate 		if ((error = kcpc_sample(t->t_cpc_set, udata1, udata2,
194*7c478bd9Sstevel@tonic-gate 		    udata3)) != 0)
195*7c478bd9Sstevel@tonic-gate 			return (set_errno(error));
196*7c478bd9Sstevel@tonic-gate 
197*7c478bd9Sstevel@tonic-gate 		return (0);
198*7c478bd9Sstevel@tonic-gate 	case CPC_PRESET:
199*7c478bd9Sstevel@tonic-gate 	case CPC_RESTART:
200*7c478bd9Sstevel@tonic-gate 		/*
201*7c478bd9Sstevel@tonic-gate 		 * These are valid only if this lwp has a bound set.
202*7c478bd9Sstevel@tonic-gate 		 */
203*7c478bd9Sstevel@tonic-gate 		if (t->t_cpc_set == NULL)
204*7c478bd9Sstevel@tonic-gate 			return (set_errno(EINVAL));
205*7c478bd9Sstevel@tonic-gate 		if (cmd == CPC_PRESET) {
206*7c478bd9Sstevel@tonic-gate 			/*
207*7c478bd9Sstevel@tonic-gate 			 * The preset is shipped up to us from userland in two
208*7c478bd9Sstevel@tonic-gate 			 * parts. This lets us handle 64-bit values from 32-bit
209*7c478bd9Sstevel@tonic-gate 			 * and 64-bit applications in the same manner.
210*7c478bd9Sstevel@tonic-gate 			 *
211*7c478bd9Sstevel@tonic-gate 			 * udata1 = index of request to preset
212*7c478bd9Sstevel@tonic-gate 			 * udata2 = new 64-bit preset (most sig. 32 bits)
213*7c478bd9Sstevel@tonic-gate 			 * udata3 = new 64-bit preset (least sig. 32 bits)
214*7c478bd9Sstevel@tonic-gate 			 */
215*7c478bd9Sstevel@tonic-gate 			if ((error = kcpc_preset(t->t_cpc_set, (intptr_t)udata1,
216*7c478bd9Sstevel@tonic-gate 			    ((uint64_t)(uintptr_t)udata2 << 32ULL) |
217*7c478bd9Sstevel@tonic-gate 			    (uint64_t)(uintptr_t)udata3)) != 0)
218*7c478bd9Sstevel@tonic-gate 				return (set_errno(error));
219*7c478bd9Sstevel@tonic-gate 		} else {
220*7c478bd9Sstevel@tonic-gate 			/*
221*7c478bd9Sstevel@tonic-gate 			 * udata[1-3] = unused
222*7c478bd9Sstevel@tonic-gate 			 */
223*7c478bd9Sstevel@tonic-gate 			if ((error = kcpc_restart(t->t_cpc_set)) != 0)
224*7c478bd9Sstevel@tonic-gate 				return (set_errno(error));
225*7c478bd9Sstevel@tonic-gate 		}
226*7c478bd9Sstevel@tonic-gate 		return (0);
227*7c478bd9Sstevel@tonic-gate 	case CPC_ENABLE:
228*7c478bd9Sstevel@tonic-gate 	case CPC_DISABLE:
229*7c478bd9Sstevel@tonic-gate 		udata1 = 0;
230*7c478bd9Sstevel@tonic-gate 		/*FALLTHROUGH*/
231*7c478bd9Sstevel@tonic-gate 	case CPC_USR_EVENTS:
232*7c478bd9Sstevel@tonic-gate 	case CPC_SYS_EVENTS:
233*7c478bd9Sstevel@tonic-gate 		if (t != curthread || t->t_cpc_set == NULL)
234*7c478bd9Sstevel@tonic-gate 			return (set_errno(EINVAL));
235*7c478bd9Sstevel@tonic-gate 		/*
236*7c478bd9Sstevel@tonic-gate 		 * Provided for backwards compatibility with CPCv1.
237*7c478bd9Sstevel@tonic-gate 		 *
238*7c478bd9Sstevel@tonic-gate 		 * Stop the counters and record the current counts. Use the
239*7c478bd9Sstevel@tonic-gate 		 * counts as the preset to rebind a new set with the requests
240*7c478bd9Sstevel@tonic-gate 		 * reconfigured as requested.
241*7c478bd9Sstevel@tonic-gate 		 *
242*7c478bd9Sstevel@tonic-gate 		 * udata1: 1 == enable; 0 == disable
243*7c478bd9Sstevel@tonic-gate 		 * udata{2,3}: unused
244*7c478bd9Sstevel@tonic-gate 		 */
245*7c478bd9Sstevel@tonic-gate 		rw_enter(&kcpc_cpuctx_lock, RW_READER);
246*7c478bd9Sstevel@tonic-gate 		if ((error = kcpc_enable(t,
247*7c478bd9Sstevel@tonic-gate 		    cmd, (int)(uintptr_t)udata1)) != 0) {
248*7c478bd9Sstevel@tonic-gate 			rw_exit(&kcpc_cpuctx_lock);
249*7c478bd9Sstevel@tonic-gate 			return (set_errno(error));
250*7c478bd9Sstevel@tonic-gate 		}
251*7c478bd9Sstevel@tonic-gate 		rw_exit(&kcpc_cpuctx_lock);
252*7c478bd9Sstevel@tonic-gate 		return (0);
253*7c478bd9Sstevel@tonic-gate 	case CPC_NPIC:
254*7c478bd9Sstevel@tonic-gate 		return (cpc_ncounters);
255*7c478bd9Sstevel@tonic-gate 	case CPC_CAPS:
256*7c478bd9Sstevel@tonic-gate 		return (pcbe_ops->pcbe_caps);
257*7c478bd9Sstevel@tonic-gate 	case CPC_EVLIST_SIZE:
258*7c478bd9Sstevel@tonic-gate 	case CPC_LIST_EVENTS:
259*7c478bd9Sstevel@tonic-gate 		/*
260*7c478bd9Sstevel@tonic-gate 		 * udata1 = pointer to user's int or buffer
261*7c478bd9Sstevel@tonic-gate 		 * udata2 = picnum
262*7c478bd9Sstevel@tonic-gate 		 * udata3 = unused
263*7c478bd9Sstevel@tonic-gate 		 */
264*7c478bd9Sstevel@tonic-gate 		if ((uintptr_t)udata2 >= cpc_ncounters)
265*7c478bd9Sstevel@tonic-gate 			return (set_errno(EINVAL));
266*7c478bd9Sstevel@tonic-gate 
267*7c478bd9Sstevel@tonic-gate 		size = strlen(
268*7c478bd9Sstevel@tonic-gate 		    pcbe_ops->pcbe_list_events((uintptr_t)udata2)) + 1;
269*7c478bd9Sstevel@tonic-gate 
270*7c478bd9Sstevel@tonic-gate 		if (cmd == CPC_EVLIST_SIZE) {
271*7c478bd9Sstevel@tonic-gate 			if (suword32(udata1, size) == -1)
272*7c478bd9Sstevel@tonic-gate 				return (set_errno(EFAULT));
273*7c478bd9Sstevel@tonic-gate 		} else {
274*7c478bd9Sstevel@tonic-gate 			if (copyout(
275*7c478bd9Sstevel@tonic-gate 			    pcbe_ops->pcbe_list_events((uintptr_t)udata2),
276*7c478bd9Sstevel@tonic-gate 			    udata1, size) == -1)
277*7c478bd9Sstevel@tonic-gate 				return (set_errno(EFAULT));
278*7c478bd9Sstevel@tonic-gate 		}
279*7c478bd9Sstevel@tonic-gate 		return (0);
280*7c478bd9Sstevel@tonic-gate 	case CPC_ATTRLIST_SIZE:
281*7c478bd9Sstevel@tonic-gate 	case CPC_LIST_ATTRS:
282*7c478bd9Sstevel@tonic-gate 		/*
283*7c478bd9Sstevel@tonic-gate 		 * udata1 = pointer to user's int or buffer
284*7c478bd9Sstevel@tonic-gate 		 * udata2 = unused
285*7c478bd9Sstevel@tonic-gate 		 * udata3 = unused
286*7c478bd9Sstevel@tonic-gate 		 *
287*7c478bd9Sstevel@tonic-gate 		 * attrlist size is length of PCBE-supported attributes, plus
288*7c478bd9Sstevel@tonic-gate 		 * room for "picnum\0" plus an optional ',' separator char.
289*7c478bd9Sstevel@tonic-gate 		 */
290*7c478bd9Sstevel@tonic-gate 		str = pcbe_ops->pcbe_list_attrs();
291*7c478bd9Sstevel@tonic-gate 		size = strlen(str) + sizeof (SEPARATOR ATTRLIST) + 1;
292*7c478bd9Sstevel@tonic-gate 		if (str[0] != '\0')
293*7c478bd9Sstevel@tonic-gate 			/*
294*7c478bd9Sstevel@tonic-gate 			 * A ',' separator character is necessary.
295*7c478bd9Sstevel@tonic-gate 			 */
296*7c478bd9Sstevel@tonic-gate 			size += 1;
297*7c478bd9Sstevel@tonic-gate 
298*7c478bd9Sstevel@tonic-gate 		if (cmd == CPC_ATTRLIST_SIZE) {
299*7c478bd9Sstevel@tonic-gate 			if (suword32(udata1, size) == -1)
300*7c478bd9Sstevel@tonic-gate 				return (set_errno(EFAULT));
301*7c478bd9Sstevel@tonic-gate 		} else {
302*7c478bd9Sstevel@tonic-gate 			/*
303*7c478bd9Sstevel@tonic-gate 			 * Copyout the PCBE attributes, and then append the
304*7c478bd9Sstevel@tonic-gate 			 * generic attribute list (with separator if necessary).
305*7c478bd9Sstevel@tonic-gate 			 */
306*7c478bd9Sstevel@tonic-gate 			if (copyout(str, udata1, strlen(str)) == -1)
307*7c478bd9Sstevel@tonic-gate 				return (set_errno(EFAULT));
308*7c478bd9Sstevel@tonic-gate 			if (str[0] != '\0') {
309*7c478bd9Sstevel@tonic-gate 				if (copyout(SEPARATOR ATTRLIST,
310*7c478bd9Sstevel@tonic-gate 				    ((char *)udata1) + strlen(str),
311*7c478bd9Sstevel@tonic-gate 				    strlen(SEPARATOR ATTRLIST) + 1)
312*7c478bd9Sstevel@tonic-gate 				    == -1)
313*7c478bd9Sstevel@tonic-gate 					return (set_errno(EFAULT));
314*7c478bd9Sstevel@tonic-gate 			} else
315*7c478bd9Sstevel@tonic-gate 				if (copyout(ATTRLIST,
316*7c478bd9Sstevel@tonic-gate 				    (char *)udata1 + strlen(str),
317*7c478bd9Sstevel@tonic-gate 				    strlen(ATTRLIST) + 1) == -1)
318*7c478bd9Sstevel@tonic-gate 					return (set_errno(EFAULT));
319*7c478bd9Sstevel@tonic-gate 		}
320*7c478bd9Sstevel@tonic-gate 		return (0);
321*7c478bd9Sstevel@tonic-gate 	case CPC_IMPL_NAME:
322*7c478bd9Sstevel@tonic-gate 	case CPC_CPUREF:
323*7c478bd9Sstevel@tonic-gate 		/*
324*7c478bd9Sstevel@tonic-gate 		 * udata1 = pointer to user's buffer
325*7c478bd9Sstevel@tonic-gate 		 * udata2 = unused
326*7c478bd9Sstevel@tonic-gate 		 * udata3 = unused
327*7c478bd9Sstevel@tonic-gate 		 */
328*7c478bd9Sstevel@tonic-gate 		if (cmd == CPC_IMPL_NAME) {
329*7c478bd9Sstevel@tonic-gate 			str = pcbe_ops->pcbe_impl_name();
330*7c478bd9Sstevel@tonic-gate 			ASSERT(strlen(str) < CPC_MAX_IMPL_NAME);
331*7c478bd9Sstevel@tonic-gate 		} else {
332*7c478bd9Sstevel@tonic-gate 			str = pcbe_ops->pcbe_cpuref();
333*7c478bd9Sstevel@tonic-gate 			ASSERT(strlen(str) < CPC_MAX_CPUREF);
334*7c478bd9Sstevel@tonic-gate 		}
335*7c478bd9Sstevel@tonic-gate 
336*7c478bd9Sstevel@tonic-gate 		if (copyout(str, udata1, strlen(str) + 1) != 0)
337*7c478bd9Sstevel@tonic-gate 			return (set_errno(EFAULT));
338*7c478bd9Sstevel@tonic-gate 		return (0);
339*7c478bd9Sstevel@tonic-gate 	case CPC_INVALIDATE:
340*7c478bd9Sstevel@tonic-gate 		kcpc_invalidate(t);
341*7c478bd9Sstevel@tonic-gate 		return (0);
342*7c478bd9Sstevel@tonic-gate 	case CPC_RELE:
343*7c478bd9Sstevel@tonic-gate 		if ((error = kcpc_unbind(t->t_cpc_set)) != 0)
344*7c478bd9Sstevel@tonic-gate 			return (set_errno(error));
345*7c478bd9Sstevel@tonic-gate 		return (0);
346*7c478bd9Sstevel@tonic-gate 	default:
347*7c478bd9Sstevel@tonic-gate 		return (set_errno(EINVAL));
348*7c478bd9Sstevel@tonic-gate 	}
349*7c478bd9Sstevel@tonic-gate }
350*7c478bd9Sstevel@tonic-gate 
351*7c478bd9Sstevel@tonic-gate /*
352*7c478bd9Sstevel@tonic-gate  * The 'shared' device allows direct access to the
353*7c478bd9Sstevel@tonic-gate  * performance counter control register of the current CPU.
354*7c478bd9Sstevel@tonic-gate  * The major difference between the contexts created here and those
355*7c478bd9Sstevel@tonic-gate  * above is that the context handlers are -not- installed, thus
356*7c478bd9Sstevel@tonic-gate  * no context switching behaviour occurs.
357*7c478bd9Sstevel@tonic-gate  *
358*7c478bd9Sstevel@tonic-gate  * Because they manipulate per-cpu state, these ioctls can
359*7c478bd9Sstevel@tonic-gate  * only be invoked from a bound lwp, by a caller with the cpc_cpu privilege
360*7c478bd9Sstevel@tonic-gate  * who can open the relevant entry in /devices (the act of holding it open
361*7c478bd9Sstevel@tonic-gate  * causes other uses of the counters to be suspended).
362*7c478bd9Sstevel@tonic-gate  *
363*7c478bd9Sstevel@tonic-gate  * Note that for correct results, the caller -must- ensure that
364*7c478bd9Sstevel@tonic-gate  * all existing per-lwp contexts are either inactive or marked invalid;
365*7c478bd9Sstevel@tonic-gate  * that's what the open routine does.
366*7c478bd9Sstevel@tonic-gate  */
367*7c478bd9Sstevel@tonic-gate /*ARGSUSED*/
368*7c478bd9Sstevel@tonic-gate static int
369*7c478bd9Sstevel@tonic-gate kcpc_ioctl(dev_t dev, int cmd, intptr_t data, int flags, cred_t *cr, int *rvp)
370*7c478bd9Sstevel@tonic-gate {
371*7c478bd9Sstevel@tonic-gate 	kthread_t	*t = curthread;
372*7c478bd9Sstevel@tonic-gate 	processorid_t	cpuid;
373*7c478bd9Sstevel@tonic-gate 	void		*udata1 = NULL;
374*7c478bd9Sstevel@tonic-gate 	void		*udata2 = NULL;
375*7c478bd9Sstevel@tonic-gate 	void		*udata3 = NULL;
376*7c478bd9Sstevel@tonic-gate 	int		error;
377*7c478bd9Sstevel@tonic-gate 	int		code;
378*7c478bd9Sstevel@tonic-gate 
379*7c478bd9Sstevel@tonic-gate 	STRUCT_DECL(__cpc_args, args);
380*7c478bd9Sstevel@tonic-gate 
381*7c478bd9Sstevel@tonic-gate 	STRUCT_INIT(args, flags);
382*7c478bd9Sstevel@tonic-gate 
383*7c478bd9Sstevel@tonic-gate 	if (curthread->t_bind_cpu != getminor(dev))
384*7c478bd9Sstevel@tonic-gate 		return (EAGAIN);  /* someone unbound it? */
385*7c478bd9Sstevel@tonic-gate 
386*7c478bd9Sstevel@tonic-gate 	cpuid = getminor(dev);
387*7c478bd9Sstevel@tonic-gate 
388*7c478bd9Sstevel@tonic-gate 	if (cmd == CPCIO_BIND || cmd == CPCIO_SAMPLE) {
389*7c478bd9Sstevel@tonic-gate 		if (copyin((void *)data, STRUCT_BUF(args),
390*7c478bd9Sstevel@tonic-gate 		    STRUCT_SIZE(args)) == -1)
391*7c478bd9Sstevel@tonic-gate 			return (EFAULT);
392*7c478bd9Sstevel@tonic-gate 
393*7c478bd9Sstevel@tonic-gate 		udata1 = STRUCT_FGETP(args, udata1);
394*7c478bd9Sstevel@tonic-gate 		udata2 = STRUCT_FGETP(args, udata2);
395*7c478bd9Sstevel@tonic-gate 		udata3 = STRUCT_FGETP(args, udata3);
396*7c478bd9Sstevel@tonic-gate 	}
397*7c478bd9Sstevel@tonic-gate 
398*7c478bd9Sstevel@tonic-gate 	switch (cmd) {
399*7c478bd9Sstevel@tonic-gate 	case CPCIO_BIND:
400*7c478bd9Sstevel@tonic-gate 		/*
401*7c478bd9Sstevel@tonic-gate 		 * udata1 = pointer to packed nvlist buffer
402*7c478bd9Sstevel@tonic-gate 		 * udata2 = size of packed nvlist buffer
403*7c478bd9Sstevel@tonic-gate 		 * udata3 = User addr to return error subcode in.
404*7c478bd9Sstevel@tonic-gate 		 */
405*7c478bd9Sstevel@tonic-gate 		if (t->t_cpc_set != NULL) {
406*7c478bd9Sstevel@tonic-gate 			(void) kcpc_unbind(t->t_cpc_set);
407*7c478bd9Sstevel@tonic-gate 			ASSERT(t->t_cpc_set == NULL);
408*7c478bd9Sstevel@tonic-gate 		}
409*7c478bd9Sstevel@tonic-gate 
410*7c478bd9Sstevel@tonic-gate 		if ((error = kcpc_copyin_set(&t->t_cpc_set, udata1,
411*7c478bd9Sstevel@tonic-gate 		    (size_t)udata2)) != 0) {
412*7c478bd9Sstevel@tonic-gate 			return (error);
413*7c478bd9Sstevel@tonic-gate 		}
414*7c478bd9Sstevel@tonic-gate 
415*7c478bd9Sstevel@tonic-gate 		if ((error = kcpc_verify_set(t->t_cpc_set)) != 0) {
416*7c478bd9Sstevel@tonic-gate 			kcpc_free_set(t->t_cpc_set);
417*7c478bd9Sstevel@tonic-gate 			t->t_cpc_set = NULL;
418*7c478bd9Sstevel@tonic-gate 			if (copyout(&error, udata3, sizeof (error)) == -1)
419*7c478bd9Sstevel@tonic-gate 				return (EFAULT);
420*7c478bd9Sstevel@tonic-gate 			return (EINVAL);
421*7c478bd9Sstevel@tonic-gate 		}
422*7c478bd9Sstevel@tonic-gate 
423*7c478bd9Sstevel@tonic-gate 		if ((error = kcpc_bind_cpu(t->t_cpc_set, cpuid, &code)) != 0) {
424*7c478bd9Sstevel@tonic-gate 			kcpc_free_set(t->t_cpc_set);
425*7c478bd9Sstevel@tonic-gate 			t->t_cpc_set = NULL;
426*7c478bd9Sstevel@tonic-gate 			/*
427*7c478bd9Sstevel@tonic-gate 			 * Subcodes are only returned for EINVAL and EACCESS.
428*7c478bd9Sstevel@tonic-gate 			 */
429*7c478bd9Sstevel@tonic-gate 			if ((error == EINVAL || error == EACCES) &&
430*7c478bd9Sstevel@tonic-gate 			    copyout(&code, udata3, sizeof (code)) == -1)
431*7c478bd9Sstevel@tonic-gate 				return (EFAULT);
432*7c478bd9Sstevel@tonic-gate 			return (error);
433*7c478bd9Sstevel@tonic-gate 		}
434*7c478bd9Sstevel@tonic-gate 
435*7c478bd9Sstevel@tonic-gate 		return (0);
436*7c478bd9Sstevel@tonic-gate 	case CPCIO_SAMPLE:
437*7c478bd9Sstevel@tonic-gate 		/*
438*7c478bd9Sstevel@tonic-gate 		 * udata1 = pointer to user's buffer
439*7c478bd9Sstevel@tonic-gate 		 * udata2 = pointer to user's hrtime
440*7c478bd9Sstevel@tonic-gate 		 * udata3 = pointer to user's tick
441*7c478bd9Sstevel@tonic-gate 		 */
442*7c478bd9Sstevel@tonic-gate 		/*
443*7c478bd9Sstevel@tonic-gate 		 * Only CPU-bound sets may be sampled via the ioctl(). If this
444*7c478bd9Sstevel@tonic-gate 		 * set has no CPU-bound context, return an error.
445*7c478bd9Sstevel@tonic-gate 		 */
446*7c478bd9Sstevel@tonic-gate 		if (t->t_cpc_set == NULL)
447*7c478bd9Sstevel@tonic-gate 			return (EINVAL);
448*7c478bd9Sstevel@tonic-gate 		if ((error = kcpc_sample(t->t_cpc_set, udata1, udata2,
449*7c478bd9Sstevel@tonic-gate 		    udata3)) != 0)
450*7c478bd9Sstevel@tonic-gate 			return (error);
451*7c478bd9Sstevel@tonic-gate 		return (0);
452*7c478bd9Sstevel@tonic-gate 	case CPCIO_RELE:
453*7c478bd9Sstevel@tonic-gate 		if (t->t_cpc_set == NULL)
454*7c478bd9Sstevel@tonic-gate 			return (EINVAL);
455*7c478bd9Sstevel@tonic-gate 		return (kcpc_unbind(t->t_cpc_set));
456*7c478bd9Sstevel@tonic-gate 	default:
457*7c478bd9Sstevel@tonic-gate 		return (EINVAL);
458*7c478bd9Sstevel@tonic-gate 	}
459*7c478bd9Sstevel@tonic-gate }
460*7c478bd9Sstevel@tonic-gate 
461*7c478bd9Sstevel@tonic-gate /*
462*7c478bd9Sstevel@tonic-gate  * The device supports multiple opens, but only one open
463*7c478bd9Sstevel@tonic-gate  * is allowed per processor.  This is to enable multiple
464*7c478bd9Sstevel@tonic-gate  * instances of tools looking at different processors.
465*7c478bd9Sstevel@tonic-gate  */
466*7c478bd9Sstevel@tonic-gate #define	KCPC_MINOR_SHARED		((minor_t)0x3fffful)
467*7c478bd9Sstevel@tonic-gate 
468*7c478bd9Sstevel@tonic-gate static ulong_t *kcpc_cpumap;		/* bitmap of cpus */
469*7c478bd9Sstevel@tonic-gate 
470*7c478bd9Sstevel@tonic-gate /*ARGSUSED1*/
471*7c478bd9Sstevel@tonic-gate static int
472*7c478bd9Sstevel@tonic-gate kcpc_open(dev_t *dev, int flags, int otyp, cred_t *cr)
473*7c478bd9Sstevel@tonic-gate {
474*7c478bd9Sstevel@tonic-gate 	processorid_t	cpuid;
475*7c478bd9Sstevel@tonic-gate 	int		error;
476*7c478bd9Sstevel@tonic-gate 
477*7c478bd9Sstevel@tonic-gate 	ASSERT(pcbe_ops != NULL);
478*7c478bd9Sstevel@tonic-gate 
479*7c478bd9Sstevel@tonic-gate 	if ((error = secpolicy_cpc_cpu(cr)) != 0)
480*7c478bd9Sstevel@tonic-gate 		return (error);
481*7c478bd9Sstevel@tonic-gate 	if (getminor(*dev) != KCPC_MINOR_SHARED)
482*7c478bd9Sstevel@tonic-gate 		return (ENXIO);
483*7c478bd9Sstevel@tonic-gate 	if ((cpuid = curthread->t_bind_cpu) == PBIND_NONE)
484*7c478bd9Sstevel@tonic-gate 		return (EINVAL);
485*7c478bd9Sstevel@tonic-gate 	if (cpuid > max_cpuid)
486*7c478bd9Sstevel@tonic-gate 		return (EINVAL);
487*7c478bd9Sstevel@tonic-gate 
488*7c478bd9Sstevel@tonic-gate 	rw_enter(&kcpc_cpuctx_lock, RW_WRITER);
489*7c478bd9Sstevel@tonic-gate 	if (++kcpc_cpuctx == 1) {
490*7c478bd9Sstevel@tonic-gate 		ASSERT(kcpc_cpumap == NULL);
491*7c478bd9Sstevel@tonic-gate 		kcpc_cpumap = kmem_zalloc(BT_SIZEOFMAP(max_cpuid + 1),
492*7c478bd9Sstevel@tonic-gate 		    KM_SLEEP);
493*7c478bd9Sstevel@tonic-gate 		/*
494*7c478bd9Sstevel@tonic-gate 		 * When this device is open for processor-based contexts,
495*7c478bd9Sstevel@tonic-gate 		 * no further lwp-based contexts can be created.
496*7c478bd9Sstevel@tonic-gate 		 *
497*7c478bd9Sstevel@tonic-gate 		 * Since this is the first open, ensure that all existing
498*7c478bd9Sstevel@tonic-gate 		 * contexts are invalidated.
499*7c478bd9Sstevel@tonic-gate 		 */
500*7c478bd9Sstevel@tonic-gate 		kcpc_invalidate_all();
501*7c478bd9Sstevel@tonic-gate 	} else if (BT_TEST(kcpc_cpumap, cpuid)) {
502*7c478bd9Sstevel@tonic-gate 		kcpc_cpuctx--;
503*7c478bd9Sstevel@tonic-gate 		rw_exit(&kcpc_cpuctx_lock);
504*7c478bd9Sstevel@tonic-gate 		return (EAGAIN);
505*7c478bd9Sstevel@tonic-gate 	} else if (kcpc_hw_cpu_hook(cpuid, kcpc_cpumap) != 0) {
506*7c478bd9Sstevel@tonic-gate 		kcpc_cpuctx--;
507*7c478bd9Sstevel@tonic-gate 		rw_exit(&kcpc_cpuctx_lock);
508*7c478bd9Sstevel@tonic-gate 		return (EACCES);
509*7c478bd9Sstevel@tonic-gate 	}
510*7c478bd9Sstevel@tonic-gate 	BT_SET(kcpc_cpumap, cpuid);
511*7c478bd9Sstevel@tonic-gate 	rw_exit(&kcpc_cpuctx_lock);
512*7c478bd9Sstevel@tonic-gate 
513*7c478bd9Sstevel@tonic-gate 	*dev = makedevice(getmajor(*dev), (minor_t)cpuid);
514*7c478bd9Sstevel@tonic-gate 
515*7c478bd9Sstevel@tonic-gate 	return (0);
516*7c478bd9Sstevel@tonic-gate }
517*7c478bd9Sstevel@tonic-gate 
518*7c478bd9Sstevel@tonic-gate /*ARGSUSED1*/
519*7c478bd9Sstevel@tonic-gate static int
520*7c478bd9Sstevel@tonic-gate kcpc_close(dev_t dev, int flags, int otyp, cred_t *cr)
521*7c478bd9Sstevel@tonic-gate {
522*7c478bd9Sstevel@tonic-gate 	rw_enter(&kcpc_cpuctx_lock, RW_WRITER);
523*7c478bd9Sstevel@tonic-gate 	BT_CLEAR(kcpc_cpumap, getminor(dev));
524*7c478bd9Sstevel@tonic-gate 	if (--kcpc_cpuctx == 0) {
525*7c478bd9Sstevel@tonic-gate 		kmem_free(kcpc_cpumap, BT_SIZEOFMAP(max_cpuid + 1));
526*7c478bd9Sstevel@tonic-gate 		kcpc_cpumap = NULL;
527*7c478bd9Sstevel@tonic-gate 	}
528*7c478bd9Sstevel@tonic-gate 	ASSERT(kcpc_cpuctx >= 0);
529*7c478bd9Sstevel@tonic-gate 	rw_exit(&kcpc_cpuctx_lock);
530*7c478bd9Sstevel@tonic-gate 
531*7c478bd9Sstevel@tonic-gate 	return (0);
532*7c478bd9Sstevel@tonic-gate }
533*7c478bd9Sstevel@tonic-gate 
534*7c478bd9Sstevel@tonic-gate /*
535*7c478bd9Sstevel@tonic-gate  * Sane boundaries on the size of packed lists. In bytes.
536*7c478bd9Sstevel@tonic-gate  */
537*7c478bd9Sstevel@tonic-gate #define	CPC_MIN_PACKSIZE 4
538*7c478bd9Sstevel@tonic-gate #define	CPC_MAX_PACKSIZE 10000
539*7c478bd9Sstevel@tonic-gate 
540*7c478bd9Sstevel@tonic-gate /*
541*7c478bd9Sstevel@tonic-gate  * Sane boundary on the number of requests a set can contain.
542*7c478bd9Sstevel@tonic-gate  */
543*7c478bd9Sstevel@tonic-gate #define	CPC_MAX_NREQS 100
544*7c478bd9Sstevel@tonic-gate 
545*7c478bd9Sstevel@tonic-gate /*
546*7c478bd9Sstevel@tonic-gate  * Sane boundary on the number of attributes a request can contain.
547*7c478bd9Sstevel@tonic-gate  */
548*7c478bd9Sstevel@tonic-gate #define	CPC_MAX_ATTRS 50
549*7c478bd9Sstevel@tonic-gate 
550*7c478bd9Sstevel@tonic-gate /*
551*7c478bd9Sstevel@tonic-gate  * Copy in a packed nvlist from the user and create a request set out of it.
552*7c478bd9Sstevel@tonic-gate  * If successful, return 0 and store a pointer to the set we've created. Returns
553*7c478bd9Sstevel@tonic-gate  * error code on error.
554*7c478bd9Sstevel@tonic-gate  */
555*7c478bd9Sstevel@tonic-gate int
556*7c478bd9Sstevel@tonic-gate kcpc_copyin_set(kcpc_set_t **inset, void *ubuf, size_t len)
557*7c478bd9Sstevel@tonic-gate {
558*7c478bd9Sstevel@tonic-gate 	kcpc_set_t	*set;
559*7c478bd9Sstevel@tonic-gate 	int		i;
560*7c478bd9Sstevel@tonic-gate 	int		j;
561*7c478bd9Sstevel@tonic-gate 	char		*packbuf;
562*7c478bd9Sstevel@tonic-gate 
563*7c478bd9Sstevel@tonic-gate 	nvlist_t	*nvl;
564*7c478bd9Sstevel@tonic-gate 	nvpair_t	*nvp = NULL;
565*7c478bd9Sstevel@tonic-gate 
566*7c478bd9Sstevel@tonic-gate 	nvlist_t	*attrs;
567*7c478bd9Sstevel@tonic-gate 	nvpair_t	*nvp_attr;
568*7c478bd9Sstevel@tonic-gate 	kcpc_attr_t	*attrp;
569*7c478bd9Sstevel@tonic-gate 
570*7c478bd9Sstevel@tonic-gate 	nvlist_t	**reqlist;
571*7c478bd9Sstevel@tonic-gate 	uint_t		nreqs;
572*7c478bd9Sstevel@tonic-gate 	uint64_t	uint64;
573*7c478bd9Sstevel@tonic-gate 	uint32_t	uint32;
574*7c478bd9Sstevel@tonic-gate 	uint32_t	setflags = (uint32_t)-1;
575*7c478bd9Sstevel@tonic-gate 	char		*string;
576*7c478bd9Sstevel@tonic-gate 	char		*name;
577*7c478bd9Sstevel@tonic-gate 
578*7c478bd9Sstevel@tonic-gate 	if (len < CPC_MIN_PACKSIZE || len > CPC_MAX_PACKSIZE)
579*7c478bd9Sstevel@tonic-gate 		return (EINVAL);
580*7c478bd9Sstevel@tonic-gate 
581*7c478bd9Sstevel@tonic-gate 	packbuf = kmem_alloc(len, KM_SLEEP);
582*7c478bd9Sstevel@tonic-gate 
583*7c478bd9Sstevel@tonic-gate 	if (copyin(ubuf, packbuf, len) == -1) {
584*7c478bd9Sstevel@tonic-gate 		kmem_free(packbuf, len);
585*7c478bd9Sstevel@tonic-gate 		return (EFAULT);
586*7c478bd9Sstevel@tonic-gate 	}
587*7c478bd9Sstevel@tonic-gate 
588*7c478bd9Sstevel@tonic-gate 	if (nvlist_unpack(packbuf, len, &nvl, KM_SLEEP) != 0) {
589*7c478bd9Sstevel@tonic-gate 		kmem_free(packbuf, len);
590*7c478bd9Sstevel@tonic-gate 		return (EINVAL);
591*7c478bd9Sstevel@tonic-gate 	}
592*7c478bd9Sstevel@tonic-gate 
593*7c478bd9Sstevel@tonic-gate 	/*
594*7c478bd9Sstevel@tonic-gate 	 * The nvlist has been unpacked so there is no need for the packed
595*7c478bd9Sstevel@tonic-gate 	 * representation from this point on.
596*7c478bd9Sstevel@tonic-gate 	 */
597*7c478bd9Sstevel@tonic-gate 	kmem_free(packbuf, len);
598*7c478bd9Sstevel@tonic-gate 
599*7c478bd9Sstevel@tonic-gate 	i = 0;
600*7c478bd9Sstevel@tonic-gate 	while ((nvp = nvlist_next_nvpair(nvl, nvp)) != NULL) {
601*7c478bd9Sstevel@tonic-gate 		switch (nvpair_type(nvp)) {
602*7c478bd9Sstevel@tonic-gate 		case DATA_TYPE_UINT32:
603*7c478bd9Sstevel@tonic-gate 			if (strcmp(nvpair_name(nvp), "flags") != 0 ||
604*7c478bd9Sstevel@tonic-gate 			    nvpair_value_uint32(nvp, &setflags) != 0) {
605*7c478bd9Sstevel@tonic-gate 				nvlist_free(nvl);
606*7c478bd9Sstevel@tonic-gate 				return (EINVAL);
607*7c478bd9Sstevel@tonic-gate 			}
608*7c478bd9Sstevel@tonic-gate 			break;
609*7c478bd9Sstevel@tonic-gate 		case DATA_TYPE_NVLIST_ARRAY:
610*7c478bd9Sstevel@tonic-gate 			if (strcmp(nvpair_name(nvp), "reqs") != 0 ||
611*7c478bd9Sstevel@tonic-gate 			    nvpair_value_nvlist_array(nvp, &reqlist,
612*7c478bd9Sstevel@tonic-gate 				&nreqs) != 0) {
613*7c478bd9Sstevel@tonic-gate 				nvlist_free(nvl);
614*7c478bd9Sstevel@tonic-gate 				return (EINVAL);
615*7c478bd9Sstevel@tonic-gate 			}
616*7c478bd9Sstevel@tonic-gate 			break;
617*7c478bd9Sstevel@tonic-gate 		default:
618*7c478bd9Sstevel@tonic-gate 			nvlist_free(nvl);
619*7c478bd9Sstevel@tonic-gate 			return (EINVAL);
620*7c478bd9Sstevel@tonic-gate 		}
621*7c478bd9Sstevel@tonic-gate 		i++;
622*7c478bd9Sstevel@tonic-gate 	}
623*7c478bd9Sstevel@tonic-gate 
624*7c478bd9Sstevel@tonic-gate 	/*
625*7c478bd9Sstevel@tonic-gate 	 * There should be two members in the top-level nvlist:
626*7c478bd9Sstevel@tonic-gate 	 * an array of nvlists consisting of the requests, and flags.
627*7c478bd9Sstevel@tonic-gate 	 * Anything else is an invalid set.
628*7c478bd9Sstevel@tonic-gate 	 */
629*7c478bd9Sstevel@tonic-gate 	if (i != 2) {
630*7c478bd9Sstevel@tonic-gate 		nvlist_free(nvl);
631*7c478bd9Sstevel@tonic-gate 		return (EINVAL);
632*7c478bd9Sstevel@tonic-gate 	}
633*7c478bd9Sstevel@tonic-gate 
634*7c478bd9Sstevel@tonic-gate 	if (nreqs > CPC_MAX_NREQS) {
635*7c478bd9Sstevel@tonic-gate 		nvlist_free(nvl);
636*7c478bd9Sstevel@tonic-gate 		return (EINVAL);
637*7c478bd9Sstevel@tonic-gate 	}
638*7c478bd9Sstevel@tonic-gate 
639*7c478bd9Sstevel@tonic-gate 	/*
640*7c478bd9Sstevel@tonic-gate 	 * The requests are now stored in the nvlist array at reqlist.
641*7c478bd9Sstevel@tonic-gate 	 */
642*7c478bd9Sstevel@tonic-gate 	set = kmem_alloc(sizeof (kcpc_set_t), KM_SLEEP);
643*7c478bd9Sstevel@tonic-gate 	set->ks_req = (kcpc_request_t *)kmem_zalloc(sizeof (kcpc_request_t) *
644*7c478bd9Sstevel@tonic-gate 	    nreqs, KM_SLEEP);
645*7c478bd9Sstevel@tonic-gate 	set->ks_nreqs = nreqs;
646*7c478bd9Sstevel@tonic-gate 	/*
647*7c478bd9Sstevel@tonic-gate 	 * If the nvlist didn't contain a flags member, setflags was initialized
648*7c478bd9Sstevel@tonic-gate 	 * with an illegal value and this set will fail sanity checks later on.
649*7c478bd9Sstevel@tonic-gate 	 */
650*7c478bd9Sstevel@tonic-gate 	set->ks_flags = setflags;
651*7c478bd9Sstevel@tonic-gate 
652*7c478bd9Sstevel@tonic-gate 	/*
653*7c478bd9Sstevel@tonic-gate 	 * Build the set up one request at a time, always keeping it self-
654*7c478bd9Sstevel@tonic-gate 	 * consistent so we can give it to kcpc_free_set() if we need to back
655*7c478bd9Sstevel@tonic-gate 	 * out and return and error.
656*7c478bd9Sstevel@tonic-gate 	 */
657*7c478bd9Sstevel@tonic-gate 	for (i = 0; i < nreqs; i++) {
658*7c478bd9Sstevel@tonic-gate 		nvp = NULL;
659*7c478bd9Sstevel@tonic-gate 		set->ks_req[i].kr_picnum = -1;
660*7c478bd9Sstevel@tonic-gate 		while ((nvp = nvlist_next_nvpair(reqlist[i], nvp)) != NULL) {
661*7c478bd9Sstevel@tonic-gate 			name = nvpair_name(nvp);
662*7c478bd9Sstevel@tonic-gate 			switch (nvpair_type(nvp)) {
663*7c478bd9Sstevel@tonic-gate 			case DATA_TYPE_UINT32:
664*7c478bd9Sstevel@tonic-gate 				if (nvpair_value_uint32(nvp, &uint32) == EINVAL)
665*7c478bd9Sstevel@tonic-gate 					goto inval;
666*7c478bd9Sstevel@tonic-gate 				if (strcmp(name, "cr_flags") == 0)
667*7c478bd9Sstevel@tonic-gate 					set->ks_req[i].kr_flags = uint32;
668*7c478bd9Sstevel@tonic-gate 				if (strcmp(name, "cr_index") == 0)
669*7c478bd9Sstevel@tonic-gate 					set->ks_req[i].kr_index = uint32;
670*7c478bd9Sstevel@tonic-gate 				break;
671*7c478bd9Sstevel@tonic-gate 			case DATA_TYPE_UINT64:
672*7c478bd9Sstevel@tonic-gate 				if (nvpair_value_uint64(nvp, &uint64) == EINVAL)
673*7c478bd9Sstevel@tonic-gate 					goto inval;
674*7c478bd9Sstevel@tonic-gate 				if (strcmp(name, "cr_preset") == 0)
675*7c478bd9Sstevel@tonic-gate 					set->ks_req[i].kr_preset = uint64;
676*7c478bd9Sstevel@tonic-gate 				break;
677*7c478bd9Sstevel@tonic-gate 			case DATA_TYPE_STRING:
678*7c478bd9Sstevel@tonic-gate 				if (nvpair_value_string(nvp, &string) == EINVAL)
679*7c478bd9Sstevel@tonic-gate 					goto inval;
680*7c478bd9Sstevel@tonic-gate 				if (strcmp(name, "cr_event") == 0)
681*7c478bd9Sstevel@tonic-gate 					(void) strncpy(set->ks_req[i].kr_event,
682*7c478bd9Sstevel@tonic-gate 					    string, CPC_MAX_EVENT_LEN);
683*7c478bd9Sstevel@tonic-gate 				break;
684*7c478bd9Sstevel@tonic-gate 			case DATA_TYPE_NVLIST:
685*7c478bd9Sstevel@tonic-gate 				if (strcmp(name, "cr_attr") != 0)
686*7c478bd9Sstevel@tonic-gate 					goto inval;
687*7c478bd9Sstevel@tonic-gate 				if (nvpair_value_nvlist(nvp, &attrs) == EINVAL)
688*7c478bd9Sstevel@tonic-gate 					goto inval;
689*7c478bd9Sstevel@tonic-gate 				nvp_attr = NULL;
690*7c478bd9Sstevel@tonic-gate 				/*
691*7c478bd9Sstevel@tonic-gate 				 * If the picnum has been specified as an
692*7c478bd9Sstevel@tonic-gate 				 * attribute, consume that attribute here and
693*7c478bd9Sstevel@tonic-gate 				 * remove it from the list of attributes.
694*7c478bd9Sstevel@tonic-gate 				 */
695*7c478bd9Sstevel@tonic-gate 				if (nvlist_lookup_uint64(attrs, "picnum",
696*7c478bd9Sstevel@tonic-gate 				    &uint64) == 0) {
697*7c478bd9Sstevel@tonic-gate 					if (nvlist_remove(attrs, "picnum",
698*7c478bd9Sstevel@tonic-gate 					    DATA_TYPE_UINT64) != 0)
699*7c478bd9Sstevel@tonic-gate 						panic("nvlist %p faulty",
700*7c478bd9Sstevel@tonic-gate 						    attrs);
701*7c478bd9Sstevel@tonic-gate 					set->ks_req[i].kr_picnum = uint64;
702*7c478bd9Sstevel@tonic-gate 				}
703*7c478bd9Sstevel@tonic-gate 
704*7c478bd9Sstevel@tonic-gate 				if ((set->ks_req[i].kr_nattrs =
705*7c478bd9Sstevel@tonic-gate 				    kcpc_nvlist_npairs(attrs)) == 0)
706*7c478bd9Sstevel@tonic-gate 					break;
707*7c478bd9Sstevel@tonic-gate 
708*7c478bd9Sstevel@tonic-gate 				if (set->ks_req[i].kr_nattrs > CPC_MAX_ATTRS)
709*7c478bd9Sstevel@tonic-gate 					goto inval;
710*7c478bd9Sstevel@tonic-gate 
711*7c478bd9Sstevel@tonic-gate 				set->ks_req[i].kr_attr =
712*7c478bd9Sstevel@tonic-gate 				    kmem_alloc(set->ks_req[i].kr_nattrs *
713*7c478bd9Sstevel@tonic-gate 				    sizeof (kcpc_attr_t), KM_SLEEP);
714*7c478bd9Sstevel@tonic-gate 				j = 0;
715*7c478bd9Sstevel@tonic-gate 
716*7c478bd9Sstevel@tonic-gate 				while ((nvp_attr = nvlist_next_nvpair(attrs,
717*7c478bd9Sstevel@tonic-gate 				    nvp_attr)) != NULL) {
718*7c478bd9Sstevel@tonic-gate 					attrp = &set->ks_req[i].kr_attr[j];
719*7c478bd9Sstevel@tonic-gate 
720*7c478bd9Sstevel@tonic-gate 					if (nvpair_type(nvp_attr) !=
721*7c478bd9Sstevel@tonic-gate 					    DATA_TYPE_UINT64)
722*7c478bd9Sstevel@tonic-gate 						goto inval;
723*7c478bd9Sstevel@tonic-gate 
724*7c478bd9Sstevel@tonic-gate 					(void) strncpy(attrp->ka_name,
725*7c478bd9Sstevel@tonic-gate 					    nvpair_name(nvp_attr),
726*7c478bd9Sstevel@tonic-gate 					    CPC_MAX_ATTR_LEN);
727*7c478bd9Sstevel@tonic-gate 
728*7c478bd9Sstevel@tonic-gate 					if (nvpair_value_uint64(nvp_attr,
729*7c478bd9Sstevel@tonic-gate 					    &(attrp->ka_val)) == EINVAL)
730*7c478bd9Sstevel@tonic-gate 						goto inval;
731*7c478bd9Sstevel@tonic-gate 					j++;
732*7c478bd9Sstevel@tonic-gate 				}
733*7c478bd9Sstevel@tonic-gate 				ASSERT(j == set->ks_req[i].kr_nattrs);
734*7c478bd9Sstevel@tonic-gate 			default:
735*7c478bd9Sstevel@tonic-gate 				break;
736*7c478bd9Sstevel@tonic-gate 			}
737*7c478bd9Sstevel@tonic-gate 		}
738*7c478bd9Sstevel@tonic-gate 	}
739*7c478bd9Sstevel@tonic-gate 
740*7c478bd9Sstevel@tonic-gate 	nvlist_free(nvl);
741*7c478bd9Sstevel@tonic-gate 	*inset = set;
742*7c478bd9Sstevel@tonic-gate 	return (0);
743*7c478bd9Sstevel@tonic-gate 
744*7c478bd9Sstevel@tonic-gate inval:
745*7c478bd9Sstevel@tonic-gate 	nvlist_free(nvl);
746*7c478bd9Sstevel@tonic-gate 	kcpc_free_set(set);
747*7c478bd9Sstevel@tonic-gate 	return (EINVAL);
748*7c478bd9Sstevel@tonic-gate }
749*7c478bd9Sstevel@tonic-gate 
750*7c478bd9Sstevel@tonic-gate /*
751*7c478bd9Sstevel@tonic-gate  * Count the number of nvpairs in the supplied nvlist.
752*7c478bd9Sstevel@tonic-gate  */
753*7c478bd9Sstevel@tonic-gate static uint32_t
754*7c478bd9Sstevel@tonic-gate kcpc_nvlist_npairs(nvlist_t *list)
755*7c478bd9Sstevel@tonic-gate {
756*7c478bd9Sstevel@tonic-gate 	nvpair_t *nvp = NULL;
757*7c478bd9Sstevel@tonic-gate 	uint32_t n = 0;
758*7c478bd9Sstevel@tonic-gate 
759*7c478bd9Sstevel@tonic-gate 	while ((nvp = nvlist_next_nvpair(list, nvp)) != NULL)
760*7c478bd9Sstevel@tonic-gate 		n++;
761*7c478bd9Sstevel@tonic-gate 
762*7c478bd9Sstevel@tonic-gate 	return (n);
763*7c478bd9Sstevel@tonic-gate }
764*7c478bd9Sstevel@tonic-gate 
765*7c478bd9Sstevel@tonic-gate /*
766*7c478bd9Sstevel@tonic-gate  * Performs sanity checks on the given set.
767*7c478bd9Sstevel@tonic-gate  * Returns 0 if the set checks out OK.
768*7c478bd9Sstevel@tonic-gate  * Returns a detailed error subcode, or -1 if there is no applicable subcode.
769*7c478bd9Sstevel@tonic-gate  */
770*7c478bd9Sstevel@tonic-gate static int
771*7c478bd9Sstevel@tonic-gate kcpc_verify_set(kcpc_set_t *set)
772*7c478bd9Sstevel@tonic-gate {
773*7c478bd9Sstevel@tonic-gate 	kcpc_request_t	*rp;
774*7c478bd9Sstevel@tonic-gate 	int		i;
775*7c478bd9Sstevel@tonic-gate 	uint64_t	bitmap = 0;
776*7c478bd9Sstevel@tonic-gate 	int		n;
777*7c478bd9Sstevel@tonic-gate 
778*7c478bd9Sstevel@tonic-gate 	if (set->ks_nreqs > cpc_ncounters)
779*7c478bd9Sstevel@tonic-gate 		return (-1);
780*7c478bd9Sstevel@tonic-gate 
781*7c478bd9Sstevel@tonic-gate 	if (CPC_SET_VALID_FLAGS(set->ks_flags) == 0)
782*7c478bd9Sstevel@tonic-gate 		return (-1);
783*7c478bd9Sstevel@tonic-gate 
784*7c478bd9Sstevel@tonic-gate 	for (i = 0; i < set->ks_nreqs; i++) {
785*7c478bd9Sstevel@tonic-gate 		rp = &set->ks_req[i];
786*7c478bd9Sstevel@tonic-gate 
787*7c478bd9Sstevel@tonic-gate 		/*
788*7c478bd9Sstevel@tonic-gate 		 * The following comparison must cast cpc_ncounters to an int,
789*7c478bd9Sstevel@tonic-gate 		 * because kr_picnum will be -1 if the request didn't explicitly
790*7c478bd9Sstevel@tonic-gate 		 * choose a PIC.
791*7c478bd9Sstevel@tonic-gate 		 */
792*7c478bd9Sstevel@tonic-gate 		if (rp->kr_picnum >= (int)cpc_ncounters)
793*7c478bd9Sstevel@tonic-gate 			return (CPC_INVALID_PICNUM);
794*7c478bd9Sstevel@tonic-gate 
795*7c478bd9Sstevel@tonic-gate 		/*
796*7c478bd9Sstevel@tonic-gate 		 * Of the pics whose physical picnum has been specified, make
797*7c478bd9Sstevel@tonic-gate 		 * sure each PIC appears only once in set.
798*7c478bd9Sstevel@tonic-gate 		 */
799*7c478bd9Sstevel@tonic-gate 		if ((n = set->ks_req[i].kr_picnum) != -1) {
800*7c478bd9Sstevel@tonic-gate 			if ((bitmap & (1 << n)) != 0)
801*7c478bd9Sstevel@tonic-gate 				return (-1);
802*7c478bd9Sstevel@tonic-gate 			bitmap |= (1 << n);
803*7c478bd9Sstevel@tonic-gate 		}
804*7c478bd9Sstevel@tonic-gate 
805*7c478bd9Sstevel@tonic-gate 		/*
806*7c478bd9Sstevel@tonic-gate 		 * Make sure the requested index falls within the range of all
807*7c478bd9Sstevel@tonic-gate 		 * requests.
808*7c478bd9Sstevel@tonic-gate 		 */
809*7c478bd9Sstevel@tonic-gate 		if (rp->kr_index < 0 || rp->kr_index >= set->ks_nreqs)
810*7c478bd9Sstevel@tonic-gate 			return (-1);
811*7c478bd9Sstevel@tonic-gate 
812*7c478bd9Sstevel@tonic-gate 		/*
813*7c478bd9Sstevel@tonic-gate 		 * Make sure there are no unknown flags.
814*7c478bd9Sstevel@tonic-gate 		 */
815*7c478bd9Sstevel@tonic-gate 		if (KCPC_REQ_VALID_FLAGS(rp->kr_flags) == 0)
816*7c478bd9Sstevel@tonic-gate 			return (CPC_REQ_INVALID_FLAGS);
817*7c478bd9Sstevel@tonic-gate 	}
818*7c478bd9Sstevel@tonic-gate 
819*7c478bd9Sstevel@tonic-gate 	return (0);
820*7c478bd9Sstevel@tonic-gate }
821*7c478bd9Sstevel@tonic-gate 
822*7c478bd9Sstevel@tonic-gate static struct cb_ops cb_ops = {
823*7c478bd9Sstevel@tonic-gate 	kcpc_open,
824*7c478bd9Sstevel@tonic-gate 	kcpc_close,
825*7c478bd9Sstevel@tonic-gate 	nodev,		/* strategy */
826*7c478bd9Sstevel@tonic-gate 	nodev,		/* print */
827*7c478bd9Sstevel@tonic-gate 	nodev,		/* dump */
828*7c478bd9Sstevel@tonic-gate 	nodev,		/* read */
829*7c478bd9Sstevel@tonic-gate 	nodev,		/* write */
830*7c478bd9Sstevel@tonic-gate 	kcpc_ioctl,
831*7c478bd9Sstevel@tonic-gate 	nodev,		/* devmap */
832*7c478bd9Sstevel@tonic-gate 	nodev,		/* mmap */
833*7c478bd9Sstevel@tonic-gate 	nodev,		/* segmap */
834*7c478bd9Sstevel@tonic-gate 	nochpoll,	/* poll */
835*7c478bd9Sstevel@tonic-gate 	ddi_prop_op,
836*7c478bd9Sstevel@tonic-gate 	NULL,
837*7c478bd9Sstevel@tonic-gate 	D_NEW | D_MP
838*7c478bd9Sstevel@tonic-gate };
839*7c478bd9Sstevel@tonic-gate 
840*7c478bd9Sstevel@tonic-gate /*ARGSUSED*/
841*7c478bd9Sstevel@tonic-gate static int
842*7c478bd9Sstevel@tonic-gate kcpc_probe(dev_info_t *devi)
843*7c478bd9Sstevel@tonic-gate {
844*7c478bd9Sstevel@tonic-gate 	return (DDI_PROBE_SUCCESS);
845*7c478bd9Sstevel@tonic-gate }
846*7c478bd9Sstevel@tonic-gate 
847*7c478bd9Sstevel@tonic-gate static dev_info_t *kcpc_devi;
848*7c478bd9Sstevel@tonic-gate 
849*7c478bd9Sstevel@tonic-gate static int
850*7c478bd9Sstevel@tonic-gate kcpc_attach(dev_info_t *devi, ddi_attach_cmd_t cmd)
851*7c478bd9Sstevel@tonic-gate {
852*7c478bd9Sstevel@tonic-gate 	if (cmd != DDI_ATTACH)
853*7c478bd9Sstevel@tonic-gate 		return (DDI_FAILURE);
854*7c478bd9Sstevel@tonic-gate 	kcpc_devi = devi;
855*7c478bd9Sstevel@tonic-gate 	return (ddi_create_minor_node(devi, "shared", S_IFCHR,
856*7c478bd9Sstevel@tonic-gate 	    KCPC_MINOR_SHARED, DDI_PSEUDO, 0));
857*7c478bd9Sstevel@tonic-gate }
858*7c478bd9Sstevel@tonic-gate 
859*7c478bd9Sstevel@tonic-gate /*ARGSUSED*/
860*7c478bd9Sstevel@tonic-gate static int
861*7c478bd9Sstevel@tonic-gate kcpc_getinfo(dev_info_t *devi, ddi_info_cmd_t cmd, void *arg, void **result)
862*7c478bd9Sstevel@tonic-gate {
863*7c478bd9Sstevel@tonic-gate 	switch (cmd) {
864*7c478bd9Sstevel@tonic-gate 	case DDI_INFO_DEVT2DEVINFO:
865*7c478bd9Sstevel@tonic-gate 		switch (getminor((dev_t)arg)) {
866*7c478bd9Sstevel@tonic-gate 		case KCPC_MINOR_SHARED:
867*7c478bd9Sstevel@tonic-gate 			*result = kcpc_devi;
868*7c478bd9Sstevel@tonic-gate 			return (DDI_SUCCESS);
869*7c478bd9Sstevel@tonic-gate 		default:
870*7c478bd9Sstevel@tonic-gate 			break;
871*7c478bd9Sstevel@tonic-gate 		}
872*7c478bd9Sstevel@tonic-gate 		break;
873*7c478bd9Sstevel@tonic-gate 	case DDI_INFO_DEVT2INSTANCE:
874*7c478bd9Sstevel@tonic-gate 		*result = 0;
875*7c478bd9Sstevel@tonic-gate 		return (DDI_SUCCESS);
876*7c478bd9Sstevel@tonic-gate 	default:
877*7c478bd9Sstevel@tonic-gate 		break;
878*7c478bd9Sstevel@tonic-gate 	}
879*7c478bd9Sstevel@tonic-gate 
880*7c478bd9Sstevel@tonic-gate 	return (DDI_FAILURE);
881*7c478bd9Sstevel@tonic-gate }
882*7c478bd9Sstevel@tonic-gate 
883*7c478bd9Sstevel@tonic-gate static struct dev_ops dev_ops = {
884*7c478bd9Sstevel@tonic-gate 	DEVO_REV,
885*7c478bd9Sstevel@tonic-gate 	0,
886*7c478bd9Sstevel@tonic-gate 	kcpc_getinfo,
887*7c478bd9Sstevel@tonic-gate 	nulldev,		/* identify */
888*7c478bd9Sstevel@tonic-gate 	kcpc_probe,
889*7c478bd9Sstevel@tonic-gate 	kcpc_attach,
890*7c478bd9Sstevel@tonic-gate 	nodev,			/* detach */
891*7c478bd9Sstevel@tonic-gate 	nodev,			/* reset */
892*7c478bd9Sstevel@tonic-gate 	&cb_ops,
893*7c478bd9Sstevel@tonic-gate 	(struct bus_ops *)0
894*7c478bd9Sstevel@tonic-gate };
895*7c478bd9Sstevel@tonic-gate 
896*7c478bd9Sstevel@tonic-gate static struct modldrv modldrv = {
897*7c478bd9Sstevel@tonic-gate 	&mod_driverops,
898*7c478bd9Sstevel@tonic-gate 	"cpc sampling driver v%I%",
899*7c478bd9Sstevel@tonic-gate 	&dev_ops
900*7c478bd9Sstevel@tonic-gate };
901*7c478bd9Sstevel@tonic-gate 
902*7c478bd9Sstevel@tonic-gate static struct sysent cpc_sysent = {
903*7c478bd9Sstevel@tonic-gate 	5,
904*7c478bd9Sstevel@tonic-gate 	SE_NOUNLOAD | SE_ARGC | SE_32RVAL1,
905*7c478bd9Sstevel@tonic-gate 	cpc
906*7c478bd9Sstevel@tonic-gate };
907*7c478bd9Sstevel@tonic-gate 
908*7c478bd9Sstevel@tonic-gate static struct modlsys modlsys = {
909*7c478bd9Sstevel@tonic-gate 	&mod_syscallops,
910*7c478bd9Sstevel@tonic-gate 	"cpc sampling system call",
911*7c478bd9Sstevel@tonic-gate 	&cpc_sysent
912*7c478bd9Sstevel@tonic-gate };
913*7c478bd9Sstevel@tonic-gate 
914*7c478bd9Sstevel@tonic-gate #ifdef _SYSCALL32_IMPL
915*7c478bd9Sstevel@tonic-gate static struct modlsys modlsys32 = {
916*7c478bd9Sstevel@tonic-gate 	&mod_syscallops32,
917*7c478bd9Sstevel@tonic-gate 	"32-bit cpc sampling system call",
918*7c478bd9Sstevel@tonic-gate 	&cpc_sysent
919*7c478bd9Sstevel@tonic-gate };
920*7c478bd9Sstevel@tonic-gate #endif
921*7c478bd9Sstevel@tonic-gate 
922*7c478bd9Sstevel@tonic-gate static struct modlinkage modl = {
923*7c478bd9Sstevel@tonic-gate 	MODREV_1,
924*7c478bd9Sstevel@tonic-gate 	&modldrv,
925*7c478bd9Sstevel@tonic-gate 	&modlsys,
926*7c478bd9Sstevel@tonic-gate #ifdef _SYSCALL32_IMPL
927*7c478bd9Sstevel@tonic-gate 	&modlsys32,
928*7c478bd9Sstevel@tonic-gate #endif
929*7c478bd9Sstevel@tonic-gate };
930*7c478bd9Sstevel@tonic-gate 
931*7c478bd9Sstevel@tonic-gate static void
932*7c478bd9Sstevel@tonic-gate kcpc_init(void)
933*7c478bd9Sstevel@tonic-gate {
934*7c478bd9Sstevel@tonic-gate 	long hash;
935*7c478bd9Sstevel@tonic-gate 
936*7c478bd9Sstevel@tonic-gate 	rw_init(&kcpc_cpuctx_lock, NULL, RW_DEFAULT, NULL);
937*7c478bd9Sstevel@tonic-gate 	for (hash = 0; hash < CPC_HASH_BUCKETS; hash++)
938*7c478bd9Sstevel@tonic-gate 		mutex_init(&kcpc_ctx_llock[hash],
939*7c478bd9Sstevel@tonic-gate 		    NULL, MUTEX_DRIVER, (void *)(uintptr_t)15);
940*7c478bd9Sstevel@tonic-gate }
941*7c478bd9Sstevel@tonic-gate 
942*7c478bd9Sstevel@tonic-gate static void
943*7c478bd9Sstevel@tonic-gate kcpc_fini(void)
944*7c478bd9Sstevel@tonic-gate {
945*7c478bd9Sstevel@tonic-gate 	long hash;
946*7c478bd9Sstevel@tonic-gate 
947*7c478bd9Sstevel@tonic-gate 	for (hash = 0; hash < CPC_HASH_BUCKETS; hash++)
948*7c478bd9Sstevel@tonic-gate 		mutex_destroy(&kcpc_ctx_llock[hash]);
949*7c478bd9Sstevel@tonic-gate 	rw_destroy(&kcpc_cpuctx_lock);
950*7c478bd9Sstevel@tonic-gate }
951*7c478bd9Sstevel@tonic-gate 
952*7c478bd9Sstevel@tonic-gate int
953*7c478bd9Sstevel@tonic-gate _init(void)
954*7c478bd9Sstevel@tonic-gate {
955*7c478bd9Sstevel@tonic-gate 	int ret;
956*7c478bd9Sstevel@tonic-gate 
957*7c478bd9Sstevel@tonic-gate 	if (kcpc_hw_load_pcbe() != 0)
958*7c478bd9Sstevel@tonic-gate 		return (ENOTSUP);
959*7c478bd9Sstevel@tonic-gate 
960*7c478bd9Sstevel@tonic-gate 	kcpc_init();
961*7c478bd9Sstevel@tonic-gate 	if ((ret = mod_install(&modl)) != 0)
962*7c478bd9Sstevel@tonic-gate 		kcpc_fini();
963*7c478bd9Sstevel@tonic-gate 	return (ret);
964*7c478bd9Sstevel@tonic-gate }
965*7c478bd9Sstevel@tonic-gate 
966*7c478bd9Sstevel@tonic-gate int
967*7c478bd9Sstevel@tonic-gate _fini(void)
968*7c478bd9Sstevel@tonic-gate {
969*7c478bd9Sstevel@tonic-gate 	int ret;
970*7c478bd9Sstevel@tonic-gate 
971*7c478bd9Sstevel@tonic-gate 	if ((ret = mod_remove(&modl)) == 0)
972*7c478bd9Sstevel@tonic-gate 		kcpc_fini();
973*7c478bd9Sstevel@tonic-gate 	return (ret);
974*7c478bd9Sstevel@tonic-gate }
975*7c478bd9Sstevel@tonic-gate 
976*7c478bd9Sstevel@tonic-gate int
977*7c478bd9Sstevel@tonic-gate _info(struct modinfo *mi)
978*7c478bd9Sstevel@tonic-gate {
979*7c478bd9Sstevel@tonic-gate 	return (mod_info(&modl, mi));
980*7c478bd9Sstevel@tonic-gate }
981