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