[patch-2.3.46-pre3] /dev/microcode (P6 ucode update char driver)

From: Tigran Aivazian (tigran@sco.COM)
Date: Wed Feb 16 2000 - 09:05:08 EST


Hi Linus,

I followed your suggestion and here is a simple misc character device
driver instead of using /proc/mtrr for microcode update.

The kernel patch, user tool and manpage for /dev/microcode are at:

   http://www.ocston.org/~tigran/patches/microcode

Also attached individually.

A typical kernel log output from two sequential runs of mcode tool looks
like this:

P6 Microcode Update Driver v1.0 registered on minor=184
microcode: CPU0 microcode updated from revision 41 to 64
microcode: CPU1 microcode updated from revision 41 to 64
microcode: CPU0 not 'upgrading' to earlier revision 64 (current=64)
microcode: CPU1 not 'upgrading' to earlier revision 64 (current=64)

Tested both as a module and as static. Both UP and SMP kernels (on SMP
machine).

There are some ideas (thanks to Rogier) for improvement, e.g. updating the
fields in /proc/cpuinfo after the successful update. Also some fiddling
with cache control MSRs may be desirable. I will look into that soon, but
for now the above *is* a working driver.

Regards,
------
Tigran A. Aivazian | http://www.sco.com
Escalations Research Group | tel: +44-(0)1923-813796
Santa Cruz Operation Ltd | http://www.ocston.org/~tigran


/*
 * mcode - Update Intel Microcode on P6 processors.
 *
 * Copyright (C) 2000, Tigran Aivazian (GPL v2, as usual)
 *
 * Reference: Section 8.10 of Volume III,
 * Intel Pentium III Manual, Order Number 243192.
 *
 * This tool is a user interface to /dev/microcode device.
 * Make sure your kernel is compiled with CONFIG_MICROCODE support
 * either static or as a module. If it is a module, please ensure you
 * have the line:
 *
 * alias char-major-10-184 microcode
 *
 * in /etc/modules.conf file and the kernel is compiled with CONFIG_KMOD
 * support.
 */

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <stdarg.h>

#include <asm/microcode.h>

struct microcode_ioc ioc;
void * microcode;
static char * myname;

static void die(const char * fmt, ...);

int main(int argc, char *argv[])
{
        int fd;
        struct stat st;

        myname = argv[0];

        if (argc != 2) {
                fprintf(stderr, "usage: %s [mcode_file]\n", argv[0]);
                exit(1);
        }

        fd = open(argv[1], O_RDONLY);
        if (fd == -1)
                die("open(%s)", argv[1]);
        if (fstat(fd, &st) == -1)
                die("fstat(fd=%d)", fd);
        if (st.st_size == 0 || (st.st_size % sizeof(struct microcode) != 0)) {
                fprintf(stderr, "%s: corrupted ucode in '%s' or 'struct microcode' "
                                "in <asm/microcode.h> needs updating\n", argv[0], argv[1]);
                exit(1);
        }
        microcode = malloc(st.st_size);
        if (!microcode)
                die("Can't malloc(%ld) to hold microcode\n", st.st_size);
        if (read(fd, microcode, st.st_size) != st.st_size)
                die("read(fd, microcode, %ld)", st.st_size);
        if (close(fd) == -1)
                die("close(fd=%d)", fd);
        fd = open("/dev/microcode", O_RDONLY);
        if (fd == -1)
                die("open(/dev/microcode)");
        ioc.num = st.st_size/sizeof(struct microcode);
        ioc.uaddr = microcode;
        if (ioctl(fd, MICROCODE_P6UPDATE, &ioc) == -1)
                die("ioctl(MICROCODE_P6UPDATE, num=%d)", ioc.num);
        if (close(fd) == -1)
                die("close(fd=%d)", fd);
        return 0;
}

static void die(const char * fmt, ...)
{
        va_list args;
        static char buf[4096];

        va_start(args, fmt);
        vsprintf(buf, fmt, args);
        va_end(args);

        fprintf(stderr, "%s: %s, errno=%d (%s)\n",
                myname, buf, errno, strerror(errno));
        exit(1);
}

