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