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