summaryrefslogtreecommitdiff
path: root/usr/src/uts/intel/io/ucode_drv.c
diff options
context:
space:
mode:
authorsherrym <none@none>2007-07-02 14:05:35 -0700
committersherrym <none@none>2007-07-02 14:05:35 -0700
commit2449e17f82f6097fd2c665b64723e31ceecbeca6 (patch)
tree4adce4537b78e91f1ac4f87433c9dddb715fffd2 /usr/src/uts/intel/io/ucode_drv.c
parent76bc40308a78598795fbedd14f726061bcd17cad (diff)
downloadillumos-joyent-2449e17f82f6097fd2c665b64723e31ceecbeca6.tar.gz
PSARC/2007/349 Intel Microcode Update Support
6558456 Need to support microcode update on Intel platforms
Diffstat (limited to 'usr/src/uts/intel/io/ucode_drv.c')
-rw-r--r--usr/src/uts/intel/io/ucode_drv.c329
1 files changed, 329 insertions, 0 deletions
diff --git a/usr/src/uts/intel/io/ucode_drv.c b/usr/src/uts/intel/io/ucode_drv.c
new file mode 100644
index 0000000000..899516393c
--- /dev/null
+++ b/usr/src/uts/intel/io/ucode_drv.c
@@ -0,0 +1,329 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sys/types.h>
+#include <sys/file.h>
+#include <sys/errno.h>
+#include <sys/open.h>
+#include <sys/cred.h>
+#include <sys/conf.h>
+#include <sys/stat.h>
+#include <sys/policy.h>
+#include <sys/processor.h>
+#include <sys/kmem.h>
+#include <sys/modctl.h>
+#include <sys/ddi.h>
+#include <sys/sunddi.h>
+
+#include <sys/auxv.h>
+#include <sys/ucode.h>
+#include <sys/systeminfo.h>
+#include <sys/x86_archext.h>
+
+static dev_info_t *ucode_devi;
+static uint32_t ucode_max_combined_size;
+static kmutex_t ucode_update_lock;
+
+/*ARGSUSED*/
+static int
+ucode_getinfo(dev_info_t *devi, ddi_info_cmd_t cmd, void *arg, void **result)
+{
+ switch (cmd) {
+ case DDI_INFO_DEVT2DEVINFO:
+ case DDI_INFO_DEVT2INSTANCE:
+ break;
+ default:
+ return (DDI_FAILURE);
+ }
+
+ switch (getminor((dev_t)arg)) {
+ case UCODE_MINOR:
+ break;
+ default:
+ return (DDI_FAILURE);
+ }
+
+ if (cmd == DDI_INFO_DEVT2INSTANCE)
+ *result = 0;
+ else
+ *result = ucode_devi;
+ return (DDI_SUCCESS);
+}
+
+static int
+ucode_attach(dev_info_t *devi, ddi_attach_cmd_t cmd)
+{
+ ASSERT(cmd != DDI_RESUME);
+
+ switch (cmd) {
+ case DDI_RESUME:
+ return (DDI_SUCCESS);
+
+ case DDI_ATTACH:
+ ucode_devi = devi;
+ ucode_max_combined_size = UCODE_MAX_COMBINED_SIZE;
+
+ if (ddi_create_minor_node(devi, UCODE_NODE_NAME, S_IFCHR,
+ UCODE_MINOR, DDI_PSEUDO, 0) != DDI_SUCCESS) {
+ cmn_err(CE_WARN, "%s: Unable to create minor node",
+ UCODE_NODE_NAME);
+ return (DDI_FAILURE);
+ }
+ ddi_report_dev(devi);
+ return (DDI_SUCCESS);
+
+ default:
+ return (DDI_FAILURE);
+ }
+}
+
+static int
+ucode_detach(dev_info_t *devi, ddi_detach_cmd_t cmd)
+{
+ /*
+ * The power management and DR framework should never invoke this
+ * driver with DDI_SUSPEND because the ucode pseudo device does not
+ * have a reg property or hardware binding. However, we will return
+ * DDI_SUCCESS so that in the unlikely event that it does get
+ * called, the system will still suspend and resume.
+ */
+ ASSERT(cmd != DDI_SUSPEND);
+
+ switch (cmd) {
+ case DDI_SUSPEND:
+ return (DDI_SUCCESS);
+
+ case DDI_DETACH:
+ ddi_remove_minor_node(devi, NULL);
+ ucode_devi = NULL;
+ return (DDI_SUCCESS);
+
+ default:
+ return (DDI_FAILURE);
+ }
+}
+
+/*ARGSUSED1*/
+static int
+ucode_open(dev_t *dev, int flag, int otyp, cred_t *cr)
+{
+ return (getminor(*dev) == UCODE_MINOR ? 0 : ENXIO);
+}
+
+
+/*ARGSUSED*/
+static int
+ucode_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *cr, int *rval)
+{
+ switch (cmd) {
+ case UCODE_GET_VERSION: {
+ int size;
+ uint32_t *revp, *rev_array;
+ ucode_errno_t rc = EM_OK;
+
+ STRUCT_DECL(ucode_get_rev_struct, h);
+ STRUCT_INIT(h, mode);
+ if (ddi_copyin((void *)arg,
+ STRUCT_BUF(h), STRUCT_SIZE(h), mode))
+ return (EFAULT);
+
+ if ((size = STRUCT_FGET(h, ugv_size)) > NCPU)
+ return (EINVAL);
+
+ if ((rev_array = STRUCT_FGETP(h, ugv_rev)) == NULL)
+ return (EINVAL);
+
+ size *= sizeof (uint32_t);
+
+ revp = kmem_zalloc(size, KM_SLEEP);
+ if (ddi_copyin((void *)rev_array, revp, size, mode) != 0) {
+ kmem_free(revp, size);
+ return (EINVAL);
+ }
+
+ rc = ucode_get_rev(revp);
+
+ STRUCT_FSET(h, ugv_errno, rc);
+
+ if (ddi_copyout(revp, (void *)rev_array, size, mode) != 0) {
+ kmem_free(revp, size);
+ return (EFAULT);
+ }
+
+ kmem_free(revp, size);
+
+ if (ddi_copyout(STRUCT_BUF(h), (void *)arg,
+ STRUCT_SIZE(h), mode))
+ return (EFAULT);
+
+ return (0);
+ }
+
+ case UCODE_UPDATE: {
+ int size;
+ uint8_t *ucodep, *uw_ucode;
+ ucode_errno_t rc = EM_OK;
+
+ /*
+ * Requires all privilege.
+ */
+ if (cr && secpolicy_ucode_update(cr))
+ return (EPERM);
+
+ STRUCT_DECL(ucode_write_struct, h);
+
+ STRUCT_INIT(h, mode);
+ if (ddi_copyin((void *)arg, STRUCT_BUF(h), STRUCT_SIZE(h),
+ mode))
+ return (EFAULT);
+
+ /*
+ * We allow the size of the combined microcode file to be up to
+ * ucode_max_combined_size. It is initialized to
+ * UCODE_MAX_COMBINED_SIZE, and can be patched if necessary.
+ */
+ size = STRUCT_FGET(h, uw_size);
+ if (size > ucode_max_combined_size || size == 0)
+ return (EINVAL);
+
+ if ((uw_ucode = STRUCT_FGETP(h, uw_ucode)) == NULL)
+ return (EINVAL);
+
+ ucodep = kmem_zalloc(size, KM_SLEEP);
+ if (ddi_copyin((void *)uw_ucode, ucodep, size, mode) != 0) {
+ kmem_free(ucodep, size);
+ return (EFAULT);
+ }
+
+ if ((rc = ucode_validate(ucodep, size)) != EM_OK) {
+ kmem_free(ucodep, size);
+ STRUCT_FSET(h, uw_errno, rc);
+ if (ddi_copyout(STRUCT_BUF(h), (void *)arg,
+ STRUCT_SIZE(h), mode))
+ return (EFAULT);
+ return (0);
+ }
+
+ mutex_enter(&ucode_update_lock);
+ rc = ucode_update(ucodep, size);
+ mutex_exit(&ucode_update_lock);
+
+ kmem_free(ucodep, size);
+
+ STRUCT_FSET(h, uw_errno, rc);
+ if (ddi_copyout(STRUCT_BUF(h), (void *)arg,
+ STRUCT_SIZE(h), mode))
+ return (EFAULT);
+
+ /*
+ * Even if rc is not EM_OK, it is a successful operation
+ * from ioctl()'s perspective. We return the detailed error
+ * code via the ucode_write_struct data structure.
+ */
+ return (0);
+ }
+
+
+ default:
+ return (ENOTTY);
+ }
+}
+
+static struct cb_ops ucode_cb_ops = {
+ ucode_open,
+ nulldev, /* close */
+ nodev, /* strategy */
+ nodev, /* print */
+ nodev, /* dump */
+ nodev, /* read */
+ nodev, /* write */
+ ucode_ioctl,
+ nodev, /* devmap */
+ nodev, /* mmap */
+ nodev, /* segmap */
+ nochpoll, /* poll */
+ ddi_prop_op,
+ NULL,
+ D_64BIT | D_NEW | D_MP
+};
+
+static struct dev_ops ucode_dv_ops = {
+ DEVO_REV,
+ 0,
+ ucode_getinfo,
+ nulldev, /* identify */
+ nulldev, /* probe */
+ ucode_attach,
+ ucode_detach,
+ nodev, /* reset */
+ &ucode_cb_ops,
+ (struct bus_ops *)0
+};
+
+static struct modldrv modldrv = {
+ &mod_driverops,
+ "ucode driver v%I%",
+ &ucode_dv_ops
+};
+
+static struct modlinkage modl = {
+ MODREV_1,
+ &modldrv
+};
+
+int
+_init(void)
+{
+ int rc;
+
+ if ((rc = mod_install(&modl)) != 0)
+ return (rc);
+
+ mutex_init(&ucode_update_lock, NULL, MUTEX_DRIVER, NULL);
+
+ return (0);
+}
+
+int
+_fini(void)
+{
+ int rc;
+
+ if ((rc = mod_remove(&modl)) != 0)
+ return (rc);
+
+ mutex_destroy(&ucode_update_lock);
+
+ return (0);
+}
+
+int
+_info(struct modinfo *modinfo)
+{
+ return (mod_info(&modl, modinfo));
+}