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 <
[email protected]>
replaced spin_lock_reschedule() with a normal semaphore.
+ 20000215 Tigran Aivazian <
[email protected]>
+ 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 p6ucode' elements, currently (15/02/2000) 48 */
+ void * uaddr; /* pointer to the array of 'struct p6ucode' */
+};
+
/* 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