lkml.org 
[lkml]   [2000]   [Feb]   [15]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
Patch in this message
/
Date
From
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
\
 
 \ /
  Last update: 2005-03-22 13:56    [W:0.994 / U:1.840 seconds]
©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site