xref: /onnv-gate/usr/src/uts/intel/io/ucode_drv.c (revision 7605)
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 /*
237542SRichard.Bean@Sun.COM  * Copyright 2008 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
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
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
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
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
1394581Ssherrym ucode_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *cr, int *rval)
1404581Ssherrym {
141*7605SMark.Johnson@Sun.COM 	/*
142*7605SMark.Johnson@Sun.COM 	 * Make sure that the ucode ops pointer has been set up.
143*7605SMark.Johnson@Sun.COM 	 */
144*7605SMark.Johnson@Sun.COM 	if (!ucode)
145*7605SMark.Johnson@Sun.COM 		return (EIO);
146*7605SMark.Johnson@Sun.COM 
1474581Ssherrym 	switch (cmd) {
1484581Ssherrym 	case UCODE_GET_VERSION: {
1494581Ssherrym 		int size;
1504581Ssherrym 		uint32_t *revp, *rev_array;
1514581Ssherrym 		ucode_errno_t rc = EM_OK;
1524581Ssherrym 
1534581Ssherrym 		STRUCT_DECL(ucode_get_rev_struct, h);
1544581Ssherrym 		STRUCT_INIT(h, mode);
1554581Ssherrym 		if (ddi_copyin((void *)arg,
1564581Ssherrym 		    STRUCT_BUF(h), STRUCT_SIZE(h), mode))
1574581Ssherrym 			return (EFAULT);
1584581Ssherrym 
1594581Ssherrym 		if ((size = STRUCT_FGET(h, ugv_size)) > NCPU)
1604581Ssherrym 			return (EINVAL);
1614581Ssherrym 
1624581Ssherrym 		if ((rev_array = STRUCT_FGETP(h, ugv_rev)) == NULL)
1634581Ssherrym 			return (EINVAL);
1644581Ssherrym 
1654581Ssherrym 		size *= sizeof (uint32_t);
1664581Ssherrym 
1674581Ssherrym 		revp = kmem_zalloc(size, KM_SLEEP);
1684581Ssherrym 		if (ddi_copyin((void *)rev_array, revp, size, mode) != 0) {
1694581Ssherrym 			kmem_free(revp, size);
1704581Ssherrym 			return (EINVAL);
1714581Ssherrym 		}
1724581Ssherrym 
1734581Ssherrym 		rc = ucode_get_rev(revp);
1744581Ssherrym 
1754581Ssherrym 		STRUCT_FSET(h, ugv_errno, rc);
1764581Ssherrym 
1774581Ssherrym 		if (ddi_copyout(revp, (void *)rev_array, size, mode) != 0) {
1784581Ssherrym 			kmem_free(revp, size);
1794581Ssherrym 			return (EFAULT);
1804581Ssherrym 		}
1814581Ssherrym 
1824581Ssherrym 		kmem_free(revp, size);
1834581Ssherrym 
1844581Ssherrym 		if (ddi_copyout(STRUCT_BUF(h), (void *)arg,
1854581Ssherrym 		    STRUCT_SIZE(h), mode))
1864581Ssherrym 			return (EFAULT);
1874581Ssherrym 
1884581Ssherrym 		return (0);
1894581Ssherrym 	}
1904581Ssherrym 
1914581Ssherrym 	case UCODE_UPDATE: {
1924581Ssherrym 		int size;
1934581Ssherrym 		uint8_t *ucodep, *uw_ucode;
1944581Ssherrym 		ucode_errno_t rc = EM_OK;
1954581Ssherrym 
1964581Ssherrym 		/*
1974581Ssherrym 		 * Requires all privilege.
1984581Ssherrym 		 */
1994581Ssherrym 		if (cr && secpolicy_ucode_update(cr))
2004581Ssherrym 			return (EPERM);
2014581Ssherrym 
2024581Ssherrym 		STRUCT_DECL(ucode_write_struct, h);
2034581Ssherrym 
2044581Ssherrym 		STRUCT_INIT(h, mode);
2054581Ssherrym 		if (ddi_copyin((void *)arg, STRUCT_BUF(h), STRUCT_SIZE(h),
2064581Ssherrym 		    mode))
2074581Ssherrym 			return (EFAULT);
2084581Ssherrym 
2094581Ssherrym 		/*
2104581Ssherrym 		 * We allow the size of the combined microcode file to be up to
2114581Ssherrym 		 * ucode_max_combined_size.  It is initialized to
2124581Ssherrym 		 * UCODE_MAX_COMBINED_SIZE, and can be patched if necessary.
2134581Ssherrym 		 */
2144581Ssherrym 		size = STRUCT_FGET(h, uw_size);
2154581Ssherrym 		if (size > ucode_max_combined_size || size == 0)
2164581Ssherrym 			return (EINVAL);
2174581Ssherrym 
2184581Ssherrym 		if ((uw_ucode = STRUCT_FGETP(h, uw_ucode)) == NULL)
2194581Ssherrym 			return (EINVAL);
2204581Ssherrym 
2214581Ssherrym 		ucodep = kmem_zalloc(size, KM_SLEEP);
2224581Ssherrym 		if (ddi_copyin((void *)uw_ucode, ucodep, size, mode) != 0) {
2234581Ssherrym 			kmem_free(ucodep, size);
2244581Ssherrym 			return (EFAULT);
2254581Ssherrym 		}
2264581Ssherrym 
227*7605SMark.Johnson@Sun.COM 		if ((rc = ucode->validate(ucodep, size)) != EM_OK) {
2284581Ssherrym 			kmem_free(ucodep, size);
2294581Ssherrym 			STRUCT_FSET(h, uw_errno, rc);
2304581Ssherrym 			if (ddi_copyout(STRUCT_BUF(h), (void *)arg,
2314581Ssherrym 			    STRUCT_SIZE(h), mode))
2324581Ssherrym 				return (EFAULT);
2334581Ssherrym 			return (0);
2344581Ssherrym 		}
2354581Ssherrym 
2364581Ssherrym 		mutex_enter(&ucode_update_lock);
2374581Ssherrym 		rc = ucode_update(ucodep, size);
2384581Ssherrym 		mutex_exit(&ucode_update_lock);
2394581Ssherrym 
2404581Ssherrym 		kmem_free(ucodep, size);
2414581Ssherrym 
2424581Ssherrym 		STRUCT_FSET(h, uw_errno, rc);
2434581Ssherrym 		if (ddi_copyout(STRUCT_BUF(h), (void *)arg,
2444581Ssherrym 		    STRUCT_SIZE(h), mode))
2454581Ssherrym 			return (EFAULT);
2464581Ssherrym 
2474581Ssherrym 		/*
2484581Ssherrym 		 * Even if rc is not EM_OK, it is a successful operation
2494581Ssherrym 		 * from ioctl()'s perspective.  We return the detailed error
2504581Ssherrym 		 * code via the ucode_write_struct data structure.
2514581Ssherrym 		 */
2524581Ssherrym 		return (0);
2534581Ssherrym 	}
2544581Ssherrym 
2554581Ssherrym 
2564581Ssherrym 	default:
2574581Ssherrym 		return (ENOTTY);
2584581Ssherrym 	}
2594581Ssherrym }
2604581Ssherrym 
2614581Ssherrym static struct cb_ops ucode_cb_ops = {
2624581Ssherrym 	ucode_open,
2634581Ssherrym 	nulldev,	/* close */
2644581Ssherrym 	nodev,		/* strategy */
2654581Ssherrym 	nodev,		/* print */
2664581Ssherrym 	nodev,		/* dump */
2674581Ssherrym 	nodev,		/* read */
2684581Ssherrym 	nodev,		/* write */
2694581Ssherrym 	ucode_ioctl,
2704581Ssherrym 	nodev,		/* devmap */
2714581Ssherrym 	nodev,		/* mmap */
2724581Ssherrym 	nodev,		/* segmap */
2734581Ssherrym 	nochpoll,	/* poll */
2744581Ssherrym 	ddi_prop_op,
2754581Ssherrym 	NULL,
2764581Ssherrym 	D_64BIT | D_NEW | D_MP
2774581Ssherrym };
2784581Ssherrym 
2794581Ssherrym static struct dev_ops ucode_dv_ops = {
2804581Ssherrym 	DEVO_REV,
2814581Ssherrym 	0,
2824581Ssherrym 	ucode_getinfo,
2834581Ssherrym 	nulldev,	/* identify */
2844581Ssherrym 	nulldev,	/* probe */
2854581Ssherrym 	ucode_attach,
2864581Ssherrym 	ucode_detach,
2874581Ssherrym 	nodev,		/* reset */
2884581Ssherrym 	&ucode_cb_ops,
2894581Ssherrym 	(struct bus_ops *)0
2904581Ssherrym };
2914581Ssherrym 
2924581Ssherrym static struct modldrv modldrv = {
2934581Ssherrym 	&mod_driverops,
2947542SRichard.Bean@Sun.COM 	"ucode driver",
2954581Ssherrym 	&ucode_dv_ops
2964581Ssherrym };
2974581Ssherrym 
2984581Ssherrym static struct modlinkage modl = {
2994581Ssherrym 	MODREV_1,
3004581Ssherrym 	&modldrv
3014581Ssherrym };
3024581Ssherrym 
3034581Ssherrym int
3044581Ssherrym _init(void)
3054581Ssherrym {
3064581Ssherrym 	int rc;
3074581Ssherrym 
3084581Ssherrym 	if ((rc = mod_install(&modl)) != 0)
3094581Ssherrym 		return (rc);
3104581Ssherrym 
3114581Ssherrym 	mutex_init(&ucode_update_lock, NULL, MUTEX_DRIVER, NULL);
3124581Ssherrym 
3134581Ssherrym 	return (0);
3144581Ssherrym }
3154581Ssherrym 
3164581Ssherrym int
3174581Ssherrym _fini(void)
3184581Ssherrym {
3194581Ssherrym 	int rc;
3204581Ssherrym 
3214581Ssherrym 	if ((rc = mod_remove(&modl)) != 0)
3224581Ssherrym 		return (rc);
3234581Ssherrym 
3244581Ssherrym 	mutex_destroy(&ucode_update_lock);
3254581Ssherrym 
3264581Ssherrym 	return (0);
3274581Ssherrym }
3284581Ssherrym 
3294581Ssherrym int
3304581Ssherrym _info(struct modinfo *modinfo)
3314581Ssherrym {
3324581Ssherrym 	return (mod_info(&modl, modinfo));
3334581Ssherrym }
334