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