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