diff -urN -X dontdiff linux/CREDITS linux-microcode/CREDITS
--- linux/CREDITS Mon Feb 14 02:21:45 2000
+++ linux-microcode/CREDITS Wed Feb 16 05:25:30 2000
@@ -41,6 +41,7 @@
 E: tigran@ocston.org
 W: http://www.ocston.org/~tigran
 D: BFS filesystem
+D: Intel P6 CPU microcode update support
 S: United Kingdom
 
 N: Werner Almesberger
diff -urN -X dontdiff linux/Documentation/Configure.help linux-microcode/Documentation/Configure.help
--- linux/Documentation/Configure.help Wed Feb 16 05:00:45 2000
+++ linux-microcode/Documentation/Configure.help Wed Feb 16 05:55:13 2000
@@ -10752,6 +10752,23 @@
   module, say M here and read Documentation/modules.txt. Most people
   will say N.
 
+Intel P6 CPU Microcode Update Support
+CONFIG_MICROCODE
+ If you say Y here and create a character special file /dev/microcode with
+ major number 10 and minor number 184 using mknod ("man mknod"), you
+ will be able to use p6mcode ("man p6mcode") utility to update
+ microcode on Intel processors in P6 family, e.g. Pentium Pro, Pentium II,
+ Pentium III, Xeon etc. The user-space required for microcode update can
+ be found at http://www.ocston.org/~tigran/patches/p6mcode . You will also
+ need the actual microcode binary data itself which is not shipped with the
+ kernel or with the p6mcode tool. Contact Intel to obtain the latest revision of
+ microcode for your CPU(s).
+
+ This driver is also available as a module ( = code which can be
+ inserted in and removed from the running kernel whenever you want).
+ The module will be called microcode.o. If you want to compile it as a
+ module, say M here and read Documentation/modules.txt.
+
 Enhanced Real Time Clock Support
 CONFIG_RTC
   If you say Y here and create a character special file /dev/rtc with
diff -urN -X dontdiff linux/Documentation/devices.txt linux-microcode/Documentation/devices.txt
--- linux/Documentation/devices.txt Fri Jan 7 20:59:38 2000
+++ linux-microcode/Documentation/devices.txt Wed Feb 16 05:57:46 2000
@@ -352,6 +352,7 @@
                 177 = /dev/cbm Serial CBM bus
                 178 = /dev/jsflash JavaStation OS flash SIMM
                 179 = /dev/xsvc High-speed shared-mem/semaphore service
+ 184 = /dev/microcode Intel P6 CPU microcode update
                 240-255 Reserved for local use
 
  11 char Raw keyboard device
diff -urN -X dontdiff linux/MAINTAINERS linux-microcode/MAINTAINERS
--- linux/MAINTAINERS Sat Feb 12 00:10:03 2000
+++ linux-microcode/MAINTAINERS Wed Feb 16 05:11:06 2000
@@ -496,6 +496,11 @@
 M: mingo@redhat.com
 S: Maintained
 
+INTEL P6 MICROCODE UPDATE SUPPORT
+P: Tigran Aivazian
+M: tigran@sco.com
+S: Maintained
+
 IP MASQUERADING:
 P: Juanjo Ciarlante
 M: jjciarla@raiz.uncu.edu.ar
diff -urN -X dontdiff linux/arch/i386/defconfig linux-microcode/arch/i386/defconfig
--- linux/arch/i386/defconfig Wed Feb 16 05:00:45 2000
+++ linux-microcode/arch/i386/defconfig Wed Feb 16 05:56:31 2000
@@ -393,6 +393,7 @@
 # CONFIG_WATCHDOG is not set
 # CONFIG_NVRAM is not set
 # CONFIG_RTC is not set
+# CONFIG_MICROCODE is not set
 
 #
 # Video For Linux
diff -urN -X dontdiff linux/drivers/char/Config.in linux-microcode/drivers/char/Config.in
--- linux/drivers/char/Config.in Wed Feb 16 05:00:46 2000
+++ linux-microcode/drivers/char/Config.in Wed Feb 16 05:49:35 2000
@@ -119,6 +119,9 @@
 fi
 endmenu
 
