Messages in this thread Patch in this message | | | Date | Wed, 16 Feb 2000 00:19:03 +0000 (GMT) | From | Tigran Aivazian <> | Subject | [patch-2.3.46-p2] P6 microcode update support |
| |
Hi Linus,
I noticed that both microcode encoding format and the loader algorithm are documented in the section 8.10 of Volume III of Intel manuals publically available on their web site. Therefore, I knocked up a simple ioctl that does the microcode update and a corresponding userspace tool. The patch and the tool are attached. Please consider the patch, as for the tool I will probably mail Andries asking to put it in util-linux (or does anyone have a better idea?), i.e. only if you accept the kernel side.
I tested both SMP and UP kernels on my home SMP machine (CPU signature=0x673, upgraded to revision=14 microcode)
http://www.ocston.org/~tigran/patches/p6ucode-2.3.46-p2.patch
http://www.ocston.org/~tigran/patches/p6uc.c
Regards, Tigran. /* * p6uc - 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. */
#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/mtrr.h>
struct mtrr_p6update ioc; void * ucode; 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 p6ucode) != 0)) { fprintf(stderr, "%s: corrupted ucode in '%s' or 'struct p6update' " "in <asm/mtrr.h> needs updating\n", argv[0], argv[1]); exit(1); } ucode = malloc(st.st_size); if (!ucode) die("Can't malloc(%ld) to hold ucode\n", st.st_size); if (read(fd, ucode, st.st_size) != st.st_size) die("read(fd, ucode, %ld)", st.st_size); if (close(fd) == -1) die("close(fd=%d)", fd); fd = open("/proc/mtrr", O_RDONLY); if (fd == -1) die("open(/proc/mtrr)"); ioc.num = st.st_size/sizeof(struct p6ucode); ioc.uaddr = ucode; if (ioctl(fd, MTRRIOC_P6UPDATE, &ioc) == -1) die("ioctl(MTRRIOC_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/arch/i386/kernel/mtrr.c linux-p6uc/arch/i386/kernel/mtrr.c --- linux/arch/i386/kernel/mtrr.c Fri Feb 11 20:07:52 2000 +++ linux-p6uc/arch/i386/kernel/mtrr.c Tue Feb 15 23:57:18 2000 @@ -225,6 +225,10 @@ success. 19991008 Manfred Spraul <manfreds@colorfullife.com> replaced spin_lock_reschedule() with a normal semaphore. + 20000215 Tigran Aivazian <tigran@sco.com> + Added MTRIOC_P6UPDATE ioctl for P6 microcode update. + Reference: Section 8.10 of Volume III, Intel Pentium III + Manual, Order Number 243192. */ #include <linux/types.h> #include <linux/errno.h> @@ -247,6 +251,7 @@ #include <asm/mtrr.h> #include <linux/init.h> #include <linux/smp.h> +#include <linux/smp_lock.h> #include <asm/uaccess.h> #include <asm/io.h> @@ -321,6 +326,8 @@ static void compute_ascii (void); #endif +static struct p6ucode * p6ucode = NULL; +static int p6ucode_num = 0; struct set_mtrr_context { @@ -1393,6 +1400,59 @@ return -EINVAL; } /* End Function mtrr_write */ +static void p6update_one(void *arg) +{ + struct cpuinfo_x86 * c; + unsigned int pf = 0, val[2], rev, sig; + int i, id; + + id = smp_processor_id(); + c = cpu_data + id; + 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<p6ucode_num; i++) + if (p6ucode[i].sig == sig && + p6ucode[i].pf == pf && + p6ucode[i].ldrver == 1 && + p6ucode[i].hdrver == 1) { + rdmsr(0x8B, val[0], rev); + if (p6ucode[i].rev <= rev) { + printk(KERN_ERR + "p6update_one(): not 'upgrading' to earlier revision" + " %d (current=%d)\n", p6ucode[i].rev, rev); + } else { + int sum = 0; + struct p6ucode *p = &p6ucode[i]; + unsigned int *sump = (unsigned int *)(p+1); + + while (--sump >= (unsigned int *)p) + sum += *sump; + if (sum != 0) { + printk(KERN_ERR "p6update_one(): aborting due to bad checksum\n"); + break; + } + wrmsr(0x79, (unsigned int)(p->bits), 0); + __asm__ __volatile__ ("cpuid"); + printk(KERN_ERR "p6update_one: CPU%d microcode updated\n", id); + } + break; + } +} + +static int do_p6update(void) +{ + if (smp_call_function (p6update_one, NULL, 1, 0) != 0) + panic("do_p6update(): timed out waiting for other CPUs\n"); + p6update_one(NULL); + return -EINVAL; +} + static int mtrr_ioctl (struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) { @@ -1400,34 +1460,65 @@ mtrr_type type; struct mtrr_sentry sentry; struct mtrr_gentry gentry; + struct mtrr_p6update p6up_ioc; + int p6up_len; switch (cmd) { default: return -ENOIOCTLCMD; + case MTRRIOC_P6UPDATE: + if ( !capable(CAP_SYS_RAWIO) ) return -EPERM; + + /* basic sanity checks for this CPU */ + if (boot_cpu_data.x86_vendor != X86_VENDOR_INTEL || + boot_cpu_data.x86 != 6) + return -EINVAL; + + lock_kernel(); + if ( copy_from_user (&p6up_ioc, (void *) arg, sizeof p6up_ioc) ) + return -EFAULT; + p6ucode_num = p6up_ioc.num; + p6up_len = p6up_ioc.num * sizeof(struct p6ucode); + p6ucode = vmalloc(p6up_len); + if (!p6ucode) { + err = -ENOMEM; + unlock_kernel(); + break; + } + if ( copy_from_user (p6ucode, p6up_ioc.uaddr, p6up_len) ) { + err = -EFAULT; + vfree(p6ucode); + unlock_kernel(); + break; + } + err = do_p6update(); + vfree(p6ucode); + unlock_kernel(); + break; case MTRRIOC_ADD_ENTRY: - if ( !suser () ) return -EPERM; + if ( !capable (CAP_SYS_RAWIO) ) return -EPERM; if ( copy_from_user (&sentry, (void *) arg, sizeof sentry) ) return -EFAULT; err = mtrr_file_add (sentry.base, sentry.size, sentry.type, 1, file); if (err < 0) return err; break; case MTRRIOC_SET_ENTRY: - if ( !suser () ) return -EPERM; + if ( !capable (CAP_SYS_RAWIO) ) return -EPERM; if ( copy_from_user (&sentry, (void *) arg, sizeof sentry) ) return -EFAULT; err = mtrr_add (sentry.base, sentry.size, sentry.type, 0); if (err < 0) return err; break; case MTRRIOC_DEL_ENTRY: - if ( !suser () ) return -EPERM; + if ( !capable (CAP_SYS_RAWIO) ) return -EPERM; if ( copy_from_user (&sentry, (void *) arg, sizeof sentry) ) return -EFAULT; err = mtrr_file_del (sentry.base, sentry.size, file); if (err < 0) return err; break; case MTRRIOC_KILL_ENTRY: - if ( !suser () ) return -EPERM; + if ( !capable (CAP_SYS_RAWIO) ) return -EPERM; if ( copy_from_user (&sentry, (void *) arg, sizeof sentry) ) return -EFAULT; err = mtrr_del (-1, sentry.base, sentry.size); diff -urN -X dontdiff linux/include/asm-i386/mtrr.h linux-p6uc/include/asm-i386/mtrr.h --- linux/include/asm-i386/mtrr.h Tue Dec 21 00:01:12 1999 +++ linux-p6uc/include/asm-i386/mtrr.h Wed Feb 16 00:00:28 2000 @@ -43,12 +43,31 @@ unsigned int type; /* Type of region */ }; +struct p6ucode { + 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]; +}; + +struct mtrr_p6update +{ + unsigned int num; /* number of 'struct p6update' elements, currently (15/02/2000) 48 */ + void * uaddr; /* pointer to the array of 'struct p6update' */ +}; + /* These are the various ioctls */ #define MTRRIOC_ADD_ENTRY _IOW(MTRR_IOCTL_BASE, 0, struct mtrr_sentry) #define MTRRIOC_SET_ENTRY _IOW(MTRR_IOCTL_BASE, 1, struct mtrr_sentry) #define MTRRIOC_DEL_ENTRY _IOW(MTRR_IOCTL_BASE, 2, struct mtrr_sentry) #define MTRRIOC_GET_ENTRY _IOWR(MTRR_IOCTL_BASE, 3, struct mtrr_gentry) #define MTRRIOC_KILL_ENTRY _IOW(MTRR_IOCTL_BASE, 4, struct mtrr_sentry) +#define MTRRIOC_P6UPDATE _IOW(MTRR_IOCTL_BASE, 5, struct mtrr_p6update) /* These are the region types */ #define MTRR_TYPE_UNCACHABLE 0 | |