xref: /onnv-gate/usr/src/uts/intel/io/ucode_drv.c (revision 4581)
1*4581Ssherrym /*
2*4581Ssherrym  * CDDL HEADER START
3*4581Ssherrym  *
4*4581Ssherrym  * The contents of this file are subject to the terms of the
5*4581Ssherrym  * Common Development and Distribution License (the "License").
6*4581Ssherrym  * You may not use this file except in compliance with the License.
7*4581Ssherrym  *
8*4581Ssherrym  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9*4581Ssherrym  * or http://www.opensolaris.org/os/licensing.
10*4581Ssherrym  * See the License for the specific language governing permissions
11*4581Ssherrym  * and limitations under the License.
12*4581Ssherrym  *
13*4581Ssherrym  * When distributing Covered Code, include this CDDL HEADER in each
14*4581Ssherrym  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15*4581Ssherrym  * If applicable, add the following below this CDDL HEADER, with the
16*4581Ssherrym  * fields enclosed by brackets "[]" replaced with your own identifying
17*4581Ssherrym  * information: Portions Copyright [yyyy] [name of copyright owner]
18*4581Ssherrym  *
19*4581Ssherrym  * CDDL HEADER END
20*4581Ssherrym  */
21*4581Ssherrym 
22*4581Ssherrym /*
23*4581Ssherrym  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
24*4581Ssherrym  * Use is subject to license terms.
25*4581Ssherrym  */
26*4581Ssherrym 
27*4581Ssherrym #pragma ident	"%Z%%M%	%I%	%E% SMI"
28*4581Ssherrym 
29*4581Ssherrym #include <sys/types.h>
30*4581Ssherrym #include <sys/file.h>
31*4581Ssherrym #include <sys/errno.h>
32*4581Ssherrym #include <sys/open.h>
33*4581Ssherrym #include <sys/cred.h>
34*4581Ssherrym #include <sys/conf.h>
35*4581Ssherrym #include <sys/stat.h>
36*4581Ssherrym #include <sys/policy.h>
37*4581Ssherrym #include <sys/processor.h>
38*4581Ssherrym #include <sys/kmem.h>
39*4581Ssherrym #include <sys/modctl.h>
40*4581Ssherrym #include <sys/ddi.h>
41*4581Ssherrym #include <sys/sunddi.h>
42*4581Ssherrym 
43*4581Ssherrym #include <sys/auxv.h>
44*4581Ssherrym #include <sys/ucode.h>
45*4581Ssherrym #include <sys/systeminfo.h>
46*4581Ssherrym #include <sys/x86_archext.h>
47*4581Ssherrym 
48*4581Ssherrym static dev_info_t *ucode_devi;
49*4581Ssherrym static uint32_t ucode_max_combined_size;
50*4581Ssherrym static kmutex_t ucode_update_lock;
51*4581Ssherrym 
52*4581Ssherrym /*ARGSUSED*/
53*4581Ssherrym static int
54*4581Ssherrym ucode_getinfo(dev_info_t *devi, ddi_info_cmd_t cmd, void *arg, void **result)
55*4581Ssherrym {
56*4581Ssherrym 	switch (cmd) {
57*4581Ssherrym 	case DDI_INFO_DEVT2DEVINFO:
58*4581Ssherrym 	case DDI_INFO_DEVT2INSTANCE:
59*4581Ssherrym 		break;
60*4581Ssherrym 	default:
61*4581Ssherrym 		return (DDI_FAILURE);
62*4581Ssherrym 	}
63*4581Ssherrym 
64*4581Ssherrym 	switch (getminor((dev_t)arg)) {
65*4581Ssherrym 	case UCODE_MINOR:
66*4581Ssherrym 		break;
67*4581Ssherrym 	default:
68*4581Ssherrym 		return (DDI_FAILURE);
69*4581Ssherrym 	}
70*4581Ssherrym 
71*4581Ssherrym 	if (cmd == DDI_INFO_DEVT2INSTANCE)
72*4581Ssherrym 		*result = 0;
73*4581Ssherrym 	else
74*4581Ssherrym 		*result = ucode_devi;
75*4581Ssherrym 	return (DDI_SUCCESS);
76*4581Ssherrym }
77*4581Ssherrym 
78*4581Ssherrym static int
79*4581Ssherrym ucode_attach(dev_info_t *devi, ddi_attach_cmd_t cmd)
80*4581Ssherrym {
81*4581Ssherrym 	ASSERT(cmd != DDI_RESUME);
82*4581Ssherrym 
83*4581Ssherrym 	switch (cmd) {
84*4581Ssherrym 	case DDI_RESUME:
85*4581Ssherrym 		return (DDI_SUCCESS);
86*4581Ssherrym 
87*4581Ssherrym 	case DDI_ATTACH:
88*4581Ssherrym 		ucode_devi = devi;
89*4581Ssherrym 		ucode_max_combined_size = UCODE_MAX_COMBINED_SIZE;
90*4581Ssherrym 
91*4581Ssherrym 		if (ddi_create_minor_node(devi, UCODE_NODE_NAME, S_IFCHR,
92*4581Ssherrym 		    UCODE_MINOR, DDI_PSEUDO, 0) != DDI_SUCCESS) {
93*4581Ssherrym 			cmn_err(CE_WARN, "%s: Unable to create minor node",
94*4581Ssherrym 			    UCODE_NODE_NAME);
95*4581Ssherrym 			return (DDI_FAILURE);
96*4581Ssherrym 		}
97*4581Ssherrym 		ddi_report_dev(devi);
98*4581Ssherrym 		return (DDI_SUCCESS);
99*4581Ssherrym 
100*4581Ssherrym 	default:
101*4581Ssherrym 		return (DDI_FAILURE);
102*4581Ssherrym 	}
103*4581Ssherrym }
104*4581Ssherrym 
105*4581Ssherrym static int
106*4581Ssherrym ucode_detach(dev_info_t *devi, ddi_detach_cmd_t cmd)
107*4581Ssherrym {
108*4581Ssherrym 	/*
109*4581Ssherrym 	 * The power management and DR framework should never invoke this
110*4581Ssherrym 	 * driver with DDI_SUSPEND because the ucode pseudo device does not
111*4581Ssherrym 	 * have a reg property or hardware binding.  However, we will return
112*4581Ssherrym 	 * DDI_SUCCESS so that in the unlikely event that it does get
113*4581Ssherrym 	 * called, the system will still suspend and resume.
114*4581Ssherrym 	 */
115*4581Ssherrym 	ASSERT(cmd != DDI_SUSPEND);
116*4581Ssherrym 
117*4581Ssherrym 	switch (cmd) {
118*4581Ssherrym 	case DDI_SUSPEND:
119*4581Ssherrym 		return (DDI_SUCCESS);
120*4581Ssherrym 
121*4581Ssherrym 	case DDI_DETACH:
122*4581Ssherrym 		ddi_remove_minor_node(devi, NULL);
123*4581Ssherrym 		ucode_devi = NULL;
124*4581Ssherrym 		return (DDI_SUCCESS);
125*4581Ssherrym 
126*4581Ssherrym 	default:
127*4581Ssherrym 		return (DDI_FAILURE);
128*4581Ssherrym 	}
129*4581Ssherrym }
130*4581Ssherrym 
131*4581Ssherrym /*ARGSUSED1*/
132*4581Ssherrym static int
133*4581Ssherrym ucode_open(dev_t *dev, int flag, int otyp, cred_t *cr)
134*4581Ssherrym {
135*4581Ssherrym 	return (getminor(*dev) == UCODE_MINOR ? 0 : ENXIO);
136*4581Ssherrym }
137*4581Ssherrym 
138*4581Ssherrym 
139*4581Ssherrym /*ARGSUSED*/
140*4581Ssherrym static int
141*4581Ssherrym ucode_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *cr, int *rval)
142*4581Ssherrym {
143*4581Ssherrym 	switch (cmd) {
144*4581Ssherrym 	case UCODE_GET_VERSION: {
145*4581Ssherrym 		int size;
146*4581Ssherrym 		uint32_t *revp, *rev_array;
147*4581Ssherrym 		ucode_errno_t rc = EM_OK;
148*4581Ssherrym 
149*4581Ssherrym 		STRUCT_DECL(ucode_get_rev_struct, h);
150*4581Ssherrym 		STRUCT_INIT(h, mode);
151*4581Ssherrym 		if (ddi_copyin((void *)arg,
152*4581Ssherrym 		    STRUCT_BUF(h), STRUCT_SIZE(h), mode))
153*4581Ssherrym 			return (EFAULT);
154*4581Ssherrym 
155*4581Ssherrym 		if ((size = STRUCT_FGET(h, ugv_size)) > NCPU)
156*4581Ssherrym 			return (EINVAL);
157*4581Ssherrym 
158*4581Ssherrym 		if ((rev_array = STRUCT_FGETP(h, ugv_rev)) == NULL)
159*4581Ssherrym 			return (EINVAL);
160*4581Ssherrym 
161*4581Ssherrym 		size *= sizeof (uint32_t);
162*4581Ssherrym 
163*4581Ssherrym 		revp = kmem_zalloc(size, KM_SLEEP);
164*4581Ssherrym 		if (ddi_copyin((void *)rev_array, revp, size, mode) != 0) {
165*4581Ssherrym 			kmem_free(revp, size);
166*4581Ssherrym 			return (EINVAL);
167*4581Ssherrym 		}
168*4581Ssherrym 
169*4581Ssherrym 		rc = ucode_get_rev(revp);
170*4581Ssherrym 
171*4581Ssherrym 		STRUCT_FSET(h, ugv_errno, rc);
172*4581Ssherrym 
173*4581Ssherrym 		if (ddi_copyout(revp, (void *)rev_array, size, mode) != 0) {
174*4581Ssherrym 			kmem_free(revp, size);
175*4581Ssherrym 			return (EFAULT);
176*4581Ssherrym 		}
177*4581Ssherrym 
178*4581Ssherrym 		kmem_free(revp, size);
179*4581Ssherrym 
180*4581Ssherrym 		if (ddi_copyout(STRUCT_BUF(h), (void *)arg,
181*4581Ssherrym 		    STRUCT_SIZE(h), mode))
182*4581Ssherrym 			return (EFAULT);
183*4581Ssherrym 
184*4581Ssherrym 		return (0);
185*4581Ssherrym 	}
186*4581Ssherrym 
187*4581Ssherrym 	case UCODE_UPDATE: {
188*4581Ssherrym 		int size;
189*4581Ssherrym 		uint8_t *ucodep, *uw_ucode;
190*4581Ssherrym 		ucode_errno_t rc = EM_OK;
191*4581Ssherrym 
192*4581Ssherrym 		/*
193*4581Ssherrym 		 * Requires all privilege.
194*4581Ssherrym 		 */
195*4581Ssherrym 		if (cr && secpolicy_ucode_update(cr))
196*4581Ssherrym 			return (EPERM);
197*4581Ssherrym 
198*4581Ssherrym 		STRUCT_DECL(ucode_write_struct, h);
199*4581Ssherrym 
200*4581Ssherrym 		STRUCT_INIT(h, mode);
201*4581Ssherrym 		if (ddi_copyin((void *)arg, STRUCT_BUF(h), STRUCT_SIZE(h),
202*4581Ssherrym 		    mode))
203*4581Ssherrym 			return (EFAULT);
204*4581Ssherrym 
205*4581Ssherrym 		/*
206*4581Ssherrym 		 * We allow the size of the combined microcode file to be up to
207*4581Ssherrym 		 * ucode_max_combined_size.  It is initialized to
208*4581Ssherrym 		 * UCODE_MAX_COMBINED_SIZE, and can be patched if necessary.
209*4581Ssherrym 		 */
210*4581Ssherrym 		size = STRUCT_FGET(h, uw_size);
211*4581Ssherrym 		if (size > ucode_max_combined_size || size == 0)
212*4581Ssherrym 			return (EINVAL);
213*4581Ssherrym 
214*4581Ssherrym 		if ((uw_ucode = STRUCT_FGETP(h, uw_ucode)) == NULL)
215*4581Ssherrym 			return (EINVAL);
216*4581Ssherrym 
217*4581Ssherrym 		ucodep = kmem_zalloc(size, KM_SLEEP);
218*4581Ssherrym 		if (ddi_copyin((void *)uw_ucode, ucodep, size, mode) != 0) {
219*4581Ssherrym 			kmem_free(ucodep, size);
220*4581Ssherrym 			return (EFAULT);
221*4581Ssherrym 		}
222*4581Ssherrym 
223*4581Ssherrym 		if ((rc = ucode_validate(ucodep, size)) != EM_OK) {
224*4581Ssherrym 			kmem_free(ucodep, size);
225*4581Ssherrym 			STRUCT_FSET(h, uw_errno, rc);
226*4581Ssherrym 			if (ddi_copyout(STRUCT_BUF(h), (void *)arg,
227*4581Ssherrym 			    STRUCT_SIZE(h), mode))
228*4581Ssherrym 				return (EFAULT);
229*4581Ssherrym 			return (0);
230*4581Ssherrym 		}
231*4581Ssherrym 
232*4581Ssherrym 		mutex_enter(&ucode_update_lock);
233*4581Ssherrym 		rc = ucode_update(ucodep, size);
234*4581Ssherrym 		mutex_exit(&ucode_update_lock);
235*4581Ssherrym 
236*4581Ssherrym 		kmem_free(ucodep, size);
237*4581Ssherrym 
238*4581Ssherrym 		STRUCT_FSET(h, uw_errno, rc);
239*4581Ssherrym 		if (ddi_copyout(STRUCT_BUF(h), (void *)arg,
240*4581Ssherrym 		    STRUCT_SIZE(h), mode))
241*4581Ssherrym 			return (EFAULT);
242*4581Ssherrym 
243*4581Ssherrym 		/*
244*4581Ssherrym 		 * Even if rc is not EM_OK, it is a successful operation
245*4581Ssherrym 		 * from ioctl()'s perspective.  We return the detailed error
246*4581Ssherrym 		 * code via the ucode_write_struct data structure.
247*4581Ssherrym 		 */
248*4581Ssherrym 		return (0);
249*4581Ssherrym 	}
250*4581Ssherrym 
251*4581Ssherrym 
252*4581Ssherrym 	default:
253*4581Ssherrym 		return (ENOTTY);
254*4581Ssherrym 	}
255*4581Ssherrym }
256*4581Ssherrym 
257*4581Ssherrym static struct cb_ops ucode_cb_ops = {
258*4581Ssherrym 	ucode_open,
259*4581Ssherrym 	nulldev,	/* close */
260*4581Ssherrym 	nodev,		/* strategy */
261*4581Ssherrym 	nodev,		/* print */
262*4581Ssherrym 	nodev,		/* dump */
263*4581Ssherrym 	nodev,		/* read */
264*4581Ssherrym 	nodev,		/* write */
265*4581Ssherrym 	ucode_ioctl,
266*4581Ssherrym 	nodev,		/* devmap */
267*4581Ssherrym 	nodev,		/* mmap */
268*4581Ssherrym 	nodev,		/* segmap */
269*4581Ssherrym 	nochpoll,	/* poll */
270*4581Ssherrym 	ddi_prop_op,
271*4581Ssherrym 	NULL,
272*4581Ssherrym 	D_64BIT | D_NEW | D_MP
273*4581Ssherrym };
274*4581Ssherrym 
275*4581Ssherrym static struct dev_ops ucode_dv_ops = {
276*4581Ssherrym 	DEVO_REV,
277*4581Ssherrym 	0,
278*4581Ssherrym 	ucode_getinfo,
279*4581Ssherrym 	nulldev,	/* identify */
280*4581Ssherrym 	nulldev,	/* probe */
281*4581Ssherrym 	ucode_attach,
282*4581Ssherrym 	ucode_detach,
283*4581Ssherrym 	nodev,		/* reset */
284*4581Ssherrym 	&ucode_cb_ops,
285*4581Ssherrym 	(struct bus_ops *)0
286*4581Ssherrym };
287*4581Ssherrym 
288*4581Ssherrym static struct modldrv modldrv = {
289*4581Ssherrym 	&mod_driverops,
290*4581Ssherrym 	"ucode driver v%I%",
291*4581Ssherrym 	&ucode_dv_ops
292*4581Ssherrym };
293*4581Ssherrym 
294*4581Ssherrym static struct modlinkage modl = {
295*4581Ssherrym 	MODREV_1,
296*4581Ssherrym 	&modldrv
297*4581Ssherrym };
298*4581Ssherrym 
299*4581Ssherrym int
300*4581Ssherrym _init(void)
301*4581Ssherrym {
302*4581Ssherrym 	int rc;
303*4581Ssherrym 
304*4581Ssherrym 	if ((rc = mod_install(&modl)) != 0)
305*4581Ssherrym 		return (rc);
306*4581Ssherrym 
307*4581Ssherrym 	mutex_init(&ucode_update_lock, NULL, MUTEX_DRIVER, NULL);
308*4581Ssherrym 
309*4581Ssherrym 	return (0);
310*4581Ssherrym }
311*4581Ssherrym 
312*4581Ssherrym int
313*4581Ssherrym _fini(void)
314*4581Ssherrym {
315*4581Ssherrym 	int rc;
316*4581Ssherrym 
317*4581Ssherrym 	if ((rc = mod_remove(&modl)) != 0)
318*4581Ssherrym 		return (rc);
319*4581Ssherrym 
320*4581Ssherrym 	mutex_destroy(&ucode_update_lock);
321*4581Ssherrym 
322*4581Ssherrym 	return (0);
323*4581Ssherrym }
324*4581Ssherrym 
325*4581Ssherrym int
326*4581Ssherrym _info(struct modinfo *modinfo)
327*4581Ssherrym {
328*4581Ssherrym 	return (mod_info(&modl, modinfo));
329*4581Ssherrym }
330