cpuid_drv.c revision 7656:2621e50fdf4a
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
49static dev_info_t *cpuid_devi;
50
51/*ARGSUSED*/
52static int
53cpuid_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
77static int
78cpuid_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
88static int
89cpuid_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*/
99static int
100cpuid_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*/
108static int
109cpuid_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*/
147static int
148cpuid_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
185static 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
203static 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
218static struct modldrv modldrv = {
219	&mod_driverops,
220	"cpuid driver",
221	&cpuid_dv_ops
222};
223
224static struct modlinkage modl = {
225	MODREV_1,
226	&modldrv
227};
228
229int
230_init(void)
231{
232	return (mod_install(&modl));
233}
234
235int
236_fini(void)
237{
238	return (mod_remove(&modl));
239}
240
241int
242_info(struct modinfo *modinfo)
243{
244	return (mod_info(&modl, modinfo));
245}
246