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