10Sstevel@tonic-gate /* 20Sstevel@tonic-gate * CDDL HEADER START 30Sstevel@tonic-gate * 40Sstevel@tonic-gate * The contents of this file are subject to the terms of the 5*6275Strevtom * Common Development and Distribution License (the "License"). 6*6275Strevtom * You may not use this file except in compliance with the License. 70Sstevel@tonic-gate * 80Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 90Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing. 100Sstevel@tonic-gate * See the License for the specific language governing permissions 110Sstevel@tonic-gate * and limitations under the License. 120Sstevel@tonic-gate * 130Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each 140Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 150Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the 160Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying 170Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner] 180Sstevel@tonic-gate * 190Sstevel@tonic-gate * CDDL HEADER END 200Sstevel@tonic-gate */ 210Sstevel@tonic-gate /* 22*6275Strevtom * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 230Sstevel@tonic-gate * Use is subject to license terms. 240Sstevel@tonic-gate */ 250Sstevel@tonic-gate 260Sstevel@tonic-gate #pragma ident "%Z%%M% %I% %E% SMI" 270Sstevel@tonic-gate 280Sstevel@tonic-gate /* 290Sstevel@tonic-gate * CPU Performance Counter system calls and device driver. 300Sstevel@tonic-gate * 310Sstevel@tonic-gate * This module uses a combination of thread context operators, and 320Sstevel@tonic-gate * thread-specific data to export CPU performance counters 330Sstevel@tonic-gate * via both a system call and a driver interface. 340Sstevel@tonic-gate * 350Sstevel@tonic-gate * There are three access methods exported - the 'shared' device 360Sstevel@tonic-gate * and the 'private' and 'agent' variants of the system call. 370Sstevel@tonic-gate * 380Sstevel@tonic-gate * The shared device treats the performance counter registers as 390Sstevel@tonic-gate * a processor metric, regardless of the work scheduled on them. 400Sstevel@tonic-gate * The private system call treats the performance counter registers 410Sstevel@tonic-gate * as a property of a single lwp. This is achieved by using the 420Sstevel@tonic-gate * thread context operators to virtualize the contents of the 430Sstevel@tonic-gate * performance counter registers between lwps. 440Sstevel@tonic-gate * 450Sstevel@tonic-gate * The agent method is like the private method, except that it must 460Sstevel@tonic-gate * be accessed via /proc's agent lwp to allow the counter context of 470Sstevel@tonic-gate * other threads to be examined safely. 480Sstevel@tonic-gate * 490Sstevel@tonic-gate * The shared usage fundamentally conflicts with the agent and private usage; 500Sstevel@tonic-gate * almost all of the complexity of the module is needed to allow these two 510Sstevel@tonic-gate * models to co-exist in a reasonable way. 520Sstevel@tonic-gate */ 530Sstevel@tonic-gate 540Sstevel@tonic-gate #include <sys/types.h> 550Sstevel@tonic-gate #include <sys/file.h> 560Sstevel@tonic-gate #include <sys/errno.h> 570Sstevel@tonic-gate #include <sys/open.h> 580Sstevel@tonic-gate #include <sys/cred.h> 590Sstevel@tonic-gate #include <sys/conf.h> 600Sstevel@tonic-gate #include <sys/stat.h> 610Sstevel@tonic-gate #include <sys/processor.h> 620Sstevel@tonic-gate #include <sys/cpuvar.h> 630Sstevel@tonic-gate #include <sys/disp.h> 640Sstevel@tonic-gate #include <sys/kmem.h> 650Sstevel@tonic-gate #include <sys/modctl.h> 660Sstevel@tonic-gate #include <sys/ddi.h> 670Sstevel@tonic-gate #include <sys/sunddi.h> 680Sstevel@tonic-gate #include <sys/nvpair.h> 690Sstevel@tonic-gate #include <sys/policy.h> 700Sstevel@tonic-gate #include <sys/machsystm.h> 710Sstevel@tonic-gate #include <sys/cpc_impl.h> 720Sstevel@tonic-gate #include <sys/cpc_pcbe.h> 730Sstevel@tonic-gate #include <sys/kcpc.h> 740Sstevel@tonic-gate 750Sstevel@tonic-gate static int kcpc_copyin_set(kcpc_set_t **set, void *ubuf, size_t len); 760Sstevel@tonic-gate static int kcpc_verify_set(kcpc_set_t *set); 770Sstevel@tonic-gate static uint32_t kcpc_nvlist_npairs(nvlist_t *list); 780Sstevel@tonic-gate 790Sstevel@tonic-gate /* 800Sstevel@tonic-gate * Generic attributes supported regardless of processor. 810Sstevel@tonic-gate */ 820Sstevel@tonic-gate 830Sstevel@tonic-gate #define ATTRLIST "picnum" 840Sstevel@tonic-gate #define SEPARATOR "," 850Sstevel@tonic-gate 860Sstevel@tonic-gate /* 870Sstevel@tonic-gate * System call to access CPU performance counters. 880Sstevel@tonic-gate */ 890Sstevel@tonic-gate static int 900Sstevel@tonic-gate cpc(int cmd, id_t lwpid, void *udata1, void *udata2, void *udata3) 910Sstevel@tonic-gate { 920Sstevel@tonic-gate kthread_t *t; 930Sstevel@tonic-gate int error; 940Sstevel@tonic-gate int size; 950Sstevel@tonic-gate const char *str; 960Sstevel@tonic-gate int code; 970Sstevel@tonic-gate 980Sstevel@tonic-gate /* 990Sstevel@tonic-gate * This CPC syscall should only be loaded if it found a PCBE to use. 1000Sstevel@tonic-gate */ 1010Sstevel@tonic-gate ASSERT(pcbe_ops != NULL); 1020Sstevel@tonic-gate 1030Sstevel@tonic-gate if (curproc->p_agenttp == curthread) { 1040Sstevel@tonic-gate /* 1050Sstevel@tonic-gate * Only if /proc is invoking this system call from 1060Sstevel@tonic-gate * the agent thread do we allow the caller to examine 1070Sstevel@tonic-gate * the contexts of other lwps in the process. And 1080Sstevel@tonic-gate * because we know we're the agent, we know we don't 1090Sstevel@tonic-gate * have to grab p_lock because no-one else can change 1100Sstevel@tonic-gate * the state of the process. 1110Sstevel@tonic-gate */ 1120Sstevel@tonic-gate if ((t = idtot(curproc, lwpid)) == NULL || t == curthread) 1130Sstevel@tonic-gate return (set_errno(ESRCH)); 1140Sstevel@tonic-gate ASSERT(t->t_tid == lwpid && ttolwp(t) != NULL); 1150Sstevel@tonic-gate } else 1160Sstevel@tonic-gate t = curthread; 1170Sstevel@tonic-gate 1180Sstevel@tonic-gate if (t->t_cpc_set == NULL && (cmd == CPC_SAMPLE || cmd == CPC_RELE)) 1190Sstevel@tonic-gate return (set_errno(EINVAL)); 1200Sstevel@tonic-gate 1210Sstevel@tonic-gate switch (cmd) { 1220Sstevel@tonic-gate case CPC_BIND: 1230Sstevel@tonic-gate /* 1240Sstevel@tonic-gate * udata1 = pointer to packed nvlist buffer 1250Sstevel@tonic-gate * udata2 = size of packed nvlist buffer 1260Sstevel@tonic-gate * udata3 = User addr to return error subcode in. 1270Sstevel@tonic-gate */ 1280Sstevel@tonic-gate 1290Sstevel@tonic-gate rw_enter(&kcpc_cpuctx_lock, RW_READER); 1300Sstevel@tonic-gate if (kcpc_cpuctx) { 1310Sstevel@tonic-gate rw_exit(&kcpc_cpuctx_lock); 1320Sstevel@tonic-gate return (set_errno(EAGAIN)); 1330Sstevel@tonic-gate } 1340Sstevel@tonic-gate 1350Sstevel@tonic-gate if (kcpc_hw_lwp_hook() != 0) { 1360Sstevel@tonic-gate rw_exit(&kcpc_cpuctx_lock); 1370Sstevel@tonic-gate return (set_errno(EACCES)); 1380Sstevel@tonic-gate } 1390Sstevel@tonic-gate 1400Sstevel@tonic-gate /* 1410Sstevel@tonic-gate * An LWP may only have one set bound to it at a time; if there 1420Sstevel@tonic-gate * is a set bound to this LWP already, we unbind it here. 1430Sstevel@tonic-gate */ 1440Sstevel@tonic-gate if (t->t_cpc_set != NULL) 1450Sstevel@tonic-gate (void) kcpc_unbind(t->t_cpc_set); 1460Sstevel@tonic-gate ASSERT(t->t_cpc_set == NULL); 1470Sstevel@tonic-gate 1480Sstevel@tonic-gate if ((error = kcpc_copyin_set(&t->t_cpc_set, udata1, 1490Sstevel@tonic-gate (size_t)udata2)) != 0) { 1500Sstevel@tonic-gate rw_exit(&kcpc_cpuctx_lock); 1510Sstevel@tonic-gate return (set_errno(error)); 1520Sstevel@tonic-gate } 1530Sstevel@tonic-gate 1540Sstevel@tonic-gate if ((error = kcpc_verify_set(t->t_cpc_set)) != 0) { 1550Sstevel@tonic-gate rw_exit(&kcpc_cpuctx_lock); 1560Sstevel@tonic-gate kcpc_free_set(t->t_cpc_set); 1570Sstevel@tonic-gate t->t_cpc_set = NULL; 1580Sstevel@tonic-gate if (copyout(&error, udata3, sizeof (error)) == -1) 1590Sstevel@tonic-gate return (set_errno(EFAULT)); 1600Sstevel@tonic-gate return (set_errno(EINVAL)); 1610Sstevel@tonic-gate } 1620Sstevel@tonic-gate 1630Sstevel@tonic-gate if ((error = kcpc_bind_thread(t->t_cpc_set, t, &code)) != 0) { 1640Sstevel@tonic-gate rw_exit(&kcpc_cpuctx_lock); 1650Sstevel@tonic-gate kcpc_free_set(t->t_cpc_set); 1660Sstevel@tonic-gate t->t_cpc_set = NULL; 1670Sstevel@tonic-gate /* 1680Sstevel@tonic-gate * EINVAL and EACCES are the only errors with more 1690Sstevel@tonic-gate * specific subcodes. 1700Sstevel@tonic-gate */ 1710Sstevel@tonic-gate if ((error == EINVAL || error == EACCES) && 1720Sstevel@tonic-gate copyout(&code, udata3, sizeof (code)) == -1) 1730Sstevel@tonic-gate return (set_errno(EFAULT)); 1740Sstevel@tonic-gate return (set_errno(error)); 1750Sstevel@tonic-gate } 1760Sstevel@tonic-gate 1770Sstevel@tonic-gate rw_exit(&kcpc_cpuctx_lock); 1780Sstevel@tonic-gate return (0); 1790Sstevel@tonic-gate case CPC_SAMPLE: 1800Sstevel@tonic-gate /* 1810Sstevel@tonic-gate * udata1 = pointer to user's buffer 1820Sstevel@tonic-gate * udata2 = pointer to user's hrtime 1830Sstevel@tonic-gate * udata3 = pointer to user's tick 1840Sstevel@tonic-gate */ 1850Sstevel@tonic-gate /* 1860Sstevel@tonic-gate * We only allow thread-bound sets to be sampled via the 1870Sstevel@tonic-gate * syscall, so if this set has a CPU-bound context, return an 1880Sstevel@tonic-gate * error. 1890Sstevel@tonic-gate */ 1900Sstevel@tonic-gate if (t->t_cpc_set->ks_ctx->kc_cpuid != -1) 1910Sstevel@tonic-gate return (set_errno(EINVAL)); 1920Sstevel@tonic-gate if ((error = kcpc_sample(t->t_cpc_set, udata1, udata2, 1930Sstevel@tonic-gate udata3)) != 0) 1940Sstevel@tonic-gate return (set_errno(error)); 1950Sstevel@tonic-gate 1960Sstevel@tonic-gate return (0); 1970Sstevel@tonic-gate case CPC_PRESET: 1980Sstevel@tonic-gate case CPC_RESTART: 1990Sstevel@tonic-gate /* 2000Sstevel@tonic-gate * These are valid only if this lwp has a bound set. 2010Sstevel@tonic-gate */ 2020Sstevel@tonic-gate if (t->t_cpc_set == NULL) 2030Sstevel@tonic-gate return (set_errno(EINVAL)); 2040Sstevel@tonic-gate if (cmd == CPC_PRESET) { 2050Sstevel@tonic-gate /* 2060Sstevel@tonic-gate * The preset is shipped up to us from userland in two 2070Sstevel@tonic-gate * parts. This lets us handle 64-bit values from 32-bit 2080Sstevel@tonic-gate * and 64-bit applications in the same manner. 2090Sstevel@tonic-gate * 2100Sstevel@tonic-gate * udata1 = index of request to preset 2110Sstevel@tonic-gate * udata2 = new 64-bit preset (most sig. 32 bits) 2120Sstevel@tonic-gate * udata3 = new 64-bit preset (least sig. 32 bits) 2130Sstevel@tonic-gate */ 2140Sstevel@tonic-gate if ((error = kcpc_preset(t->t_cpc_set, (intptr_t)udata1, 2150Sstevel@tonic-gate ((uint64_t)(uintptr_t)udata2 << 32ULL) | 2160Sstevel@tonic-gate (uint64_t)(uintptr_t)udata3)) != 0) 2170Sstevel@tonic-gate return (set_errno(error)); 2180Sstevel@tonic-gate } else { 2190Sstevel@tonic-gate /* 2200Sstevel@tonic-gate * udata[1-3] = unused 2210Sstevel@tonic-gate */ 2220Sstevel@tonic-gate if ((error = kcpc_restart(t->t_cpc_set)) != 0) 2230Sstevel@tonic-gate return (set_errno(error)); 2240Sstevel@tonic-gate } 2250Sstevel@tonic-gate return (0); 2260Sstevel@tonic-gate case CPC_ENABLE: 2270Sstevel@tonic-gate case CPC_DISABLE: 2280Sstevel@tonic-gate udata1 = 0; 2290Sstevel@tonic-gate /*FALLTHROUGH*/ 2300Sstevel@tonic-gate case CPC_USR_EVENTS: 2310Sstevel@tonic-gate case CPC_SYS_EVENTS: 2320Sstevel@tonic-gate if (t != curthread || t->t_cpc_set == NULL) 2330Sstevel@tonic-gate return (set_errno(EINVAL)); 2340Sstevel@tonic-gate /* 2350Sstevel@tonic-gate * Provided for backwards compatibility with CPCv1. 2360Sstevel@tonic-gate * 2370Sstevel@tonic-gate * Stop the counters and record the current counts. Use the 2380Sstevel@tonic-gate * counts as the preset to rebind a new set with the requests 2390Sstevel@tonic-gate * reconfigured as requested. 2400Sstevel@tonic-gate * 2410Sstevel@tonic-gate * udata1: 1 == enable; 0 == disable 2420Sstevel@tonic-gate * udata{2,3}: unused 2430Sstevel@tonic-gate */ 2440Sstevel@tonic-gate rw_enter(&kcpc_cpuctx_lock, RW_READER); 2450Sstevel@tonic-gate if ((error = kcpc_enable(t, 2460Sstevel@tonic-gate cmd, (int)(uintptr_t)udata1)) != 0) { 2470Sstevel@tonic-gate rw_exit(&kcpc_cpuctx_lock); 2480Sstevel@tonic-gate return (set_errno(error)); 2490Sstevel@tonic-gate } 2500Sstevel@tonic-gate rw_exit(&kcpc_cpuctx_lock); 2510Sstevel@tonic-gate return (0); 2520Sstevel@tonic-gate case CPC_NPIC: 2530Sstevel@tonic-gate return (cpc_ncounters); 2540Sstevel@tonic-gate case CPC_CAPS: 2550Sstevel@tonic-gate return (pcbe_ops->pcbe_caps); 2560Sstevel@tonic-gate case CPC_EVLIST_SIZE: 2570Sstevel@tonic-gate case CPC_LIST_EVENTS: 2580Sstevel@tonic-gate /* 2590Sstevel@tonic-gate * udata1 = pointer to user's int or buffer 2600Sstevel@tonic-gate * udata2 = picnum 2610Sstevel@tonic-gate * udata3 = unused 2620Sstevel@tonic-gate */ 2630Sstevel@tonic-gate if ((uintptr_t)udata2 >= cpc_ncounters) 2640Sstevel@tonic-gate return (set_errno(EINVAL)); 2650Sstevel@tonic-gate 2660Sstevel@tonic-gate size = strlen( 2670Sstevel@tonic-gate pcbe_ops->pcbe_list_events((uintptr_t)udata2)) + 1; 2680Sstevel@tonic-gate 2690Sstevel@tonic-gate if (cmd == CPC_EVLIST_SIZE) { 2700Sstevel@tonic-gate if (suword32(udata1, size) == -1) 2710Sstevel@tonic-gate return (set_errno(EFAULT)); 2720Sstevel@tonic-gate } else { 2730Sstevel@tonic-gate if (copyout( 2740Sstevel@tonic-gate pcbe_ops->pcbe_list_events((uintptr_t)udata2), 2750Sstevel@tonic-gate udata1, size) == -1) 2760Sstevel@tonic-gate return (set_errno(EFAULT)); 2770Sstevel@tonic-gate } 2780Sstevel@tonic-gate return (0); 2790Sstevel@tonic-gate case CPC_ATTRLIST_SIZE: 2800Sstevel@tonic-gate case CPC_LIST_ATTRS: 2810Sstevel@tonic-gate /* 2820Sstevel@tonic-gate * udata1 = pointer to user's int or buffer 2830Sstevel@tonic-gate * udata2 = unused 2840Sstevel@tonic-gate * udata3 = unused 2850Sstevel@tonic-gate * 2860Sstevel@tonic-gate * attrlist size is length of PCBE-supported attributes, plus 2870Sstevel@tonic-gate * room for "picnum\0" plus an optional ',' separator char. 2880Sstevel@tonic-gate */ 2890Sstevel@tonic-gate str = pcbe_ops->pcbe_list_attrs(); 2900Sstevel@tonic-gate size = strlen(str) + sizeof (SEPARATOR ATTRLIST) + 1; 2910Sstevel@tonic-gate if (str[0] != '\0') 2920Sstevel@tonic-gate /* 2930Sstevel@tonic-gate * A ',' separator character is necessary. 2940Sstevel@tonic-gate */ 2950Sstevel@tonic-gate size += 1; 2960Sstevel@tonic-gate 2970Sstevel@tonic-gate if (cmd == CPC_ATTRLIST_SIZE) { 2980Sstevel@tonic-gate if (suword32(udata1, size) == -1) 2990Sstevel@tonic-gate return (set_errno(EFAULT)); 3000Sstevel@tonic-gate } else { 3010Sstevel@tonic-gate /* 3020Sstevel@tonic-gate * Copyout the PCBE attributes, and then append the 3030Sstevel@tonic-gate * generic attribute list (with separator if necessary). 3040Sstevel@tonic-gate */ 3050Sstevel@tonic-gate if (copyout(str, udata1, strlen(str)) == -1) 3060Sstevel@tonic-gate return (set_errno(EFAULT)); 3070Sstevel@tonic-gate if (str[0] != '\0') { 3080Sstevel@tonic-gate if (copyout(SEPARATOR ATTRLIST, 3090Sstevel@tonic-gate ((char *)udata1) + strlen(str), 3100Sstevel@tonic-gate strlen(SEPARATOR ATTRLIST) + 1) 3110Sstevel@tonic-gate == -1) 3120Sstevel@tonic-gate return (set_errno(EFAULT)); 3130Sstevel@tonic-gate } else 3140Sstevel@tonic-gate if (copyout(ATTRLIST, 3150Sstevel@tonic-gate (char *)udata1 + strlen(str), 3160Sstevel@tonic-gate strlen(ATTRLIST) + 1) == -1) 3170Sstevel@tonic-gate return (set_errno(EFAULT)); 3180Sstevel@tonic-gate } 3190Sstevel@tonic-gate return (0); 3200Sstevel@tonic-gate case CPC_IMPL_NAME: 3210Sstevel@tonic-gate case CPC_CPUREF: 3220Sstevel@tonic-gate /* 3230Sstevel@tonic-gate * udata1 = pointer to user's buffer 3240Sstevel@tonic-gate * udata2 = unused 3250Sstevel@tonic-gate * udata3 = unused 3260Sstevel@tonic-gate */ 3270Sstevel@tonic-gate if (cmd == CPC_IMPL_NAME) { 3280Sstevel@tonic-gate str = pcbe_ops->pcbe_impl_name(); 3290Sstevel@tonic-gate ASSERT(strlen(str) < CPC_MAX_IMPL_NAME); 3300Sstevel@tonic-gate } else { 3310Sstevel@tonic-gate str = pcbe_ops->pcbe_cpuref(); 3320Sstevel@tonic-gate ASSERT(strlen(str) < CPC_MAX_CPUREF); 3330Sstevel@tonic-gate } 3340Sstevel@tonic-gate 3350Sstevel@tonic-gate if (copyout(str, udata1, strlen(str) + 1) != 0) 3360Sstevel@tonic-gate return (set_errno(EFAULT)); 3370Sstevel@tonic-gate return (0); 3380Sstevel@tonic-gate case CPC_INVALIDATE: 3390Sstevel@tonic-gate kcpc_invalidate(t); 3400Sstevel@tonic-gate return (0); 3410Sstevel@tonic-gate case CPC_RELE: 3420Sstevel@tonic-gate if ((error = kcpc_unbind(t->t_cpc_set)) != 0) 3430Sstevel@tonic-gate return (set_errno(error)); 3440Sstevel@tonic-gate return (0); 3450Sstevel@tonic-gate default: 3460Sstevel@tonic-gate return (set_errno(EINVAL)); 3470Sstevel@tonic-gate } 3480Sstevel@tonic-gate } 3490Sstevel@tonic-gate 3500Sstevel@tonic-gate /* 3510Sstevel@tonic-gate * The 'shared' device allows direct access to the 3520Sstevel@tonic-gate * performance counter control register of the current CPU. 3530Sstevel@tonic-gate * The major difference between the contexts created here and those 3540Sstevel@tonic-gate * above is that the context handlers are -not- installed, thus 3550Sstevel@tonic-gate * no context switching behaviour occurs. 3560Sstevel@tonic-gate * 3570Sstevel@tonic-gate * Because they manipulate per-cpu state, these ioctls can 3580Sstevel@tonic-gate * only be invoked from a bound lwp, by a caller with the cpc_cpu privilege 3590Sstevel@tonic-gate * who can open the relevant entry in /devices (the act of holding it open 3600Sstevel@tonic-gate * causes other uses of the counters to be suspended). 3610Sstevel@tonic-gate * 3620Sstevel@tonic-gate * Note that for correct results, the caller -must- ensure that 3630Sstevel@tonic-gate * all existing per-lwp contexts are either inactive or marked invalid; 3640Sstevel@tonic-gate * that's what the open routine does. 3650Sstevel@tonic-gate */ 3660Sstevel@tonic-gate /*ARGSUSED*/ 3670Sstevel@tonic-gate static int 3680Sstevel@tonic-gate kcpc_ioctl(dev_t dev, int cmd, intptr_t data, int flags, cred_t *cr, int *rvp) 3690Sstevel@tonic-gate { 3700Sstevel@tonic-gate kthread_t *t = curthread; 3710Sstevel@tonic-gate processorid_t cpuid; 3720Sstevel@tonic-gate void *udata1 = NULL; 3730Sstevel@tonic-gate void *udata2 = NULL; 3740Sstevel@tonic-gate void *udata3 = NULL; 3750Sstevel@tonic-gate int error; 3760Sstevel@tonic-gate int code; 3770Sstevel@tonic-gate 3780Sstevel@tonic-gate STRUCT_DECL(__cpc_args, args); 3790Sstevel@tonic-gate 3800Sstevel@tonic-gate STRUCT_INIT(args, flags); 3810Sstevel@tonic-gate 3820Sstevel@tonic-gate if (curthread->t_bind_cpu != getminor(dev)) 3830Sstevel@tonic-gate return (EAGAIN); /* someone unbound it? */ 3840Sstevel@tonic-gate 3850Sstevel@tonic-gate cpuid = getminor(dev); 3860Sstevel@tonic-gate 3870Sstevel@tonic-gate if (cmd == CPCIO_BIND || cmd == CPCIO_SAMPLE) { 3880Sstevel@tonic-gate if (copyin((void *)data, STRUCT_BUF(args), 3890Sstevel@tonic-gate STRUCT_SIZE(args)) == -1) 3900Sstevel@tonic-gate return (EFAULT); 3910Sstevel@tonic-gate 3920Sstevel@tonic-gate udata1 = STRUCT_FGETP(args, udata1); 3930Sstevel@tonic-gate udata2 = STRUCT_FGETP(args, udata2); 3940Sstevel@tonic-gate udata3 = STRUCT_FGETP(args, udata3); 3950Sstevel@tonic-gate } 3960Sstevel@tonic-gate 3970Sstevel@tonic-gate switch (cmd) { 3980Sstevel@tonic-gate case CPCIO_BIND: 3990Sstevel@tonic-gate /* 4000Sstevel@tonic-gate * udata1 = pointer to packed nvlist buffer 4010Sstevel@tonic-gate * udata2 = size of packed nvlist buffer 4020Sstevel@tonic-gate * udata3 = User addr to return error subcode in. 4030Sstevel@tonic-gate */ 4040Sstevel@tonic-gate if (t->t_cpc_set != NULL) { 4050Sstevel@tonic-gate (void) kcpc_unbind(t->t_cpc_set); 4060Sstevel@tonic-gate ASSERT(t->t_cpc_set == NULL); 4070Sstevel@tonic-gate } 4080Sstevel@tonic-gate 4090Sstevel@tonic-gate if ((error = kcpc_copyin_set(&t->t_cpc_set, udata1, 4100Sstevel@tonic-gate (size_t)udata2)) != 0) { 4110Sstevel@tonic-gate return (error); 4120Sstevel@tonic-gate } 4130Sstevel@tonic-gate 4140Sstevel@tonic-gate if ((error = kcpc_verify_set(t->t_cpc_set)) != 0) { 4150Sstevel@tonic-gate kcpc_free_set(t->t_cpc_set); 4160Sstevel@tonic-gate t->t_cpc_set = NULL; 4170Sstevel@tonic-gate if (copyout(&error, udata3, sizeof (error)) == -1) 4180Sstevel@tonic-gate return (EFAULT); 4190Sstevel@tonic-gate return (EINVAL); 4200Sstevel@tonic-gate } 4210Sstevel@tonic-gate 4220Sstevel@tonic-gate if ((error = kcpc_bind_cpu(t->t_cpc_set, cpuid, &code)) != 0) { 4230Sstevel@tonic-gate kcpc_free_set(t->t_cpc_set); 4240Sstevel@tonic-gate t->t_cpc_set = NULL; 4250Sstevel@tonic-gate /* 4260Sstevel@tonic-gate * Subcodes are only returned for EINVAL and EACCESS. 4270Sstevel@tonic-gate */ 4280Sstevel@tonic-gate if ((error == EINVAL || error == EACCES) && 4290Sstevel@tonic-gate copyout(&code, udata3, sizeof (code)) == -1) 4300Sstevel@tonic-gate return (EFAULT); 4310Sstevel@tonic-gate return (error); 4320Sstevel@tonic-gate } 4330Sstevel@tonic-gate 4340Sstevel@tonic-gate return (0); 4350Sstevel@tonic-gate case CPCIO_SAMPLE: 4360Sstevel@tonic-gate /* 4370Sstevel@tonic-gate * udata1 = pointer to user's buffer 4380Sstevel@tonic-gate * udata2 = pointer to user's hrtime 4390Sstevel@tonic-gate * udata3 = pointer to user's tick 4400Sstevel@tonic-gate */ 4410Sstevel@tonic-gate /* 4420Sstevel@tonic-gate * Only CPU-bound sets may be sampled via the ioctl(). If this 4430Sstevel@tonic-gate * set has no CPU-bound context, return an error. 4440Sstevel@tonic-gate */ 4450Sstevel@tonic-gate if (t->t_cpc_set == NULL) 4460Sstevel@tonic-gate return (EINVAL); 4470Sstevel@tonic-gate if ((error = kcpc_sample(t->t_cpc_set, udata1, udata2, 4480Sstevel@tonic-gate udata3)) != 0) 4490Sstevel@tonic-gate return (error); 4500Sstevel@tonic-gate return (0); 4510Sstevel@tonic-gate case CPCIO_RELE: 4520Sstevel@tonic-gate if (t->t_cpc_set == NULL) 4530Sstevel@tonic-gate return (EINVAL); 4540Sstevel@tonic-gate return (kcpc_unbind(t->t_cpc_set)); 4550Sstevel@tonic-gate default: 4560Sstevel@tonic-gate return (EINVAL); 4570Sstevel@tonic-gate } 4580Sstevel@tonic-gate } 4590Sstevel@tonic-gate 4600Sstevel@tonic-gate /* 4610Sstevel@tonic-gate * The device supports multiple opens, but only one open 4620Sstevel@tonic-gate * is allowed per processor. This is to enable multiple 4630Sstevel@tonic-gate * instances of tools looking at different processors. 4640Sstevel@tonic-gate */ 4650Sstevel@tonic-gate #define KCPC_MINOR_SHARED ((minor_t)0x3fffful) 4660Sstevel@tonic-gate 4670Sstevel@tonic-gate static ulong_t *kcpc_cpumap; /* bitmap of cpus */ 4680Sstevel@tonic-gate 4690Sstevel@tonic-gate /*ARGSUSED1*/ 4700Sstevel@tonic-gate static int 4710Sstevel@tonic-gate kcpc_open(dev_t *dev, int flags, int otyp, cred_t *cr) 4720Sstevel@tonic-gate { 4730Sstevel@tonic-gate processorid_t cpuid; 4740Sstevel@tonic-gate int error; 4750Sstevel@tonic-gate 4760Sstevel@tonic-gate ASSERT(pcbe_ops != NULL); 4770Sstevel@tonic-gate 4780Sstevel@tonic-gate if ((error = secpolicy_cpc_cpu(cr)) != 0) 4790Sstevel@tonic-gate return (error); 4800Sstevel@tonic-gate if (getminor(*dev) != KCPC_MINOR_SHARED) 4810Sstevel@tonic-gate return (ENXIO); 4820Sstevel@tonic-gate if ((cpuid = curthread->t_bind_cpu) == PBIND_NONE) 4830Sstevel@tonic-gate return (EINVAL); 4840Sstevel@tonic-gate if (cpuid > max_cpuid) 4850Sstevel@tonic-gate return (EINVAL); 4860Sstevel@tonic-gate 4870Sstevel@tonic-gate rw_enter(&kcpc_cpuctx_lock, RW_WRITER); 4880Sstevel@tonic-gate if (++kcpc_cpuctx == 1) { 4890Sstevel@tonic-gate ASSERT(kcpc_cpumap == NULL); 4900Sstevel@tonic-gate kcpc_cpumap = kmem_zalloc(BT_SIZEOFMAP(max_cpuid + 1), 4910Sstevel@tonic-gate KM_SLEEP); 4920Sstevel@tonic-gate /* 4930Sstevel@tonic-gate * When this device is open for processor-based contexts, 4940Sstevel@tonic-gate * no further lwp-based contexts can be created. 4950Sstevel@tonic-gate * 4960Sstevel@tonic-gate * Since this is the first open, ensure that all existing 4970Sstevel@tonic-gate * contexts are invalidated. 4980Sstevel@tonic-gate */ 4990Sstevel@tonic-gate kcpc_invalidate_all(); 5000Sstevel@tonic-gate } else if (BT_TEST(kcpc_cpumap, cpuid)) { 5010Sstevel@tonic-gate kcpc_cpuctx--; 5020Sstevel@tonic-gate rw_exit(&kcpc_cpuctx_lock); 5030Sstevel@tonic-gate return (EAGAIN); 5040Sstevel@tonic-gate } else if (kcpc_hw_cpu_hook(cpuid, kcpc_cpumap) != 0) { 5050Sstevel@tonic-gate kcpc_cpuctx--; 5060Sstevel@tonic-gate rw_exit(&kcpc_cpuctx_lock); 5070Sstevel@tonic-gate return (EACCES); 5080Sstevel@tonic-gate } 5090Sstevel@tonic-gate BT_SET(kcpc_cpumap, cpuid); 5100Sstevel@tonic-gate rw_exit(&kcpc_cpuctx_lock); 5110Sstevel@tonic-gate 5120Sstevel@tonic-gate *dev = makedevice(getmajor(*dev), (minor_t)cpuid); 5130Sstevel@tonic-gate 5140Sstevel@tonic-gate return (0); 5150Sstevel@tonic-gate } 5160Sstevel@tonic-gate 5170Sstevel@tonic-gate /*ARGSUSED1*/ 5180Sstevel@tonic-gate static int 5190Sstevel@tonic-gate kcpc_close(dev_t dev, int flags, int otyp, cred_t *cr) 5200Sstevel@tonic-gate { 5210Sstevel@tonic-gate rw_enter(&kcpc_cpuctx_lock, RW_WRITER); 5220Sstevel@tonic-gate BT_CLEAR(kcpc_cpumap, getminor(dev)); 5230Sstevel@tonic-gate if (--kcpc_cpuctx == 0) { 5240Sstevel@tonic-gate kmem_free(kcpc_cpumap, BT_SIZEOFMAP(max_cpuid + 1)); 5250Sstevel@tonic-gate kcpc_cpumap = NULL; 5260Sstevel@tonic-gate } 5270Sstevel@tonic-gate ASSERT(kcpc_cpuctx >= 0); 5280Sstevel@tonic-gate rw_exit(&kcpc_cpuctx_lock); 5290Sstevel@tonic-gate 5300Sstevel@tonic-gate return (0); 5310Sstevel@tonic-gate } 5320Sstevel@tonic-gate 5330Sstevel@tonic-gate /* 5340Sstevel@tonic-gate * Sane boundaries on the size of packed lists. In bytes. 5350Sstevel@tonic-gate */ 5360Sstevel@tonic-gate #define CPC_MIN_PACKSIZE 4 5370Sstevel@tonic-gate #define CPC_MAX_PACKSIZE 10000 5380Sstevel@tonic-gate 5390Sstevel@tonic-gate /* 5400Sstevel@tonic-gate * Sane boundary on the number of requests a set can contain. 5410Sstevel@tonic-gate */ 5420Sstevel@tonic-gate #define CPC_MAX_NREQS 100 5430Sstevel@tonic-gate 5440Sstevel@tonic-gate /* 5450Sstevel@tonic-gate * Sane boundary on the number of attributes a request can contain. 5460Sstevel@tonic-gate */ 5470Sstevel@tonic-gate #define CPC_MAX_ATTRS 50 5480Sstevel@tonic-gate 5490Sstevel@tonic-gate /* 5500Sstevel@tonic-gate * Copy in a packed nvlist from the user and create a request set out of it. 5510Sstevel@tonic-gate * If successful, return 0 and store a pointer to the set we've created. Returns 5520Sstevel@tonic-gate * error code on error. 5530Sstevel@tonic-gate */ 5540Sstevel@tonic-gate int 5550Sstevel@tonic-gate kcpc_copyin_set(kcpc_set_t **inset, void *ubuf, size_t len) 5560Sstevel@tonic-gate { 5570Sstevel@tonic-gate kcpc_set_t *set; 5580Sstevel@tonic-gate int i; 5590Sstevel@tonic-gate int j; 5600Sstevel@tonic-gate char *packbuf; 5610Sstevel@tonic-gate 5620Sstevel@tonic-gate nvlist_t *nvl; 5630Sstevel@tonic-gate nvpair_t *nvp = NULL; 5640Sstevel@tonic-gate 5650Sstevel@tonic-gate nvlist_t *attrs; 5660Sstevel@tonic-gate nvpair_t *nvp_attr; 5670Sstevel@tonic-gate kcpc_attr_t *attrp; 5680Sstevel@tonic-gate 5690Sstevel@tonic-gate nvlist_t **reqlist; 5700Sstevel@tonic-gate uint_t nreqs; 5710Sstevel@tonic-gate uint64_t uint64; 5720Sstevel@tonic-gate uint32_t uint32; 5730Sstevel@tonic-gate uint32_t setflags = (uint32_t)-1; 5740Sstevel@tonic-gate char *string; 5750Sstevel@tonic-gate char *name; 5760Sstevel@tonic-gate 5770Sstevel@tonic-gate if (len < CPC_MIN_PACKSIZE || len > CPC_MAX_PACKSIZE) 5780Sstevel@tonic-gate return (EINVAL); 5790Sstevel@tonic-gate 5800Sstevel@tonic-gate packbuf = kmem_alloc(len, KM_SLEEP); 5810Sstevel@tonic-gate 5820Sstevel@tonic-gate if (copyin(ubuf, packbuf, len) == -1) { 5830Sstevel@tonic-gate kmem_free(packbuf, len); 5840Sstevel@tonic-gate return (EFAULT); 5850Sstevel@tonic-gate } 5860Sstevel@tonic-gate 5870Sstevel@tonic-gate if (nvlist_unpack(packbuf, len, &nvl, KM_SLEEP) != 0) { 5880Sstevel@tonic-gate kmem_free(packbuf, len); 5890Sstevel@tonic-gate return (EINVAL); 5900Sstevel@tonic-gate } 5910Sstevel@tonic-gate 5920Sstevel@tonic-gate /* 5930Sstevel@tonic-gate * The nvlist has been unpacked so there is no need for the packed 5940Sstevel@tonic-gate * representation from this point on. 5950Sstevel@tonic-gate */ 5960Sstevel@tonic-gate kmem_free(packbuf, len); 5970Sstevel@tonic-gate 5980Sstevel@tonic-gate i = 0; 5990Sstevel@tonic-gate while ((nvp = nvlist_next_nvpair(nvl, nvp)) != NULL) { 6000Sstevel@tonic-gate switch (nvpair_type(nvp)) { 6010Sstevel@tonic-gate case DATA_TYPE_UINT32: 6020Sstevel@tonic-gate if (strcmp(nvpair_name(nvp), "flags") != 0 || 6030Sstevel@tonic-gate nvpair_value_uint32(nvp, &setflags) != 0) { 6040Sstevel@tonic-gate nvlist_free(nvl); 6050Sstevel@tonic-gate return (EINVAL); 6060Sstevel@tonic-gate } 6070Sstevel@tonic-gate break; 6080Sstevel@tonic-gate case DATA_TYPE_NVLIST_ARRAY: 6090Sstevel@tonic-gate if (strcmp(nvpair_name(nvp), "reqs") != 0 || 6100Sstevel@tonic-gate nvpair_value_nvlist_array(nvp, &reqlist, 6110Sstevel@tonic-gate &nreqs) != 0) { 6120Sstevel@tonic-gate nvlist_free(nvl); 6130Sstevel@tonic-gate return (EINVAL); 6140Sstevel@tonic-gate } 6150Sstevel@tonic-gate break; 6160Sstevel@tonic-gate default: 6170Sstevel@tonic-gate nvlist_free(nvl); 6180Sstevel@tonic-gate return (EINVAL); 6190Sstevel@tonic-gate } 6200Sstevel@tonic-gate i++; 6210Sstevel@tonic-gate } 6220Sstevel@tonic-gate 6230Sstevel@tonic-gate /* 6240Sstevel@tonic-gate * There should be two members in the top-level nvlist: 6250Sstevel@tonic-gate * an array of nvlists consisting of the requests, and flags. 6260Sstevel@tonic-gate * Anything else is an invalid set. 6270Sstevel@tonic-gate */ 6280Sstevel@tonic-gate if (i != 2) { 6290Sstevel@tonic-gate nvlist_free(nvl); 6300Sstevel@tonic-gate return (EINVAL); 6310Sstevel@tonic-gate } 6320Sstevel@tonic-gate 6330Sstevel@tonic-gate if (nreqs > CPC_MAX_NREQS) { 6340Sstevel@tonic-gate nvlist_free(nvl); 6350Sstevel@tonic-gate return (EINVAL); 6360Sstevel@tonic-gate } 6370Sstevel@tonic-gate 6380Sstevel@tonic-gate /* 6390Sstevel@tonic-gate * The requests are now stored in the nvlist array at reqlist. 640*6275Strevtom * Note that the use of kmem_zalloc() to alloc the kcpc_set_t means 641*6275Strevtom * we don't need to call the init routines for ks_lock and ks_condv. 6420Sstevel@tonic-gate */ 643*6275Strevtom set = kmem_zalloc(sizeof (kcpc_set_t), KM_SLEEP); 6440Sstevel@tonic-gate set->ks_req = (kcpc_request_t *)kmem_zalloc(sizeof (kcpc_request_t) * 6450Sstevel@tonic-gate nreqs, KM_SLEEP); 6460Sstevel@tonic-gate set->ks_nreqs = nreqs; 6470Sstevel@tonic-gate /* 6480Sstevel@tonic-gate * If the nvlist didn't contain a flags member, setflags was initialized 6490Sstevel@tonic-gate * with an illegal value and this set will fail sanity checks later on. 6500Sstevel@tonic-gate */ 6510Sstevel@tonic-gate set->ks_flags = setflags; 652*6275Strevtom /* 653*6275Strevtom * Initialize bind/unbind set synchronization. 654*6275Strevtom */ 655*6275Strevtom set->ks_state &= ~KCPC_SET_BOUND; 6560Sstevel@tonic-gate 6570Sstevel@tonic-gate /* 6580Sstevel@tonic-gate * Build the set up one request at a time, always keeping it self- 6590Sstevel@tonic-gate * consistent so we can give it to kcpc_free_set() if we need to back 6600Sstevel@tonic-gate * out and return and error. 6610Sstevel@tonic-gate */ 6620Sstevel@tonic-gate for (i = 0; i < nreqs; i++) { 6630Sstevel@tonic-gate nvp = NULL; 6640Sstevel@tonic-gate set->ks_req[i].kr_picnum = -1; 6650Sstevel@tonic-gate while ((nvp = nvlist_next_nvpair(reqlist[i], nvp)) != NULL) { 6660Sstevel@tonic-gate name = nvpair_name(nvp); 6670Sstevel@tonic-gate switch (nvpair_type(nvp)) { 6680Sstevel@tonic-gate case DATA_TYPE_UINT32: 6690Sstevel@tonic-gate if (nvpair_value_uint32(nvp, &uint32) == EINVAL) 6700Sstevel@tonic-gate goto inval; 6710Sstevel@tonic-gate if (strcmp(name, "cr_flags") == 0) 6720Sstevel@tonic-gate set->ks_req[i].kr_flags = uint32; 6730Sstevel@tonic-gate if (strcmp(name, "cr_index") == 0) 6740Sstevel@tonic-gate set->ks_req[i].kr_index = uint32; 6750Sstevel@tonic-gate break; 6760Sstevel@tonic-gate case DATA_TYPE_UINT64: 6770Sstevel@tonic-gate if (nvpair_value_uint64(nvp, &uint64) == EINVAL) 6780Sstevel@tonic-gate goto inval; 6790Sstevel@tonic-gate if (strcmp(name, "cr_preset") == 0) 6800Sstevel@tonic-gate set->ks_req[i].kr_preset = uint64; 6810Sstevel@tonic-gate break; 6820Sstevel@tonic-gate case DATA_TYPE_STRING: 6830Sstevel@tonic-gate if (nvpair_value_string(nvp, &string) == EINVAL) 6840Sstevel@tonic-gate goto inval; 6850Sstevel@tonic-gate if (strcmp(name, "cr_event") == 0) 6860Sstevel@tonic-gate (void) strncpy(set->ks_req[i].kr_event, 6870Sstevel@tonic-gate string, CPC_MAX_EVENT_LEN); 6880Sstevel@tonic-gate break; 6890Sstevel@tonic-gate case DATA_TYPE_NVLIST: 6900Sstevel@tonic-gate if (strcmp(name, "cr_attr") != 0) 6910Sstevel@tonic-gate goto inval; 6920Sstevel@tonic-gate if (nvpair_value_nvlist(nvp, &attrs) == EINVAL) 6930Sstevel@tonic-gate goto inval; 6940Sstevel@tonic-gate nvp_attr = NULL; 6950Sstevel@tonic-gate /* 6960Sstevel@tonic-gate * If the picnum has been specified as an 6970Sstevel@tonic-gate * attribute, consume that attribute here and 6980Sstevel@tonic-gate * remove it from the list of attributes. 6990Sstevel@tonic-gate */ 7000Sstevel@tonic-gate if (nvlist_lookup_uint64(attrs, "picnum", 7010Sstevel@tonic-gate &uint64) == 0) { 7020Sstevel@tonic-gate if (nvlist_remove(attrs, "picnum", 7030Sstevel@tonic-gate DATA_TYPE_UINT64) != 0) 7040Sstevel@tonic-gate panic("nvlist %p faulty", 7050Sstevel@tonic-gate attrs); 7060Sstevel@tonic-gate set->ks_req[i].kr_picnum = uint64; 7070Sstevel@tonic-gate } 7080Sstevel@tonic-gate 7090Sstevel@tonic-gate if ((set->ks_req[i].kr_nattrs = 7100Sstevel@tonic-gate kcpc_nvlist_npairs(attrs)) == 0) 7110Sstevel@tonic-gate break; 7120Sstevel@tonic-gate 7130Sstevel@tonic-gate if (set->ks_req[i].kr_nattrs > CPC_MAX_ATTRS) 7140Sstevel@tonic-gate goto inval; 7150Sstevel@tonic-gate 7160Sstevel@tonic-gate set->ks_req[i].kr_attr = 7170Sstevel@tonic-gate kmem_alloc(set->ks_req[i].kr_nattrs * 7180Sstevel@tonic-gate sizeof (kcpc_attr_t), KM_SLEEP); 7190Sstevel@tonic-gate j = 0; 7200Sstevel@tonic-gate 7210Sstevel@tonic-gate while ((nvp_attr = nvlist_next_nvpair(attrs, 7220Sstevel@tonic-gate nvp_attr)) != NULL) { 7230Sstevel@tonic-gate attrp = &set->ks_req[i].kr_attr[j]; 7240Sstevel@tonic-gate 7250Sstevel@tonic-gate if (nvpair_type(nvp_attr) != 7260Sstevel@tonic-gate DATA_TYPE_UINT64) 7270Sstevel@tonic-gate goto inval; 7280Sstevel@tonic-gate 7290Sstevel@tonic-gate (void) strncpy(attrp->ka_name, 7300Sstevel@tonic-gate nvpair_name(nvp_attr), 7310Sstevel@tonic-gate CPC_MAX_ATTR_LEN); 7320Sstevel@tonic-gate 7330Sstevel@tonic-gate if (nvpair_value_uint64(nvp_attr, 7340Sstevel@tonic-gate &(attrp->ka_val)) == EINVAL) 7350Sstevel@tonic-gate goto inval; 7360Sstevel@tonic-gate j++; 7370Sstevel@tonic-gate } 7380Sstevel@tonic-gate ASSERT(j == set->ks_req[i].kr_nattrs); 7390Sstevel@tonic-gate default: 7400Sstevel@tonic-gate break; 7410Sstevel@tonic-gate } 7420Sstevel@tonic-gate } 7430Sstevel@tonic-gate } 7440Sstevel@tonic-gate 7450Sstevel@tonic-gate nvlist_free(nvl); 7460Sstevel@tonic-gate *inset = set; 7470Sstevel@tonic-gate return (0); 7480Sstevel@tonic-gate 7490Sstevel@tonic-gate inval: 7500Sstevel@tonic-gate nvlist_free(nvl); 7510Sstevel@tonic-gate kcpc_free_set(set); 7520Sstevel@tonic-gate return (EINVAL); 7530Sstevel@tonic-gate } 7540Sstevel@tonic-gate 7550Sstevel@tonic-gate /* 7560Sstevel@tonic-gate * Count the number of nvpairs in the supplied nvlist. 7570Sstevel@tonic-gate */ 7580Sstevel@tonic-gate static uint32_t 7590Sstevel@tonic-gate kcpc_nvlist_npairs(nvlist_t *list) 7600Sstevel@tonic-gate { 7610Sstevel@tonic-gate nvpair_t *nvp = NULL; 7620Sstevel@tonic-gate uint32_t n = 0; 7630Sstevel@tonic-gate 7640Sstevel@tonic-gate while ((nvp = nvlist_next_nvpair(list, nvp)) != NULL) 7650Sstevel@tonic-gate n++; 7660Sstevel@tonic-gate 7670Sstevel@tonic-gate return (n); 7680Sstevel@tonic-gate } 7690Sstevel@tonic-gate 7700Sstevel@tonic-gate /* 7710Sstevel@tonic-gate * Performs sanity checks on the given set. 7720Sstevel@tonic-gate * Returns 0 if the set checks out OK. 7730Sstevel@tonic-gate * Returns a detailed error subcode, or -1 if there is no applicable subcode. 7740Sstevel@tonic-gate */ 7750Sstevel@tonic-gate static int 7760Sstevel@tonic-gate kcpc_verify_set(kcpc_set_t *set) 7770Sstevel@tonic-gate { 7780Sstevel@tonic-gate kcpc_request_t *rp; 7790Sstevel@tonic-gate int i; 7800Sstevel@tonic-gate uint64_t bitmap = 0; 7810Sstevel@tonic-gate int n; 7820Sstevel@tonic-gate 7830Sstevel@tonic-gate if (set->ks_nreqs > cpc_ncounters) 7840Sstevel@tonic-gate return (-1); 7850Sstevel@tonic-gate 7860Sstevel@tonic-gate if (CPC_SET_VALID_FLAGS(set->ks_flags) == 0) 7870Sstevel@tonic-gate return (-1); 7880Sstevel@tonic-gate 7890Sstevel@tonic-gate for (i = 0; i < set->ks_nreqs; i++) { 7900Sstevel@tonic-gate rp = &set->ks_req[i]; 7910Sstevel@tonic-gate 7920Sstevel@tonic-gate /* 7930Sstevel@tonic-gate * The following comparison must cast cpc_ncounters to an int, 7940Sstevel@tonic-gate * because kr_picnum will be -1 if the request didn't explicitly 7950Sstevel@tonic-gate * choose a PIC. 7960Sstevel@tonic-gate */ 7970Sstevel@tonic-gate if (rp->kr_picnum >= (int)cpc_ncounters) 7980Sstevel@tonic-gate return (CPC_INVALID_PICNUM); 7990Sstevel@tonic-gate 8000Sstevel@tonic-gate /* 8010Sstevel@tonic-gate * Of the pics whose physical picnum has been specified, make 8020Sstevel@tonic-gate * sure each PIC appears only once in set. 8030Sstevel@tonic-gate */ 8040Sstevel@tonic-gate if ((n = set->ks_req[i].kr_picnum) != -1) { 8050Sstevel@tonic-gate if ((bitmap & (1 << n)) != 0) 8060Sstevel@tonic-gate return (-1); 8070Sstevel@tonic-gate bitmap |= (1 << n); 8080Sstevel@tonic-gate } 8090Sstevel@tonic-gate 8100Sstevel@tonic-gate /* 8110Sstevel@tonic-gate * Make sure the requested index falls within the range of all 8120Sstevel@tonic-gate * requests. 8130Sstevel@tonic-gate */ 8140Sstevel@tonic-gate if (rp->kr_index < 0 || rp->kr_index >= set->ks_nreqs) 8150Sstevel@tonic-gate return (-1); 8160Sstevel@tonic-gate 8170Sstevel@tonic-gate /* 8180Sstevel@tonic-gate * Make sure there are no unknown flags. 8190Sstevel@tonic-gate */ 8200Sstevel@tonic-gate if (KCPC_REQ_VALID_FLAGS(rp->kr_flags) == 0) 8210Sstevel@tonic-gate return (CPC_REQ_INVALID_FLAGS); 8220Sstevel@tonic-gate } 8230Sstevel@tonic-gate 8240Sstevel@tonic-gate return (0); 8250Sstevel@tonic-gate } 8260Sstevel@tonic-gate 8270Sstevel@tonic-gate static struct cb_ops cb_ops = { 8280Sstevel@tonic-gate kcpc_open, 8290Sstevel@tonic-gate kcpc_close, 8300Sstevel@tonic-gate nodev, /* strategy */ 8310Sstevel@tonic-gate nodev, /* print */ 8320Sstevel@tonic-gate nodev, /* dump */ 8330Sstevel@tonic-gate nodev, /* read */ 8340Sstevel@tonic-gate nodev, /* write */ 8350Sstevel@tonic-gate kcpc_ioctl, 8360Sstevel@tonic-gate nodev, /* devmap */ 8370Sstevel@tonic-gate nodev, /* mmap */ 8380Sstevel@tonic-gate nodev, /* segmap */ 8390Sstevel@tonic-gate nochpoll, /* poll */ 8400Sstevel@tonic-gate ddi_prop_op, 8410Sstevel@tonic-gate NULL, 8420Sstevel@tonic-gate D_NEW | D_MP 8430Sstevel@tonic-gate }; 8440Sstevel@tonic-gate 8450Sstevel@tonic-gate /*ARGSUSED*/ 8460Sstevel@tonic-gate static int 8470Sstevel@tonic-gate kcpc_probe(dev_info_t *devi) 8480Sstevel@tonic-gate { 8490Sstevel@tonic-gate return (DDI_PROBE_SUCCESS); 8500Sstevel@tonic-gate } 8510Sstevel@tonic-gate 8520Sstevel@tonic-gate static dev_info_t *kcpc_devi; 8530Sstevel@tonic-gate 8540Sstevel@tonic-gate static int 8550Sstevel@tonic-gate kcpc_attach(dev_info_t *devi, ddi_attach_cmd_t cmd) 8560Sstevel@tonic-gate { 8570Sstevel@tonic-gate if (cmd != DDI_ATTACH) 8580Sstevel@tonic-gate return (DDI_FAILURE); 8590Sstevel@tonic-gate kcpc_devi = devi; 8600Sstevel@tonic-gate return (ddi_create_minor_node(devi, "shared", S_IFCHR, 8610Sstevel@tonic-gate KCPC_MINOR_SHARED, DDI_PSEUDO, 0)); 8620Sstevel@tonic-gate } 8630Sstevel@tonic-gate 8640Sstevel@tonic-gate /*ARGSUSED*/ 8650Sstevel@tonic-gate static int 8660Sstevel@tonic-gate kcpc_getinfo(dev_info_t *devi, ddi_info_cmd_t cmd, void *arg, void **result) 8670Sstevel@tonic-gate { 8680Sstevel@tonic-gate switch (cmd) { 8690Sstevel@tonic-gate case DDI_INFO_DEVT2DEVINFO: 8700Sstevel@tonic-gate switch (getminor((dev_t)arg)) { 8710Sstevel@tonic-gate case KCPC_MINOR_SHARED: 8720Sstevel@tonic-gate *result = kcpc_devi; 8730Sstevel@tonic-gate return (DDI_SUCCESS); 8740Sstevel@tonic-gate default: 8750Sstevel@tonic-gate break; 8760Sstevel@tonic-gate } 8770Sstevel@tonic-gate break; 8780Sstevel@tonic-gate case DDI_INFO_DEVT2INSTANCE: 8790Sstevel@tonic-gate *result = 0; 8800Sstevel@tonic-gate return (DDI_SUCCESS); 8810Sstevel@tonic-gate default: 8820Sstevel@tonic-gate break; 8830Sstevel@tonic-gate } 8840Sstevel@tonic-gate 8850Sstevel@tonic-gate return (DDI_FAILURE); 8860Sstevel@tonic-gate } 8870Sstevel@tonic-gate 8880Sstevel@tonic-gate static struct dev_ops dev_ops = { 8890Sstevel@tonic-gate DEVO_REV, 8900Sstevel@tonic-gate 0, 8910Sstevel@tonic-gate kcpc_getinfo, 8920Sstevel@tonic-gate nulldev, /* identify */ 8930Sstevel@tonic-gate kcpc_probe, 8940Sstevel@tonic-gate kcpc_attach, 8950Sstevel@tonic-gate nodev, /* detach */ 8960Sstevel@tonic-gate nodev, /* reset */ 8970Sstevel@tonic-gate &cb_ops, 8980Sstevel@tonic-gate (struct bus_ops *)0 8990Sstevel@tonic-gate }; 9000Sstevel@tonic-gate 9010Sstevel@tonic-gate static struct modldrv modldrv = { 9020Sstevel@tonic-gate &mod_driverops, 9030Sstevel@tonic-gate "cpc sampling driver v%I%", 9040Sstevel@tonic-gate &dev_ops 9050Sstevel@tonic-gate }; 9060Sstevel@tonic-gate 9070Sstevel@tonic-gate static struct sysent cpc_sysent = { 9080Sstevel@tonic-gate 5, 9090Sstevel@tonic-gate SE_NOUNLOAD | SE_ARGC | SE_32RVAL1, 9100Sstevel@tonic-gate cpc 9110Sstevel@tonic-gate }; 9120Sstevel@tonic-gate 9130Sstevel@tonic-gate static struct modlsys modlsys = { 9140Sstevel@tonic-gate &mod_syscallops, 9150Sstevel@tonic-gate "cpc sampling system call", 9160Sstevel@tonic-gate &cpc_sysent 9170Sstevel@tonic-gate }; 9180Sstevel@tonic-gate 9190Sstevel@tonic-gate #ifdef _SYSCALL32_IMPL 9200Sstevel@tonic-gate static struct modlsys modlsys32 = { 9210Sstevel@tonic-gate &mod_syscallops32, 9220Sstevel@tonic-gate "32-bit cpc sampling system call", 9230Sstevel@tonic-gate &cpc_sysent 9240Sstevel@tonic-gate }; 9250Sstevel@tonic-gate #endif 9260Sstevel@tonic-gate 9270Sstevel@tonic-gate static struct modlinkage modl = { 9280Sstevel@tonic-gate MODREV_1, 9290Sstevel@tonic-gate &modldrv, 9300Sstevel@tonic-gate &modlsys, 9310Sstevel@tonic-gate #ifdef _SYSCALL32_IMPL 9320Sstevel@tonic-gate &modlsys32, 9330Sstevel@tonic-gate #endif 9340Sstevel@tonic-gate }; 9350Sstevel@tonic-gate 9360Sstevel@tonic-gate static void 9370Sstevel@tonic-gate kcpc_init(void) 9380Sstevel@tonic-gate { 9390Sstevel@tonic-gate long hash; 9400Sstevel@tonic-gate 9410Sstevel@tonic-gate rw_init(&kcpc_cpuctx_lock, NULL, RW_DEFAULT, NULL); 9420Sstevel@tonic-gate for (hash = 0; hash < CPC_HASH_BUCKETS; hash++) 9430Sstevel@tonic-gate mutex_init(&kcpc_ctx_llock[hash], 9440Sstevel@tonic-gate NULL, MUTEX_DRIVER, (void *)(uintptr_t)15); 9450Sstevel@tonic-gate } 9460Sstevel@tonic-gate 9470Sstevel@tonic-gate static void 9480Sstevel@tonic-gate kcpc_fini(void) 9490Sstevel@tonic-gate { 9500Sstevel@tonic-gate long hash; 9510Sstevel@tonic-gate 9520Sstevel@tonic-gate for (hash = 0; hash < CPC_HASH_BUCKETS; hash++) 9530Sstevel@tonic-gate mutex_destroy(&kcpc_ctx_llock[hash]); 9540Sstevel@tonic-gate rw_destroy(&kcpc_cpuctx_lock); 9550Sstevel@tonic-gate } 9560Sstevel@tonic-gate 9570Sstevel@tonic-gate int 9580Sstevel@tonic-gate _init(void) 9590Sstevel@tonic-gate { 9600Sstevel@tonic-gate int ret; 9610Sstevel@tonic-gate 9620Sstevel@tonic-gate if (kcpc_hw_load_pcbe() != 0) 9630Sstevel@tonic-gate return (ENOTSUP); 9640Sstevel@tonic-gate 9650Sstevel@tonic-gate kcpc_init(); 9660Sstevel@tonic-gate if ((ret = mod_install(&modl)) != 0) 9670Sstevel@tonic-gate kcpc_fini(); 9680Sstevel@tonic-gate return (ret); 9690Sstevel@tonic-gate } 9700Sstevel@tonic-gate 9710Sstevel@tonic-gate int 9720Sstevel@tonic-gate _fini(void) 9730Sstevel@tonic-gate { 9740Sstevel@tonic-gate int ret; 9750Sstevel@tonic-gate 9760Sstevel@tonic-gate if ((ret = mod_remove(&modl)) == 0) 9770Sstevel@tonic-gate kcpc_fini(); 9780Sstevel@tonic-gate return (ret); 9790Sstevel@tonic-gate } 9800Sstevel@tonic-gate 9810Sstevel@tonic-gate int 9820Sstevel@tonic-gate _info(struct modinfo *mi) 9830Sstevel@tonic-gate { 9840Sstevel@tonic-gate return (mod_info(&modl, mi)); 9850Sstevel@tonic-gate } 986