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 2004 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 * kernel statistics driver 31*0Sstevel@tonic-gate */ 32*0Sstevel@tonic-gate 33*0Sstevel@tonic-gate #include <sys/types.h> 34*0Sstevel@tonic-gate #include <sys/time.h> 35*0Sstevel@tonic-gate #include <sys/param.h> 36*0Sstevel@tonic-gate #include <sys/sysmacros.h> 37*0Sstevel@tonic-gate #include <sys/file.h> 38*0Sstevel@tonic-gate #include <sys/cmn_err.h> 39*0Sstevel@tonic-gate #include <sys/t_lock.h> 40*0Sstevel@tonic-gate #include <sys/proc.h> 41*0Sstevel@tonic-gate #include <sys/fcntl.h> 42*0Sstevel@tonic-gate #include <sys/uio.h> 43*0Sstevel@tonic-gate #include <sys/kmem.h> 44*0Sstevel@tonic-gate #include <sys/cred.h> 45*0Sstevel@tonic-gate #include <sys/mman.h> 46*0Sstevel@tonic-gate #include <sys/errno.h> 47*0Sstevel@tonic-gate #include <sys/ioccom.h> 48*0Sstevel@tonic-gate #include <sys/cpuvar.h> 49*0Sstevel@tonic-gate #include <sys/stat.h> 50*0Sstevel@tonic-gate #include <sys/conf.h> 51*0Sstevel@tonic-gate #include <sys/ddi.h> 52*0Sstevel@tonic-gate #include <sys/sunddi.h> 53*0Sstevel@tonic-gate #include <sys/modctl.h> 54*0Sstevel@tonic-gate #include <sys/kobj.h> 55*0Sstevel@tonic-gate #include <sys/kstat.h> 56*0Sstevel@tonic-gate #include <sys/atomic.h> 57*0Sstevel@tonic-gate #include <sys/policy.h> 58*0Sstevel@tonic-gate #include <sys/zone.h> 59*0Sstevel@tonic-gate 60*0Sstevel@tonic-gate static dev_info_t *kstat_devi; 61*0Sstevel@tonic-gate 62*0Sstevel@tonic-gate static int 63*0Sstevel@tonic-gate read_kstat_data(int *rvalp, void *user_ksp, int flag) 64*0Sstevel@tonic-gate { 65*0Sstevel@tonic-gate kstat_t user_kstat, *ksp; 66*0Sstevel@tonic-gate #ifdef _MULTI_DATAMODEL 67*0Sstevel@tonic-gate kstat32_t user_kstat32; 68*0Sstevel@tonic-gate #endif 69*0Sstevel@tonic-gate void *kbuf = NULL; 70*0Sstevel@tonic-gate size_t kbufsize, ubufsize, copysize; 71*0Sstevel@tonic-gate int error = 0; 72*0Sstevel@tonic-gate uint_t model; 73*0Sstevel@tonic-gate 74*0Sstevel@tonic-gate switch (model = ddi_model_convert_from(flag & FMODELS)) { 75*0Sstevel@tonic-gate #ifdef _MULTI_DATAMODEL 76*0Sstevel@tonic-gate case DDI_MODEL_ILP32: 77*0Sstevel@tonic-gate if (copyin(user_ksp, &user_kstat32, sizeof (kstat32_t)) != 0) 78*0Sstevel@tonic-gate return (EFAULT); 79*0Sstevel@tonic-gate user_kstat.ks_kid = user_kstat32.ks_kid; 80*0Sstevel@tonic-gate user_kstat.ks_data = (void *)(uintptr_t)user_kstat32.ks_data; 81*0Sstevel@tonic-gate user_kstat.ks_data_size = (size_t)user_kstat32.ks_data_size; 82*0Sstevel@tonic-gate break; 83*0Sstevel@tonic-gate #endif 84*0Sstevel@tonic-gate default: 85*0Sstevel@tonic-gate case DDI_MODEL_NONE: 86*0Sstevel@tonic-gate if (copyin(user_ksp, &user_kstat, sizeof (kstat_t)) != 0) 87*0Sstevel@tonic-gate return (EFAULT); 88*0Sstevel@tonic-gate } 89*0Sstevel@tonic-gate 90*0Sstevel@tonic-gate ksp = kstat_hold_bykid(user_kstat.ks_kid, getzoneid()); 91*0Sstevel@tonic-gate if (ksp == NULL) { 92*0Sstevel@tonic-gate /* 93*0Sstevel@tonic-gate * There is no kstat with the specified KID 94*0Sstevel@tonic-gate */ 95*0Sstevel@tonic-gate return (ENXIO); 96*0Sstevel@tonic-gate } 97*0Sstevel@tonic-gate if (ksp->ks_flags & KSTAT_FLAG_INVALID) { 98*0Sstevel@tonic-gate /* 99*0Sstevel@tonic-gate * The kstat exists, but is momentarily in some 100*0Sstevel@tonic-gate * indeterminate state (e.g. the data section is not 101*0Sstevel@tonic-gate * yet initialized). Try again in a few milliseconds. 102*0Sstevel@tonic-gate */ 103*0Sstevel@tonic-gate kstat_rele(ksp); 104*0Sstevel@tonic-gate return (EAGAIN); 105*0Sstevel@tonic-gate } 106*0Sstevel@tonic-gate 107*0Sstevel@tonic-gate /* 108*0Sstevel@tonic-gate * If it's a fixed-size kstat, allocate the buffer now, so we 109*0Sstevel@tonic-gate * don't have to do it under the kstat's data lock. (If it's a 110*0Sstevel@tonic-gate * var-size kstat, we don't know the size until after the update 111*0Sstevel@tonic-gate * routine is called, so we can't do this optimization.) 112*0Sstevel@tonic-gate * The allocator relies on this behavior to prevent recursive 113*0Sstevel@tonic-gate * mutex_enter in its (fixed-size) kstat update routine. 114*0Sstevel@tonic-gate * It's a zalloc to prevent unintentional exposure of random 115*0Sstevel@tonic-gate * juicy morsels of (old) kernel data. 116*0Sstevel@tonic-gate */ 117*0Sstevel@tonic-gate if (!(ksp->ks_flags & KSTAT_FLAG_VAR_SIZE)) { 118*0Sstevel@tonic-gate kbufsize = ksp->ks_data_size; 119*0Sstevel@tonic-gate kbuf = kmem_zalloc(kbufsize + 1, KM_NOSLEEP); 120*0Sstevel@tonic-gate if (kbuf == NULL) { 121*0Sstevel@tonic-gate kstat_rele(ksp); 122*0Sstevel@tonic-gate return (EAGAIN); 123*0Sstevel@tonic-gate } 124*0Sstevel@tonic-gate } 125*0Sstevel@tonic-gate KSTAT_ENTER(ksp); 126*0Sstevel@tonic-gate if ((error = KSTAT_UPDATE(ksp, KSTAT_READ)) != 0) { 127*0Sstevel@tonic-gate KSTAT_EXIT(ksp); 128*0Sstevel@tonic-gate kstat_rele(ksp); 129*0Sstevel@tonic-gate if (kbuf != NULL) 130*0Sstevel@tonic-gate kmem_free(kbuf, kbufsize + 1); 131*0Sstevel@tonic-gate return (error); 132*0Sstevel@tonic-gate } 133*0Sstevel@tonic-gate 134*0Sstevel@tonic-gate kbufsize = ksp->ks_data_size; 135*0Sstevel@tonic-gate ubufsize = user_kstat.ks_data_size; 136*0Sstevel@tonic-gate 137*0Sstevel@tonic-gate if (ubufsize < kbufsize) { 138*0Sstevel@tonic-gate error = ENOMEM; 139*0Sstevel@tonic-gate } else { 140*0Sstevel@tonic-gate if (kbuf == NULL) 141*0Sstevel@tonic-gate kbuf = kmem_zalloc(kbufsize + 1, KM_NOSLEEP); 142*0Sstevel@tonic-gate if (kbuf == NULL) { 143*0Sstevel@tonic-gate error = EAGAIN; 144*0Sstevel@tonic-gate } else { 145*0Sstevel@tonic-gate error = KSTAT_SNAPSHOT(ksp, kbuf, KSTAT_READ); 146*0Sstevel@tonic-gate } 147*0Sstevel@tonic-gate } 148*0Sstevel@tonic-gate 149*0Sstevel@tonic-gate /* 150*0Sstevel@tonic-gate * The following info must be returned to user level, 151*0Sstevel@tonic-gate * even if the the update or snapshot failed. This allows 152*0Sstevel@tonic-gate * kstat readers to get a handle on variable-size kstats, 153*0Sstevel@tonic-gate * detect dormant kstats, etc. 154*0Sstevel@tonic-gate */ 155*0Sstevel@tonic-gate user_kstat.ks_ndata = ksp->ks_ndata; 156*0Sstevel@tonic-gate user_kstat.ks_data_size = kbufsize; 157*0Sstevel@tonic-gate user_kstat.ks_flags = ksp->ks_flags; 158*0Sstevel@tonic-gate user_kstat.ks_snaptime = ksp->ks_snaptime; 159*0Sstevel@tonic-gate 160*0Sstevel@tonic-gate *rvalp = kstat_chain_id; 161*0Sstevel@tonic-gate KSTAT_EXIT(ksp); 162*0Sstevel@tonic-gate kstat_rele(ksp); 163*0Sstevel@tonic-gate 164*0Sstevel@tonic-gate /* 165*0Sstevel@tonic-gate * Copy the buffer containing the kstat back to userland. 166*0Sstevel@tonic-gate */ 167*0Sstevel@tonic-gate copysize = kbufsize; 168*0Sstevel@tonic-gate if (kbuf != NULL) { 169*0Sstevel@tonic-gate #ifdef _MULTI_DATAMODEL 170*0Sstevel@tonic-gate kstat32_t *k32; 171*0Sstevel@tonic-gate kstat_t *k; 172*0Sstevel@tonic-gate #endif 173*0Sstevel@tonic-gate int i; 174*0Sstevel@tonic-gate 175*0Sstevel@tonic-gate switch (model) { 176*0Sstevel@tonic-gate #ifdef _MULTI_DATAMODEL 177*0Sstevel@tonic-gate case DDI_MODEL_ILP32: 178*0Sstevel@tonic-gate 179*0Sstevel@tonic-gate if (ksp->ks_type == KSTAT_TYPE_NAMED) { 180*0Sstevel@tonic-gate kstat_named_t *kn = kbuf; 181*0Sstevel@tonic-gate 182*0Sstevel@tonic-gate for (i = 0; i < user_kstat.ks_ndata; kn++, i++) 183*0Sstevel@tonic-gate switch (kn->data_type) { 184*0Sstevel@tonic-gate /* 185*0Sstevel@tonic-gate * Named statistics have fields of type 186*0Sstevel@tonic-gate * 'long'. For a 32-bit application 187*0Sstevel@tonic-gate * looking at a 64-bit kernel, 188*0Sstevel@tonic-gate * forcibly truncate these 64-bit 189*0Sstevel@tonic-gate * quantities to 32-bit values. 190*0Sstevel@tonic-gate */ 191*0Sstevel@tonic-gate case KSTAT_DATA_LONG: 192*0Sstevel@tonic-gate kn->value.i32 = 193*0Sstevel@tonic-gate (int32_t)kn->value.l; 194*0Sstevel@tonic-gate kn->data_type = 195*0Sstevel@tonic-gate KSTAT_DATA_INT32; 196*0Sstevel@tonic-gate break; 197*0Sstevel@tonic-gate case KSTAT_DATA_ULONG: 198*0Sstevel@tonic-gate kn->value.ui32 = 199*0Sstevel@tonic-gate (uint32_t)kn->value.ul; 200*0Sstevel@tonic-gate kn->data_type = 201*0Sstevel@tonic-gate KSTAT_DATA_UINT32; 202*0Sstevel@tonic-gate break; 203*0Sstevel@tonic-gate /* 204*0Sstevel@tonic-gate * Long strings must be massaged before 205*0Sstevel@tonic-gate * being copied out to userland. Do 206*0Sstevel@tonic-gate * that here. 207*0Sstevel@tonic-gate */ 208*0Sstevel@tonic-gate case KSTAT_DATA_STRING: 209*0Sstevel@tonic-gate if (KSTAT_NAMED_STR_PTR(kn) 210*0Sstevel@tonic-gate == NULL) 211*0Sstevel@tonic-gate break; 212*0Sstevel@tonic-gate /* 213*0Sstevel@tonic-gate * The offsets within the 214*0Sstevel@tonic-gate * buffers are the same, so add 215*0Sstevel@tonic-gate * the offset to the beginning 216*0Sstevel@tonic-gate * of the new buffer to fix the 217*0Sstevel@tonic-gate * pointer. 218*0Sstevel@tonic-gate */ 219*0Sstevel@tonic-gate KSTAT_NAMED_STR_PTR(kn) = 220*0Sstevel@tonic-gate (char *)user_kstat.ks_data + 221*0Sstevel@tonic-gate (KSTAT_NAMED_STR_PTR(kn) - 222*0Sstevel@tonic-gate (char *)kbuf); 223*0Sstevel@tonic-gate /* 224*0Sstevel@tonic-gate * Make sure the string pointer 225*0Sstevel@tonic-gate * lies within the allocated 226*0Sstevel@tonic-gate * buffer. 227*0Sstevel@tonic-gate */ 228*0Sstevel@tonic-gate ASSERT(KSTAT_NAMED_STR_PTR(kn) + 229*0Sstevel@tonic-gate KSTAT_NAMED_STR_BUFLEN(kn) 230*0Sstevel@tonic-gate <= 231*0Sstevel@tonic-gate ((char *) 232*0Sstevel@tonic-gate user_kstat.ks_data + 233*0Sstevel@tonic-gate ubufsize)); 234*0Sstevel@tonic-gate ASSERT(KSTAT_NAMED_STR_PTR(kn) 235*0Sstevel@tonic-gate >= 236*0Sstevel@tonic-gate (char *) 237*0Sstevel@tonic-gate ((kstat_named_t *) 238*0Sstevel@tonic-gate user_kstat.ks_data + 239*0Sstevel@tonic-gate user_kstat.ks_ndata)); 240*0Sstevel@tonic-gate /* 241*0Sstevel@tonic-gate * Cast 64-bit ptr to 32-bit. 242*0Sstevel@tonic-gate */ 243*0Sstevel@tonic-gate kn->value.string.addr.ptr32 = 244*0Sstevel@tonic-gate (caddr32_t)(uintptr_t) 245*0Sstevel@tonic-gate KSTAT_NAMED_STR_PTR(kn); 246*0Sstevel@tonic-gate break; 247*0Sstevel@tonic-gate default: 248*0Sstevel@tonic-gate break; 249*0Sstevel@tonic-gate } 250*0Sstevel@tonic-gate } 251*0Sstevel@tonic-gate 252*0Sstevel@tonic-gate if (user_kstat.ks_kid != 0) 253*0Sstevel@tonic-gate break; 254*0Sstevel@tonic-gate 255*0Sstevel@tonic-gate /* 256*0Sstevel@tonic-gate * This is the special case of the kstat header 257*0Sstevel@tonic-gate * list for the entire system. Reshape the 258*0Sstevel@tonic-gate * array in place, then copy it out. 259*0Sstevel@tonic-gate */ 260*0Sstevel@tonic-gate k32 = kbuf; 261*0Sstevel@tonic-gate k = kbuf; 262*0Sstevel@tonic-gate for (i = 0; i < user_kstat.ks_ndata; k32++, k++, i++) { 263*0Sstevel@tonic-gate k32->ks_crtime = k->ks_crtime; 264*0Sstevel@tonic-gate k32->ks_next = 0; 265*0Sstevel@tonic-gate k32->ks_kid = k->ks_kid; 266*0Sstevel@tonic-gate (void) strcpy(k32->ks_module, k->ks_module); 267*0Sstevel@tonic-gate k32->ks_resv = k->ks_resv; 268*0Sstevel@tonic-gate k32->ks_instance = k->ks_instance; 269*0Sstevel@tonic-gate (void) strcpy(k32->ks_name, k->ks_name); 270*0Sstevel@tonic-gate k32->ks_type = k->ks_type; 271*0Sstevel@tonic-gate (void) strcpy(k32->ks_class, k->ks_class); 272*0Sstevel@tonic-gate k32->ks_flags = k->ks_flags; 273*0Sstevel@tonic-gate k32->ks_data = 0; 274*0Sstevel@tonic-gate k32->ks_ndata = k->ks_ndata; 275*0Sstevel@tonic-gate if (k->ks_data_size > UINT32_MAX) { 276*0Sstevel@tonic-gate error = EOVERFLOW; 277*0Sstevel@tonic-gate break; 278*0Sstevel@tonic-gate } 279*0Sstevel@tonic-gate k32->ks_data_size = (size32_t)k->ks_data_size; 280*0Sstevel@tonic-gate k32->ks_snaptime = k->ks_snaptime; 281*0Sstevel@tonic-gate } 282*0Sstevel@tonic-gate 283*0Sstevel@tonic-gate /* 284*0Sstevel@tonic-gate * XXX In this case we copy less data than is 285*0Sstevel@tonic-gate * claimed in the header. 286*0Sstevel@tonic-gate */ 287*0Sstevel@tonic-gate copysize = user_kstat.ks_ndata * sizeof (kstat32_t); 288*0Sstevel@tonic-gate break; 289*0Sstevel@tonic-gate #endif /* _MULTI_DATAMODEL */ 290*0Sstevel@tonic-gate default: 291*0Sstevel@tonic-gate case DDI_MODEL_NONE: 292*0Sstevel@tonic-gate if (ksp->ks_type == KSTAT_TYPE_NAMED) { 293*0Sstevel@tonic-gate kstat_named_t *kn = kbuf; 294*0Sstevel@tonic-gate 295*0Sstevel@tonic-gate for (i = 0; i < user_kstat.ks_ndata; kn++, i++) 296*0Sstevel@tonic-gate switch (kn->data_type) { 297*0Sstevel@tonic-gate #ifdef _LP64 298*0Sstevel@tonic-gate case KSTAT_DATA_LONG: 299*0Sstevel@tonic-gate kn->data_type = 300*0Sstevel@tonic-gate KSTAT_DATA_INT64; 301*0Sstevel@tonic-gate break; 302*0Sstevel@tonic-gate case KSTAT_DATA_ULONG: 303*0Sstevel@tonic-gate kn->data_type = 304*0Sstevel@tonic-gate KSTAT_DATA_UINT64; 305*0Sstevel@tonic-gate break; 306*0Sstevel@tonic-gate #endif /* _LP64 */ 307*0Sstevel@tonic-gate case KSTAT_DATA_STRING: 308*0Sstevel@tonic-gate if (KSTAT_NAMED_STR_PTR(kn) 309*0Sstevel@tonic-gate == NULL) 310*0Sstevel@tonic-gate break; 311*0Sstevel@tonic-gate KSTAT_NAMED_STR_PTR(kn) = 312*0Sstevel@tonic-gate (char *)user_kstat.ks_data + 313*0Sstevel@tonic-gate (KSTAT_NAMED_STR_PTR(kn) - 314*0Sstevel@tonic-gate (char *)kbuf); 315*0Sstevel@tonic-gate ASSERT(KSTAT_NAMED_STR_PTR(kn) + 316*0Sstevel@tonic-gate KSTAT_NAMED_STR_BUFLEN(kn) 317*0Sstevel@tonic-gate <= 318*0Sstevel@tonic-gate ((char *) 319*0Sstevel@tonic-gate user_kstat.ks_data + 320*0Sstevel@tonic-gate ubufsize)); 321*0Sstevel@tonic-gate ASSERT(KSTAT_NAMED_STR_PTR(kn) 322*0Sstevel@tonic-gate >= 323*0Sstevel@tonic-gate (char *) 324*0Sstevel@tonic-gate ((kstat_named_t *) 325*0Sstevel@tonic-gate user_kstat.ks_data + 326*0Sstevel@tonic-gate user_kstat.ks_ndata)); 327*0Sstevel@tonic-gate break; 328*0Sstevel@tonic-gate default: 329*0Sstevel@tonic-gate break; 330*0Sstevel@tonic-gate } 331*0Sstevel@tonic-gate } 332*0Sstevel@tonic-gate break; 333*0Sstevel@tonic-gate } 334*0Sstevel@tonic-gate 335*0Sstevel@tonic-gate if (error == 0 && 336*0Sstevel@tonic-gate copyout(kbuf, user_kstat.ks_data, copysize)) 337*0Sstevel@tonic-gate error = EFAULT; 338*0Sstevel@tonic-gate kmem_free(kbuf, kbufsize + 1); 339*0Sstevel@tonic-gate } 340*0Sstevel@tonic-gate 341*0Sstevel@tonic-gate /* 342*0Sstevel@tonic-gate * We have modified the ks_ndata, ks_data_size, ks_flags, and 343*0Sstevel@tonic-gate * ks_snaptime fields of the user kstat; now copy it back to userland. 344*0Sstevel@tonic-gate */ 345*0Sstevel@tonic-gate switch (model) { 346*0Sstevel@tonic-gate #ifdef _MULTI_DATAMODEL 347*0Sstevel@tonic-gate case DDI_MODEL_ILP32: 348*0Sstevel@tonic-gate if (kbufsize > UINT32_MAX) { 349*0Sstevel@tonic-gate error = EOVERFLOW; 350*0Sstevel@tonic-gate break; 351*0Sstevel@tonic-gate } 352*0Sstevel@tonic-gate user_kstat32.ks_ndata = user_kstat.ks_ndata; 353*0Sstevel@tonic-gate user_kstat32.ks_data_size = (size32_t)kbufsize; 354*0Sstevel@tonic-gate user_kstat32.ks_flags = user_kstat.ks_flags; 355*0Sstevel@tonic-gate user_kstat32.ks_snaptime = user_kstat.ks_snaptime; 356*0Sstevel@tonic-gate if (copyout(&user_kstat32, user_ksp, sizeof (kstat32_t)) && 357*0Sstevel@tonic-gate error == 0) 358*0Sstevel@tonic-gate error = EFAULT; 359*0Sstevel@tonic-gate break; 360*0Sstevel@tonic-gate #endif 361*0Sstevel@tonic-gate default: 362*0Sstevel@tonic-gate case DDI_MODEL_NONE: 363*0Sstevel@tonic-gate if (copyout(&user_kstat, user_ksp, sizeof (kstat_t)) && 364*0Sstevel@tonic-gate error == 0) 365*0Sstevel@tonic-gate error = EFAULT; 366*0Sstevel@tonic-gate break; 367*0Sstevel@tonic-gate } 368*0Sstevel@tonic-gate 369*0Sstevel@tonic-gate return (error); 370*0Sstevel@tonic-gate } 371*0Sstevel@tonic-gate 372*0Sstevel@tonic-gate static int 373*0Sstevel@tonic-gate write_kstat_data(int *rvalp, void *user_ksp, int flag, cred_t *cred) 374*0Sstevel@tonic-gate { 375*0Sstevel@tonic-gate kstat_t user_kstat, *ksp; 376*0Sstevel@tonic-gate void *buf = NULL; 377*0Sstevel@tonic-gate size_t bufsize; 378*0Sstevel@tonic-gate int error = 0; 379*0Sstevel@tonic-gate 380*0Sstevel@tonic-gate if (secpolicy_sys_config(cred, B_FALSE) != 0) 381*0Sstevel@tonic-gate return (EPERM); 382*0Sstevel@tonic-gate 383*0Sstevel@tonic-gate switch (ddi_model_convert_from(flag & FMODELS)) { 384*0Sstevel@tonic-gate #ifdef _MULTI_DATAMODEL 385*0Sstevel@tonic-gate kstat32_t user_kstat32; 386*0Sstevel@tonic-gate 387*0Sstevel@tonic-gate case DDI_MODEL_ILP32: 388*0Sstevel@tonic-gate if (copyin(user_ksp, &user_kstat32, sizeof (kstat32_t))) 389*0Sstevel@tonic-gate return (EFAULT); 390*0Sstevel@tonic-gate /* 391*0Sstevel@tonic-gate * These are the only fields we actually look at. 392*0Sstevel@tonic-gate */ 393*0Sstevel@tonic-gate user_kstat.ks_kid = user_kstat32.ks_kid; 394*0Sstevel@tonic-gate user_kstat.ks_data = (void *)(uintptr_t)user_kstat32.ks_data; 395*0Sstevel@tonic-gate user_kstat.ks_data_size = (size_t)user_kstat32.ks_data_size; 396*0Sstevel@tonic-gate user_kstat.ks_ndata = user_kstat32.ks_ndata; 397*0Sstevel@tonic-gate break; 398*0Sstevel@tonic-gate #endif 399*0Sstevel@tonic-gate default: 400*0Sstevel@tonic-gate case DDI_MODEL_NONE: 401*0Sstevel@tonic-gate if (copyin(user_ksp, &user_kstat, sizeof (kstat_t))) 402*0Sstevel@tonic-gate return (EFAULT); 403*0Sstevel@tonic-gate } 404*0Sstevel@tonic-gate 405*0Sstevel@tonic-gate bufsize = user_kstat.ks_data_size; 406*0Sstevel@tonic-gate buf = kmem_alloc(bufsize + 1, KM_NOSLEEP); 407*0Sstevel@tonic-gate if (buf == NULL) 408*0Sstevel@tonic-gate return (EAGAIN); 409*0Sstevel@tonic-gate 410*0Sstevel@tonic-gate if (copyin(user_kstat.ks_data, buf, bufsize)) { 411*0Sstevel@tonic-gate kmem_free(buf, bufsize + 1); 412*0Sstevel@tonic-gate return (EFAULT); 413*0Sstevel@tonic-gate } 414*0Sstevel@tonic-gate 415*0Sstevel@tonic-gate ksp = kstat_hold_bykid(user_kstat.ks_kid, getzoneid()); 416*0Sstevel@tonic-gate if (ksp == NULL) { 417*0Sstevel@tonic-gate kmem_free(buf, bufsize + 1); 418*0Sstevel@tonic-gate return (ENXIO); 419*0Sstevel@tonic-gate } 420*0Sstevel@tonic-gate if (ksp->ks_flags & KSTAT_FLAG_INVALID) { 421*0Sstevel@tonic-gate kstat_rele(ksp); 422*0Sstevel@tonic-gate kmem_free(buf, bufsize + 1); 423*0Sstevel@tonic-gate return (EAGAIN); 424*0Sstevel@tonic-gate } 425*0Sstevel@tonic-gate if (!(ksp->ks_flags & KSTAT_FLAG_WRITABLE)) { 426*0Sstevel@tonic-gate kstat_rele(ksp); 427*0Sstevel@tonic-gate kmem_free(buf, bufsize + 1); 428*0Sstevel@tonic-gate return (EACCES); 429*0Sstevel@tonic-gate } 430*0Sstevel@tonic-gate 431*0Sstevel@tonic-gate /* 432*0Sstevel@tonic-gate * With KSTAT_FLAG_VARIABLE, one must call the kstat's update callback 433*0Sstevel@tonic-gate * routine to ensure ks_data_size is up to date. 434*0Sstevel@tonic-gate * In this case it makes sense to do it anyhow, as it will be shortly 435*0Sstevel@tonic-gate * followed by a KSTAT_SNAPSHOT(). 436*0Sstevel@tonic-gate */ 437*0Sstevel@tonic-gate KSTAT_ENTER(ksp); 438*0Sstevel@tonic-gate error = KSTAT_UPDATE(ksp, KSTAT_READ); 439*0Sstevel@tonic-gate if (error || user_kstat.ks_data_size != ksp->ks_data_size || 440*0Sstevel@tonic-gate user_kstat.ks_ndata != ksp->ks_ndata) { 441*0Sstevel@tonic-gate KSTAT_EXIT(ksp); 442*0Sstevel@tonic-gate kstat_rele(ksp); 443*0Sstevel@tonic-gate kmem_free(buf, bufsize + 1); 444*0Sstevel@tonic-gate return (error ? error : EINVAL); 445*0Sstevel@tonic-gate } 446*0Sstevel@tonic-gate 447*0Sstevel@tonic-gate /* 448*0Sstevel@tonic-gate * We have to ensure that we don't accidentally change the type of 449*0Sstevel@tonic-gate * existing kstat_named statistics when writing over them. 450*0Sstevel@tonic-gate * Since read_kstat_data() modifies some of the types on their way 451*0Sstevel@tonic-gate * out, we need to be sure to handle these types seperately. 452*0Sstevel@tonic-gate */ 453*0Sstevel@tonic-gate if (ksp->ks_type == KSTAT_TYPE_NAMED) { 454*0Sstevel@tonic-gate void *kbuf; 455*0Sstevel@tonic-gate kstat_named_t *kold; 456*0Sstevel@tonic-gate kstat_named_t *knew = buf; 457*0Sstevel@tonic-gate int i; 458*0Sstevel@tonic-gate 459*0Sstevel@tonic-gate #ifdef _MULTI_DATAMODEL 460*0Sstevel@tonic-gate int model = ddi_model_convert_from(flag & FMODELS); 461*0Sstevel@tonic-gate #endif 462*0Sstevel@tonic-gate 463*0Sstevel@tonic-gate /* 464*0Sstevel@tonic-gate * Since ksp->ks_data may be NULL, we need to take a snapshot 465*0Sstevel@tonic-gate * of the published data to look at the types. 466*0Sstevel@tonic-gate */ 467*0Sstevel@tonic-gate kbuf = kmem_alloc(bufsize + 1, KM_NOSLEEP); 468*0Sstevel@tonic-gate if (kbuf == NULL) { 469*0Sstevel@tonic-gate KSTAT_EXIT(ksp); 470*0Sstevel@tonic-gate kstat_rele(ksp); 471*0Sstevel@tonic-gate kmem_free(buf, bufsize + 1); 472*0Sstevel@tonic-gate return (EAGAIN); 473*0Sstevel@tonic-gate } 474*0Sstevel@tonic-gate error = KSTAT_SNAPSHOT(ksp, kbuf, KSTAT_READ); 475*0Sstevel@tonic-gate if (error) { 476*0Sstevel@tonic-gate KSTAT_EXIT(ksp); 477*0Sstevel@tonic-gate kstat_rele(ksp); 478*0Sstevel@tonic-gate kmem_free(kbuf, bufsize + 1); 479*0Sstevel@tonic-gate kmem_free(buf, bufsize + 1); 480*0Sstevel@tonic-gate return (error); 481*0Sstevel@tonic-gate } 482*0Sstevel@tonic-gate kold = kbuf; 483*0Sstevel@tonic-gate 484*0Sstevel@tonic-gate /* 485*0Sstevel@tonic-gate * read_kstat_data() changes the types of 486*0Sstevel@tonic-gate * KSTAT_DATA_LONG / KSTAT_DATA_ULONG, so we need to 487*0Sstevel@tonic-gate * make sure that these (modified) types are considered 488*0Sstevel@tonic-gate * valid. 489*0Sstevel@tonic-gate */ 490*0Sstevel@tonic-gate for (i = 0; i < ksp->ks_ndata; i++, kold++, knew++) { 491*0Sstevel@tonic-gate switch (kold->data_type) { 492*0Sstevel@tonic-gate #ifdef _MULTI_DATAMODEL 493*0Sstevel@tonic-gate case KSTAT_DATA_LONG: 494*0Sstevel@tonic-gate switch (model) { 495*0Sstevel@tonic-gate case DDI_MODEL_ILP32: 496*0Sstevel@tonic-gate if (knew->data_type == 497*0Sstevel@tonic-gate KSTAT_DATA_INT32) { 498*0Sstevel@tonic-gate knew->value.l = 499*0Sstevel@tonic-gate (long)knew->value.i32; 500*0Sstevel@tonic-gate knew->data_type = 501*0Sstevel@tonic-gate KSTAT_DATA_LONG; 502*0Sstevel@tonic-gate } 503*0Sstevel@tonic-gate break; 504*0Sstevel@tonic-gate default: 505*0Sstevel@tonic-gate case DDI_MODEL_NONE: 506*0Sstevel@tonic-gate #ifdef _LP64 507*0Sstevel@tonic-gate if (knew->data_type == 508*0Sstevel@tonic-gate KSTAT_DATA_INT64) { 509*0Sstevel@tonic-gate knew->value.l = 510*0Sstevel@tonic-gate (long)knew->value.i64; 511*0Sstevel@tonic-gate knew->data_type = 512*0Sstevel@tonic-gate KSTAT_DATA_LONG; 513*0Sstevel@tonic-gate } 514*0Sstevel@tonic-gate #endif /* _LP64 */ 515*0Sstevel@tonic-gate break; 516*0Sstevel@tonic-gate } 517*0Sstevel@tonic-gate break; 518*0Sstevel@tonic-gate case KSTAT_DATA_ULONG: 519*0Sstevel@tonic-gate switch (model) { 520*0Sstevel@tonic-gate case DDI_MODEL_ILP32: 521*0Sstevel@tonic-gate if (knew->data_type == 522*0Sstevel@tonic-gate KSTAT_DATA_UINT32) { 523*0Sstevel@tonic-gate knew->value.ul = 524*0Sstevel@tonic-gate (ulong_t)knew->value.ui32; 525*0Sstevel@tonic-gate knew->data_type = 526*0Sstevel@tonic-gate KSTAT_DATA_ULONG; 527*0Sstevel@tonic-gate } 528*0Sstevel@tonic-gate break; 529*0Sstevel@tonic-gate default: 530*0Sstevel@tonic-gate case DDI_MODEL_NONE: 531*0Sstevel@tonic-gate #ifdef _LP64 532*0Sstevel@tonic-gate if (knew->data_type == 533*0Sstevel@tonic-gate KSTAT_DATA_UINT64) { 534*0Sstevel@tonic-gate knew->value.ul = 535*0Sstevel@tonic-gate (ulong_t)knew->value.ui64; 536*0Sstevel@tonic-gate knew->data_type = 537*0Sstevel@tonic-gate KSTAT_DATA_ULONG; 538*0Sstevel@tonic-gate } 539*0Sstevel@tonic-gate #endif /* _LP64 */ 540*0Sstevel@tonic-gate break; 541*0Sstevel@tonic-gate } 542*0Sstevel@tonic-gate break; 543*0Sstevel@tonic-gate #endif /* _MULTI_DATAMODEL */ 544*0Sstevel@tonic-gate case KSTAT_DATA_STRING: 545*0Sstevel@tonic-gate if (knew->data_type != KSTAT_DATA_STRING) { 546*0Sstevel@tonic-gate KSTAT_EXIT(ksp); 547*0Sstevel@tonic-gate kstat_rele(ksp); 548*0Sstevel@tonic-gate kmem_free(kbuf, bufsize + 1); 549*0Sstevel@tonic-gate kmem_free(buf, bufsize + 1); 550*0Sstevel@tonic-gate return (EINVAL); 551*0Sstevel@tonic-gate } 552*0Sstevel@tonic-gate 553*0Sstevel@tonic-gate #ifdef _MULTI_DATAMODEL 554*0Sstevel@tonic-gate if (model == DDI_MODEL_ILP32) 555*0Sstevel@tonic-gate KSTAT_NAMED_STR_PTR(knew) = 556*0Sstevel@tonic-gate (char *)(uintptr_t) 557*0Sstevel@tonic-gate knew->value.string.addr.ptr32; 558*0Sstevel@tonic-gate #endif 559*0Sstevel@tonic-gate /* 560*0Sstevel@tonic-gate * Nothing special for NULL 561*0Sstevel@tonic-gate */ 562*0Sstevel@tonic-gate if (KSTAT_NAMED_STR_PTR(knew) == NULL) 563*0Sstevel@tonic-gate break; 564*0Sstevel@tonic-gate 565*0Sstevel@tonic-gate /* 566*0Sstevel@tonic-gate * Check to see that the pointers all point 567*0Sstevel@tonic-gate * to within the buffer and after the array 568*0Sstevel@tonic-gate * of kstat_named_t's. 569*0Sstevel@tonic-gate */ 570*0Sstevel@tonic-gate if (KSTAT_NAMED_STR_PTR(knew) < 571*0Sstevel@tonic-gate (char *) 572*0Sstevel@tonic-gate ((kstat_named_t *)user_kstat.ks_data + 573*0Sstevel@tonic-gate ksp->ks_ndata)) { 574*0Sstevel@tonic-gate KSTAT_EXIT(ksp); 575*0Sstevel@tonic-gate kstat_rele(ksp); 576*0Sstevel@tonic-gate kmem_free(kbuf, bufsize + 1); 577*0Sstevel@tonic-gate kmem_free(buf, bufsize + 1); 578*0Sstevel@tonic-gate return (EINVAL); 579*0Sstevel@tonic-gate } 580*0Sstevel@tonic-gate if (KSTAT_NAMED_STR_PTR(knew) + 581*0Sstevel@tonic-gate KSTAT_NAMED_STR_BUFLEN(knew) > 582*0Sstevel@tonic-gate ((char *)user_kstat.ks_data + 583*0Sstevel@tonic-gate ksp->ks_data_size)) { 584*0Sstevel@tonic-gate KSTAT_EXIT(ksp); 585*0Sstevel@tonic-gate kstat_rele(ksp); 586*0Sstevel@tonic-gate kmem_free(kbuf, bufsize + 1); 587*0Sstevel@tonic-gate kmem_free(buf, bufsize + 1); 588*0Sstevel@tonic-gate return (EINVAL); 589*0Sstevel@tonic-gate } 590*0Sstevel@tonic-gate 591*0Sstevel@tonic-gate /* 592*0Sstevel@tonic-gate * Update the pointers within the buffer 593*0Sstevel@tonic-gate */ 594*0Sstevel@tonic-gate KSTAT_NAMED_STR_PTR(knew) = 595*0Sstevel@tonic-gate (char *)buf + 596*0Sstevel@tonic-gate (KSTAT_NAMED_STR_PTR(knew) - 597*0Sstevel@tonic-gate (char *)user_kstat.ks_data); 598*0Sstevel@tonic-gate break; 599*0Sstevel@tonic-gate default: 600*0Sstevel@tonic-gate break; 601*0Sstevel@tonic-gate } 602*0Sstevel@tonic-gate } 603*0Sstevel@tonic-gate 604*0Sstevel@tonic-gate kold = kbuf; 605*0Sstevel@tonic-gate knew = buf; 606*0Sstevel@tonic-gate 607*0Sstevel@tonic-gate /* 608*0Sstevel@tonic-gate * Now make sure the types are what we expected them to be. 609*0Sstevel@tonic-gate */ 610*0Sstevel@tonic-gate for (i = 0; i < ksp->ks_ndata; i++, kold++, knew++) 611*0Sstevel@tonic-gate if (kold->data_type != knew->data_type) { 612*0Sstevel@tonic-gate KSTAT_EXIT(ksp); 613*0Sstevel@tonic-gate kstat_rele(ksp); 614*0Sstevel@tonic-gate kmem_free(kbuf, bufsize + 1); 615*0Sstevel@tonic-gate kmem_free(buf, bufsize + 1); 616*0Sstevel@tonic-gate return (EINVAL); 617*0Sstevel@tonic-gate } 618*0Sstevel@tonic-gate 619*0Sstevel@tonic-gate kmem_free(kbuf, bufsize + 1); 620*0Sstevel@tonic-gate } 621*0Sstevel@tonic-gate 622*0Sstevel@tonic-gate error = KSTAT_SNAPSHOT(ksp, buf, KSTAT_WRITE); 623*0Sstevel@tonic-gate if (!error) 624*0Sstevel@tonic-gate error = KSTAT_UPDATE(ksp, KSTAT_WRITE); 625*0Sstevel@tonic-gate *rvalp = kstat_chain_id; 626*0Sstevel@tonic-gate KSTAT_EXIT(ksp); 627*0Sstevel@tonic-gate kstat_rele(ksp); 628*0Sstevel@tonic-gate kmem_free(buf, bufsize + 1); 629*0Sstevel@tonic-gate return (error); 630*0Sstevel@tonic-gate } 631*0Sstevel@tonic-gate 632*0Sstevel@tonic-gate /*ARGSUSED*/ 633*0Sstevel@tonic-gate static int 634*0Sstevel@tonic-gate kstat_ioctl(dev_t dev, int cmd, intptr_t data, int flag, cred_t *cr, int *rvalp) 635*0Sstevel@tonic-gate { 636*0Sstevel@tonic-gate int rc = 0; 637*0Sstevel@tonic-gate 638*0Sstevel@tonic-gate switch (cmd) { 639*0Sstevel@tonic-gate 640*0Sstevel@tonic-gate case KSTAT_IOC_CHAIN_ID: 641*0Sstevel@tonic-gate *rvalp = kstat_chain_id; 642*0Sstevel@tonic-gate break; 643*0Sstevel@tonic-gate 644*0Sstevel@tonic-gate case KSTAT_IOC_READ: 645*0Sstevel@tonic-gate rc = read_kstat_data(rvalp, (void *)data, flag); 646*0Sstevel@tonic-gate break; 647*0Sstevel@tonic-gate 648*0Sstevel@tonic-gate case KSTAT_IOC_WRITE: 649*0Sstevel@tonic-gate rc = write_kstat_data(rvalp, (void *)data, flag, cr); 650*0Sstevel@tonic-gate break; 651*0Sstevel@tonic-gate 652*0Sstevel@tonic-gate default: 653*0Sstevel@tonic-gate /* invalid request */ 654*0Sstevel@tonic-gate rc = EINVAL; 655*0Sstevel@tonic-gate } 656*0Sstevel@tonic-gate return (rc); 657*0Sstevel@tonic-gate } 658*0Sstevel@tonic-gate 659*0Sstevel@tonic-gate /* ARGSUSED */ 660*0Sstevel@tonic-gate static int 661*0Sstevel@tonic-gate kstat_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, 662*0Sstevel@tonic-gate void **result) 663*0Sstevel@tonic-gate { 664*0Sstevel@tonic-gate switch (infocmd) { 665*0Sstevel@tonic-gate case DDI_INFO_DEVT2DEVINFO: 666*0Sstevel@tonic-gate *result = kstat_devi; 667*0Sstevel@tonic-gate return (DDI_SUCCESS); 668*0Sstevel@tonic-gate case DDI_INFO_DEVT2INSTANCE: 669*0Sstevel@tonic-gate *result = NULL; 670*0Sstevel@tonic-gate return (DDI_SUCCESS); 671*0Sstevel@tonic-gate } 672*0Sstevel@tonic-gate return (DDI_FAILURE); 673*0Sstevel@tonic-gate } 674*0Sstevel@tonic-gate 675*0Sstevel@tonic-gate static int 676*0Sstevel@tonic-gate kstat_attach(dev_info_t *devi, ddi_attach_cmd_t cmd) 677*0Sstevel@tonic-gate { 678*0Sstevel@tonic-gate if (cmd != DDI_ATTACH) 679*0Sstevel@tonic-gate return (DDI_FAILURE); 680*0Sstevel@tonic-gate 681*0Sstevel@tonic-gate if (ddi_create_minor_node(devi, "kstat", S_IFCHR, 682*0Sstevel@tonic-gate 0, DDI_PSEUDO, NULL) == DDI_FAILURE) { 683*0Sstevel@tonic-gate ddi_remove_minor_node(devi, NULL); 684*0Sstevel@tonic-gate return (DDI_FAILURE); 685*0Sstevel@tonic-gate } 686*0Sstevel@tonic-gate kstat_devi = devi; 687*0Sstevel@tonic-gate return (DDI_SUCCESS); 688*0Sstevel@tonic-gate } 689*0Sstevel@tonic-gate 690*0Sstevel@tonic-gate static int 691*0Sstevel@tonic-gate kstat_detach(dev_info_t *devi, ddi_detach_cmd_t cmd) 692*0Sstevel@tonic-gate { 693*0Sstevel@tonic-gate if (cmd != DDI_DETACH) 694*0Sstevel@tonic-gate return (DDI_FAILURE); 695*0Sstevel@tonic-gate 696*0Sstevel@tonic-gate ddi_remove_minor_node(devi, NULL); 697*0Sstevel@tonic-gate return (DDI_SUCCESS); 698*0Sstevel@tonic-gate } 699*0Sstevel@tonic-gate 700*0Sstevel@tonic-gate static struct cb_ops kstat_cb_ops = { 701*0Sstevel@tonic-gate nulldev, /* open */ 702*0Sstevel@tonic-gate nulldev, /* close */ 703*0Sstevel@tonic-gate nodev, /* strategy */ 704*0Sstevel@tonic-gate nodev, /* print */ 705*0Sstevel@tonic-gate nodev, /* dump */ 706*0Sstevel@tonic-gate nodev, /* read */ 707*0Sstevel@tonic-gate nodev, /* write */ 708*0Sstevel@tonic-gate kstat_ioctl, /* ioctl */ 709*0Sstevel@tonic-gate nodev, /* devmap */ 710*0Sstevel@tonic-gate nodev, /* mmap */ 711*0Sstevel@tonic-gate nodev, /* segmap */ 712*0Sstevel@tonic-gate nochpoll, /* poll */ 713*0Sstevel@tonic-gate ddi_prop_op, /* prop_op */ 714*0Sstevel@tonic-gate 0, /* streamtab */ 715*0Sstevel@tonic-gate D_NEW | D_MP /* Driver compatibility flag */ 716*0Sstevel@tonic-gate }; 717*0Sstevel@tonic-gate 718*0Sstevel@tonic-gate static struct dev_ops kstat_ops = { 719*0Sstevel@tonic-gate DEVO_REV, /* devo_rev, */ 720*0Sstevel@tonic-gate 0, /* refcnt */ 721*0Sstevel@tonic-gate kstat_info, /* get_dev_info */ 722*0Sstevel@tonic-gate nulldev, /* identify */ 723*0Sstevel@tonic-gate nulldev, /* probe */ 724*0Sstevel@tonic-gate kstat_attach, /* attach */ 725*0Sstevel@tonic-gate kstat_detach, /* detach */ 726*0Sstevel@tonic-gate nodev, /* reset */ 727*0Sstevel@tonic-gate &kstat_cb_ops, /* driver operations */ 728*0Sstevel@tonic-gate (struct bus_ops *)0 /* no bus operations */ 729*0Sstevel@tonic-gate }; 730*0Sstevel@tonic-gate 731*0Sstevel@tonic-gate static struct modldrv modldrv = { 732*0Sstevel@tonic-gate &mod_driverops, "kernel statistics driver %I%", &kstat_ops, 733*0Sstevel@tonic-gate }; 734*0Sstevel@tonic-gate 735*0Sstevel@tonic-gate static struct modlinkage modlinkage = { 736*0Sstevel@tonic-gate MODREV_1, &modldrv, NULL 737*0Sstevel@tonic-gate }; 738*0Sstevel@tonic-gate 739*0Sstevel@tonic-gate int 740*0Sstevel@tonic-gate _init(void) 741*0Sstevel@tonic-gate { 742*0Sstevel@tonic-gate return (mod_install(&modlinkage)); 743*0Sstevel@tonic-gate } 744*0Sstevel@tonic-gate 745*0Sstevel@tonic-gate int 746*0Sstevel@tonic-gate _fini(void) 747*0Sstevel@tonic-gate { 748*0Sstevel@tonic-gate return (mod_remove(&modlinkage)); 749*0Sstevel@tonic-gate } 750*0Sstevel@tonic-gate 751*0Sstevel@tonic-gate int 752*0Sstevel@tonic-gate _info(struct modinfo *modinfop) 753*0Sstevel@tonic-gate { 754*0Sstevel@tonic-gate return (mod_info(&modlinkage, modinfop)); 755*0Sstevel@tonic-gate } 756