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 * Copyright 2019 Joyent, Inc. 26 */ 27 28 29 #include <sys/types.h> 30 #include <sys/file.h> 31 #include <sys/errno.h> 32 #include <sys/open.h> 33 #include <sys/cred.h> 34 #include <sys/conf.h> 35 #include <sys/stat.h> 36 #include <sys/processor.h> 37 #include <sys/cpuvar.h> 38 #include <sys/kmem.h> 39 #include <sys/modctl.h> 40 #include <sys/ddi.h> 41 #include <sys/sunddi.h> 42 #include <sys/policy.h> 43 44 #include <sys/auxv.h> 45 #include <sys/cpuid_drv.h> 46 #include <sys/systeminfo.h> 47 48 #if defined(__x86) 49 #include <sys/x86_archext.h> 50 #endif 51 52 static dev_info_t *cpuid_devi; 53 54 /*ARGSUSED*/ 55 static int 56 cpuid_getinfo(dev_info_t *devi, ddi_info_cmd_t cmd, void *arg, void **result) 57 { 58 switch (cmd) { 59 case DDI_INFO_DEVT2DEVINFO: 60 case DDI_INFO_DEVT2INSTANCE: 61 break; 62 default: 63 return (DDI_FAILURE); 64 } 65 66 switch (getminor((dev_t)arg)) { 67 case CPUID_SELF_CPUID_MINOR: 68 break; 69 default: 70 return (DDI_FAILURE); 71 } 72 73 if (cmd == DDI_INFO_DEVT2INSTANCE) 74 *result = 0; 75 else 76 *result = cpuid_devi; 77 return (DDI_SUCCESS); 78 } 79 80 static int 81 cpuid_attach(dev_info_t *devi, ddi_attach_cmd_t cmd) 82 { 83 if (cmd != DDI_ATTACH) 84 return (DDI_FAILURE); 85 cpuid_devi = devi; 86 87 return (ddi_create_minor_node(devi, CPUID_DRIVER_SELF_NODE, S_IFCHR, 88 CPUID_SELF_CPUID_MINOR, DDI_PSEUDO, 0)); 89 } 90 91 static int 92 cpuid_detach(dev_info_t *devi, ddi_detach_cmd_t cmd) 93 { 94 if (cmd != DDI_DETACH) 95 return (DDI_FAILURE); 96 ddi_remove_minor_node(devi, NULL); 97 cpuid_devi = NULL; 98 return (DDI_SUCCESS); 99 } 100 101 /*ARGSUSED1*/ 102 static int 103 cpuid_open(dev_t *dev, int flag, int otyp, cred_t *cr) 104 { 105 return (getminor(*dev) == CPUID_SELF_CPUID_MINOR ? 0 : ENXIO); 106 } 107 108 #if defined(_HAVE_CPUID_INSN) 109 110 /*ARGSUSED*/ 111 static int 112 cpuid_read(dev_t dev, uio_t *uio, cred_t *cr) 113 { 114 struct cpuid_regs crs; 115 int error = 0; 116 117 if (!is_x86_feature(x86_featureset, X86FSET_CPUID)) 118 return (ENXIO); 119 120 if (uio->uio_resid & (sizeof (crs) - 1)) 121 return (EINVAL); 122 123 while (uio->uio_resid > 0) { 124 u_offset_t uoff; 125 126 if ((uoff = (u_offset_t)uio->uio_loffset) > UINT_MAX) { 127 error = EINVAL; 128 break; 129 } 130 131 crs.cp_eax = (uint32_t)uoff; 132 crs.cp_ebx = crs.cp_ecx = crs.cp_edx = 0; 133 (void) cpuid_insn(NULL, &crs); 134 135 if ((error = uiomove(&crs, sizeof (crs), UIO_READ, uio)) != 0) 136 break; 137 uio->uio_loffset = uoff + 1; 138 } 139 140 return (error); 141 } 142 143 #else 144 145 #define cpuid_read nodev 146 147 #endif /* _HAVE_CPUID_INSN */ 148 149 /*ARGSUSED*/ 150 static int 151 cpuid_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *cr, int *rval) 152 { 153 char areq[16]; 154 void *ustr; 155 156 switch (cmd) { 157 case CPUID_GET_HWCAP: { 158 STRUCT_DECL(cpuid_get_hwcap, h); 159 160 STRUCT_INIT(h, mode); 161 if (ddi_copyin((void *)arg, 162 STRUCT_BUF(h), STRUCT_SIZE(h), mode)) 163 return (EFAULT); 164 if ((ustr = STRUCT_FGETP(h, cgh_archname)) != NULL && 165 copyinstr(ustr, areq, sizeof (areq), NULL) != 0) 166 return (EFAULT); 167 areq[sizeof (areq) - 1] = '\0'; 168 169 if (strcmp(areq, architecture) == 0) { 170 STRUCT_FSET(h, cgh_hwcap[0], auxv_hwcap); 171 STRUCT_FSET(h, cgh_hwcap[1], auxv_hwcap_2); 172 #if defined(_SYSCALL32_IMPL) 173 } else if (strcmp(areq, architecture_32) == 0) { 174 STRUCT_FSET(h, cgh_hwcap[0], auxv_hwcap32); 175 STRUCT_FSET(h, cgh_hwcap[1], auxv_hwcap32_2); 176 #endif 177 } else { 178 STRUCT_FSET(h, cgh_hwcap[0], 0); 179 STRUCT_FSET(h, cgh_hwcap[1], 0); 180 } 181 if (ddi_copyout(STRUCT_BUF(h), 182 (void *)arg, STRUCT_SIZE(h), mode)) 183 return (EFAULT); 184 return (0); 185 } 186 187 #ifdef __x86 188 case CPUID_RDMSR: { 189 struct cpuid_rdmsr crm = { 0, }; 190 label_t label; 191 192 if (secpolicy_sys_config(cr, B_FALSE) != 0) 193 return (EPERM); 194 195 if (ddi_copyin((void *)arg, &crm, sizeof (crm), mode)) 196 return (EFAULT); 197 198 kpreempt_disable(); 199 200 if (on_fault(&label)) { 201 kpreempt_enable(); 202 return (ENOENT); 203 } 204 205 crm.cr_msr_val = rdmsr(crm.cr_msr_nr); 206 207 no_fault(); 208 kpreempt_enable(); 209 210 if (ddi_copyout(&crm, (void *)arg, sizeof (crm), mode)) 211 return (EFAULT); 212 return (0); 213 } 214 #endif 215 216 default: 217 return (ENOTTY); 218 } 219 } 220 221 static struct cb_ops cpuid_cb_ops = { 222 cpuid_open, 223 nulldev, /* close */ 224 nodev, /* strategy */ 225 nodev, /* print */ 226 nodev, /* dump */ 227 cpuid_read, 228 nodev, /* write */ 229 cpuid_ioctl, 230 nodev, /* devmap */ 231 nodev, /* mmap */ 232 nodev, /* segmap */ 233 nochpoll, /* poll */ 234 ddi_prop_op, 235 NULL, 236 D_64BIT | D_NEW | D_MP 237 }; 238 239 static struct dev_ops cpuid_dv_ops = { 240 DEVO_REV, 241 0, 242 cpuid_getinfo, 243 nulldev, /* identify */ 244 nulldev, /* probe */ 245 cpuid_attach, 246 cpuid_detach, 247 nodev, /* reset */ 248 &cpuid_cb_ops, 249 (struct bus_ops *)0, 250 NULL, 251 ddi_quiesce_not_needed, /* quiesce */ 252 }; 253 254 static struct modldrv modldrv = { 255 &mod_driverops, 256 "cpuid driver", 257 &cpuid_dv_ops 258 }; 259 260 static struct modlinkage modl = { 261 MODREV_1, 262 &modldrv 263 }; 264 265 int 266 _init(void) 267 { 268 return (mod_install(&modl)); 269 } 270 271 int 272 _fini(void) 273 { 274 return (mod_remove(&modl)); 275 } 276 277 int 278 _info(struct modinfo *modinfo) 279 { 280 return (mod_info(&modl, modinfo)); 281 }