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 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 { 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 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 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 3374581Ssherrym _info(struct modinfo *modinfo) 3384581Ssherrym { 3394581Ssherrym return (mod_info(&modl, modinfo)); 3404581Ssherrym } 341