[patch-2.3.46-p3] /proc/driver/microcode P6 ucode support

From: Tigran Aivazian (tigran@aivazian.fsnet.co.uk)
Date: Wed Feb 16 2000 - 16:52:57 EST


Hi Linus,

Here is the implementation of your idea. I shouldn't have moaned at you -
I thought it will be an hour of work but it was 5 minutes instead - the
rest of the time was just testing.

I still like the old /dev/microcode patch because it was autoloading on
demand. With /proc/driver/microcode compiled as module one has to insmod
it before running mcode utility.

Regards,
Tigran.

diff -urN -X dontdiff linux/CREDITS linux-microcode-proc/CREDITS
--- linux/CREDITS Mon Feb 14 20:59:06 2000
+++ linux-microcode-proc/CREDITS Wed Feb 16 20:55:10 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-proc/Documentation/Configure.help
--- linux/Documentation/Configure.help Wed Feb 16 20:50:47 2000
+++ linux-microcode-proc/Documentation/Configure.help Wed Feb 16 21:17:11 2000
@@ -10752,6 +10752,22 @@
   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 you will be able to use mcode ("man microcode")
+ utility to update microcode on Intel processors in P6 family, e.g.
+ Pentium Pro, Pentium II, Pentium III, Xeon etc. The user-space tool
+ required for microcode update can be found at
+ http://www.ocston.org/~tigran/patches/microcode . 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/MAINTAINERS linux-microcode-proc/MAINTAINERS
--- linux/MAINTAINERS Sun Feb 13 15:32:49 2000
+++ linux-microcode-proc/MAINTAINERS Wed Feb 16 20:55:10 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/config.in linux-microcode-proc/arch/i386/config.in
--- linux/arch/i386/config.in Wed Feb 2 10:09:49 2000
+++ linux-microcode-proc/arch/i386/config.in Wed Feb 16 21:10:05 2000
@@ -49,6 +49,10 @@
    define_bool CONFIG_X86_USE_3DNOW y
 fi
 
+if [ "$CONFIG_M686" = "y" -a "$CONFIG_PROC_FS" = "y" ]; then
+ tristate '/dev/microcode - Intel P6 CPU microcode update support' CONFIG_MICROCODE
+fi
+
 choice 'High Memory Support' \
         "off CONFIG_NOHIGHMEM \
         4GB CONFIG_HIGHMEM4G \
diff -urN -X dontdiff linux/arch/i386/defconfig linux-microcode-proc/arch/i386/defconfig
--- linux/arch/i386/defconfig Wed Feb 16 20:50:47 2000
+++ linux-microcode-proc/arch/i386/defconfig Wed Feb 16 20:55:10 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/arch/i386/kernel/Makefile linux-microcode-proc/arch/i386/kernel/Makefile
--- linux/arch/i386/kernel/Makefile Fri Feb 11 20:07:52 2000
+++ linux-microcode-proc/arch/i386/kernel/Makefile Wed Feb 16 21:26:04 2000
@@ -40,6 +40,16 @@
   endif
 endif
 
+ifeq ($(CONFIG_PROC_FS),y)
+ifeq ($(CONFIG_MICROCODE),y)
+OX_OBJS += microcode.o
+else
+ ifeq ($(CONFIG_MICROCODE),m)
+ MX_OBJS += microcode.o
+ endif
+endif
+endif
+
 ifeq ($(CONFIG_ACPI),y)
 O_OBJS += acpi.o
 else
diff -urN -X dontdiff linux/arch/i386/kernel/microcode.c linux-microcode-proc/arch/i386/kernel/microcode.c
--- linux/arch/i386/kernel/microcode.c Thu Jan 1 01:00:00 1970
+++ linux-microcode-proc/arch/i386/kernel/microcode.c Wed Feb 16 21:35:30 2000
@@ -0,0 +1,221 @@
+/*
+ * 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.
+ *
+ * Reference: Section 8.10 of Volume III, Intel Pentium III Manual,
+ * Order Number 243192 or download from:
+ *
+ * http://developer.intel.com/design/pentiumii/manuals/243192.htm
+ *
+ * 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 16 February 2000, 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/proc_fs.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 inode_operations microcode_inops = {
+ default_file_ops: &microcode_fops,
+};
+
+static struct proc_dir_entry *proc_microcode;
+
+static int __init microcode_init(void)
+{
+ proc_microcode = create_proc_entry("microcode", S_IWUSR|S_IRUGO, proc_root_driver);
+ if (!proc_microcode) {
+ printk(KERN_ERR "microcode: can't create /proc/driver/microcode entry\n");
+ return -ENOMEM;
+ }
+ proc_microcode->ops = &microcode_inops;
+ printk(KERN_ERR "P6 Microcode Update Driver v%s registered\n", MICROCODE_VERSION);
+ return 0;
+}
+
+static void __exit microcode_exit(void)
+{
+ remove_proc_entry("microcode", proc_root_driver);
+ 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_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-proc/include/asm-i386/microcode.h
--- linux/include/asm-i386/microcode.h Thu Jan 1 01:00:00 1970
+++ linux-microcode-proc/include/asm-i386/microcode.h Wed Feb 16 20:55:10 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 */

-
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:16 EST