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