xref: /illumos-gate/usr/src/uts/intel/io/ucode_drv.c (revision 581cede61ac9c14d8d4ea452562a567189eead78)
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 2008 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 		ucode_errno_t rc = EM_OK;
152 
153 		STRUCT_DECL(ucode_get_rev_struct, h);
154 		STRUCT_INIT(h, mode);
155 		if (ddi_copyin((void *)arg,
156 		    STRUCT_BUF(h), STRUCT_SIZE(h), mode))
157 			return (EFAULT);
158 
159 		if ((size = STRUCT_FGET(h, ugv_size)) > NCPU)
160 			return (EINVAL);
161 
162 		if ((rev_array = STRUCT_FGETP(h, ugv_rev)) == NULL)
163 			return (EINVAL);
164 
165 		size *= sizeof (uint32_t);
166 
167 		revp = kmem_zalloc(size, KM_SLEEP);
168 		if (ddi_copyin((void *)rev_array, revp, size, mode) != 0) {
169 			kmem_free(revp, size);
170 			return (EINVAL);
171 		}
172 
173 		rc = ucode_get_rev(revp);
174 
175 		STRUCT_FSET(h, ugv_errno, rc);
176 
177 		if (ddi_copyout(revp, (void *)rev_array, size, mode) != 0) {
178 			kmem_free(revp, size);
179 			return (EFAULT);
180 		}
181 
182 		kmem_free(revp, size);
183 
184 		if (ddi_copyout(STRUCT_BUF(h), (void *)arg,
185 		    STRUCT_SIZE(h), mode))
186 			return (EFAULT);
187 
188 		return (0);
189 	}
190 
191 	case UCODE_UPDATE: {
192 		int size;
193 		uint8_t *ucodep, *uw_ucode;
194 		ucode_errno_t rc = EM_OK;
195 
196 		/*
197 		 * Requires all privilege.
198 		 */
199 		if (cr && secpolicy_ucode_update(cr))
200 			return (EPERM);
201 
202 		STRUCT_DECL(ucode_write_struct, h);
203 
204 		STRUCT_INIT(h, mode);
205 		if (ddi_copyin((void *)arg, STRUCT_BUF(h), STRUCT_SIZE(h),
206 		    mode))
207 			return (EFAULT);
208 
209 		/*
210 		 * We allow the size of the combined microcode file to be up to
211 		 * ucode_max_combined_size.  It is initialized to
212 		 * UCODE_MAX_COMBINED_SIZE, and can be patched if necessary.
213 		 */
214 		size = STRUCT_FGET(h, uw_size);
215 		if (size > ucode_max_combined_size || size == 0)
216 			return (EINVAL);
217 
218 		if ((uw_ucode = STRUCT_FGETP(h, uw_ucode)) == NULL)
219 			return (EINVAL);
220 
221 		ucodep = kmem_zalloc(size, KM_SLEEP);
222 		if (ddi_copyin((void *)uw_ucode, ucodep, size, mode) != 0) {
223 			kmem_free(ucodep, size);
224 			return (EFAULT);
225 		}
226 
227 		if ((rc = ucode->validate(ucodep, size)) != EM_OK) {
228 			kmem_free(ucodep, size);
229 			STRUCT_FSET(h, uw_errno, rc);
230 			if (ddi_copyout(STRUCT_BUF(h), (void *)arg,
231 			    STRUCT_SIZE(h), mode))
232 				return (EFAULT);
233 			return (0);
234 		}
235 
236 		mutex_enter(&ucode_update_lock);
237 		rc = ucode_update(ucodep, size);
238 		mutex_exit(&ucode_update_lock);
239 
240 		kmem_free(ucodep, size);
241 
242 		STRUCT_FSET(h, uw_errno, rc);
243 		if (ddi_copyout(STRUCT_BUF(h), (void *)arg,
244 		    STRUCT_SIZE(h), mode))
245 			return (EFAULT);
246 
247 		/*
248 		 * Even if rc is not EM_OK, it is a successful operation
249 		 * from ioctl()'s perspective.  We return the detailed error
250 		 * code via the ucode_write_struct data structure.
251 		 */
252 		return (0);
253 	}
254 
255 
256 	default:
257 		return (ENOTTY);
258 	}
259 }
260 
261 static struct cb_ops ucode_cb_ops = {
262 	ucode_open,
263 	nulldev,	/* close */
264 	nodev,		/* strategy */
265 	nodev,		/* print */
266 	nodev,		/* dump */
267 	nodev,		/* read */
268 	nodev,		/* write */
269 	ucode_ioctl,
270 	nodev,		/* devmap */
271 	nodev,		/* mmap */
272 	nodev,		/* segmap */
273 	nochpoll,	/* poll */
274 	ddi_prop_op,
275 	NULL,
276 	D_64BIT | D_NEW | D_MP
277 };
278 
279 static struct dev_ops ucode_dv_ops = {
280 	DEVO_REV,
281 	0,
282 	ucode_getinfo,
283 	nulldev,		/* identify */
284 	nulldev,		/* probe */
285 	ucode_attach,
286 	ucode_detach,
287 	nodev,			/* reset */
288 	&ucode_cb_ops,
289 	(struct bus_ops *)0,
290 	NULL,			/* power */
291 	ddi_quiesce_not_needed,		/* quiesce */
292 };
293 
294 static struct modldrv modldrv = {
295 	&mod_driverops,
296 	"ucode driver",
297 	&ucode_dv_ops
298 };
299 
300 static struct modlinkage modl = {
301 	MODREV_1,
302 	&modldrv
303 };
304 
305 int
306 _init(void)
307 {
308 	int rc;
309 
310 	if ((rc = mod_install(&modl)) != 0)
311 		return (rc);
312 
313 	mutex_init(&ucode_update_lock, NULL, MUTEX_DRIVER, NULL);
314 
315 	return (0);
316 }
317 
318 int
319 _fini(void)
320 {
321 	int rc;
322 
323 	if ((rc = mod_remove(&modl)) != 0)
324 		return (rc);
325 
326 	mutex_destroy(&ucode_update_lock);
327 
328 	return (0);
329 }
330 
331 int
332 _info(struct modinfo *modinfo)
333 {
334 	return (mod_info(&modl, modinfo));
335 }
336