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