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 * Copyright 2022 Oxide Computer Co. 27 */ 28 29 30 #include <sys/types.h> 31 #include <sys/file.h> 32 #include <sys/errno.h> 33 #include <sys/open.h> 34 #include <sys/cred.h> 35 #include <sys/conf.h> 36 #include <sys/stat.h> 37 #include <sys/processor.h> 38 #include <sys/cpuvar.h> 39 #include <sys/kmem.h> 40 #include <sys/modctl.h> 41 #include <sys/ddi.h> 42 #include <sys/sunddi.h> 43 #include <sys/policy.h> 44 45 #include <sys/auxv.h> 46 #include <sys/cpuid_drv.h> 47 #include <sys/systeminfo.h> 48 49 #if defined(__x86) 50 #include <sys/x86_archext.h> 51 #endif 52 53 static dev_info_t *cpuid_devi; 54 55 /*ARGSUSED*/ 56 static int 57 cpuid_getinfo(dev_info_t *devi, ddi_info_cmd_t cmd, void *arg, void **result) 58 { 59 switch (cmd) { 60 case DDI_INFO_DEVT2DEVINFO: 61 case DDI_INFO_DEVT2INSTANCE: 62 break; 63 default: 64 return (DDI_FAILURE); 65 } 66 67 switch (getminor((dev_t)arg)) { 68 case CPUID_SELF_CPUID_MINOR: 69 break; 70 default: 71 return (DDI_FAILURE); 72 } 73 74 if (cmd == DDI_INFO_DEVT2INSTANCE) 75 *result = 0; 76 else 77 *result = cpuid_devi; 78 return (DDI_SUCCESS); 79 } 80 81 static int 82 cpuid_attach(dev_info_t *devi, ddi_attach_cmd_t cmd) 83 { 84 if (cmd != DDI_ATTACH) 85 return (DDI_FAILURE); 86 cpuid_devi = devi; 87 88 return (ddi_create_minor_node(devi, CPUID_DRIVER_SELF_NODE, S_IFCHR, 89 CPUID_SELF_CPUID_MINOR, DDI_PSEUDO, 0)); 90 } 91 92 static int 93 cpuid_detach(dev_info_t *devi, ddi_detach_cmd_t cmd) 94 { 95 if (cmd != DDI_DETACH) 96 return (DDI_FAILURE); 97 ddi_remove_minor_node(devi, NULL); 98 cpuid_devi = NULL; 99 return (DDI_SUCCESS); 100 } 101 102 /*ARGSUSED1*/ 103 static int 104 cpuid_open(dev_t *dev, int flag, int otyp, cred_t *cr) 105 { 106 return (getminor(*dev) == CPUID_SELF_CPUID_MINOR ? 0 : ENXIO); 107 } 108 109 #if defined(_HAVE_CPUID_INSN) 110 111 /*ARGSUSED*/ 112 static int 113 cpuid_read(dev_t dev, uio_t *uio, cred_t *cr) 114 { 115 struct cpuid_regs crs; 116 int error = 0; 117 118 ASSERT(is_x86_feature(x86_featureset, X86FSET_CPUID)); 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 STRUCT_FSET(h, cgh_hwcap[2], auxv_hwcap_3); 173 #if defined(_SYSCALL32_IMPL) 174 } else if (strcmp(areq, architecture_32) == 0) { 175 STRUCT_FSET(h, cgh_hwcap[0], auxv_hwcap32); 176 STRUCT_FSET(h, cgh_hwcap[1], auxv_hwcap32_2); 177 STRUCT_FSET(h, cgh_hwcap[2], auxv_hwcap32_3); 178 #endif 179 } else { 180 STRUCT_FSET(h, cgh_hwcap[0], 0); 181 STRUCT_FSET(h, cgh_hwcap[1], 0); 182 STRUCT_FSET(h, cgh_hwcap[2], 0); 183 } 184 if (ddi_copyout(STRUCT_BUF(h), 185 (void *)arg, STRUCT_SIZE(h), mode)) 186 return (EFAULT); 187 return (0); 188 } 189 190 #ifdef __x86 191 case CPUID_RDMSR: { 192 struct cpuid_rdmsr crm = { 0, }; 193 label_t label; 194 195 if (secpolicy_sys_config(cr, B_FALSE) != 0) 196 return (EPERM); 197 198 if (ddi_copyin((void *)arg, &crm, sizeof (crm), mode)) 199 return (EFAULT); 200 201 kpreempt_disable(); 202 203 if (on_fault(&label)) { 204 kpreempt_enable(); 205 return (ENOENT); 206 } 207 208 crm.cr_msr_val = rdmsr(crm.cr_msr_nr); 209 210 no_fault(); 211 kpreempt_enable(); 212 213 if (ddi_copyout(&crm, (void *)arg, sizeof (crm), mode)) 214 return (EFAULT); 215 return (0); 216 } 217 #endif 218 219 default: 220 return (ENOTTY); 221 } 222 } 223 224 static struct cb_ops cpuid_cb_ops = { 225 cpuid_open, 226 nulldev, /* close */ 227 nodev, /* strategy */ 228 nodev, /* print */ 229 nodev, /* dump */ 230 cpuid_read, 231 nodev, /* write */ 232 cpuid_ioctl, 233 nodev, /* devmap */ 234 nodev, /* mmap */ 235 nodev, /* segmap */ 236 nochpoll, /* poll */ 237 ddi_prop_op, 238 NULL, 239 D_64BIT | D_NEW | D_MP 240 }; 241 242 static struct dev_ops cpuid_dv_ops = { 243 DEVO_REV, 244 0, 245 cpuid_getinfo, 246 nulldev, /* identify */ 247 nulldev, /* probe */ 248 cpuid_attach, 249 cpuid_detach, 250 nodev, /* reset */ 251 &cpuid_cb_ops, 252 (struct bus_ops *)0, 253 NULL, 254 ddi_quiesce_not_needed, /* quiesce */ 255 }; 256 257 static struct modldrv modldrv = { 258 &mod_driverops, 259 "cpuid driver", 260 &cpuid_dv_ops 261 }; 262 263 static struct modlinkage modl = { 264 MODREV_1, 265 &modldrv 266 }; 267 268 int 269 _init(void) 270 { 271 return (mod_install(&modl)); 272 } 273 274 int 275 _fini(void) 276 { 277 return (mod_remove(&modl)); 278 } 279 280 int 281 _info(struct modinfo *modinfo) 282 { 283 return (mod_info(&modl, modinfo)); 284 } 285