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