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
cpuid_getinfo(dev_info_t * devi,ddi_info_cmd_t cmd,void * arg,void ** result)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
cpuid_attach(dev_info_t * devi,ddi_attach_cmd_t cmd)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
cpuid_detach(dev_info_t * devi,ddi_detach_cmd_t cmd)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
cpuid_open(dev_t * dev,int flag,int otyp,cred_t * cr)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
cpuid_read(dev_t dev,uio_t * uio,cred_t * cr)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
cpuid_ioctl(dev_t dev,int cmd,intptr_t arg,int mode,cred_t * cr,int * rval)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
_init(void)269 _init(void)
270 {
271 return (mod_install(&modl));
272 }
273
274 int
_fini(void)275 _fini(void)
276 {
277 return (mod_remove(&modl));
278 }
279
280 int
_info(struct modinfo * modinfo)281 _info(struct modinfo *modinfo)
282 {
283 return (mod_info(&modl, modinfo));
284 }
285