xref: /illumos-gate/usr/src/uts/intel/io/ucode_drv.c (revision 2a6e99a0f1f7d22c0396e8b2ce9b9babbd1056cf)
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 /*
23  * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
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/policy.h>
35 #include <sys/processor.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/ucode.h>
43 #include <sys/systeminfo.h>
44 #include <sys/x86_archext.h>
45 
46 static dev_info_t *ucode_devi;
47 static uint32_t ucode_max_combined_size;
48 static kmutex_t ucode_update_lock;
49 
50 /*ARGSUSED*/
51 static int
52 ucode_getinfo(dev_info_t *devi, ddi_info_cmd_t cmd, void *arg, void **result)
53 {
54 	switch (cmd) {
55 	case DDI_INFO_DEVT2DEVINFO:
56 	case DDI_INFO_DEVT2INSTANCE:
57 		break;
58 	default:
59 		return (DDI_FAILURE);
60 	}
61 
62 	switch (getminor((dev_t)arg)) {
63 	case UCODE_MINOR:
64 		break;
65 	default:
66 		return (DDI_FAILURE);
67 	}
68 
69 	if (cmd == DDI_INFO_DEVT2INSTANCE)
70 		*result = 0;
71 	else
72 		*result = ucode_devi;
73 	return (DDI_SUCCESS);
74 }
75 
76 static int
77 ucode_attach(dev_info_t *devi, ddi_attach_cmd_t cmd)
78 {
79 	ASSERT(cmd != DDI_RESUME);
80 
81 	switch (cmd) {
82 	case DDI_RESUME:
83 		return (DDI_SUCCESS);
84 
85 	case DDI_ATTACH:
86 		ucode_devi = devi;
87 		ucode_max_combined_size = UCODE_MAX_COMBINED_SIZE;
88 
89 		if (ddi_create_minor_node(devi, UCODE_NODE_NAME, S_IFCHR,
90 		    UCODE_MINOR, DDI_PSEUDO, 0) != DDI_SUCCESS) {
91 			cmn_err(CE_WARN, "%s: Unable to create minor node",
92 			    UCODE_NODE_NAME);
93 			return (DDI_FAILURE);
94 		}
95 		ddi_report_dev(devi);
96 		return (DDI_SUCCESS);
97 
98 	default:
99 		return (DDI_FAILURE);
100 	}
101 }
102 
103 static int
104 ucode_detach(dev_info_t *devi, ddi_detach_cmd_t cmd)
105 {
106 	/*
107 	 * The power management and DR framework should never invoke this
108 	 * driver with DDI_SUSPEND because the ucode pseudo device does not
109 	 * have a reg property or hardware binding.  However, we will return
110 	 * DDI_SUCCESS so that in the unlikely event that it does get
111 	 * called, the system will still suspend and resume.
112 	 */
113 	ASSERT(cmd != DDI_SUSPEND);
114 
115 	switch (cmd) {
116 	case DDI_SUSPEND:
117 		return (DDI_SUCCESS);
118 
119 	case DDI_DETACH:
120 		ddi_remove_minor_node(devi, NULL);
121 		ucode_devi = NULL;
122 		return (DDI_SUCCESS);
123 
124 	default:
125 		return (DDI_FAILURE);
126 	}
127 }
128 
129 /*ARGSUSED1*/
130 static int
131 ucode_open(dev_t *dev, int flag, int otyp, cred_t *cr)
132 {
133 	return (getminor(*dev) == UCODE_MINOR ? 0 : ENXIO);
134 }
135 
136 
137 /*ARGSUSED*/
138 static int
139 ucode_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *cr, int *rval)
140 {
141 	/*
142 	 * Make sure that the ucode ops pointer has been set up.
143 	 */
144 	if (!ucode)
145 		return (EIO);
146 
147 	switch (cmd) {
148 	case UCODE_GET_VERSION: {
149 		int size;
150 		uint32_t *revp, *rev_array;
151 		size_t bufsz = NCPU * sizeof (*revp);
152 		ucode_errno_t rc = EM_OK;
153 
154 		STRUCT_DECL(ucode_get_rev_struct, h);
155 		STRUCT_INIT(h, mode);
156 		if (ddi_copyin((void *)arg,
157 		    STRUCT_BUF(h), STRUCT_SIZE(h), mode))
158 			return (EFAULT);
159 
160 		if ((size = STRUCT_FGET(h, ugv_size)) > NCPU || size < 0)
161 			return (EINVAL);
162 
163 		if (size == 0)
164 			return (0);
165 
166 		if ((rev_array = STRUCT_FGETP(h, ugv_rev)) == NULL)
167 			return (EINVAL);
168 
169 		size *= sizeof (uint32_t);
170 
171 		/* Can't rely on caller for kernel's buffer size. */
172 		revp = kmem_zalloc(bufsz, KM_SLEEP);
173 		if (ddi_copyin((void *)rev_array, revp, size, mode) != 0) {
174 			kmem_free(revp, bufsz);
175 			return (EINVAL);
176 		}
177 
178 		rc = ucode_get_rev(revp);
179 
180 		STRUCT_FSET(h, ugv_errno, rc);
181 
182 		if (ddi_copyout(revp, (void *)rev_array, size, mode) != 0) {
183 			kmem_free(revp, bufsz);
184 			return (EFAULT);
185 		}
186 
187 		kmem_free(revp, bufsz);
188 
189 		if (ddi_copyout(STRUCT_BUF(h), (void *)arg,
190 		    STRUCT_SIZE(h), mode))
191 			return (EFAULT);
192 
193 		return (0);
194 	}
195 
196 	case UCODE_UPDATE: {
197 		int size;
198 		uint8_t *ucodep, *uw_ucode;
199 		ucode_errno_t rc = EM_OK;
200 
201 		/*
202 		 * Requires all privilege.
203 		 */
204 		if (cr && secpolicy_ucode_update(cr))
205 			return (EPERM);
206 
207 		STRUCT_DECL(ucode_write_struct, h);
208 
209 		STRUCT_INIT(h, mode);
210 		if (ddi_copyin((void *)arg, STRUCT_BUF(h), STRUCT_SIZE(h),
211 		    mode))
212 			return (EFAULT);
213 
214 		/*
215 		 * We allow the size of the combined microcode file to be up to
216 		 * ucode_max_combined_size.  It is initialized to
217 		 * UCODE_MAX_COMBINED_SIZE, and can be patched if necessary.
218 		 */
219 		size = STRUCT_FGET(h, uw_size);
220 		if (size > ucode_max_combined_size || size == 0)
221 			return (EINVAL);
222 
223 		if ((uw_ucode = STRUCT_FGETP(h, uw_ucode)) == NULL)
224 			return (EINVAL);
225 
226 		ucodep = kmem_zalloc(size, KM_SLEEP);
227 		if (ddi_copyin((void *)uw_ucode, ucodep, size, mode) != 0) {
228 			kmem_free(ucodep, size);
229 			return (EFAULT);
230 		}
231 
232 		if ((rc = ucode->validate(ucodep, size)) != EM_OK) {
233 			kmem_free(ucodep, size);
234 			STRUCT_FSET(h, uw_errno, rc);
235 			if (ddi_copyout(STRUCT_BUF(h), (void *)arg,
236 			    STRUCT_SIZE(h), mode))
237 				return (EFAULT);
238 			return (0);
239 		}
240 
241 		mutex_enter(&ucode_update_lock);
242 		rc = ucode_update(ucodep, size);
243 		mutex_exit(&ucode_update_lock);
244 
245 		kmem_free(ucodep, size);
246 
247 		STRUCT_FSET(h, uw_errno, rc);
248 		if (ddi_copyout(STRUCT_BUF(h), (void *)arg,
249 		    STRUCT_SIZE(h), mode))
250 			return (EFAULT);
251 
252 		/*
253 		 * Even if rc is not EM_OK, it is a successful operation
254 		 * from ioctl()'s perspective.  We return the detailed error
255 		 * code via the ucode_write_struct data structure.
256 		 */
257 		return (0);
258 	}
259 
260 
261 	default:
262 		return (ENOTTY);
263 	}
264 }
265 
266 static struct cb_ops ucode_cb_ops = {
267 	ucode_open,
268 	nulldev,	/* close */
269 	nodev,		/* strategy */
270 	nodev,		/* print */
271 	nodev,		/* dump */
272 	nodev,		/* read */
273 	nodev,		/* write */
274 	ucode_ioctl,
275 	nodev,		/* devmap */
276 	nodev,		/* mmap */
277 	nodev,		/* segmap */
278 	nochpoll,	/* poll */
279 	ddi_prop_op,
280 	NULL,
281 	D_64BIT | D_NEW | D_MP
282 };
283 
284 static struct dev_ops ucode_dv_ops = {
285 	DEVO_REV,
286 	0,
287 	ucode_getinfo,
288 	nulldev,		/* identify */
289 	nulldev,		/* probe */
290 	ucode_attach,
291 	ucode_detach,
292 	nodev,			/* reset */
293 	&ucode_cb_ops,
294 	(struct bus_ops *)0,
295 	NULL,			/* power */
296 	ddi_quiesce_not_needed,		/* quiesce */
297 };
298 
299 static struct modldrv modldrv = {
300 	&mod_driverops,
301 	"ucode driver",
302 	&ucode_dv_ops
303 };
304 
305 static struct modlinkage modl = {
306 	MODREV_1,
307 	&modldrv
308 };
309 
310 int
311 _init(void)
312 {
313 	int rc;
314 
315 	if ((rc = mod_install(&modl)) != 0)
316 		return (rc);
317 
318 	mutex_init(&ucode_update_lock, NULL, MUTEX_DRIVER, NULL);
319 
320 	return (0);
321 }
322 
323 int
324 _fini(void)
325 {
326 	int rc;
327 
328 	if ((rc = mod_remove(&modl)) != 0)
329 		return (rc);
330 
331 	mutex_destroy(&ucode_update_lock);
332 
333 	return (0);
334 }
335 
336 int
337 _info(struct modinfo *modinfo)
338 {
339 	return (mod_info(&modl, modinfo));
340 }
341