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