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 }