+if [ "$CONFIG_M686" = "y" ]; then
+ tristate '/dev/microcode - Intel P6 CPU microcode update support' CONFIG_MICROCODE
+fi
 
 tristate '/dev/nvram support' CONFIG_NVRAM
 tristate 'Enhanced Real Time Clock Support' CONFIG_RTC
diff -urN -X dontdiff linux/drivers/char/Makefile linux-microcode/drivers/char/Makefile
--- linux/drivers/char/Makefile Sun Feb 13 19:21:42 2000
+++ linux-microcode/drivers/char/Makefile Wed Feb 16 05:24:04 2000
@@ -147,6 +147,7 @@
 obj-$(CONFIG_PC110_PAD) += pc110pad.o
 obj-$(CONFIG_WDT) += wdt.o
 obj-$(CONFIG_RTC) += rtc.o
+obj-$(CONFIG_MICROCODE) += microcode.o
 ifeq ($(CONFIG_PPC),)
   obj-$(CONFIG_NVRAM) += nvram.o
 endif
diff -urN -X dontdiff linux/drivers/char/microcode.c linux-microcode/drivers/char/microcode.c
--- linux/drivers/char/microcode.c Thu Jan 1 01:00:00 1970
+++ linux-microcode/drivers/char/microcode.c Wed Feb 16 10:56:18 2000
@@ -0,0 +1,216 @@
+/*
+ * CPU Microcode Update interface for Linux
+ *
+ * Copyright (C) 2000 Tigran Aivazian
+ *
+ * This driver allows to upgrade microcode on Intel processors
+ * belonging to P6 family - PentiumPro, Pentium II, Pentium III etc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ * 1.0 Tigran Aivazian <tigran@sco.com>
+ * Initial release.
+ */
+
+#include <linux/init.h>
+#include <linux/sched.h>
+#include <linux/module.h>
+#include <linux/vmalloc.h>
+#include <linux/smp_lock.h>
+#include <linux/miscdevice.h>
+
+#include <asm/msr.h>
+#include <asm/uaccess.h>
+#include <asm/microcode.h>
+#include <asm/processor.h>
+
+#define MICROCODE_VERSION "1.0"
+
+MODULE_DESCRIPTION("CPU (P6) microcode update driver");
+MODULE_AUTHOR("Tigran Aivazian <tigran@ocston.org>");
+EXPORT_NO_SYMBOLS;
+
+/* VFS interface */
+static int microcode_open(struct inode *, struct file *);
+static int microcode_release(struct inode *, struct file *);
+static int microcode_ioctl(struct inode *, struct file *, unsigned int, unsigned long);
+
+/* internal helpers to do the work */
+static int do_microcode_update(void);
+static void do_update_one(void *);
+
+/*
+ * Bits in microcode_status. (7 bits of room for future expansion)
+ */
+#define MICROCODE_IS_OPEN 0x01 /* set if /dev/microcode is in use */
+
+static atomic_t microcode_status = ATOMIC_INIT(0); /* bitmapped status byte */
+
+/* the actual array of microcode blocks, each 2048 bytes */
+static struct microcode * microcode = NULL;
+static unsigned int microcode_num = 0;
+
+static struct file_operations microcode_fops = {
+ ioctl: microcode_ioctl,
+ open: microcode_open,
+ release: microcode_release,
+};
+
+static struct miscdevice microcode_dev = {
+ minor: MICROCODE_MINOR,
+ name: "microcode",
+ fops: &microcode_fops
+};
+
+static int __init microcode_init(void)
+{
+ if (misc_register(&microcode_dev)) {
+ printk(KERN_ERR "Can't register /dev/microcode on minor=%d\n",
+ MICROCODE_MINOR);
+ return -EBUSY;
+ }
+ printk(KERN_ERR "P6 Microcode Update Driver v%s registered on minor=%d\n",
+ MICROCODE_VERSION, MICROCODE_MINOR);
+ return 0;
+}
+
+static void __exit microcode_exit(void)
+{
+ misc_deregister(&microcode_dev);
+ printk(KERN_ERR "P6 Microcode Update Driver v%s unregistered\n", MICROCODE_VERSION);
+}
+
+module_init(microcode_init);
+module_exit(microcode_exit);
+
+/*
+ * We enforce only one user at a time here with open/close.
+ */
+static int microcode_open(struct inode *inode, struct file *file)
+{
+ if (!capable(CAP_SYS_RAWIO))
+ return -EPERM;
+
+ /* one at a time, please */
+ if (atomic_read(&microcode_status) & MICROCODE_IS_OPEN)
+ return -EBUSY;
+
+ MOD_INC_USE_COUNT;
+
+ atomic_set(&microcode_status,
+ atomic_read(&microcode_status) | MICROCODE_IS_OPEN);
+
+ return 0;
+}
+
+static int microcode_release(struct inode *inode, struct file *file)
+{
+ MOD_DEC_USE_COUNT;
+
+ atomic_set(&microcode_status,
+ atomic_read(&microcode_status) & ~MICROCODE_IS_OPEN);
+
+ return 0;
+}
+
+
+static int microcode_ioctl(struct inode *inode, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ int err;
+ struct microcode_ioc ioc;
+ unsigned int microcode_bytes;
+
+ err = -EINVAL;
+ switch (cmd) {
+ case MICROCODE_P6UPDATE:
+ if (copy_from_user(&ioc, (void *)arg, sizeof ioc))
+ return -EFAULT;
+
+ lock_kernel();
+ microcode_num = ioc.num;
+ microcode_bytes = microcode_num * sizeof(struct microcode);
+ microcode = vmalloc(microcode_bytes);
+ if (!microcode) {
+ err = -ENOMEM;
+ unlock_kernel();
+ break;
+ }
+ if (copy_from_user(microcode, ioc.uaddr, microcode_bytes)) {
+ vfree(microcode);
+ err = -EFAULT;
+ unlock_kernel();
+ break;
+ }
+ err = do_microcode_update();
+ vfree(microcode);
+ unlock_kernel();
+ break;
+ default:
+ break;
+ }
+ return err;
+}
+
+static int do_microcode_update(void)
+{
+ if (smp_call_function(do_update_one, NULL, 1, 0) != 0)
+ panic("do_microcode_update(): timed out waiting for other CPUs\n");
+ do_update_one(NULL);
+ return 0;
+}
+
+static void do_update_one(void *unused)
+{
+ struct cpuinfo_x86 * c;
+ unsigned int pf = 0, val[2], rev, sig;
+ int i, id;
+
+ id = smp_processor_id();
+ c = cpu_data + id;
+
+
+ if (c->x86_vendor != X86_VENDOR_INTEL || c->x86 < 6)
+ return;
+
+ sig = c->x86_mask + (c->x86_model<<4) + (c->x86<<8);
+
+ if (c->x86 >= 6 && c->x86_model >= 5) {
+ /* get processor flags from BBL_CR_OVRD MSR (0x17) */
+ rdmsr(0x17, val[0], val[1]);
+ pf = 1 << ((val[1] >> 18) & 7);
+ }
+
+ for (i=0; i<microcode_num; i++)
+ if (microcode[i].sig == sig && microcode[i].pf == pf &&
+ microcode[i].ldrver == 1 && microcode[i].hdrver == 1) {
+
+ rdmsr(0x8B, val[0], rev);
+ if (microcode[i].rev <= rev) {
+ printk(KERN_ERR
+ "microcode: CPU%d not 'upgrading' to earlier revision"
+ " %d (current=%d)\n", id, microcode[i].rev, rev);
+ } else {
+ int sum = 0;
+ struct microcode *m = &microcode[i];
+ unsigned int *sump = (unsigned int *)(m+1);
+
+ while (--sump >= (unsigned int *)m)
+ sum += *sump;
+ if (sum != 0) {
+ printk(KERN_ERR "microcode: CPU%d aborting, "
+ "bad checksum\n", id);
+ break;
+ }
+ wrmsr(0x79, (unsigned int)(m->bits), 0);
+ __asm__ __volatile__ ("cpuid");
+ rdmsr(0x8B, val[0], val[1]);
+ printk(KERN_ERR "microcode: CPU%d microcode updated "
+ "from revision %d to %d\n", id, rev, val[1]);
+ }
+ break;
+ }
+}
diff -urN -X dontdiff linux/include/asm-i386/microcode.h linux-microcode/include/asm-i386/microcode.h
--- linux/include/asm-i386/microcode.h Thu Jan 1 01:00:00 1970
+++ linux-microcode/include/asm-i386/microcode.h Wed Feb 16 09:52:13 2000
@@ -0,0 +1,29 @@
+/*
+ * Intel P6 Microcode Update interface
+ *
+ */
+
+#ifndef _ASM_MICROCODE_H
+#define _ASM_MICROCODE_H
+
+struct microcode_ioc {
+ unsigned int num;
+ void * uaddr;
+};
+
+#define MICROCODE_IOCTL_BASE 'i'
+#define MICROCODE_P6UPDATE _IOW(MICROCODE_IOCTL_BASE, 0, struct microcode_ioc)
+
+struct microcode {
+ unsigned int hdrver;
+ unsigned int rev;
+ unsigned int date;
+ unsigned int sig;
+ unsigned int cksum;
+ unsigned int ldrver;
+ unsigned int pf;
+ unsigned int reserved[5];
+ unsigned int bits[500];
+};
+
+#endif /* _ASM_MICROCODE_H */
diff -urN -X dontdiff linux/include/linux/miscdevice.h linux-microcode/include/linux/miscdevice.h
--- linux/include/linux/miscdevice.h Thu Feb 10 03:45:43 2000
+++ linux-microcode/include/linux/miscdevice.h Wed Feb 16 07:57:56 2000
@@ -18,6 +18,7 @@
 #define SUN_OPENPROM_MINOR 139
 #define NVRAM_MINOR 144
 #define I2O_MINOR 166
