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