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