1*4581Ssherrym /* 2*4581Ssherrym * CDDL HEADER START 3*4581Ssherrym * 4*4581Ssherrym * The contents of this file are subject to the terms of the 5*4581Ssherrym * Common Development and Distribution License (the "License"). 6*4581Ssherrym * You may not use this file except in compliance with the License. 7*4581Ssherrym * 8*4581Ssherrym * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9*4581Ssherrym * or http://www.opensolaris.org/os/licensing. 10*4581Ssherrym * See the License for the specific language governing permissions 11*4581Ssherrym * and limitations under the License. 12*4581Ssherrym * 13*4581Ssherrym * When distributing Covered Code, include this CDDL HEADER in each 14*4581Ssherrym * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15*4581Ssherrym * If applicable, add the following below this CDDL HEADER, with the 16*4581Ssherrym * fields enclosed by brackets "[]" replaced with your own identifying 17*4581Ssherrym * information: Portions Copyright [yyyy] [name of copyright owner] 18*4581Ssherrym * 19*4581Ssherrym * CDDL HEADER END 20*4581Ssherrym */ 21*4581Ssherrym 22*4581Ssherrym /* 23*4581Ssherrym * Copyright 2007 Sun Microsystems, Inc. All rights reserved. 24*4581Ssherrym * Use is subject to license terms. 25*4581Ssherrym */ 26*4581Ssherrym 27*4581Ssherrym #pragma ident "%Z%%M% %I% %E% SMI" 28*4581Ssherrym 29*4581Ssherrym #include <sys/types.h> 30*4581Ssherrym #include <sys/file.h> 31*4581Ssherrym #include <sys/errno.h> 32*4581Ssherrym #include <sys/open.h> 33*4581Ssherrym #include <sys/cred.h> 34*4581Ssherrym #include <sys/conf.h> 35*4581Ssherrym #include <sys/stat.h> 36*4581Ssherrym #include <sys/policy.h> 37*4581Ssherrym #include <sys/processor.h> 38*4581Ssherrym #include <sys/kmem.h> 39*4581Ssherrym #include <sys/modctl.h> 40*4581Ssherrym #include <sys/ddi.h> 41*4581Ssherrym #include <sys/sunddi.h> 42*4581Ssherrym 43*4581Ssherrym #include <sys/auxv.h> 44*4581Ssherrym #include <sys/ucode.h> 45*4581Ssherrym #include <sys/systeminfo.h> 46*4581Ssherrym #include <sys/x86_archext.h> 47*4581Ssherrym 48*4581Ssherrym static dev_info_t *ucode_devi; 49*4581Ssherrym static uint32_t ucode_max_combined_size; 50*4581Ssherrym static kmutex_t ucode_update_lock; 51*4581Ssherrym 52*4581Ssherrym /*ARGSUSED*/ 53*4581Ssherrym static int 54*4581Ssherrym ucode_getinfo(dev_info_t *devi, ddi_info_cmd_t cmd, void *arg, void **result) 55*4581Ssherrym { 56*4581Ssherrym switch (cmd) { 57*4581Ssherrym case DDI_INFO_DEVT2DEVINFO: 58*4581Ssherrym case DDI_INFO_DEVT2INSTANCE: 59*4581Ssherrym break; 60*4581Ssherrym default: 61*4581Ssherrym return (DDI_FAILURE); 62*4581Ssherrym } 63*4581Ssherrym 64*4581Ssherrym switch (getminor((dev_t)arg)) { 65*4581Ssherrym case UCODE_MINOR: 66*4581Ssherrym break; 67*4581Ssherrym default: 68*4581Ssherrym return (DDI_FAILURE); 69*4581Ssherrym } 70*4581Ssherrym 71*4581Ssherrym if (cmd == DDI_INFO_DEVT2INSTANCE) 72*4581Ssherrym *result = 0; 73*4581Ssherrym else 74*4581Ssherrym *result = ucode_devi; 75*4581Ssherrym return (DDI_SUCCESS); 76*4581Ssherrym } 77*4581Ssherrym 78*4581Ssherrym static int 79*4581Ssherrym ucode_attach(dev_info_t *devi, ddi_attach_cmd_t cmd) 80*4581Ssherrym { 81*4581Ssherrym ASSERT(cmd != DDI_RESUME); 82*4581Ssherrym 83*4581Ssherrym switch (cmd) { 84*4581Ssherrym case DDI_RESUME: 85*4581Ssherrym return (DDI_SUCCESS); 86*4581Ssherrym 87*4581Ssherrym case DDI_ATTACH: 88*4581Ssherrym ucode_devi = devi; 89*4581Ssherrym ucode_max_combined_size = UCODE_MAX_COMBINED_SIZE; 90*4581Ssherrym 91*4581Ssherrym if (ddi_create_minor_node(devi, UCODE_NODE_NAME, S_IFCHR, 92*4581Ssherrym UCODE_MINOR, DDI_PSEUDO, 0) != DDI_SUCCESS) { 93*4581Ssherrym cmn_err(CE_WARN, "%s: Unable to create minor node", 94*4581Ssherrym UCODE_NODE_NAME); 95*4581Ssherrym return (DDI_FAILURE); 96*4581Ssherrym } 97*4581Ssherrym ddi_report_dev(devi); 98*4581Ssherrym return (DDI_SUCCESS); 99*4581Ssherrym 100*4581Ssherrym default: 101*4581Ssherrym return (DDI_FAILURE); 102*4581Ssherrym } 103*4581Ssherrym } 104*4581Ssherrym 105*4581Ssherrym static int 106*4581Ssherrym ucode_detach(dev_info_t *devi, ddi_detach_cmd_t cmd) 107*4581Ssherrym { 108*4581Ssherrym /* 109*4581Ssherrym * The power management and DR framework should never invoke this 110*4581Ssherrym * driver with DDI_SUSPEND because the ucode pseudo device does not 111*4581Ssherrym * have a reg property or hardware binding. However, we will return 112*4581Ssherrym * DDI_SUCCESS so that in the unlikely event that it does get 113*4581Ssherrym * called, the system will still suspend and resume. 114*4581Ssherrym */ 115*4581Ssherrym ASSERT(cmd != DDI_SUSPEND); 116*4581Ssherrym 117*4581Ssherrym switch (cmd) { 118*4581Ssherrym case DDI_SUSPEND: 119*4581Ssherrym return (DDI_SUCCESS); 120*4581Ssherrym 121*4581Ssherrym case DDI_DETACH: 122*4581Ssherrym ddi_remove_minor_node(devi, NULL); 123*4581Ssherrym ucode_devi = NULL; 124*4581Ssherrym return (DDI_SUCCESS); 125*4581Ssherrym 126*4581Ssherrym default: 127*4581Ssherrym return (DDI_FAILURE); 128*4581Ssherrym } 129*4581Ssherrym } 130*4581Ssherrym 131*4581Ssherrym /*ARGSUSED1*/ 132*4581Ssherrym static int 133*4581Ssherrym ucode_open(dev_t *dev, int flag, int otyp, cred_t *cr) 134*4581Ssherrym { 135*4581Ssherrym return (getminor(*dev) == UCODE_MINOR ? 0 : ENXIO); 136*4581Ssherrym } 137*4581Ssherrym 138*4581Ssherrym 139*4581Ssherrym /*ARGSUSED*/ 140*4581Ssherrym static int 141*4581Ssherrym ucode_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *cr, int *rval) 142*4581Ssherrym { 143*4581Ssherrym switch (cmd) { 144*4581Ssherrym case UCODE_GET_VERSION: { 145*4581Ssherrym int size; 146*4581Ssherrym uint32_t *revp, *rev_array; 147*4581Ssherrym ucode_errno_t rc = EM_OK; 148*4581Ssherrym 149*4581Ssherrym STRUCT_DECL(ucode_get_rev_struct, h); 150*4581Ssherrym STRUCT_INIT(h, mode); 151*4581Ssherrym if (ddi_copyin((void *)arg, 152*4581Ssherrym STRUCT_BUF(h), STRUCT_SIZE(h), mode)) 153*4581Ssherrym return (EFAULT); 154*4581Ssherrym 155*4581Ssherrym if ((size = STRUCT_FGET(h, ugv_size)) > NCPU) 156*4581Ssherrym return (EINVAL); 157*4581Ssherrym 158*4581Ssherrym if ((rev_array = STRUCT_FGETP(h, ugv_rev)) == NULL) 159*4581Ssherrym return (EINVAL); 160*4581Ssherrym 161*4581Ssherrym size *= sizeof (uint32_t); 162*4581Ssherrym 163*4581Ssherrym revp = kmem_zalloc(size, KM_SLEEP); 164*4581Ssherrym if (ddi_copyin((void *)rev_array, revp, size, mode) != 0) { 165*4581Ssherrym kmem_free(revp, size); 166*4581Ssherrym return (EINVAL); 167*4581Ssherrym } 168*4581Ssherrym 169*4581Ssherrym rc = ucode_get_rev(revp); 170*4581Ssherrym 171*4581Ssherrym STRUCT_FSET(h, ugv_errno, rc); 172*4581Ssherrym 173*4581Ssherrym if (ddi_copyout(revp, (void *)rev_array, size, mode) != 0) { 174*4581Ssherrym kmem_free(revp, size); 175*4581Ssherrym return (EFAULT); 176*4581Ssherrym } 177*4581Ssherrym 178*4581Ssherrym kmem_free(revp, size); 179*4581Ssherrym 180*4581Ssherrym if (ddi_copyout(STRUCT_BUF(h), (void *)arg, 181*4581Ssherrym STRUCT_SIZE(h), mode)) 182*4581Ssherrym return (EFAULT); 183*4581Ssherrym 184*4581Ssherrym return (0); 185*4581Ssherrym } 186*4581Ssherrym 187*4581Ssherrym case UCODE_UPDATE: { 188*4581Ssherrym int size; 189*4581Ssherrym uint8_t *ucodep, *uw_ucode; 190*4581Ssherrym ucode_errno_t rc = EM_OK; 191*4581Ssherrym 192*4581Ssherrym /* 193*4581Ssherrym * Requires all privilege. 194*4581Ssherrym */ 195*4581Ssherrym if (cr && secpolicy_ucode_update(cr)) 196*4581Ssherrym return (EPERM); 197*4581Ssherrym 198*4581Ssherrym STRUCT_DECL(ucode_write_struct, h); 199*4581Ssherrym 200*4581Ssherrym STRUCT_INIT(h, mode); 201*4581Ssherrym if (ddi_copyin((void *)arg, STRUCT_BUF(h), STRUCT_SIZE(h), 202*4581Ssherrym mode)) 203*4581Ssherrym return (EFAULT); 204*4581Ssherrym 205*4581Ssherrym /* 206*4581Ssherrym * We allow the size of the combined microcode file to be up to 207*4581Ssherrym * ucode_max_combined_size. It is initialized to 208*4581Ssherrym * UCODE_MAX_COMBINED_SIZE, and can be patched if necessary. 209*4581Ssherrym */ 210*4581Ssherrym size = STRUCT_FGET(h, uw_size); 211*4581Ssherrym if (size > ucode_max_combined_size || size == 0) 212*4581Ssherrym return (EINVAL); 213*4581Ssherrym 214*4581Ssherrym if ((uw_ucode = STRUCT_FGETP(h, uw_ucode)) == NULL) 215*4581Ssherrym return (EINVAL); 216*4581Ssherrym 217*4581Ssherrym ucodep = kmem_zalloc(size, KM_SLEEP); 218*4581Ssherrym if (ddi_copyin((void *)uw_ucode, ucodep, size, mode) != 0) { 219*4581Ssherrym kmem_free(ucodep, size); 220*4581Ssherrym return (EFAULT); 221*4581Ssherrym } 222*4581Ssherrym 223*4581Ssherrym if ((rc = ucode_validate(ucodep, size)) != EM_OK) { 224*4581Ssherrym kmem_free(ucodep, size); 225*4581Ssherrym STRUCT_FSET(h, uw_errno, rc); 226*4581Ssherrym if (ddi_copyout(STRUCT_BUF(h), (void *)arg, 227*4581Ssherrym STRUCT_SIZE(h), mode)) 228*4581Ssherrym return (EFAULT); 229*4581Ssherrym return (0); 230*4581Ssherrym } 231*4581Ssherrym 232*4581Ssherrym mutex_enter(&ucode_update_lock); 233*4581Ssherrym rc = ucode_update(ucodep, size); 234*4581Ssherrym mutex_exit(&ucode_update_lock); 235*4581Ssherrym 236*4581Ssherrym kmem_free(ucodep, size); 237*4581Ssherrym 238*4581Ssherrym STRUCT_FSET(h, uw_errno, rc); 239*4581Ssherrym if (ddi_copyout(STRUCT_BUF(h), (void *)arg, 240*4581Ssherrym STRUCT_SIZE(h), mode)) 241*4581Ssherrym return (EFAULT); 242*4581Ssherrym 243*4581Ssherrym /* 244*4581Ssherrym * Even if rc is not EM_OK, it is a successful operation 245*4581Ssherrym * from ioctl()'s perspective. We return the detailed error 246*4581Ssherrym * code via the ucode_write_struct data structure. 247*4581Ssherrym */ 248*4581Ssherrym return (0); 249*4581Ssherrym } 250*4581Ssherrym 251*4581Ssherrym 252*4581Ssherrym default: 253*4581Ssherrym return (ENOTTY); 254*4581Ssherrym } 255*4581Ssherrym } 256*4581Ssherrym 257*4581Ssherrym static struct cb_ops ucode_cb_ops = { 258*4581Ssherrym ucode_open, 259*4581Ssherrym nulldev, /* close */ 260*4581Ssherrym nodev, /* strategy */ 261*4581Ssherrym nodev, /* print */ 262*4581Ssherrym nodev, /* dump */ 263*4581Ssherrym nodev, /* read */ 264*4581Ssherrym nodev, /* write */ 265*4581Ssherrym ucode_ioctl, 266*4581Ssherrym nodev, /* devmap */ 267*4581Ssherrym nodev, /* mmap */ 268*4581Ssherrym nodev, /* segmap */ 269*4581Ssherrym nochpoll, /* poll */ 270*4581Ssherrym ddi_prop_op, 271*4581Ssherrym NULL, 272*4581Ssherrym D_64BIT | D_NEW | D_MP 273*4581Ssherrym }; 274*4581Ssherrym 275*4581Ssherrym static struct dev_ops ucode_dv_ops = { 276*4581Ssherrym DEVO_REV, 277*4581Ssherrym 0, 278*4581Ssherrym ucode_getinfo, 279*4581Ssherrym nulldev, /* identify */ 280*4581Ssherrym nulldev, /* probe */ 281*4581Ssherrym ucode_attach, 282*4581Ssherrym ucode_detach, 283*4581Ssherrym nodev, /* reset */ 284*4581Ssherrym &ucode_cb_ops, 285*4581Ssherrym (struct bus_ops *)0 286*4581Ssherrym }; 287*4581Ssherrym 288*4581Ssherrym static struct modldrv modldrv = { 289*4581Ssherrym &mod_driverops, 290*4581Ssherrym "ucode driver v%I%", 291*4581Ssherrym &ucode_dv_ops 292*4581Ssherrym }; 293*4581Ssherrym 294*4581Ssherrym static struct modlinkage modl = { 295*4581Ssherrym MODREV_1, 296*4581Ssherrym &modldrv 297*4581Ssherrym }; 298*4581Ssherrym 299*4581Ssherrym int 300*4581Ssherrym _init(void) 301*4581Ssherrym { 302*4581Ssherrym int rc; 303*4581Ssherrym 304*4581Ssherrym if ((rc = mod_install(&modl)) != 0) 305*4581Ssherrym return (rc); 306*4581Ssherrym 307*4581Ssherrym mutex_init(&ucode_update_lock, NULL, MUTEX_DRIVER, NULL); 308*4581Ssherrym 309*4581Ssherrym return (0); 310*4581Ssherrym } 311*4581Ssherrym 312*4581Ssherrym int 313*4581Ssherrym _fini(void) 314*4581Ssherrym { 315*4581Ssherrym int rc; 316*4581Ssherrym 317*4581Ssherrym if ((rc = mod_remove(&modl)) != 0) 318*4581Ssherrym return (rc); 319*4581Ssherrym 320*4581Ssherrym mutex_destroy(&ucode_update_lock); 321*4581Ssherrym 322*4581Ssherrym return (0); 323*4581Ssherrym } 324*4581Ssherrym 325*4581Ssherrym int 326*4581Ssherrym _info(struct modinfo *modinfo) 327*4581Ssherrym { 328*4581Ssherrym return (mod_info(&modl, modinfo)); 329*4581Ssherrym } 330