xref: /titanic_51/usr/src/uts/intel/io/ucode_drv.c (revision 9c1d4953b1540f5c51deba9b9684ca504ee2f46f)
12449e17fSsherrym /*
22449e17fSsherrym  * CDDL HEADER START
32449e17fSsherrym  *
42449e17fSsherrym  * The contents of this file are subject to the terms of the
52449e17fSsherrym  * Common Development and Distribution License (the "License").
62449e17fSsherrym  * You may not use this file except in compliance with the License.
72449e17fSsherrym  *
82449e17fSsherrym  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
92449e17fSsherrym  * or http://www.opensolaris.org/os/licensing.
102449e17fSsherrym  * See the License for the specific language governing permissions
112449e17fSsherrym  * and limitations under the License.
122449e17fSsherrym  *
132449e17fSsherrym  * When distributing Covered Code, include this CDDL HEADER in each
142449e17fSsherrym  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
152449e17fSsherrym  * If applicable, add the following below this CDDL HEADER, with the
162449e17fSsherrym  * fields enclosed by brackets "[]" replaced with your own identifying
172449e17fSsherrym  * information: Portions Copyright [yyyy] [name of copyright owner]
182449e17fSsherrym  *
192449e17fSsherrym  * CDDL HEADER END
202449e17fSsherrym  */
212449e17fSsherrym 
222449e17fSsherrym /*
23*9c1d4953SPeter Telford  * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
242449e17fSsherrym  * Use is subject to license terms.
252449e17fSsherrym  */
262449e17fSsherrym 
272449e17fSsherrym #include <sys/types.h>
282449e17fSsherrym #include <sys/file.h>
292449e17fSsherrym #include <sys/errno.h>
302449e17fSsherrym #include <sys/open.h>
312449e17fSsherrym #include <sys/cred.h>
322449e17fSsherrym #include <sys/conf.h>
332449e17fSsherrym #include <sys/stat.h>
342449e17fSsherrym #include <sys/policy.h>
352449e17fSsherrym #include <sys/processor.h>
362449e17fSsherrym #include <sys/kmem.h>
372449e17fSsherrym #include <sys/modctl.h>
382449e17fSsherrym #include <sys/ddi.h>
392449e17fSsherrym #include <sys/sunddi.h>
402449e17fSsherrym 
412449e17fSsherrym #include <sys/auxv.h>
422449e17fSsherrym #include <sys/ucode.h>
432449e17fSsherrym #include <sys/systeminfo.h>
442449e17fSsherrym #include <sys/x86_archext.h>
452449e17fSsherrym 
462449e17fSsherrym static dev_info_t *ucode_devi;
472449e17fSsherrym static uint32_t ucode_max_combined_size;
482449e17fSsherrym static kmutex_t ucode_update_lock;
492449e17fSsherrym 
502449e17fSsherrym /*ARGSUSED*/
512449e17fSsherrym static int
522449e17fSsherrym ucode_getinfo(dev_info_t *devi, ddi_info_cmd_t cmd, void *arg, void **result)
532449e17fSsherrym {
542449e17fSsherrym 	switch (cmd) {
552449e17fSsherrym 	case DDI_INFO_DEVT2DEVINFO:
562449e17fSsherrym 	case DDI_INFO_DEVT2INSTANCE:
572449e17fSsherrym 		break;
582449e17fSsherrym 	default:
592449e17fSsherrym 		return (DDI_FAILURE);
602449e17fSsherrym 	}
612449e17fSsherrym 
622449e17fSsherrym 	switch (getminor((dev_t)arg)) {
632449e17fSsherrym 	case UCODE_MINOR:
642449e17fSsherrym 		break;
652449e17fSsherrym 	default:
662449e17fSsherrym 		return (DDI_FAILURE);
672449e17fSsherrym 	}
682449e17fSsherrym 
692449e17fSsherrym 	if (cmd == DDI_INFO_DEVT2INSTANCE)
702449e17fSsherrym 		*result = 0;
712449e17fSsherrym 	else
722449e17fSsherrym 		*result = ucode_devi;
732449e17fSsherrym 	return (DDI_SUCCESS);
742449e17fSsherrym }
752449e17fSsherrym 
762449e17fSsherrym static int
772449e17fSsherrym ucode_attach(dev_info_t *devi, ddi_attach_cmd_t cmd)
782449e17fSsherrym {
792449e17fSsherrym 	ASSERT(cmd != DDI_RESUME);
802449e17fSsherrym 
812449e17fSsherrym 	switch (cmd) {
822449e17fSsherrym 	case DDI_RESUME:
832449e17fSsherrym 		return (DDI_SUCCESS);
842449e17fSsherrym 
852449e17fSsherrym 	case DDI_ATTACH:
862449e17fSsherrym 		ucode_devi = devi;
872449e17fSsherrym 		ucode_max_combined_size = UCODE_MAX_COMBINED_SIZE;
882449e17fSsherrym 
892449e17fSsherrym 		if (ddi_create_minor_node(devi, UCODE_NODE_NAME, S_IFCHR,
902449e17fSsherrym 		    UCODE_MINOR, DDI_PSEUDO, 0) != DDI_SUCCESS) {
912449e17fSsherrym 			cmn_err(CE_WARN, "%s: Unable to create minor node",
922449e17fSsherrym 			    UCODE_NODE_NAME);
932449e17fSsherrym 			return (DDI_FAILURE);
942449e17fSsherrym 		}
952449e17fSsherrym 		ddi_report_dev(devi);
962449e17fSsherrym 		return (DDI_SUCCESS);
972449e17fSsherrym 
982449e17fSsherrym 	default:
992449e17fSsherrym 		return (DDI_FAILURE);
1002449e17fSsherrym 	}
1012449e17fSsherrym }
1022449e17fSsherrym 
1032449e17fSsherrym static int
1042449e17fSsherrym ucode_detach(dev_info_t *devi, ddi_detach_cmd_t cmd)
1052449e17fSsherrym {
1062449e17fSsherrym 	/*
1072449e17fSsherrym 	 * The power management and DR framework should never invoke this
1082449e17fSsherrym 	 * driver with DDI_SUSPEND because the ucode pseudo device does not
1092449e17fSsherrym 	 * have a reg property or hardware binding.  However, we will return
1102449e17fSsherrym 	 * DDI_SUCCESS so that in the unlikely event that it does get
1112449e17fSsherrym 	 * called, the system will still suspend and resume.
1122449e17fSsherrym 	 */
1132449e17fSsherrym 	ASSERT(cmd != DDI_SUSPEND);
1142449e17fSsherrym 
1152449e17fSsherrym 	switch (cmd) {
1162449e17fSsherrym 	case DDI_SUSPEND:
1172449e17fSsherrym 		return (DDI_SUCCESS);
1182449e17fSsherrym 
1192449e17fSsherrym 	case DDI_DETACH:
1202449e17fSsherrym 		ddi_remove_minor_node(devi, NULL);
1212449e17fSsherrym 		ucode_devi = NULL;
1222449e17fSsherrym 		return (DDI_SUCCESS);
1232449e17fSsherrym 
1242449e17fSsherrym 	default:
1252449e17fSsherrym 		return (DDI_FAILURE);
1262449e17fSsherrym 	}
1272449e17fSsherrym }
1282449e17fSsherrym 
1292449e17fSsherrym /*ARGSUSED1*/
1302449e17fSsherrym static int
1312449e17fSsherrym ucode_open(dev_t *dev, int flag, int otyp, cred_t *cr)
1322449e17fSsherrym {
1332449e17fSsherrym 	return (getminor(*dev) == UCODE_MINOR ? 0 : ENXIO);
1342449e17fSsherrym }
1352449e17fSsherrym 
1362449e17fSsherrym 
1372449e17fSsherrym /*ARGSUSED*/
1382449e17fSsherrym static int
1392449e17fSsherrym ucode_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *cr, int *rval)
1402449e17fSsherrym {
141adc586deSMark Johnson 	/*
142adc586deSMark Johnson 	 * Make sure that the ucode ops pointer has been set up.
143adc586deSMark Johnson 	 */
144adc586deSMark Johnson 	if (!ucode)
145adc586deSMark Johnson 		return (EIO);
146adc586deSMark Johnson 
1472449e17fSsherrym 	switch (cmd) {
1482449e17fSsherrym 	case UCODE_GET_VERSION: {
1492449e17fSsherrym 		int size;
1502449e17fSsherrym 		uint32_t *revp, *rev_array;
151*9c1d4953SPeter Telford 		size_t bufsz = NCPU * sizeof (*revp);
1522449e17fSsherrym 		ucode_errno_t rc = EM_OK;
1532449e17fSsherrym 
1542449e17fSsherrym 		STRUCT_DECL(ucode_get_rev_struct, h);
1552449e17fSsherrym 		STRUCT_INIT(h, mode);
1562449e17fSsherrym 		if (ddi_copyin((void *)arg,
1572449e17fSsherrym 		    STRUCT_BUF(h), STRUCT_SIZE(h), mode))
1582449e17fSsherrym 			return (EFAULT);
1592449e17fSsherrym 
160*9c1d4953SPeter Telford 		if ((size = STRUCT_FGET(h, ugv_size)) > NCPU || size < 0)
1612449e17fSsherrym 			return (EINVAL);
1622449e17fSsherrym 
163*9c1d4953SPeter Telford 		if (size == 0)
164*9c1d4953SPeter Telford 			return (0);
165*9c1d4953SPeter Telford 
1662449e17fSsherrym 		if ((rev_array = STRUCT_FGETP(h, ugv_rev)) == NULL)
1672449e17fSsherrym 			return (EINVAL);
1682449e17fSsherrym 
1692449e17fSsherrym 		size *= sizeof (uint32_t);
1702449e17fSsherrym 
171*9c1d4953SPeter Telford 		/* Can't rely on caller for kernel's buffer size. */
172*9c1d4953SPeter Telford 		revp = kmem_zalloc(bufsz, KM_SLEEP);
1732449e17fSsherrym 		if (ddi_copyin((void *)rev_array, revp, size, mode) != 0) {
174*9c1d4953SPeter Telford 			kmem_free(revp, bufsz);
1752449e17fSsherrym 			return (EINVAL);
1762449e17fSsherrym 		}
1772449e17fSsherrym 
1782449e17fSsherrym 		rc = ucode_get_rev(revp);
1792449e17fSsherrym 
1802449e17fSsherrym 		STRUCT_FSET(h, ugv_errno, rc);
1812449e17fSsherrym 
1822449e17fSsherrym 		if (ddi_copyout(revp, (void *)rev_array, size, mode) != 0) {
183*9c1d4953SPeter Telford 			kmem_free(revp, bufsz);
1842449e17fSsherrym 			return (EFAULT);
1852449e17fSsherrym 		}
1862449e17fSsherrym 
187*9c1d4953SPeter Telford 		kmem_free(revp, bufsz);
1882449e17fSsherrym 
1892449e17fSsherrym 		if (ddi_copyout(STRUCT_BUF(h), (void *)arg,
1902449e17fSsherrym 		    STRUCT_SIZE(h), mode))
1912449e17fSsherrym 			return (EFAULT);
1922449e17fSsherrym 
1932449e17fSsherrym 		return (0);
1942449e17fSsherrym 	}
1952449e17fSsherrym 
1962449e17fSsherrym 	case UCODE_UPDATE: {
1972449e17fSsherrym 		int size;
1982449e17fSsherrym 		uint8_t *ucodep, *uw_ucode;
1992449e17fSsherrym 		ucode_errno_t rc = EM_OK;
2002449e17fSsherrym 
2012449e17fSsherrym 		/*
2022449e17fSsherrym 		 * Requires all privilege.
2032449e17fSsherrym 		 */
2042449e17fSsherrym 		if (cr && secpolicy_ucode_update(cr))
2052449e17fSsherrym 			return (EPERM);
2062449e17fSsherrym 
2072449e17fSsherrym 		STRUCT_DECL(ucode_write_struct, h);
2082449e17fSsherrym 
2092449e17fSsherrym 		STRUCT_INIT(h, mode);
2102449e17fSsherrym 		if (ddi_copyin((void *)arg, STRUCT_BUF(h), STRUCT_SIZE(h),
2112449e17fSsherrym 		    mode))
2122449e17fSsherrym 			return (EFAULT);
2132449e17fSsherrym 
2142449e17fSsherrym 		/*
2152449e17fSsherrym 		 * We allow the size of the combined microcode file to be up to
2162449e17fSsherrym 		 * ucode_max_combined_size.  It is initialized to
2172449e17fSsherrym 		 * UCODE_MAX_COMBINED_SIZE, and can be patched if necessary.
2182449e17fSsherrym 		 */
2192449e17fSsherrym 		size = STRUCT_FGET(h, uw_size);
2202449e17fSsherrym 		if (size > ucode_max_combined_size || size == 0)
2212449e17fSsherrym 			return (EINVAL);
2222449e17fSsherrym 
2232449e17fSsherrym 		if ((uw_ucode = STRUCT_FGETP(h, uw_ucode)) == NULL)
2242449e17fSsherrym 			return (EINVAL);
2252449e17fSsherrym 
2262449e17fSsherrym 		ucodep = kmem_zalloc(size, KM_SLEEP);
2272449e17fSsherrym 		if (ddi_copyin((void *)uw_ucode, ucodep, size, mode) != 0) {
2282449e17fSsherrym 			kmem_free(ucodep, size);
2292449e17fSsherrym 			return (EFAULT);
2302449e17fSsherrym 		}
2312449e17fSsherrym 
232adc586deSMark Johnson 		if ((rc = ucode->validate(ucodep, size)) != EM_OK) {
2332449e17fSsherrym 			kmem_free(ucodep, size);
2342449e17fSsherrym 			STRUCT_FSET(h, uw_errno, rc);
2352449e17fSsherrym 			if (ddi_copyout(STRUCT_BUF(h), (void *)arg,
2362449e17fSsherrym 			    STRUCT_SIZE(h), mode))
2372449e17fSsherrym 				return (EFAULT);
2382449e17fSsherrym 			return (0);
2392449e17fSsherrym 		}
2402449e17fSsherrym 
2412449e17fSsherrym 		mutex_enter(&ucode_update_lock);
2422449e17fSsherrym 		rc = ucode_update(ucodep, size);
2432449e17fSsherrym 		mutex_exit(&ucode_update_lock);
2442449e17fSsherrym 
2452449e17fSsherrym 		kmem_free(ucodep, size);
2462449e17fSsherrym 
2472449e17fSsherrym 		STRUCT_FSET(h, uw_errno, rc);
2482449e17fSsherrym 		if (ddi_copyout(STRUCT_BUF(h), (void *)arg,
2492449e17fSsherrym 		    STRUCT_SIZE(h), mode))
2502449e17fSsherrym 			return (EFAULT);
2512449e17fSsherrym 
2522449e17fSsherrym 		/*
2532449e17fSsherrym 		 * Even if rc is not EM_OK, it is a successful operation
2542449e17fSsherrym 		 * from ioctl()'s perspective.  We return the detailed error
2552449e17fSsherrym 		 * code via the ucode_write_struct data structure.
2562449e17fSsherrym 		 */
2572449e17fSsherrym 		return (0);
2582449e17fSsherrym 	}
2592449e17fSsherrym 
2602449e17fSsherrym 
2612449e17fSsherrym 	default:
2622449e17fSsherrym 		return (ENOTTY);
2632449e17fSsherrym 	}
2642449e17fSsherrym }
2652449e17fSsherrym 
2662449e17fSsherrym static struct cb_ops ucode_cb_ops = {
2672449e17fSsherrym 	ucode_open,
2682449e17fSsherrym 	nulldev,	/* close */
2692449e17fSsherrym 	nodev,		/* strategy */
2702449e17fSsherrym 	nodev,		/* print */
2712449e17fSsherrym 	nodev,		/* dump */
2722449e17fSsherrym 	nodev,		/* read */
2732449e17fSsherrym 	nodev,		/* write */
2742449e17fSsherrym 	ucode_ioctl,
2752449e17fSsherrym 	nodev,		/* devmap */
2762449e17fSsherrym 	nodev,		/* mmap */
2772449e17fSsherrym 	nodev,		/* segmap */
2782449e17fSsherrym 	nochpoll,	/* poll */
2792449e17fSsherrym 	ddi_prop_op,
2802449e17fSsherrym 	NULL,
2812449e17fSsherrym 	D_64BIT | D_NEW | D_MP
2822449e17fSsherrym };
2832449e17fSsherrym 
2842449e17fSsherrym static struct dev_ops ucode_dv_ops = {
2852449e17fSsherrym 	DEVO_REV,
2862449e17fSsherrym 	0,
2872449e17fSsherrym 	ucode_getinfo,
2882449e17fSsherrym 	nulldev,		/* identify */
2892449e17fSsherrym 	nulldev,		/* probe */
2902449e17fSsherrym 	ucode_attach,
2912449e17fSsherrym 	ucode_detach,
2922449e17fSsherrym 	nodev,			/* reset */
2932449e17fSsherrym 	&ucode_cb_ops,
29419397407SSherry Moore 	(struct bus_ops *)0,
29519397407SSherry Moore 	NULL,			/* power */
29619397407SSherry Moore 	ddi_quiesce_not_needed,		/* quiesce */
2972449e17fSsherrym };
2982449e17fSsherrym 
2992449e17fSsherrym static struct modldrv modldrv = {
3002449e17fSsherrym 	&mod_driverops,
301613b2871SRichard Bean 	"ucode driver",
3022449e17fSsherrym 	&ucode_dv_ops
3032449e17fSsherrym };
3042449e17fSsherrym 
3052449e17fSsherrym static struct modlinkage modl = {
3062449e17fSsherrym 	MODREV_1,
3072449e17fSsherrym 	&modldrv
3082449e17fSsherrym };
3092449e17fSsherrym 
3102449e17fSsherrym int
3112449e17fSsherrym _init(void)
3122449e17fSsherrym {
3132449e17fSsherrym 	int rc;
3142449e17fSsherrym 
3152449e17fSsherrym 	if ((rc = mod_install(&modl)) != 0)
3162449e17fSsherrym 		return (rc);
3172449e17fSsherrym 
3182449e17fSsherrym 	mutex_init(&ucode_update_lock, NULL, MUTEX_DRIVER, NULL);
3192449e17fSsherrym 
3202449e17fSsherrym 	return (0);
3212449e17fSsherrym }
3222449e17fSsherrym 
3232449e17fSsherrym int
3242449e17fSsherrym _fini(void)
3252449e17fSsherrym {
3262449e17fSsherrym 	int rc;
3272449e17fSsherrym 
3282449e17fSsherrym 	if ((rc = mod_remove(&modl)) != 0)
3292449e17fSsherrym 		return (rc);
3302449e17fSsherrym 
3312449e17fSsherrym 	mutex_destroy(&ucode_update_lock);
3322449e17fSsherrym 
3332449e17fSsherrym 	return (0);
3342449e17fSsherrym }
3352449e17fSsherrym 
3362449e17fSsherrym int
3372449e17fSsherrym _info(struct modinfo *modinfo)
3382449e17fSsherrym {
3392449e17fSsherrym 	return (mod_info(&modl, modinfo));
3402449e17fSsherrym }
341