+#define MICROCODE_MINOR 184
 #define MISC_DYNAMIC_MINOR 255
 
 #define SGI_GRAPHICS_MINOR 146

.\" Copyright (c) 2000 Tigran Aivazian (tigran@ocston.org), Wed Feb 16 11:04:14 GMT 2000
.\"
.\" This is free documentation; you can redistribute it and/or
.\" modify it under the terms of the GNU General Public License as
.\" published by the Free Software Foundation; either version 2 of
.\" the License, or (at your option) any later version.
.\"
.\" The GNU General Public License's references to "object code"
.\" and "executables" are to be interpreted as the output of any
.\" document formatting or typesetting system, including
.\" intermediate and printed output.
.\"
.\" This manual is distributed in the hope that it will be useful,
.\" but WITHOUT ANY WARRANTY; without even the implied warranty of
.\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
.\" GNU General Public License for more details.
.\"
.\" You should have received a copy of the GNU General Public
.\" License along with this manual; if not, write to the Free
.\" Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111,
.\" USA.
.\"
.TH MICROCODE 4 "21 November 1992" "Linux" "Linux Programmer's Manual"
.SH NAME
microcode, \- CPU microcode update device
.SH DESCRIPTION
\fB/dev/microcode\fP is a character device file that allows user process
to update the microcode on some processors that support this feature.
Currently, only processors in Intel P6 family can update their microcodes,
including Pentium Pro, Pentium II, Pentium III, Xeon etc.
.LP

It is typically created by:
.RS
.sp
mknod -m 660 /dev/microcode c 10 184
.br
.RE
.LP
The programming interface is provided with the
.IR ioctl(2)
system call.
.nf
.sp
#include <sys/types.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <asm/microcode.h>

struct microcode_ioc ioc;

ioc.num = number_of_blocks;
ioc.uaddr = address_of_microcode;
fd = open("/dev/microcode", O_RDONLY);
ioctl(fd, MICROCODE_P6UPDATE, &ioc);

.RE
The user interface to /dev/microcode device is provided by the mcode
utility which accepts a single argument - the name of the file containing
microcode data in binary form.
.SH AUTHOR
.sp
Tigran Aivazian <tigran@ocston.org>, see the project site
http://www.ocston.org/~tigran/patches/microcode for updates.
.SH FILES
/dev/microcode
.SH "SEE ALSO"
.BR mknod "(1) "

-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.rutgers.edu
Please read the FAQ at http://www.tux.org/lkml/



This archive was generated by hypermail 2b29 : Wed Feb 23 2000 - 21:00:15 EST