1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 /* 22 * Copyright (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved. 23 */ 24 25 26 #include <sys/types.h> 27 #include <sys/file.h> 28 #include <sys/errno.h> 29 #include <sys/open.h> 30 #include <sys/cred.h> 31 #include <sys/conf.h> 32 #include <sys/stat.h> 33 #include <sys/processor.h> 34 #include <sys/cpuvar.h> 35 #include <sys/kmem.h> 36 #include <sys/modctl.h> 37 #include <sys/ddi.h> 38 #include <sys/sunddi.h> 39 40 #include <sys/auxv.h> 41 #include <sys/cpuid_drv.h> 42 #include <sys/systeminfo.h> 43 44 #if defined(__x86) 45 #include <sys/x86_archext.h> 46 #endif 47 48 static dev_info_t *cpuid_devi; 49 50 /*ARGSUSED*/ 51 static int 52 cpuid_getinfo(dev_info_t *devi, ddi_info_cmd_t cmd, void *arg, void **result) 53 { 54 switch (cmd) { 55 case DDI_INFO_DEVT2DEVINFO: 56 case DDI_INFO_DEVT2INSTANCE: 57 break; 58 default: 59 return (DDI_FAILURE); 60 } 61 62 switch (getminor((dev_t)arg)) { 63 case CPUID_SELF_CPUID_MINOR: 64 break; 65 default: 66 return (DDI_FAILURE); 67 } 68 69 if (cmd == DDI_INFO_DEVT2INSTANCE) 70 *result = 0; 71 else 72 *result = cpuid_devi; 73 return (DDI_SUCCESS); 74 } 75 76 static int 77 cpuid_attach(dev_info_t *devi, ddi_attach_cmd_t cmd) 78 { 79 if (cmd != DDI_ATTACH) 80 return (DDI_FAILURE); 81 cpuid_devi = devi; 82 83 return (ddi_create_minor_node(devi, CPUID_DRIVER_SELF_NODE, S_IFCHR, 84 CPUID_SELF_CPUID_MINOR, DDI_PSEUDO, 0)); 85 } 86 87 static int 88 cpuid_detach(dev_info_t *devi, ddi_detach_cmd_t cmd) 89 { 90 if (cmd != DDI_DETACH) 91 return (DDI_FAILURE); 92 ddi_remove_minor_node(devi, NULL); 93 cpuid_devi = NULL; 94 return (DDI_SUCCESS); 95 } 96 97 /*ARGSUSED1*/ 98 static int 99 cpuid_open(dev_t *dev, int flag, int otyp, cred_t *cr) 100 { 101 return (getminor(*dev) == CPUID_SELF_CPUID_MINOR ? 0 : ENXIO); 102 } 103 104 #if defined(_HAVE_CPUID_INSN) 105 106 /*ARGSUSED*/ 107 static int 108 cpuid_read(dev_t dev, uio_t *uio, cred_t *cr) 109 { 110 struct cpuid_regs crs; 111 int error = 0; 112 113 if (!is_x86_feature(x86_featureset, X86FSET_CPUID)) 114 return (ENXIO); 115 116 if (uio->uio_resid & (sizeof (crs) - 1)) 117 return (EINVAL); 118 119 while (uio->uio_resid > 0) { 120 u_offset_t uoff; 121 122 if ((uoff = (u_offset_t)uio->uio_loffset) > UINT_MAX) { 123 error = EINVAL; 124 break; 125 } 126 127 crs.cp_eax = (uint32_t)uoff; 128 crs.cp_ebx = crs.cp_ecx = crs.cp_edx = 0; 129 (void) cpuid_insn(NULL, &crs); 130 131 if ((error = uiomove(&crs, sizeof (crs), UIO_READ, uio)) != 0) 132 break; 133 uio->uio_loffset = uoff + 1; 134 } 135 136 return (error); 137 } 138 139 #else 140 141 #define cpuid_read nodev 142 143 #endif /* _HAVE_CPUID_INSN */ 144 145 /*ARGSUSED*/ 146 static int 147 cpuid_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *cr, int *rval) 148 { 149 char areq[16]; 150 void *ustr; 151 152 switch (cmd) { 153 case CPUID_GET_HWCAP: { 154 STRUCT_DECL(cpuid_get_hwcap, h); 155 156 STRUCT_INIT(h, mode); 157 if (ddi_copyin((void *)arg, 158 STRUCT_BUF(h), STRUCT_SIZE(h), mode)) 159 return (EFAULT); 160 if ((ustr = STRUCT_FGETP(h, cgh_archname)) != NULL && 161 copyinstr(ustr, areq, sizeof (areq), NULL) != 0) 162 return (EFAULT); 163 areq[sizeof (areq) - 1] = '\0'; 164 165 if (strcmp(areq, architecture) == 0) 166 STRUCT_FSET(h, cgh_hwcap, auxv_hwcap); 167 #if defined(_SYSCALL32_IMPL) 168 else if (strcmp(areq, architecture_32) == 0) 169 STRUCT_FSET(h, cgh_hwcap, auxv_hwcap32); 170 #endif 171 else 172 STRUCT_FSET(h, cgh_hwcap, 0); 173 if (ddi_copyout(STRUCT_BUF(h), 174 (void *)arg, STRUCT_SIZE(h), mode)) 175 return (EFAULT); 176 return (0); 177 } 178 179 default: 180 return (ENOTTY); 181 } 182 } 183 184 static struct cb_ops cpuid_cb_ops = { 185 cpuid_open, 186 nulldev, /* close */ 187 nodev, /* strategy */ 188 nodev, /* print */ 189 nodev, /* dump */ 190 cpuid_read, 191 nodev, /* write */ 192 cpuid_ioctl, 193 nodev, /* devmap */ 194 nodev, /* mmap */ 195 nodev, /* segmap */ 196 nochpoll, /* poll */ 197 ddi_prop_op, 198 NULL, 199 D_64BIT | D_NEW | D_MP 200 }; 201 202 static struct dev_ops cpuid_dv_ops = { 203 DEVO_REV, 204 0, 205 cpuid_getinfo, 206 nulldev, /* identify */ 207 nulldev, /* probe */ 208 cpuid_attach, 209 cpuid_detach, 210 nodev, /* reset */ 211 &cpuid_cb_ops, 212 (struct bus_ops *)0, 213 NULL, 214 ddi_quiesce_not_needed, /* quiesce */ 215 }; 216 217 static struct modldrv modldrv = { 218 &mod_driverops, 219 "cpuid driver", 220 &cpuid_dv_ops 221 }; 222 223 static struct modlinkage modl = { 224 MODREV_1, 225 &modldrv 226 }; 227 228 int 229 _init(void) 230 { 231 return (mod_install(&modl)); 232 } 233 234 int 235 _fini(void) 236 { 237 return (mod_remove(&modl)); 238 } 239 240 int 241 _info(struct modinfo *modinfo) 242 { 243 return (mod_info(&modl, modinfo)); 244 } 245