xref: /titanic_51/usr/src/uts/common/io/cpc.c (revision 4568bee7eb526744ad873b26264367b35234c9f4)
17c478bd9Sstevel@tonic-gate /*
27c478bd9Sstevel@tonic-gate  * CDDL HEADER START
37c478bd9Sstevel@tonic-gate  *
47c478bd9Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
5*4568bee7Strevtom  * Common Development and Distribution License (the "License").
6*4568bee7Strevtom  * You may not use this file except in compliance with the License.
77c478bd9Sstevel@tonic-gate  *
87c478bd9Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
97c478bd9Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
107c478bd9Sstevel@tonic-gate  * See the License for the specific language governing permissions
117c478bd9Sstevel@tonic-gate  * and limitations under the License.
127c478bd9Sstevel@tonic-gate  *
137c478bd9Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
147c478bd9Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
157c478bd9Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
167c478bd9Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
177c478bd9Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
187c478bd9Sstevel@tonic-gate  *
197c478bd9Sstevel@tonic-gate  * CDDL HEADER END
207c478bd9Sstevel@tonic-gate  */
217c478bd9Sstevel@tonic-gate /*
22*4568bee7Strevtom  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
237c478bd9Sstevel@tonic-gate  * Use is subject to license terms.
247c478bd9Sstevel@tonic-gate  */
257c478bd9Sstevel@tonic-gate 
267c478bd9Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
277c478bd9Sstevel@tonic-gate 
287c478bd9Sstevel@tonic-gate /*
297c478bd9Sstevel@tonic-gate  * CPU Performance Counter system calls and device driver.
307c478bd9Sstevel@tonic-gate  *
317c478bd9Sstevel@tonic-gate  * This module uses a combination of thread context operators, and
327c478bd9Sstevel@tonic-gate  * thread-specific data to export CPU performance counters
337c478bd9Sstevel@tonic-gate  * via both a system call and a driver interface.
347c478bd9Sstevel@tonic-gate  *
357c478bd9Sstevel@tonic-gate  * There are three access methods exported - the 'shared' device
367c478bd9Sstevel@tonic-gate  * and the 'private' and 'agent' variants of the system call.
377c478bd9Sstevel@tonic-gate  *
387c478bd9Sstevel@tonic-gate  * The shared device treats the performance counter registers as
397c478bd9Sstevel@tonic-gate  * a processor metric, regardless of the work scheduled on them.
407c478bd9Sstevel@tonic-gate  * The private system call treats the performance counter registers
417c478bd9Sstevel@tonic-gate  * as a property of a single lwp.  This is achieved by using the
427c478bd9Sstevel@tonic-gate  * thread context operators to virtualize the contents of the
437c478bd9Sstevel@tonic-gate  * performance counter registers between lwps.
447c478bd9Sstevel@tonic-gate  *
457c478bd9Sstevel@tonic-gate  * The agent method is like the private method, except that it must
467c478bd9Sstevel@tonic-gate  * be accessed via /proc's agent lwp to allow the counter context of
477c478bd9Sstevel@tonic-gate  * other threads to be examined safely.
487c478bd9Sstevel@tonic-gate  *
497c478bd9Sstevel@tonic-gate  * The shared usage fundamentally conflicts with the agent and private usage;
507c478bd9Sstevel@tonic-gate  * almost all of the complexity of the module is needed to allow these two
517c478bd9Sstevel@tonic-gate  * models to co-exist in a reasonable way.
527c478bd9Sstevel@tonic-gate  */
537c478bd9Sstevel@tonic-gate 
547c478bd9Sstevel@tonic-gate #include <sys/types.h>
557c478bd9Sstevel@tonic-gate #include <sys/file.h>
567c478bd9Sstevel@tonic-gate #include <sys/errno.h>
577c478bd9Sstevel@tonic-gate #include <sys/open.h>
587c478bd9Sstevel@tonic-gate #include <sys/cred.h>
597c478bd9Sstevel@tonic-gate #include <sys/conf.h>
607c478bd9Sstevel@tonic-gate #include <sys/stat.h>
617c478bd9Sstevel@tonic-gate #include <sys/processor.h>
627c478bd9Sstevel@tonic-gate #include <sys/cpuvar.h>
637c478bd9Sstevel@tonic-gate #include <sys/disp.h>
647c478bd9Sstevel@tonic-gate #include <sys/kmem.h>
657c478bd9Sstevel@tonic-gate #include <sys/modctl.h>
667c478bd9Sstevel@tonic-gate #include <sys/ddi.h>
677c478bd9Sstevel@tonic-gate #include <sys/sunddi.h>
687c478bd9Sstevel@tonic-gate #include <sys/nvpair.h>
697c478bd9Sstevel@tonic-gate #include <sys/policy.h>
707c478bd9Sstevel@tonic-gate #include <sys/machsystm.h>
717c478bd9Sstevel@tonic-gate #include <sys/cpc_impl.h>
727c478bd9Sstevel@tonic-gate #include <sys/cpc_pcbe.h>
737c478bd9Sstevel@tonic-gate #include <sys/kcpc.h>
747c478bd9Sstevel@tonic-gate 
757c478bd9Sstevel@tonic-gate static int kcpc_copyin_set(kcpc_set_t **set, void *ubuf, size_t len);
767c478bd9Sstevel@tonic-gate static int kcpc_verify_set(kcpc_set_t *set);
777c478bd9Sstevel@tonic-gate static uint32_t kcpc_nvlist_npairs(nvlist_t *list);
787c478bd9Sstevel@tonic-gate 
797c478bd9Sstevel@tonic-gate /*
807c478bd9Sstevel@tonic-gate  * Generic attributes supported regardless of processor.
817c478bd9Sstevel@tonic-gate  */
827c478bd9Sstevel@tonic-gate 
837c478bd9Sstevel@tonic-gate #define	ATTRLIST "picnum"
847c478bd9Sstevel@tonic-gate #define	SEPARATOR ","
857c478bd9Sstevel@tonic-gate 
867c478bd9Sstevel@tonic-gate /*
877c478bd9Sstevel@tonic-gate  * System call to access CPU performance counters.
887c478bd9Sstevel@tonic-gate  */
897c478bd9Sstevel@tonic-gate static int
907c478bd9Sstevel@tonic-gate cpc(int cmd, id_t lwpid, void *udata1, void *udata2, void *udata3)
917c478bd9Sstevel@tonic-gate {
927c478bd9Sstevel@tonic-gate 	kthread_t	*t;
937c478bd9Sstevel@tonic-gate 	int		error;
947c478bd9Sstevel@tonic-gate 	int		size;
957c478bd9Sstevel@tonic-gate 	const char	*str;
967c478bd9Sstevel@tonic-gate 	int		code;
977c478bd9Sstevel@tonic-gate 
987c478bd9Sstevel@tonic-gate 	/*
997c478bd9Sstevel@tonic-gate 	 * This CPC syscall should only be loaded if it found a PCBE to use.
1007c478bd9Sstevel@tonic-gate 	 */
1017c478bd9Sstevel@tonic-gate 	ASSERT(pcbe_ops != NULL);
1027c478bd9Sstevel@tonic-gate 
1037c478bd9Sstevel@tonic-gate 	if (curproc->p_agenttp == curthread) {
1047c478bd9Sstevel@tonic-gate 		/*
1057c478bd9Sstevel@tonic-gate 		 * Only if /proc is invoking this system call from
1067c478bd9Sstevel@tonic-gate 		 * the agent thread do we allow the caller to examine
1077c478bd9Sstevel@tonic-gate 		 * the contexts of other lwps in the process.  And
1087c478bd9Sstevel@tonic-gate 		 * because we know we're the agent, we know we don't
1097c478bd9Sstevel@tonic-gate 		 * have to grab p_lock because no-one else can change
1107c478bd9Sstevel@tonic-gate 		 * the state of the process.
1117c478bd9Sstevel@tonic-gate 		 */
1127c478bd9Sstevel@tonic-gate 		if ((t = idtot(curproc, lwpid)) == NULL || t == curthread)
1137c478bd9Sstevel@tonic-gate 			return (set_errno(ESRCH));
1147c478bd9Sstevel@tonic-gate 		ASSERT(t->t_tid == lwpid && ttolwp(t) != NULL);
1157c478bd9Sstevel@tonic-gate 	} else
1167c478bd9Sstevel@tonic-gate 		t = curthread;
1177c478bd9Sstevel@tonic-gate 
1187c478bd9Sstevel@tonic-gate 	if (t->t_cpc_set == NULL && (cmd == CPC_SAMPLE || cmd == CPC_RELE))
1197c478bd9Sstevel@tonic-gate 		return (set_errno(EINVAL));
1207c478bd9Sstevel@tonic-gate 
1217c478bd9Sstevel@tonic-gate 	switch (cmd) {
1227c478bd9Sstevel@tonic-gate 	case CPC_BIND:
1237c478bd9Sstevel@tonic-gate 		/*
1247c478bd9Sstevel@tonic-gate 		 * udata1 = pointer to packed nvlist buffer
1257c478bd9Sstevel@tonic-gate 		 * udata2 = size of packed nvlist buffer
1267c478bd9Sstevel@tonic-gate 		 * udata3 = User addr to return error subcode in.
1277c478bd9Sstevel@tonic-gate 		 */
1287c478bd9Sstevel@tonic-gate 
1297c478bd9Sstevel@tonic-gate 		rw_enter(&kcpc_cpuctx_lock, RW_READER);
1307c478bd9Sstevel@tonic-gate 		if (kcpc_cpuctx) {
1317c478bd9Sstevel@tonic-gate 			rw_exit(&kcpc_cpuctx_lock);
1327c478bd9Sstevel@tonic-gate 			return (set_errno(EAGAIN));
1337c478bd9Sstevel@tonic-gate 		}
1347c478bd9Sstevel@tonic-gate 
1357c478bd9Sstevel@tonic-gate 		if (kcpc_hw_lwp_hook() != 0) {
1367c478bd9Sstevel@tonic-gate 			rw_exit(&kcpc_cpuctx_lock);
1377c478bd9Sstevel@tonic-gate 			return (set_errno(EACCES));
1387c478bd9Sstevel@tonic-gate 		}
1397c478bd9Sstevel@tonic-gate 
1407c478bd9Sstevel@tonic-gate 		/*
1417c478bd9Sstevel@tonic-gate 		 * An LWP may only have one set bound to it at a time; if there
1427c478bd9Sstevel@tonic-gate 		 * is a set bound to this LWP already, we unbind it here.
1437c478bd9Sstevel@tonic-gate 		 */
1447c478bd9Sstevel@tonic-gate 		if (t->t_cpc_set != NULL)
1457c478bd9Sstevel@tonic-gate 			(void) kcpc_unbind(t->t_cpc_set);
1467c478bd9Sstevel@tonic-gate 		ASSERT(t->t_cpc_set == NULL);
1477c478bd9Sstevel@tonic-gate 
1487c478bd9Sstevel@tonic-gate 		if ((error = kcpc_copyin_set(&t->t_cpc_set, udata1,
1497c478bd9Sstevel@tonic-gate 		    (size_t)udata2)) != 0) {
1507c478bd9Sstevel@tonic-gate 			rw_exit(&kcpc_cpuctx_lock);
1517c478bd9Sstevel@tonic-gate 			return (set_errno(error));
1527c478bd9Sstevel@tonic-gate 		}
1537c478bd9Sstevel@tonic-gate 
1547c478bd9Sstevel@tonic-gate 		if ((error = kcpc_verify_set(t->t_cpc_set)) != 0) {
1557c478bd9Sstevel@tonic-gate 			rw_exit(&kcpc_cpuctx_lock);
1567c478bd9Sstevel@tonic-gate 			kcpc_free_set(t->t_cpc_set);
1577c478bd9Sstevel@tonic-gate 			t->t_cpc_set = NULL;
1587c478bd9Sstevel@tonic-gate 			if (copyout(&error, udata3, sizeof (error)) == -1)
1597c478bd9Sstevel@tonic-gate 				return (set_errno(EFAULT));
1607c478bd9Sstevel@tonic-gate 			return (set_errno(EINVAL));
1617c478bd9Sstevel@tonic-gate 		}
1627c478bd9Sstevel@tonic-gate 
1637c478bd9Sstevel@tonic-gate 		if ((error = kcpc_bind_thread(t->t_cpc_set, t, &code)) != 0) {
1647c478bd9Sstevel@tonic-gate 			rw_exit(&kcpc_cpuctx_lock);
1657c478bd9Sstevel@tonic-gate 			kcpc_free_set(t->t_cpc_set);
1667c478bd9Sstevel@tonic-gate 			t->t_cpc_set = NULL;
1677c478bd9Sstevel@tonic-gate 			/*
1687c478bd9Sstevel@tonic-gate 			 * EINVAL and EACCES are the only errors with more
1697c478bd9Sstevel@tonic-gate 			 * specific subcodes.
1707c478bd9Sstevel@tonic-gate 			 */
1717c478bd9Sstevel@tonic-gate 			if ((error == EINVAL || error == EACCES) &&
1727c478bd9Sstevel@tonic-gate 			    copyout(&code, udata3, sizeof (code)) == -1)
1737c478bd9Sstevel@tonic-gate 				return (set_errno(EFAULT));
1747c478bd9Sstevel@tonic-gate 			return (set_errno(error));
1757c478bd9Sstevel@tonic-gate 		}
1767c478bd9Sstevel@tonic-gate 
1777c478bd9Sstevel@tonic-gate 		rw_exit(&kcpc_cpuctx_lock);
1787c478bd9Sstevel@tonic-gate 		return (0);
1797c478bd9Sstevel@tonic-gate 	case CPC_SAMPLE:
1807c478bd9Sstevel@tonic-gate 		/*
1817c478bd9Sstevel@tonic-gate 		 * udata1 = pointer to user's buffer
1827c478bd9Sstevel@tonic-gate 		 * udata2 = pointer to user's hrtime
1837c478bd9Sstevel@tonic-gate 		 * udata3 = pointer to user's tick
1847c478bd9Sstevel@tonic-gate 		 */
1857c478bd9Sstevel@tonic-gate 		/*
1867c478bd9Sstevel@tonic-gate 		 * We only allow thread-bound sets to be sampled via the
1877c478bd9Sstevel@tonic-gate 		 * syscall, so if this set has a CPU-bound context, return an
1887c478bd9Sstevel@tonic-gate 		 * error.
1897c478bd9Sstevel@tonic-gate 		 */
1907c478bd9Sstevel@tonic-gate 		if (t->t_cpc_set->ks_ctx->kc_cpuid != -1)
1917c478bd9Sstevel@tonic-gate 			return (set_errno(EINVAL));
1927c478bd9Sstevel@tonic-gate 		if ((error = kcpc_sample(t->t_cpc_set, udata1, udata2,
1937c478bd9Sstevel@tonic-gate 		    udata3)) != 0)
1947c478bd9Sstevel@tonic-gate 			return (set_errno(error));
1957c478bd9Sstevel@tonic-gate 
1967c478bd9Sstevel@tonic-gate 		return (0);
1977c478bd9Sstevel@tonic-gate 	case CPC_PRESET:
1987c478bd9Sstevel@tonic-gate 	case CPC_RESTART:
1997c478bd9Sstevel@tonic-gate 		/*
2007c478bd9Sstevel@tonic-gate 		 * These are valid only if this lwp has a bound set.
2017c478bd9Sstevel@tonic-gate 		 */
2027c478bd9Sstevel@tonic-gate 		if (t->t_cpc_set == NULL)
2037c478bd9Sstevel@tonic-gate 			return (set_errno(EINVAL));
2047c478bd9Sstevel@tonic-gate 		if (cmd == CPC_PRESET) {
2057c478bd9Sstevel@tonic-gate 			/*
2067c478bd9Sstevel@tonic-gate 			 * The preset is shipped up to us from userland in two
2077c478bd9Sstevel@tonic-gate 			 * parts. This lets us handle 64-bit values from 32-bit
2087c478bd9Sstevel@tonic-gate 			 * and 64-bit applications in the same manner.
2097c478bd9Sstevel@tonic-gate 			 *
2107c478bd9Sstevel@tonic-gate 			 * udata1 = index of request to preset
2117c478bd9Sstevel@tonic-gate 			 * udata2 = new 64-bit preset (most sig. 32 bits)
2127c478bd9Sstevel@tonic-gate 			 * udata3 = new 64-bit preset (least sig. 32 bits)
2137c478bd9Sstevel@tonic-gate 			 */
2147c478bd9Sstevel@tonic-gate 			if ((error = kcpc_preset(t->t_cpc_set, (intptr_t)udata1,
2157c478bd9Sstevel@tonic-gate 			    ((uint64_t)(uintptr_t)udata2 << 32ULL) |
2167c478bd9Sstevel@tonic-gate 			    (uint64_t)(uintptr_t)udata3)) != 0)
2177c478bd9Sstevel@tonic-gate 				return (set_errno(error));
2187c478bd9Sstevel@tonic-gate 		} else {
2197c478bd9Sstevel@tonic-gate 			/*
2207c478bd9Sstevel@tonic-gate 			 * udata[1-3] = unused
2217c478bd9Sstevel@tonic-gate 			 */
2227c478bd9Sstevel@tonic-gate 			if ((error = kcpc_restart(t->t_cpc_set)) != 0)
2237c478bd9Sstevel@tonic-gate 				return (set_errno(error));
2247c478bd9Sstevel@tonic-gate 		}
2257c478bd9Sstevel@tonic-gate 		return (0);
2267c478bd9Sstevel@tonic-gate 	case CPC_ENABLE:
2277c478bd9Sstevel@tonic-gate 	case CPC_DISABLE:
2287c478bd9Sstevel@tonic-gate 		udata1 = 0;
2297c478bd9Sstevel@tonic-gate 		/*FALLTHROUGH*/
2307c478bd9Sstevel@tonic-gate 	case CPC_USR_EVENTS:
2317c478bd9Sstevel@tonic-gate 	case CPC_SYS_EVENTS:
2327c478bd9Sstevel@tonic-gate 		if (t != curthread || t->t_cpc_set == NULL)
2337c478bd9Sstevel@tonic-gate 			return (set_errno(EINVAL));
2347c478bd9Sstevel@tonic-gate 		/*
2357c478bd9Sstevel@tonic-gate 		 * Provided for backwards compatibility with CPCv1.
2367c478bd9Sstevel@tonic-gate 		 *
2377c478bd9Sstevel@tonic-gate 		 * Stop the counters and record the current counts. Use the
2387c478bd9Sstevel@tonic-gate 		 * counts as the preset to rebind a new set with the requests
2397c478bd9Sstevel@tonic-gate 		 * reconfigured as requested.
2407c478bd9Sstevel@tonic-gate 		 *
2417c478bd9Sstevel@tonic-gate 		 * udata1: 1 == enable; 0 == disable
2427c478bd9Sstevel@tonic-gate 		 * udata{2,3}: unused
2437c478bd9Sstevel@tonic-gate 		 */
2447c478bd9Sstevel@tonic-gate 		rw_enter(&kcpc_cpuctx_lock, RW_READER);
2457c478bd9Sstevel@tonic-gate 		if ((error = kcpc_enable(t,
2467c478bd9Sstevel@tonic-gate 		    cmd, (int)(uintptr_t)udata1)) != 0) {
2477c478bd9Sstevel@tonic-gate 			rw_exit(&kcpc_cpuctx_lock);
2487c478bd9Sstevel@tonic-gate 			return (set_errno(error));
2497c478bd9Sstevel@tonic-gate 		}
2507c478bd9Sstevel@tonic-gate 		rw_exit(&kcpc_cpuctx_lock);
2517c478bd9Sstevel@tonic-gate 		return (0);
2527c478bd9Sstevel@tonic-gate 	case CPC_NPIC:
2537c478bd9Sstevel@tonic-gate 		return (cpc_ncounters);
2547c478bd9Sstevel@tonic-gate 	case CPC_CAPS:
2557c478bd9Sstevel@tonic-gate 		return (pcbe_ops->pcbe_caps);
2567c478bd9Sstevel@tonic-gate 	case CPC_EVLIST_SIZE:
2577c478bd9Sstevel@tonic-gate 	case CPC_LIST_EVENTS:
2587c478bd9Sstevel@tonic-gate 		/*
2597c478bd9Sstevel@tonic-gate 		 * udata1 = pointer to user's int or buffer
2607c478bd9Sstevel@tonic-gate 		 * udata2 = picnum
2617c478bd9Sstevel@tonic-gate 		 * udata3 = unused
2627c478bd9Sstevel@tonic-gate 		 */
2637c478bd9Sstevel@tonic-gate 		if ((uintptr_t)udata2 >= cpc_ncounters)
2647c478bd9Sstevel@tonic-gate 			return (set_errno(EINVAL));
2657c478bd9Sstevel@tonic-gate 
2667c478bd9Sstevel@tonic-gate 		size = strlen(
2677c478bd9Sstevel@tonic-gate 		    pcbe_ops->pcbe_list_events((uintptr_t)udata2)) + 1;
2687c478bd9Sstevel@tonic-gate 
2697c478bd9Sstevel@tonic-gate 		if (cmd == CPC_EVLIST_SIZE) {
2707c478bd9Sstevel@tonic-gate 			if (suword32(udata1, size) == -1)
2717c478bd9Sstevel@tonic-gate 				return (set_errno(EFAULT));
2727c478bd9Sstevel@tonic-gate 		} else {
2737c478bd9Sstevel@tonic-gate 			if (copyout(
2747c478bd9Sstevel@tonic-gate 			    pcbe_ops->pcbe_list_events((uintptr_t)udata2),
2757c478bd9Sstevel@tonic-gate 			    udata1, size) == -1)
2767c478bd9Sstevel@tonic-gate 				return (set_errno(EFAULT));
2777c478bd9Sstevel@tonic-gate 		}
2787c478bd9Sstevel@tonic-gate 		return (0);
2797c478bd9Sstevel@tonic-gate 	case CPC_ATTRLIST_SIZE:
2807c478bd9Sstevel@tonic-gate 	case CPC_LIST_ATTRS:
2817c478bd9Sstevel@tonic-gate 		/*
2827c478bd9Sstevel@tonic-gate 		 * udata1 = pointer to user's int or buffer
2837c478bd9Sstevel@tonic-gate 		 * udata2 = unused
2847c478bd9Sstevel@tonic-gate 		 * udata3 = unused
2857c478bd9Sstevel@tonic-gate 		 *
2867c478bd9Sstevel@tonic-gate 		 * attrlist size is length of PCBE-supported attributes, plus
2877c478bd9Sstevel@tonic-gate 		 * room for "picnum\0" plus an optional ',' separator char.
2887c478bd9Sstevel@tonic-gate 		 */
2897c478bd9Sstevel@tonic-gate 		str = pcbe_ops->pcbe_list_attrs();
2907c478bd9Sstevel@tonic-gate 		size = strlen(str) + sizeof (SEPARATOR ATTRLIST) + 1;
2917c478bd9Sstevel@tonic-gate 		if (str[0] != '\0')
2927c478bd9Sstevel@tonic-gate 			/*
2937c478bd9Sstevel@tonic-gate 			 * A ',' separator character is necessary.
2947c478bd9Sstevel@tonic-gate 			 */
2957c478bd9Sstevel@tonic-gate 			size += 1;
2967c478bd9Sstevel@tonic-gate 
2977c478bd9Sstevel@tonic-gate 		if (cmd == CPC_ATTRLIST_SIZE) {
2987c478bd9Sstevel@tonic-gate 			if (suword32(udata1, size) == -1)
2997c478bd9Sstevel@tonic-gate 				return (set_errno(EFAULT));
3007c478bd9Sstevel@tonic-gate 		} else {
3017c478bd9Sstevel@tonic-gate 			/*
3027c478bd9Sstevel@tonic-gate 			 * Copyout the PCBE attributes, and then append the
3037c478bd9Sstevel@tonic-gate 			 * generic attribute list (with separator if necessary).
3047c478bd9Sstevel@tonic-gate 			 */
3057c478bd9Sstevel@tonic-gate 			if (copyout(str, udata1, strlen(str)) == -1)
3067c478bd9Sstevel@tonic-gate 				return (set_errno(EFAULT));
3077c478bd9Sstevel@tonic-gate 			if (str[0] != '\0') {
3087c478bd9Sstevel@tonic-gate 				if (copyout(SEPARATOR ATTRLIST,
3097c478bd9Sstevel@tonic-gate 				    ((char *)udata1) + strlen(str),
3107c478bd9Sstevel@tonic-gate 				    strlen(SEPARATOR ATTRLIST) + 1)
3117c478bd9Sstevel@tonic-gate 				    == -1)
3127c478bd9Sstevel@tonic-gate 					return (set_errno(EFAULT));
3137c478bd9Sstevel@tonic-gate 			} else
3147c478bd9Sstevel@tonic-gate 				if (copyout(ATTRLIST,
3157c478bd9Sstevel@tonic-gate 				    (char *)udata1 + strlen(str),
3167c478bd9Sstevel@tonic-gate 				    strlen(ATTRLIST) + 1) == -1)
3177c478bd9Sstevel@tonic-gate 					return (set_errno(EFAULT));
3187c478bd9Sstevel@tonic-gate 		}
3197c478bd9Sstevel@tonic-gate 		return (0);
3207c478bd9Sstevel@tonic-gate 	case CPC_IMPL_NAME:
3217c478bd9Sstevel@tonic-gate 	case CPC_CPUREF:
3227c478bd9Sstevel@tonic-gate 		/*
3237c478bd9Sstevel@tonic-gate 		 * udata1 = pointer to user's buffer
3247c478bd9Sstevel@tonic-gate 		 * udata2 = unused
3257c478bd9Sstevel@tonic-gate 		 * udata3 = unused
3267c478bd9Sstevel@tonic-gate 		 */
3277c478bd9Sstevel@tonic-gate 		if (cmd == CPC_IMPL_NAME) {
3287c478bd9Sstevel@tonic-gate 			str = pcbe_ops->pcbe_impl_name();
3297c478bd9Sstevel@tonic-gate 			ASSERT(strlen(str) < CPC_MAX_IMPL_NAME);
3307c478bd9Sstevel@tonic-gate 		} else {
3317c478bd9Sstevel@tonic-gate 			str = pcbe_ops->pcbe_cpuref();
3327c478bd9Sstevel@tonic-gate 			ASSERT(strlen(str) < CPC_MAX_CPUREF);
3337c478bd9Sstevel@tonic-gate 		}
3347c478bd9Sstevel@tonic-gate 
3357c478bd9Sstevel@tonic-gate 		if (copyout(str, udata1, strlen(str) + 1) != 0)
3367c478bd9Sstevel@tonic-gate 			return (set_errno(EFAULT));
3377c478bd9Sstevel@tonic-gate 		return (0);
3387c478bd9Sstevel@tonic-gate 	case CPC_INVALIDATE:
3397c478bd9Sstevel@tonic-gate 		kcpc_invalidate(t);
3407c478bd9Sstevel@tonic-gate 		return (0);
3417c478bd9Sstevel@tonic-gate 	case CPC_RELE:
3427c478bd9Sstevel@tonic-gate 		if ((error = kcpc_unbind(t->t_cpc_set)) != 0)
3437c478bd9Sstevel@tonic-gate 			return (set_errno(error));
3447c478bd9Sstevel@tonic-gate 		return (0);
3457c478bd9Sstevel@tonic-gate 	default:
3467c478bd9Sstevel@tonic-gate 		return (set_errno(EINVAL));
3477c478bd9Sstevel@tonic-gate 	}
3487c478bd9Sstevel@tonic-gate }
3497c478bd9Sstevel@tonic-gate 
3507c478bd9Sstevel@tonic-gate /*
3517c478bd9Sstevel@tonic-gate  * The 'shared' device allows direct access to the
3527c478bd9Sstevel@tonic-gate  * performance counter control register of the current CPU.
3537c478bd9Sstevel@tonic-gate  * The major difference between the contexts created here and those
3547c478bd9Sstevel@tonic-gate  * above is that the context handlers are -not- installed, thus
3557c478bd9Sstevel@tonic-gate  * no context switching behaviour occurs.
3567c478bd9Sstevel@tonic-gate  *
3577c478bd9Sstevel@tonic-gate  * Because they manipulate per-cpu state, these ioctls can
3587c478bd9Sstevel@tonic-gate  * only be invoked from a bound lwp, by a caller with the cpc_cpu privilege
3597c478bd9Sstevel@tonic-gate  * who can open the relevant entry in /devices (the act of holding it open
3607c478bd9Sstevel@tonic-gate  * causes other uses of the counters to be suspended).
3617c478bd9Sstevel@tonic-gate  *
3627c478bd9Sstevel@tonic-gate  * Note that for correct results, the caller -must- ensure that
3637c478bd9Sstevel@tonic-gate  * all existing per-lwp contexts are either inactive or marked invalid;
3647c478bd9Sstevel@tonic-gate  * that's what the open routine does.
3657c478bd9Sstevel@tonic-gate  */
3667c478bd9Sstevel@tonic-gate /*ARGSUSED*/
3677c478bd9Sstevel@tonic-gate static int
3687c478bd9Sstevel@tonic-gate kcpc_ioctl(dev_t dev, int cmd, intptr_t data, int flags, cred_t *cr, int *rvp)
3697c478bd9Sstevel@tonic-gate {
3707c478bd9Sstevel@tonic-gate 	kthread_t	*t = curthread;
3717c478bd9Sstevel@tonic-gate 	processorid_t	cpuid;
3727c478bd9Sstevel@tonic-gate 	void		*udata1 = NULL;
3737c478bd9Sstevel@tonic-gate 	void		*udata2 = NULL;
3747c478bd9Sstevel@tonic-gate 	void		*udata3 = NULL;
3757c478bd9Sstevel@tonic-gate 	int		error;
3767c478bd9Sstevel@tonic-gate 	int		code;
3777c478bd9Sstevel@tonic-gate 
3787c478bd9Sstevel@tonic-gate 	STRUCT_DECL(__cpc_args, args);
3797c478bd9Sstevel@tonic-gate 
3807c478bd9Sstevel@tonic-gate 	STRUCT_INIT(args, flags);
3817c478bd9Sstevel@tonic-gate 
3827c478bd9Sstevel@tonic-gate 	if (curthread->t_bind_cpu != getminor(dev))
3837c478bd9Sstevel@tonic-gate 		return (EAGAIN);  /* someone unbound it? */
3847c478bd9Sstevel@tonic-gate 
3857c478bd9Sstevel@tonic-gate 	cpuid = getminor(dev);
3867c478bd9Sstevel@tonic-gate 
3877c478bd9Sstevel@tonic-gate 	if (cmd == CPCIO_BIND || cmd == CPCIO_SAMPLE) {
3887c478bd9Sstevel@tonic-gate 		if (copyin((void *)data, STRUCT_BUF(args),
3897c478bd9Sstevel@tonic-gate 		    STRUCT_SIZE(args)) == -1)
3907c478bd9Sstevel@tonic-gate 			return (EFAULT);
3917c478bd9Sstevel@tonic-gate 
3927c478bd9Sstevel@tonic-gate 		udata1 = STRUCT_FGETP(args, udata1);
3937c478bd9Sstevel@tonic-gate 		udata2 = STRUCT_FGETP(args, udata2);
3947c478bd9Sstevel@tonic-gate 		udata3 = STRUCT_FGETP(args, udata3);
3957c478bd9Sstevel@tonic-gate 	}
3967c478bd9Sstevel@tonic-gate 
3977c478bd9Sstevel@tonic-gate 	switch (cmd) {
3987c478bd9Sstevel@tonic-gate 	case CPCIO_BIND:
3997c478bd9Sstevel@tonic-gate 		/*
4007c478bd9Sstevel@tonic-gate 		 * udata1 = pointer to packed nvlist buffer
4017c478bd9Sstevel@tonic-gate 		 * udata2 = size of packed nvlist buffer
4027c478bd9Sstevel@tonic-gate 		 * udata3 = User addr to return error subcode in.
4037c478bd9Sstevel@tonic-gate 		 */
4047c478bd9Sstevel@tonic-gate 		if (t->t_cpc_set != NULL) {
4057c478bd9Sstevel@tonic-gate 			(void) kcpc_unbind(t->t_cpc_set);
4067c478bd9Sstevel@tonic-gate 			ASSERT(t->t_cpc_set == NULL);
4077c478bd9Sstevel@tonic-gate 		}
4087c478bd9Sstevel@tonic-gate 
4097c478bd9Sstevel@tonic-gate 		if ((error = kcpc_copyin_set(&t->t_cpc_set, udata1,
4107c478bd9Sstevel@tonic-gate 		    (size_t)udata2)) != 0) {
4117c478bd9Sstevel@tonic-gate 			return (error);
4127c478bd9Sstevel@tonic-gate 		}
4137c478bd9Sstevel@tonic-gate 
4147c478bd9Sstevel@tonic-gate 		if ((error = kcpc_verify_set(t->t_cpc_set)) != 0) {
4157c478bd9Sstevel@tonic-gate 			kcpc_free_set(t->t_cpc_set);
4167c478bd9Sstevel@tonic-gate 			t->t_cpc_set = NULL;
4177c478bd9Sstevel@tonic-gate 			if (copyout(&error, udata3, sizeof (error)) == -1)
4187c478bd9Sstevel@tonic-gate 				return (EFAULT);
4197c478bd9Sstevel@tonic-gate 			return (EINVAL);
4207c478bd9Sstevel@tonic-gate 		}
4217c478bd9Sstevel@tonic-gate 
4227c478bd9Sstevel@tonic-gate 		if ((error = kcpc_bind_cpu(t->t_cpc_set, cpuid, &code)) != 0) {
4237c478bd9Sstevel@tonic-gate 			kcpc_free_set(t->t_cpc_set);
4247c478bd9Sstevel@tonic-gate 			t->t_cpc_set = NULL;
4257c478bd9Sstevel@tonic-gate 			/*
4267c478bd9Sstevel@tonic-gate 			 * Subcodes are only returned for EINVAL and EACCESS.
4277c478bd9Sstevel@tonic-gate 			 */
4287c478bd9Sstevel@tonic-gate 			if ((error == EINVAL || error == EACCES) &&
4297c478bd9Sstevel@tonic-gate 			    copyout(&code, udata3, sizeof (code)) == -1)
4307c478bd9Sstevel@tonic-gate 				return (EFAULT);
4317c478bd9Sstevel@tonic-gate 			return (error);
4327c478bd9Sstevel@tonic-gate 		}
4337c478bd9Sstevel@tonic-gate 
4347c478bd9Sstevel@tonic-gate 		return (0);
4357c478bd9Sstevel@tonic-gate 	case CPCIO_SAMPLE:
4367c478bd9Sstevel@tonic-gate 		/*
4377c478bd9Sstevel@tonic-gate 		 * udata1 = pointer to user's buffer
4387c478bd9Sstevel@tonic-gate 		 * udata2 = pointer to user's hrtime
4397c478bd9Sstevel@tonic-gate 		 * udata3 = pointer to user's tick
4407c478bd9Sstevel@tonic-gate 		 */
4417c478bd9Sstevel@tonic-gate 		/*
4427c478bd9Sstevel@tonic-gate 		 * Only CPU-bound sets may be sampled via the ioctl(). If this
4437c478bd9Sstevel@tonic-gate 		 * set has no CPU-bound context, return an error.
4447c478bd9Sstevel@tonic-gate 		 */
4457c478bd9Sstevel@tonic-gate 		if (t->t_cpc_set == NULL)
4467c478bd9Sstevel@tonic-gate 			return (EINVAL);
4477c478bd9Sstevel@tonic-gate 		if ((error = kcpc_sample(t->t_cpc_set, udata1, udata2,
4487c478bd9Sstevel@tonic-gate 		    udata3)) != 0)
4497c478bd9Sstevel@tonic-gate 			return (error);
4507c478bd9Sstevel@tonic-gate 		return (0);
4517c478bd9Sstevel@tonic-gate 	case CPCIO_RELE:
4527c478bd9Sstevel@tonic-gate 		if (t->t_cpc_set == NULL)
4537c478bd9Sstevel@tonic-gate 			return (EINVAL);
4547c478bd9Sstevel@tonic-gate 		return (kcpc_unbind(t->t_cpc_set));
4557c478bd9Sstevel@tonic-gate 	default:
4567c478bd9Sstevel@tonic-gate 		return (EINVAL);
4577c478bd9Sstevel@tonic-gate 	}
4587c478bd9Sstevel@tonic-gate }
4597c478bd9Sstevel@tonic-gate 
4607c478bd9Sstevel@tonic-gate /*
4617c478bd9Sstevel@tonic-gate  * The device supports multiple opens, but only one open
4627c478bd9Sstevel@tonic-gate  * is allowed per processor.  This is to enable multiple
4637c478bd9Sstevel@tonic-gate  * instances of tools looking at different processors.
4647c478bd9Sstevel@tonic-gate  */
4657c478bd9Sstevel@tonic-gate #define	KCPC_MINOR_SHARED		((minor_t)0x3fffful)
4667c478bd9Sstevel@tonic-gate 
4677c478bd9Sstevel@tonic-gate static ulong_t *kcpc_cpumap;		/* bitmap of cpus */
4687c478bd9Sstevel@tonic-gate 
4697c478bd9Sstevel@tonic-gate /*ARGSUSED1*/
4707c478bd9Sstevel@tonic-gate static int
4717c478bd9Sstevel@tonic-gate kcpc_open(dev_t *dev, int flags, int otyp, cred_t *cr)
4727c478bd9Sstevel@tonic-gate {
4737c478bd9Sstevel@tonic-gate 	processorid_t	cpuid;
4747c478bd9Sstevel@tonic-gate 	int		error;
4757c478bd9Sstevel@tonic-gate 
4767c478bd9Sstevel@tonic-gate 	ASSERT(pcbe_ops != NULL);
4777c478bd9Sstevel@tonic-gate 
4787c478bd9Sstevel@tonic-gate 	if ((error = secpolicy_cpc_cpu(cr)) != 0)
4797c478bd9Sstevel@tonic-gate 		return (error);
4807c478bd9Sstevel@tonic-gate 	if (getminor(*dev) != KCPC_MINOR_SHARED)
4817c478bd9Sstevel@tonic-gate 		return (ENXIO);
4827c478bd9Sstevel@tonic-gate 	if ((cpuid = curthread->t_bind_cpu) == PBIND_NONE)
4837c478bd9Sstevel@tonic-gate 		return (EINVAL);
4847c478bd9Sstevel@tonic-gate 	if (cpuid > max_cpuid)
4857c478bd9Sstevel@tonic-gate 		return (EINVAL);
4867c478bd9Sstevel@tonic-gate 
4877c478bd9Sstevel@tonic-gate 	rw_enter(&kcpc_cpuctx_lock, RW_WRITER);
4887c478bd9Sstevel@tonic-gate 	if (++kcpc_cpuctx == 1) {
4897c478bd9Sstevel@tonic-gate 		ASSERT(kcpc_cpumap == NULL);
4907c478bd9Sstevel@tonic-gate 		kcpc_cpumap = kmem_zalloc(BT_SIZEOFMAP(max_cpuid + 1),
4917c478bd9Sstevel@tonic-gate 		    KM_SLEEP);
4927c478bd9Sstevel@tonic-gate 		/*
4937c478bd9Sstevel@tonic-gate 		 * When this device is open for processor-based contexts,
4947c478bd9Sstevel@tonic-gate 		 * no further lwp-based contexts can be created.
4957c478bd9Sstevel@tonic-gate 		 *
4967c478bd9Sstevel@tonic-gate 		 * Since this is the first open, ensure that all existing
4977c478bd9Sstevel@tonic-gate 		 * contexts are invalidated.
4987c478bd9Sstevel@tonic-gate 		 */
4997c478bd9Sstevel@tonic-gate 		kcpc_invalidate_all();
5007c478bd9Sstevel@tonic-gate 	} else if (BT_TEST(kcpc_cpumap, cpuid)) {
5017c478bd9Sstevel@tonic-gate 		kcpc_cpuctx--;
5027c478bd9Sstevel@tonic-gate 		rw_exit(&kcpc_cpuctx_lock);
5037c478bd9Sstevel@tonic-gate 		return (EAGAIN);
5047c478bd9Sstevel@tonic-gate 	} else if (kcpc_hw_cpu_hook(cpuid, kcpc_cpumap) != 0) {
5057c478bd9Sstevel@tonic-gate 		kcpc_cpuctx--;
5067c478bd9Sstevel@tonic-gate 		rw_exit(&kcpc_cpuctx_lock);
5077c478bd9Sstevel@tonic-gate 		return (EACCES);
5087c478bd9Sstevel@tonic-gate 	}
5097c478bd9Sstevel@tonic-gate 	BT_SET(kcpc_cpumap, cpuid);
5107c478bd9Sstevel@tonic-gate 	rw_exit(&kcpc_cpuctx_lock);
5117c478bd9Sstevel@tonic-gate 
5127c478bd9Sstevel@tonic-gate 	*dev = makedevice(getmajor(*dev), (minor_t)cpuid);
5137c478bd9Sstevel@tonic-gate 
5147c478bd9Sstevel@tonic-gate 	return (0);
5157c478bd9Sstevel@tonic-gate }
5167c478bd9Sstevel@tonic-gate 
5177c478bd9Sstevel@tonic-gate /*ARGSUSED1*/
5187c478bd9Sstevel@tonic-gate static int
5197c478bd9Sstevel@tonic-gate kcpc_close(dev_t dev, int flags, int otyp, cred_t *cr)
5207c478bd9Sstevel@tonic-gate {
5217c478bd9Sstevel@tonic-gate 	rw_enter(&kcpc_cpuctx_lock, RW_WRITER);
5227c478bd9Sstevel@tonic-gate 	BT_CLEAR(kcpc_cpumap, getminor(dev));
5237c478bd9Sstevel@tonic-gate 	if (--kcpc_cpuctx == 0) {
5247c478bd9Sstevel@tonic-gate 		kmem_free(kcpc_cpumap, BT_SIZEOFMAP(max_cpuid + 1));
5257c478bd9Sstevel@tonic-gate 		kcpc_cpumap = NULL;
5267c478bd9Sstevel@tonic-gate 	}
5277c478bd9Sstevel@tonic-gate 	ASSERT(kcpc_cpuctx >= 0);
5287c478bd9Sstevel@tonic-gate 	rw_exit(&kcpc_cpuctx_lock);
5297c478bd9Sstevel@tonic-gate 
5307c478bd9Sstevel@tonic-gate 	return (0);
5317c478bd9Sstevel@tonic-gate }
5327c478bd9Sstevel@tonic-gate 
5337c478bd9Sstevel@tonic-gate /*
5347c478bd9Sstevel@tonic-gate  * Sane boundaries on the size of packed lists. In bytes.
5357c478bd9Sstevel@tonic-gate  */
5367c478bd9Sstevel@tonic-gate #define	CPC_MIN_PACKSIZE 4
5377c478bd9Sstevel@tonic-gate #define	CPC_MAX_PACKSIZE 10000
5387c478bd9Sstevel@tonic-gate 
5397c478bd9Sstevel@tonic-gate /*
5407c478bd9Sstevel@tonic-gate  * Sane boundary on the number of requests a set can contain.
5417c478bd9Sstevel@tonic-gate  */
5427c478bd9Sstevel@tonic-gate #define	CPC_MAX_NREQS 100
5437c478bd9Sstevel@tonic-gate 
5447c478bd9Sstevel@tonic-gate /*
5457c478bd9Sstevel@tonic-gate  * Sane boundary on the number of attributes a request can contain.
5467c478bd9Sstevel@tonic-gate  */
5477c478bd9Sstevel@tonic-gate #define	CPC_MAX_ATTRS 50
5487c478bd9Sstevel@tonic-gate 
5497c478bd9Sstevel@tonic-gate /*
5507c478bd9Sstevel@tonic-gate  * Copy in a packed nvlist from the user and create a request set out of it.
5517c478bd9Sstevel@tonic-gate  * If successful, return 0 and store a pointer to the set we've created. Returns
5527c478bd9Sstevel@tonic-gate  * error code on error.
5537c478bd9Sstevel@tonic-gate  */
5547c478bd9Sstevel@tonic-gate int
5557c478bd9Sstevel@tonic-gate kcpc_copyin_set(kcpc_set_t **inset, void *ubuf, size_t len)
5567c478bd9Sstevel@tonic-gate {
5577c478bd9Sstevel@tonic-gate 	kcpc_set_t	*set;
5587c478bd9Sstevel@tonic-gate 	int		i;
5597c478bd9Sstevel@tonic-gate 	int		j;
5607c478bd9Sstevel@tonic-gate 	char		*packbuf;
5617c478bd9Sstevel@tonic-gate 
5627c478bd9Sstevel@tonic-gate 	nvlist_t	*nvl;
5637c478bd9Sstevel@tonic-gate 	nvpair_t	*nvp = NULL;
5647c478bd9Sstevel@tonic-gate 
5657c478bd9Sstevel@tonic-gate 	nvlist_t	*attrs;
5667c478bd9Sstevel@tonic-gate 	nvpair_t	*nvp_attr;
5677c478bd9Sstevel@tonic-gate 	kcpc_attr_t	*attrp;
5687c478bd9Sstevel@tonic-gate 
5697c478bd9Sstevel@tonic-gate 	nvlist_t	**reqlist;
5707c478bd9Sstevel@tonic-gate 	uint_t		nreqs;
5717c478bd9Sstevel@tonic-gate 	uint64_t	uint64;
5727c478bd9Sstevel@tonic-gate 	uint32_t	uint32;
5737c478bd9Sstevel@tonic-gate 	uint32_t	setflags = (uint32_t)-1;
5747c478bd9Sstevel@tonic-gate 	char		*string;
5757c478bd9Sstevel@tonic-gate 	char		*name;
5767c478bd9Sstevel@tonic-gate 
5777c478bd9Sstevel@tonic-gate 	if (len < CPC_MIN_PACKSIZE || len > CPC_MAX_PACKSIZE)
5787c478bd9Sstevel@tonic-gate 		return (EINVAL);
5797c478bd9Sstevel@tonic-gate 
5807c478bd9Sstevel@tonic-gate 	packbuf = kmem_alloc(len, KM_SLEEP);
5817c478bd9Sstevel@tonic-gate 
5827c478bd9Sstevel@tonic-gate 	if (copyin(ubuf, packbuf, len) == -1) {
5837c478bd9Sstevel@tonic-gate 		kmem_free(packbuf, len);
5847c478bd9Sstevel@tonic-gate 		return (EFAULT);
5857c478bd9Sstevel@tonic-gate 	}
5867c478bd9Sstevel@tonic-gate 
5877c478bd9Sstevel@tonic-gate 	if (nvlist_unpack(packbuf, len, &nvl, KM_SLEEP) != 0) {
5887c478bd9Sstevel@tonic-gate 		kmem_free(packbuf, len);
5897c478bd9Sstevel@tonic-gate 		return (EINVAL);
5907c478bd9Sstevel@tonic-gate 	}
5917c478bd9Sstevel@tonic-gate 
5927c478bd9Sstevel@tonic-gate 	/*
5937c478bd9Sstevel@tonic-gate 	 * The nvlist has been unpacked so there is no need for the packed
5947c478bd9Sstevel@tonic-gate 	 * representation from this point on.
5957c478bd9Sstevel@tonic-gate 	 */
5967c478bd9Sstevel@tonic-gate 	kmem_free(packbuf, len);
5977c478bd9Sstevel@tonic-gate 
5987c478bd9Sstevel@tonic-gate 	i = 0;
5997c478bd9Sstevel@tonic-gate 	while ((nvp = nvlist_next_nvpair(nvl, nvp)) != NULL) {
6007c478bd9Sstevel@tonic-gate 		switch (nvpair_type(nvp)) {
6017c478bd9Sstevel@tonic-gate 		case DATA_TYPE_UINT32:
6027c478bd9Sstevel@tonic-gate 			if (strcmp(nvpair_name(nvp), "flags") != 0 ||
6037c478bd9Sstevel@tonic-gate 			    nvpair_value_uint32(nvp, &setflags) != 0) {
6047c478bd9Sstevel@tonic-gate 				nvlist_free(nvl);
6057c478bd9Sstevel@tonic-gate 				return (EINVAL);
6067c478bd9Sstevel@tonic-gate 			}
6077c478bd9Sstevel@tonic-gate 			break;
6087c478bd9Sstevel@tonic-gate 		case DATA_TYPE_NVLIST_ARRAY:
6097c478bd9Sstevel@tonic-gate 			if (strcmp(nvpair_name(nvp), "reqs") != 0 ||
6107c478bd9Sstevel@tonic-gate 			    nvpair_value_nvlist_array(nvp, &reqlist,
6117c478bd9Sstevel@tonic-gate 				&nreqs) != 0) {
6127c478bd9Sstevel@tonic-gate 				nvlist_free(nvl);
6137c478bd9Sstevel@tonic-gate 				return (EINVAL);
6147c478bd9Sstevel@tonic-gate 			}
6157c478bd9Sstevel@tonic-gate 			break;
6167c478bd9Sstevel@tonic-gate 		default:
6177c478bd9Sstevel@tonic-gate 			nvlist_free(nvl);
6187c478bd9Sstevel@tonic-gate 			return (EINVAL);
6197c478bd9Sstevel@tonic-gate 		}
6207c478bd9Sstevel@tonic-gate 		i++;
6217c478bd9Sstevel@tonic-gate 	}
6227c478bd9Sstevel@tonic-gate 
6237c478bd9Sstevel@tonic-gate 	/*
6247c478bd9Sstevel@tonic-gate 	 * There should be two members in the top-level nvlist:
6257c478bd9Sstevel@tonic-gate 	 * an array of nvlists consisting of the requests, and flags.
6267c478bd9Sstevel@tonic-gate 	 * Anything else is an invalid set.
6277c478bd9Sstevel@tonic-gate 	 */
6287c478bd9Sstevel@tonic-gate 	if (i != 2) {
6297c478bd9Sstevel@tonic-gate 		nvlist_free(nvl);
6307c478bd9Sstevel@tonic-gate 		return (EINVAL);
6317c478bd9Sstevel@tonic-gate 	}
6327c478bd9Sstevel@tonic-gate 
6337c478bd9Sstevel@tonic-gate 	if (nreqs > CPC_MAX_NREQS) {
6347c478bd9Sstevel@tonic-gate 		nvlist_free(nvl);
6357c478bd9Sstevel@tonic-gate 		return (EINVAL);
6367c478bd9Sstevel@tonic-gate 	}
6377c478bd9Sstevel@tonic-gate 
6387c478bd9Sstevel@tonic-gate 	/*
6397c478bd9Sstevel@tonic-gate 	 * The requests are now stored in the nvlist array at reqlist.
640*4568bee7Strevtom 	 * Note that the use of kmem_zalloc() to alloc the kcpc_set_t means
641*4568bee7Strevtom 	 * we don't need to call the init routines for ks_lock and ks_condv.
6427c478bd9Sstevel@tonic-gate 	 */
643*4568bee7Strevtom 	set = kmem_zalloc(sizeof (kcpc_set_t), KM_SLEEP);
6447c478bd9Sstevel@tonic-gate 	set->ks_req = (kcpc_request_t *)kmem_zalloc(sizeof (kcpc_request_t) *
6457c478bd9Sstevel@tonic-gate 	    nreqs, KM_SLEEP);
6467c478bd9Sstevel@tonic-gate 	set->ks_nreqs = nreqs;
6477c478bd9Sstevel@tonic-gate 	/*
6487c478bd9Sstevel@tonic-gate 	 * If the nvlist didn't contain a flags member, setflags was initialized
6497c478bd9Sstevel@tonic-gate 	 * with an illegal value and this set will fail sanity checks later on.
6507c478bd9Sstevel@tonic-gate 	 */
6517c478bd9Sstevel@tonic-gate 	set->ks_flags = setflags;
652*4568bee7Strevtom 	/*
653*4568bee7Strevtom 	 * Initialize bind/unbind set synchronization.
654*4568bee7Strevtom 	 */
655*4568bee7Strevtom 	set->ks_state &= ~KCPC_SET_BOUND;
6567c478bd9Sstevel@tonic-gate 
6577c478bd9Sstevel@tonic-gate 	/*
6587c478bd9Sstevel@tonic-gate 	 * Build the set up one request at a time, always keeping it self-
6597c478bd9Sstevel@tonic-gate 	 * consistent so we can give it to kcpc_free_set() if we need to back
6607c478bd9Sstevel@tonic-gate 	 * out and return and error.
6617c478bd9Sstevel@tonic-gate 	 */
6627c478bd9Sstevel@tonic-gate 	for (i = 0; i < nreqs; i++) {
6637c478bd9Sstevel@tonic-gate 		nvp = NULL;
6647c478bd9Sstevel@tonic-gate 		set->ks_req[i].kr_picnum = -1;
6657c478bd9Sstevel@tonic-gate 		while ((nvp = nvlist_next_nvpair(reqlist[i], nvp)) != NULL) {
6667c478bd9Sstevel@tonic-gate 			name = nvpair_name(nvp);
6677c478bd9Sstevel@tonic-gate 			switch (nvpair_type(nvp)) {
6687c478bd9Sstevel@tonic-gate 			case DATA_TYPE_UINT32:
6697c478bd9Sstevel@tonic-gate 				if (nvpair_value_uint32(nvp, &uint32) == EINVAL)
6707c478bd9Sstevel@tonic-gate 					goto inval;
6717c478bd9Sstevel@tonic-gate 				if (strcmp(name, "cr_flags") == 0)
6727c478bd9Sstevel@tonic-gate 					set->ks_req[i].kr_flags = uint32;
6737c478bd9Sstevel@tonic-gate 				if (strcmp(name, "cr_index") == 0)
6747c478bd9Sstevel@tonic-gate 					set->ks_req[i].kr_index = uint32;
6757c478bd9Sstevel@tonic-gate 				break;
6767c478bd9Sstevel@tonic-gate 			case DATA_TYPE_UINT64:
6777c478bd9Sstevel@tonic-gate 				if (nvpair_value_uint64(nvp, &uint64) == EINVAL)
6787c478bd9Sstevel@tonic-gate 					goto inval;
6797c478bd9Sstevel@tonic-gate 				if (strcmp(name, "cr_preset") == 0)
6807c478bd9Sstevel@tonic-gate 					set->ks_req[i].kr_preset = uint64;
6817c478bd9Sstevel@tonic-gate 				break;
6827c478bd9Sstevel@tonic-gate 			case DATA_TYPE_STRING:
6837c478bd9Sstevel@tonic-gate 				if (nvpair_value_string(nvp, &string) == EINVAL)
6847c478bd9Sstevel@tonic-gate 					goto inval;
6857c478bd9Sstevel@tonic-gate 				if (strcmp(name, "cr_event") == 0)
6867c478bd9Sstevel@tonic-gate 					(void) strncpy(set->ks_req[i].kr_event,
6877c478bd9Sstevel@tonic-gate 					    string, CPC_MAX_EVENT_LEN);
6887c478bd9Sstevel@tonic-gate 				break;
6897c478bd9Sstevel@tonic-gate 			case DATA_TYPE_NVLIST:
6907c478bd9Sstevel@tonic-gate 				if (strcmp(name, "cr_attr") != 0)
6917c478bd9Sstevel@tonic-gate 					goto inval;
6927c478bd9Sstevel@tonic-gate 				if (nvpair_value_nvlist(nvp, &attrs) == EINVAL)
6937c478bd9Sstevel@tonic-gate 					goto inval;
6947c478bd9Sstevel@tonic-gate 				nvp_attr = NULL;
6957c478bd9Sstevel@tonic-gate 				/*
6967c478bd9Sstevel@tonic-gate 				 * If the picnum has been specified as an
6977c478bd9Sstevel@tonic-gate 				 * attribute, consume that attribute here and
6987c478bd9Sstevel@tonic-gate 				 * remove it from the list of attributes.
6997c478bd9Sstevel@tonic-gate 				 */
7007c478bd9Sstevel@tonic-gate 				if (nvlist_lookup_uint64(attrs, "picnum",
7017c478bd9Sstevel@tonic-gate 				    &uint64) == 0) {
7027c478bd9Sstevel@tonic-gate 					if (nvlist_remove(attrs, "picnum",
7037c478bd9Sstevel@tonic-gate 					    DATA_TYPE_UINT64) != 0)
7047c478bd9Sstevel@tonic-gate 						panic("nvlist %p faulty",
7057c478bd9Sstevel@tonic-gate 						    attrs);
7067c478bd9Sstevel@tonic-gate 					set->ks_req[i].kr_picnum = uint64;
7077c478bd9Sstevel@tonic-gate 				}
7087c478bd9Sstevel@tonic-gate 
7097c478bd9Sstevel@tonic-gate 				if ((set->ks_req[i].kr_nattrs =
7107c478bd9Sstevel@tonic-gate 				    kcpc_nvlist_npairs(attrs)) == 0)
7117c478bd9Sstevel@tonic-gate 					break;
7127c478bd9Sstevel@tonic-gate 
7137c478bd9Sstevel@tonic-gate 				if (set->ks_req[i].kr_nattrs > CPC_MAX_ATTRS)
7147c478bd9Sstevel@tonic-gate 					goto inval;
7157c478bd9Sstevel@tonic-gate 
7167c478bd9Sstevel@tonic-gate 				set->ks_req[i].kr_attr =
7177c478bd9Sstevel@tonic-gate 				    kmem_alloc(set->ks_req[i].kr_nattrs *
7187c478bd9Sstevel@tonic-gate 				    sizeof (kcpc_attr_t), KM_SLEEP);
7197c478bd9Sstevel@tonic-gate 				j = 0;
7207c478bd9Sstevel@tonic-gate 
7217c478bd9Sstevel@tonic-gate 				while ((nvp_attr = nvlist_next_nvpair(attrs,
7227c478bd9Sstevel@tonic-gate 				    nvp_attr)) != NULL) {
7237c478bd9Sstevel@tonic-gate 					attrp = &set->ks_req[i].kr_attr[j];
7247c478bd9Sstevel@tonic-gate 
7257c478bd9Sstevel@tonic-gate 					if (nvpair_type(nvp_attr) !=
7267c478bd9Sstevel@tonic-gate 					    DATA_TYPE_UINT64)
7277c478bd9Sstevel@tonic-gate 						goto inval;
7287c478bd9Sstevel@tonic-gate 
7297c478bd9Sstevel@tonic-gate 					(void) strncpy(attrp->ka_name,
7307c478bd9Sstevel@tonic-gate 					    nvpair_name(nvp_attr),
7317c478bd9Sstevel@tonic-gate 					    CPC_MAX_ATTR_LEN);
7327c478bd9Sstevel@tonic-gate 
7337c478bd9Sstevel@tonic-gate 					if (nvpair_value_uint64(nvp_attr,
7347c478bd9Sstevel@tonic-gate 					    &(attrp->ka_val)) == EINVAL)
7357c478bd9Sstevel@tonic-gate 						goto inval;
7367c478bd9Sstevel@tonic-gate 					j++;
7377c478bd9Sstevel@tonic-gate 				}
7387c478bd9Sstevel@tonic-gate 				ASSERT(j == set->ks_req[i].kr_nattrs);
7397c478bd9Sstevel@tonic-gate 			default:
7407c478bd9Sstevel@tonic-gate 				break;
7417c478bd9Sstevel@tonic-gate 			}
7427c478bd9Sstevel@tonic-gate 		}
7437c478bd9Sstevel@tonic-gate 	}
7447c478bd9Sstevel@tonic-gate 
7457c478bd9Sstevel@tonic-gate 	nvlist_free(nvl);
7467c478bd9Sstevel@tonic-gate 	*inset = set;
7477c478bd9Sstevel@tonic-gate 	return (0);
7487c478bd9Sstevel@tonic-gate 
7497c478bd9Sstevel@tonic-gate inval:
7507c478bd9Sstevel@tonic-gate 	nvlist_free(nvl);
7517c478bd9Sstevel@tonic-gate 	kcpc_free_set(set);
7527c478bd9Sstevel@tonic-gate 	return (EINVAL);
7537c478bd9Sstevel@tonic-gate }
7547c478bd9Sstevel@tonic-gate 
7557c478bd9Sstevel@tonic-gate /*
7567c478bd9Sstevel@tonic-gate  * Count the number of nvpairs in the supplied nvlist.
7577c478bd9Sstevel@tonic-gate  */
7587c478bd9Sstevel@tonic-gate static uint32_t
7597c478bd9Sstevel@tonic-gate kcpc_nvlist_npairs(nvlist_t *list)
7607c478bd9Sstevel@tonic-gate {
7617c478bd9Sstevel@tonic-gate 	nvpair_t *nvp = NULL;
7627c478bd9Sstevel@tonic-gate 	uint32_t n = 0;
7637c478bd9Sstevel@tonic-gate 
7647c478bd9Sstevel@tonic-gate 	while ((nvp = nvlist_next_nvpair(list, nvp)) != NULL)
7657c478bd9Sstevel@tonic-gate 		n++;
7667c478bd9Sstevel@tonic-gate 
7677c478bd9Sstevel@tonic-gate 	return (n);
7687c478bd9Sstevel@tonic-gate }
7697c478bd9Sstevel@tonic-gate 
7707c478bd9Sstevel@tonic-gate /*
7717c478bd9Sstevel@tonic-gate  * Performs sanity checks on the given set.
7727c478bd9Sstevel@tonic-gate  * Returns 0 if the set checks out OK.
7737c478bd9Sstevel@tonic-gate  * Returns a detailed error subcode, or -1 if there is no applicable subcode.
7747c478bd9Sstevel@tonic-gate  */
7757c478bd9Sstevel@tonic-gate static int
7767c478bd9Sstevel@tonic-gate kcpc_verify_set(kcpc_set_t *set)
7777c478bd9Sstevel@tonic-gate {
7787c478bd9Sstevel@tonic-gate 	kcpc_request_t	*rp;
7797c478bd9Sstevel@tonic-gate 	int		i;
7807c478bd9Sstevel@tonic-gate 	uint64_t	bitmap = 0;
7817c478bd9Sstevel@tonic-gate 	int		n;
7827c478bd9Sstevel@tonic-gate 
7837c478bd9Sstevel@tonic-gate 	if (set->ks_nreqs > cpc_ncounters)
7847c478bd9Sstevel@tonic-gate 		return (-1);
7857c478bd9Sstevel@tonic-gate 
7867c478bd9Sstevel@tonic-gate 	if (CPC_SET_VALID_FLAGS(set->ks_flags) == 0)
7877c478bd9Sstevel@tonic-gate 		return (-1);
7887c478bd9Sstevel@tonic-gate 
7897c478bd9Sstevel@tonic-gate 	for (i = 0; i < set->ks_nreqs; i++) {
7907c478bd9Sstevel@tonic-gate 		rp = &set->ks_req[i];
7917c478bd9Sstevel@tonic-gate 
7927c478bd9Sstevel@tonic-gate 		/*
7937c478bd9Sstevel@tonic-gate 		 * The following comparison must cast cpc_ncounters to an int,
7947c478bd9Sstevel@tonic-gate 		 * because kr_picnum will be -1 if the request didn't explicitly
7957c478bd9Sstevel@tonic-gate 		 * choose a PIC.
7967c478bd9Sstevel@tonic-gate 		 */
7977c478bd9Sstevel@tonic-gate 		if (rp->kr_picnum >= (int)cpc_ncounters)
7987c478bd9Sstevel@tonic-gate 			return (CPC_INVALID_PICNUM);
7997c478bd9Sstevel@tonic-gate 
8007c478bd9Sstevel@tonic-gate 		/*
8017c478bd9Sstevel@tonic-gate 		 * Of the pics whose physical picnum has been specified, make
8027c478bd9Sstevel@tonic-gate 		 * sure each PIC appears only once in set.
8037c478bd9Sstevel@tonic-gate 		 */
8047c478bd9Sstevel@tonic-gate 		if ((n = set->ks_req[i].kr_picnum) != -1) {
8057c478bd9Sstevel@tonic-gate 			if ((bitmap & (1 << n)) != 0)
8067c478bd9Sstevel@tonic-gate 				return (-1);
8077c478bd9Sstevel@tonic-gate 			bitmap |= (1 << n);
8087c478bd9Sstevel@tonic-gate 		}
8097c478bd9Sstevel@tonic-gate 
8107c478bd9Sstevel@tonic-gate 		/*
8117c478bd9Sstevel@tonic-gate 		 * Make sure the requested index falls within the range of all
8127c478bd9Sstevel@tonic-gate 		 * requests.
8137c478bd9Sstevel@tonic-gate 		 */
8147c478bd9Sstevel@tonic-gate 		if (rp->kr_index < 0 || rp->kr_index >= set->ks_nreqs)
8157c478bd9Sstevel@tonic-gate 			return (-1);
8167c478bd9Sstevel@tonic-gate 
8177c478bd9Sstevel@tonic-gate 		/*
8187c478bd9Sstevel@tonic-gate 		 * Make sure there are no unknown flags.
8197c478bd9Sstevel@tonic-gate 		 */
8207c478bd9Sstevel@tonic-gate 		if (KCPC_REQ_VALID_FLAGS(rp->kr_flags) == 0)
8217c478bd9Sstevel@tonic-gate 			return (CPC_REQ_INVALID_FLAGS);
8227c478bd9Sstevel@tonic-gate 	}
8237c478bd9Sstevel@tonic-gate 
8247c478bd9Sstevel@tonic-gate 	return (0);
8257c478bd9Sstevel@tonic-gate }
8267c478bd9Sstevel@tonic-gate 
8277c478bd9Sstevel@tonic-gate static struct cb_ops cb_ops = {
8287c478bd9Sstevel@tonic-gate 	kcpc_open,
8297c478bd9Sstevel@tonic-gate 	kcpc_close,
8307c478bd9Sstevel@tonic-gate 	nodev,		/* strategy */
8317c478bd9Sstevel@tonic-gate 	nodev,		/* print */
8327c478bd9Sstevel@tonic-gate 	nodev,		/* dump */
8337c478bd9Sstevel@tonic-gate 	nodev,		/* read */
8347c478bd9Sstevel@tonic-gate 	nodev,		/* write */
8357c478bd9Sstevel@tonic-gate 	kcpc_ioctl,
8367c478bd9Sstevel@tonic-gate 	nodev,		/* devmap */
8377c478bd9Sstevel@tonic-gate 	nodev,		/* mmap */
8387c478bd9Sstevel@tonic-gate 	nodev,		/* segmap */
8397c478bd9Sstevel@tonic-gate 	nochpoll,	/* poll */
8407c478bd9Sstevel@tonic-gate 	ddi_prop_op,
8417c478bd9Sstevel@tonic-gate 	NULL,
8427c478bd9Sstevel@tonic-gate 	D_NEW | D_MP
8437c478bd9Sstevel@tonic-gate };
8447c478bd9Sstevel@tonic-gate 
8457c478bd9Sstevel@tonic-gate /*ARGSUSED*/
8467c478bd9Sstevel@tonic-gate static int
8477c478bd9Sstevel@tonic-gate kcpc_probe(dev_info_t *devi)
8487c478bd9Sstevel@tonic-gate {
8497c478bd9Sstevel@tonic-gate 	return (DDI_PROBE_SUCCESS);
8507c478bd9Sstevel@tonic-gate }
8517c478bd9Sstevel@tonic-gate 
8527c478bd9Sstevel@tonic-gate static dev_info_t *kcpc_devi;
8537c478bd9Sstevel@tonic-gate 
8547c478bd9Sstevel@tonic-gate static int
8557c478bd9Sstevel@tonic-gate kcpc_attach(dev_info_t *devi, ddi_attach_cmd_t cmd)
8567c478bd9Sstevel@tonic-gate {
8577c478bd9Sstevel@tonic-gate 	if (cmd != DDI_ATTACH)
8587c478bd9Sstevel@tonic-gate 		return (DDI_FAILURE);
8597c478bd9Sstevel@tonic-gate 	kcpc_devi = devi;
8607c478bd9Sstevel@tonic-gate 	return (ddi_create_minor_node(devi, "shared", S_IFCHR,
8617c478bd9Sstevel@tonic-gate 	    KCPC_MINOR_SHARED, DDI_PSEUDO, 0));
8627c478bd9Sstevel@tonic-gate }
8637c478bd9Sstevel@tonic-gate 
8647c478bd9Sstevel@tonic-gate /*ARGSUSED*/
8657c478bd9Sstevel@tonic-gate static int
8667c478bd9Sstevel@tonic-gate kcpc_getinfo(dev_info_t *devi, ddi_info_cmd_t cmd, void *arg, void **result)
8677c478bd9Sstevel@tonic-gate {
8687c478bd9Sstevel@tonic-gate 	switch (cmd) {
8697c478bd9Sstevel@tonic-gate 	case DDI_INFO_DEVT2DEVINFO:
8707c478bd9Sstevel@tonic-gate 		switch (getminor((dev_t)arg)) {
8717c478bd9Sstevel@tonic-gate 		case KCPC_MINOR_SHARED:
8727c478bd9Sstevel@tonic-gate 			*result = kcpc_devi;
8737c478bd9Sstevel@tonic-gate 			return (DDI_SUCCESS);
8747c478bd9Sstevel@tonic-gate 		default:
8757c478bd9Sstevel@tonic-gate 			break;
8767c478bd9Sstevel@tonic-gate 		}
8777c478bd9Sstevel@tonic-gate 		break;
8787c478bd9Sstevel@tonic-gate 	case DDI_INFO_DEVT2INSTANCE:
8797c478bd9Sstevel@tonic-gate 		*result = 0;
8807c478bd9Sstevel@tonic-gate 		return (DDI_SUCCESS);
8817c478bd9Sstevel@tonic-gate 	default:
8827c478bd9Sstevel@tonic-gate 		break;
8837c478bd9Sstevel@tonic-gate 	}
8847c478bd9Sstevel@tonic-gate 
8857c478bd9Sstevel@tonic-gate 	return (DDI_FAILURE);
8867c478bd9Sstevel@tonic-gate }
8877c478bd9Sstevel@tonic-gate 
8887c478bd9Sstevel@tonic-gate static struct dev_ops dev_ops = {
8897c478bd9Sstevel@tonic-gate 	DEVO_REV,
8907c478bd9Sstevel@tonic-gate 	0,
8917c478bd9Sstevel@tonic-gate 	kcpc_getinfo,
8927c478bd9Sstevel@tonic-gate 	nulldev,		/* identify */
8937c478bd9Sstevel@tonic-gate 	kcpc_probe,
8947c478bd9Sstevel@tonic-gate 	kcpc_attach,
8957c478bd9Sstevel@tonic-gate 	nodev,			/* detach */
8967c478bd9Sstevel@tonic-gate 	nodev,			/* reset */
8977c478bd9Sstevel@tonic-gate 	&cb_ops,
8987c478bd9Sstevel@tonic-gate 	(struct bus_ops *)0
8997c478bd9Sstevel@tonic-gate };
9007c478bd9Sstevel@tonic-gate 
9017c478bd9Sstevel@tonic-gate static struct modldrv modldrv = {
9027c478bd9Sstevel@tonic-gate 	&mod_driverops,
9037c478bd9Sstevel@tonic-gate 	"cpc sampling driver v%I%",
9047c478bd9Sstevel@tonic-gate 	&dev_ops
9057c478bd9Sstevel@tonic-gate };
9067c478bd9Sstevel@tonic-gate 
9077c478bd9Sstevel@tonic-gate static struct sysent cpc_sysent = {
9087c478bd9Sstevel@tonic-gate 	5,
9097c478bd9Sstevel@tonic-gate 	SE_NOUNLOAD | SE_ARGC | SE_32RVAL1,
9107c478bd9Sstevel@tonic-gate 	cpc
9117c478bd9Sstevel@tonic-gate };
9127c478bd9Sstevel@tonic-gate 
9137c478bd9Sstevel@tonic-gate static struct modlsys modlsys = {
9147c478bd9Sstevel@tonic-gate 	&mod_syscallops,
9157c478bd9Sstevel@tonic-gate 	"cpc sampling system call",
9167c478bd9Sstevel@tonic-gate 	&cpc_sysent
9177c478bd9Sstevel@tonic-gate };
9187c478bd9Sstevel@tonic-gate 
9197c478bd9Sstevel@tonic-gate #ifdef _SYSCALL32_IMPL
9207c478bd9Sstevel@tonic-gate static struct modlsys modlsys32 = {
9217c478bd9Sstevel@tonic-gate 	&mod_syscallops32,
9227c478bd9Sstevel@tonic-gate 	"32-bit cpc sampling system call",
9237c478bd9Sstevel@tonic-gate 	&cpc_sysent
9247c478bd9Sstevel@tonic-gate };
9257c478bd9Sstevel@tonic-gate #endif
9267c478bd9Sstevel@tonic-gate 
9277c478bd9Sstevel@tonic-gate static struct modlinkage modl = {
9287c478bd9Sstevel@tonic-gate 	MODREV_1,
9297c478bd9Sstevel@tonic-gate 	&modldrv,
9307c478bd9Sstevel@tonic-gate 	&modlsys,
9317c478bd9Sstevel@tonic-gate #ifdef _SYSCALL32_IMPL
9327c478bd9Sstevel@tonic-gate 	&modlsys32,
9337c478bd9Sstevel@tonic-gate #endif
9347c478bd9Sstevel@tonic-gate };
9357c478bd9Sstevel@tonic-gate 
9367c478bd9Sstevel@tonic-gate static void
9377c478bd9Sstevel@tonic-gate kcpc_init(void)
9387c478bd9Sstevel@tonic-gate {
9397c478bd9Sstevel@tonic-gate 	long hash;
9407c478bd9Sstevel@tonic-gate 
9417c478bd9Sstevel@tonic-gate 	rw_init(&kcpc_cpuctx_lock, NULL, RW_DEFAULT, NULL);
9427c478bd9Sstevel@tonic-gate 	for (hash = 0; hash < CPC_HASH_BUCKETS; hash++)
9437c478bd9Sstevel@tonic-gate 		mutex_init(&kcpc_ctx_llock[hash],
9447c478bd9Sstevel@tonic-gate 		    NULL, MUTEX_DRIVER, (void *)(uintptr_t)15);
9457c478bd9Sstevel@tonic-gate }
9467c478bd9Sstevel@tonic-gate 
9477c478bd9Sstevel@tonic-gate static void
9487c478bd9Sstevel@tonic-gate kcpc_fini(void)
9497c478bd9Sstevel@tonic-gate {
9507c478bd9Sstevel@tonic-gate 	long hash;
9517c478bd9Sstevel@tonic-gate 
9527c478bd9Sstevel@tonic-gate 	for (hash = 0; hash < CPC_HASH_BUCKETS; hash++)
9537c478bd9Sstevel@tonic-gate 		mutex_destroy(&kcpc_ctx_llock[hash]);
9547c478bd9Sstevel@tonic-gate 	rw_destroy(&kcpc_cpuctx_lock);
9557c478bd9Sstevel@tonic-gate }
9567c478bd9Sstevel@tonic-gate 
9577c478bd9Sstevel@tonic-gate int
9587c478bd9Sstevel@tonic-gate _init(void)
9597c478bd9Sstevel@tonic-gate {
9607c478bd9Sstevel@tonic-gate 	int ret;
9617c478bd9Sstevel@tonic-gate 
9627c478bd9Sstevel@tonic-gate 	if (kcpc_hw_load_pcbe() != 0)
9637c478bd9Sstevel@tonic-gate 		return (ENOTSUP);
9647c478bd9Sstevel@tonic-gate 
9657c478bd9Sstevel@tonic-gate 	kcpc_init();
9667c478bd9Sstevel@tonic-gate 	if ((ret = mod_install(&modl)) != 0)
9677c478bd9Sstevel@tonic-gate 		kcpc_fini();
9687c478bd9Sstevel@tonic-gate 	return (ret);
9697c478bd9Sstevel@tonic-gate }
9707c478bd9Sstevel@tonic-gate 
9717c478bd9Sstevel@tonic-gate int
9727c478bd9Sstevel@tonic-gate _fini(void)
9737c478bd9Sstevel@tonic-gate {
9747c478bd9Sstevel@tonic-gate 	int ret;
9757c478bd9Sstevel@tonic-gate 
9767c478bd9Sstevel@tonic-gate 	if ((ret = mod_remove(&modl)) == 0)
9777c478bd9Sstevel@tonic-gate 		kcpc_fini();
9787c478bd9Sstevel@tonic-gate 	return (ret);
9797c478bd9Sstevel@tonic-gate }
9807c478bd9Sstevel@tonic-gate 
9817c478bd9Sstevel@tonic-gate int
9827c478bd9Sstevel@tonic-gate _info(struct modinfo *mi)
9837c478bd9Sstevel@tonic-gate {
9847c478bd9Sstevel@tonic-gate 	return (mod_info(&modl, mi));
9857c478bd9Sstevel@tonic-gate }
986