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
ucode_getinfo(dev_info_t * devi,ddi_info_cmd_t cmd,void * arg,void ** result)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
ucode_attach(dev_info_t * devi,ddi_attach_cmd_t cmd)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
ucode_detach(dev_info_t * devi,ddi_detach_cmd_t cmd)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
ucode_open(dev_t * dev,int flag,int otyp,cred_t * cr)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
ucode_ioctl(dev_t dev,int cmd,intptr_t arg,int mode,cred_t * cr,int * rval)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
_init(void)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
_fini(void)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
_info(struct modinfo * modinfo)3372449e17fSsherrym _info(struct modinfo *modinfo)
3382449e17fSsherrym {
3392449e17fSsherrym return (mod_info(&modl, modinfo));
3402449e17fSsherrym }
341