xref: /onnv-gate/usr/src/uts/intel/io/ucode_drv.c (revision 11607:8efcb308cfc3)
14581Ssherrym /*
24581Ssherrym  * CDDL HEADER START
34581Ssherrym  *
44581Ssherrym  * The contents of this file are subject to the terms of the
54581Ssherrym  * Common Development and Distribution License (the "License").
64581Ssherrym  * You may not use this file except in compliance with the License.
74581Ssherrym  *
84581Ssherrym  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
94581Ssherrym  * or http://www.opensolaris.org/os/licensing.
104581Ssherrym  * See the License for the specific language governing permissions
114581Ssherrym  * and limitations under the License.
124581Ssherrym  *
134581Ssherrym  * When distributing Covered Code, include this CDDL HEADER in each
144581Ssherrym  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
154581Ssherrym  * If applicable, add the following below this CDDL HEADER, with the
164581Ssherrym  * fields enclosed by brackets "[]" replaced with your own identifying
174581Ssherrym  * information: Portions Copyright [yyyy] [name of copyright owner]
184581Ssherrym  *
194581Ssherrym  * CDDL HEADER END
204581Ssherrym  */
214581Ssherrym 
224581Ssherrym /*
23*11607SPeter.Telford@Sun.COM  * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
244581Ssherrym  * Use is subject to license terms.
254581Ssherrym  */
264581Ssherrym 
274581Ssherrym #include <sys/types.h>
284581Ssherrym #include <sys/file.h>
294581Ssherrym #include <sys/errno.h>
304581Ssherrym #include <sys/open.h>
314581Ssherrym #include <sys/cred.h>
324581Ssherrym #include <sys/conf.h>
334581Ssherrym #include <sys/stat.h>
344581Ssherrym #include <sys/policy.h>
354581Ssherrym #include <sys/processor.h>
364581Ssherrym #include <sys/kmem.h>
374581Ssherrym #include <sys/modctl.h>
384581Ssherrym #include <sys/ddi.h>
394581Ssherrym #include <sys/sunddi.h>
404581Ssherrym 
414581Ssherrym #include <sys/auxv.h>
424581Ssherrym #include <sys/ucode.h>
434581Ssherrym #include <sys/systeminfo.h>
444581Ssherrym #include <sys/x86_archext.h>
454581Ssherrym 
464581Ssherrym static dev_info_t *ucode_devi;
474581Ssherrym static uint32_t ucode_max_combined_size;
484581Ssherrym static kmutex_t ucode_update_lock;
494581Ssherrym 
504581Ssherrym /*ARGSUSED*/
514581Ssherrym static int
ucode_getinfo(dev_info_t * devi,ddi_info_cmd_t cmd,void * arg,void ** result)524581Ssherrym ucode_getinfo(dev_info_t *devi, ddi_info_cmd_t cmd, void *arg, void **result)
534581Ssherrym {
544581Ssherrym 	switch (cmd) {
554581Ssherrym 	case DDI_INFO_DEVT2DEVINFO:
564581Ssherrym 	case DDI_INFO_DEVT2INSTANCE:
574581Ssherrym 		break;
584581Ssherrym 	default:
594581Ssherrym 		return (DDI_FAILURE);
604581Ssherrym 	}
614581Ssherrym 
624581Ssherrym 	switch (getminor((dev_t)arg)) {
634581Ssherrym 	case UCODE_MINOR:
644581Ssherrym 		break;
654581Ssherrym 	default:
664581Ssherrym 		return (DDI_FAILURE);
674581Ssherrym 	}
684581Ssherrym 
694581Ssherrym 	if (cmd == DDI_INFO_DEVT2INSTANCE)
704581Ssherrym 		*result = 0;
714581Ssherrym 	else
724581Ssherrym 		*result = ucode_devi;
734581Ssherrym 	return (DDI_SUCCESS);
744581Ssherrym }
754581Ssherrym 
764581Ssherrym static int
ucode_attach(dev_info_t * devi,ddi_attach_cmd_t cmd)774581Ssherrym ucode_attach(dev_info_t *devi, ddi_attach_cmd_t cmd)
784581Ssherrym {
794581Ssherrym 	ASSERT(cmd != DDI_RESUME);
804581Ssherrym 
814581Ssherrym 	switch (cmd) {
824581Ssherrym 	case DDI_RESUME:
834581Ssherrym 		return (DDI_SUCCESS);
844581Ssherrym 
854581Ssherrym 	case DDI_ATTACH:
864581Ssherrym 		ucode_devi = devi;
874581Ssherrym 		ucode_max_combined_size = UCODE_MAX_COMBINED_SIZE;
884581Ssherrym 
894581Ssherrym 		if (ddi_create_minor_node(devi, UCODE_NODE_NAME, S_IFCHR,
904581Ssherrym 		    UCODE_MINOR, DDI_PSEUDO, 0) != DDI_SUCCESS) {
914581Ssherrym 			cmn_err(CE_WARN, "%s: Unable to create minor node",
924581Ssherrym 			    UCODE_NODE_NAME);
934581Ssherrym 			return (DDI_FAILURE);
944581Ssherrym 		}
954581Ssherrym 		ddi_report_dev(devi);
964581Ssherrym 		return (DDI_SUCCESS);
974581Ssherrym 
984581Ssherrym 	default:
994581Ssherrym 		return (DDI_FAILURE);
1004581Ssherrym 	}
1014581Ssherrym }
1024581Ssherrym 
1034581Ssherrym static int
ucode_detach(dev_info_t * devi,ddi_detach_cmd_t cmd)1044581Ssherrym ucode_detach(dev_info_t *devi, ddi_detach_cmd_t cmd)
1054581Ssherrym {
1064581Ssherrym 	/*
1074581Ssherrym 	 * The power management and DR framework should never invoke this
1084581Ssherrym 	 * driver with DDI_SUSPEND because the ucode pseudo device does not
1094581Ssherrym 	 * have a reg property or hardware binding.  However, we will return
1104581Ssherrym 	 * DDI_SUCCESS so that in the unlikely event that it does get
1114581Ssherrym 	 * called, the system will still suspend and resume.
1124581Ssherrym 	 */
1134581Ssherrym 	ASSERT(cmd != DDI_SUSPEND);
1144581Ssherrym 
1154581Ssherrym 	switch (cmd) {
1164581Ssherrym 	case DDI_SUSPEND:
1174581Ssherrym 		return (DDI_SUCCESS);
1184581Ssherrym 
1194581Ssherrym 	case DDI_DETACH:
1204581Ssherrym 		ddi_remove_minor_node(devi, NULL);
1214581Ssherrym 		ucode_devi = NULL;
1224581Ssherrym 		return (DDI_SUCCESS);
1234581Ssherrym 
1244581Ssherrym 	default:
1254581Ssherrym 		return (DDI_FAILURE);
1264581Ssherrym 	}
1274581Ssherrym }
1284581Ssherrym 
1294581Ssherrym /*ARGSUSED1*/
1304581Ssherrym static int
ucode_open(dev_t * dev,int flag,int otyp,cred_t * cr)1314581Ssherrym ucode_open(dev_t *dev, int flag, int otyp, cred_t *cr)
1324581Ssherrym {
1334581Ssherrym 	return (getminor(*dev) == UCODE_MINOR ? 0 : ENXIO);
1344581Ssherrym }
1354581Ssherrym 
1364581Ssherrym 
1374581Ssherrym /*ARGSUSED*/
1384581Ssherrym static int
ucode_ioctl(dev_t dev,int cmd,intptr_t arg,int mode,cred_t * cr,int * rval)1394581Ssherrym ucode_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *cr, int *rval)
1404581Ssherrym {
1417605SMark.Johnson@Sun.COM 	/*
1427605SMark.Johnson@Sun.COM 	 * Make sure that the ucode ops pointer has been set up.
1437605SMark.Johnson@Sun.COM 	 */
1447605SMark.Johnson@Sun.COM 	if (!ucode)
1457605SMark.Johnson@Sun.COM 		return (EIO);
1467605SMark.Johnson@Sun.COM 
1474581Ssherrym 	switch (cmd) {
1484581Ssherrym 	case UCODE_GET_VERSION: {
1494581Ssherrym 		int size;
1504581Ssherrym 		uint32_t *revp, *rev_array;
151*11607SPeter.Telford@Sun.COM 		size_t bufsz = NCPU * sizeof (*revp);
1524581Ssherrym 		ucode_errno_t rc = EM_OK;
1534581Ssherrym 
1544581Ssherrym 		STRUCT_DECL(ucode_get_rev_struct, h);
1554581Ssherrym 		STRUCT_INIT(h, mode);
1564581Ssherrym 		if (ddi_copyin((void *)arg,
1574581Ssherrym 		    STRUCT_BUF(h), STRUCT_SIZE(h), mode))
1584581Ssherrym 			return (EFAULT);
1594581Ssherrym 
160*11607SPeter.Telford@Sun.COM 		if ((size = STRUCT_FGET(h, ugv_size)) > NCPU || size < 0)
1614581Ssherrym 			return (EINVAL);
1624581Ssherrym 
163*11607SPeter.Telford@Sun.COM 		if (size == 0)
164*11607SPeter.Telford@Sun.COM 			return (0);
165*11607SPeter.Telford@Sun.COM 
1664581Ssherrym 		if ((rev_array = STRUCT_FGETP(h, ugv_rev)) == NULL)
1674581Ssherrym 			return (EINVAL);
1684581Ssherrym 
1694581Ssherrym 		size *= sizeof (uint32_t);
1704581Ssherrym 
171*11607SPeter.Telford@Sun.COM 		/* Can't rely on caller for kernel's buffer size. */
172*11607SPeter.Telford@Sun.COM 		revp = kmem_zalloc(bufsz, KM_SLEEP);
1734581Ssherrym 		if (ddi_copyin((void *)rev_array, revp, size, mode) != 0) {
174*11607SPeter.Telford@Sun.COM 			kmem_free(revp, bufsz);
1754581Ssherrym 			return (EINVAL);
1764581Ssherrym 		}
1774581Ssherrym 
1784581Ssherrym 		rc = ucode_get_rev(revp);
1794581Ssherrym 
1804581Ssherrym 		STRUCT_FSET(h, ugv_errno, rc);
1814581Ssherrym 
1824581Ssherrym 		if (ddi_copyout(revp, (void *)rev_array, size, mode) != 0) {
183*11607SPeter.Telford@Sun.COM 			kmem_free(revp, bufsz);
1844581Ssherrym 			return (EFAULT);
1854581Ssherrym 		}
1864581Ssherrym 
187*11607SPeter.Telford@Sun.COM 		kmem_free(revp, bufsz);
1884581Ssherrym 
1894581Ssherrym 		if (ddi_copyout(STRUCT_BUF(h), (void *)arg,
1904581Ssherrym 		    STRUCT_SIZE(h), mode))
1914581Ssherrym 			return (EFAULT);
1924581Ssherrym 
1934581Ssherrym 		return (0);
1944581Ssherrym 	}
1954581Ssherrym 
1964581Ssherrym 	case UCODE_UPDATE: {
1974581Ssherrym 		int size;
1984581Ssherrym 		uint8_t *ucodep, *uw_ucode;
1994581Ssherrym 		ucode_errno_t rc = EM_OK;
2004581Ssherrym 
2014581Ssherrym 		/*
2024581Ssherrym 		 * Requires all privilege.
2034581Ssherrym 		 */
2044581Ssherrym 		if (cr && secpolicy_ucode_update(cr))
2054581Ssherrym 			return (EPERM);
2064581Ssherrym 
2074581Ssherrym 		STRUCT_DECL(ucode_write_struct, h);
2084581Ssherrym 
2094581Ssherrym 		STRUCT_INIT(h, mode);
2104581Ssherrym 		if (ddi_copyin((void *)arg, STRUCT_BUF(h), STRUCT_SIZE(h),
2114581Ssherrym 		    mode))
2124581Ssherrym 			return (EFAULT);
2134581Ssherrym 
2144581Ssherrym 		/*
2154581Ssherrym 		 * We allow the size of the combined microcode file to be up to
2164581Ssherrym 		 * ucode_max_combined_size.  It is initialized to
2174581Ssherrym 		 * UCODE_MAX_COMBINED_SIZE, and can be patched if necessary.
2184581Ssherrym 		 */
2194581Ssherrym 		size = STRUCT_FGET(h, uw_size);
2204581Ssherrym 		if (size > ucode_max_combined_size || size == 0)
2214581Ssherrym 			return (EINVAL);
2224581Ssherrym 
2234581Ssherrym 		if ((uw_ucode = STRUCT_FGETP(h, uw_ucode)) == NULL)
2244581Ssherrym 			return (EINVAL);
2254581Ssherrym 
2264581Ssherrym 		ucodep = kmem_zalloc(size, KM_SLEEP);
2274581Ssherrym 		if (ddi_copyin((void *)uw_ucode, ucodep, size, mode) != 0) {
2284581Ssherrym 			kmem_free(ucodep, size);
2294581Ssherrym 			return (EFAULT);
2304581Ssherrym 		}
2314581Ssherrym 
2327605SMark.Johnson@Sun.COM 		if ((rc = ucode->validate(ucodep, size)) != EM_OK) {
2334581Ssherrym 			kmem_free(ucodep, size);
2344581Ssherrym 			STRUCT_FSET(h, uw_errno, rc);
2354581Ssherrym 			if (ddi_copyout(STRUCT_BUF(h), (void *)arg,
2364581Ssherrym 			    STRUCT_SIZE(h), mode))
2374581Ssherrym 				return (EFAULT);
2384581Ssherrym 			return (0);
2394581Ssherrym 		}
2404581Ssherrym 
2414581Ssherrym 		mutex_enter(&ucode_update_lock);
2424581Ssherrym 		rc = ucode_update(ucodep, size);
2434581Ssherrym 		mutex_exit(&ucode_update_lock);
2444581Ssherrym 
2454581Ssherrym 		kmem_free(ucodep, size);
2464581Ssherrym 
2474581Ssherrym 		STRUCT_FSET(h, uw_errno, rc);
2484581Ssherrym 		if (ddi_copyout(STRUCT_BUF(h), (void *)arg,
2494581Ssherrym 		    STRUCT_SIZE(h), mode))
2504581Ssherrym 			return (EFAULT);
2514581Ssherrym 
2524581Ssherrym 		/*
2534581Ssherrym 		 * Even if rc is not EM_OK, it is a successful operation
2544581Ssherrym 		 * from ioctl()'s perspective.  We return the detailed error
2554581Ssherrym 		 * code via the ucode_write_struct data structure.
2564581Ssherrym 		 */
2574581Ssherrym 		return (0);
2584581Ssherrym 	}
2594581Ssherrym 
2604581Ssherrym 
2614581Ssherrym 	default:
2624581Ssherrym 		return (ENOTTY);
2634581Ssherrym 	}
2644581Ssherrym }
2654581Ssherrym 
2664581Ssherrym static struct cb_ops ucode_cb_ops = {
2674581Ssherrym 	ucode_open,
2684581Ssherrym 	nulldev,	/* close */
2694581Ssherrym 	nodev,		/* strategy */
2704581Ssherrym 	nodev,		/* print */
2714581Ssherrym 	nodev,		/* dump */
2724581Ssherrym 	nodev,		/* read */
2734581Ssherrym 	nodev,		/* write */
2744581Ssherrym 	ucode_ioctl,
2754581Ssherrym 	nodev,		/* devmap */
2764581Ssherrym 	nodev,		/* mmap */
2774581Ssherrym 	nodev,		/* segmap */
2784581Ssherrym 	nochpoll,	/* poll */
2794581Ssherrym 	ddi_prop_op,
2804581Ssherrym 	NULL,
2814581Ssherrym 	D_64BIT | D_NEW | D_MP
2824581Ssherrym };
2834581Ssherrym 
2844581Ssherrym static struct dev_ops ucode_dv_ops = {
2854581Ssherrym 	DEVO_REV,
2864581Ssherrym 	0,
2874581Ssherrym 	ucode_getinfo,
2887656SSherry.Moore@Sun.COM 	nulldev,		/* identify */
2897656SSherry.Moore@Sun.COM 	nulldev,		/* probe */
2904581Ssherrym 	ucode_attach,
2914581Ssherrym 	ucode_detach,
2927656SSherry.Moore@Sun.COM 	nodev,			/* reset */
2934581Ssherrym 	&ucode_cb_ops,
2947656SSherry.Moore@Sun.COM 	(struct bus_ops *)0,
2957656SSherry.Moore@Sun.COM 	NULL,			/* power */
2967656SSherry.Moore@Sun.COM 	ddi_quiesce_not_needed,		/* quiesce */
2974581Ssherrym };
2984581Ssherrym 
2994581Ssherrym static struct modldrv modldrv = {
3004581Ssherrym 	&mod_driverops,
3017542SRichard.Bean@Sun.COM 	"ucode driver",
3024581Ssherrym 	&ucode_dv_ops
3034581Ssherrym };
3044581Ssherrym 
3054581Ssherrym static struct modlinkage modl = {
3064581Ssherrym 	MODREV_1,
3074581Ssherrym 	&modldrv
3084581Ssherrym };
3094581Ssherrym 
3104581Ssherrym int
_init(void)3114581Ssherrym _init(void)
3124581Ssherrym {
3134581Ssherrym 	int rc;
3144581Ssherrym 
3154581Ssherrym 	if ((rc = mod_install(&modl)) != 0)
3164581Ssherrym 		return (rc);
3174581Ssherrym 
3184581Ssherrym 	mutex_init(&ucode_update_lock, NULL, MUTEX_DRIVER, NULL);
3194581Ssherrym 
3204581Ssherrym 	return (0);
3214581Ssherrym }
3224581Ssherrym 
3234581Ssherrym int
_fini(void)3244581Ssherrym _fini(void)
3254581Ssherrym {
3264581Ssherrym 	int rc;
3274581Ssherrym 
3284581Ssherrym 	if ((rc = mod_remove(&modl)) != 0)
3294581Ssherrym 		return (rc);
3304581Ssherrym 
3314581Ssherrym 	mutex_destroy(&ucode_update_lock);
3324581Ssherrym 
3334581Ssherrym 	return (0);
3344581Ssherrym }
3354581Ssherrym 
3364581Ssherrym int
_info(struct modinfo * modinfo)3374581Ssherrym _info(struct modinfo *modinfo)
3384581Ssherrym {
3394581Ssherrym 	return (mod_info(&modl, modinfo));
3404581Ssherrym }
341