Commit 8897452d authored by Dave Jones's avatar Dave Jones

[PATCH] x86 microcode driver update

From Tigran via 2.4
parent 7e7a44c1
...@@ -51,6 +51,12 @@ ...@@ -51,6 +51,12 @@
* Bugfix for HT (Hyper-Threading) enabled processors * Bugfix for HT (Hyper-Threading) enabled processors
* whereby processor resources are shared by all logical processors * whereby processor resources are shared by all logical processors
* in a single CPU package. * in a single CPU package.
* 1.10 28 Feb 2002 Asit K Mallick <asit.k.mallick@intel.com> and
* Tigran Aivazian <tigran@veritas.com>,
* Serialize updates as required on HT processors due to speculative
* nature of implementation.
* 1.11 22 Mar 2001 Tigran Aivazian <tigran@veritas.com>
* Fix the panic when writing zero-length microcode chunk.
*/ */
#include <linux/init.h> #include <linux/init.h>
...@@ -60,12 +66,16 @@ ...@@ -60,12 +66,16 @@
#include <linux/vmalloc.h> #include <linux/vmalloc.h>
#include <linux/miscdevice.h> #include <linux/miscdevice.h>
#include <linux/devfs_fs_kernel.h> #include <linux/devfs_fs_kernel.h>
#include <linux/spinlock.h>
#include <asm/msr.h> #include <asm/msr.h>
#include <asm/uaccess.h> #include <asm/uaccess.h>
#include <asm/processor.h> #include <asm/processor.h>
#define MICROCODE_VERSION "1.09"
static spinlock_t microcode_update_lock = SPIN_LOCK_UNLOCKED;
#define MICROCODE_VERSION "1.11"
MODULE_DESCRIPTION("Intel CPU (IA-32) microcode update driver"); MODULE_DESCRIPTION("Intel CPU (IA-32) microcode update driver");
MODULE_AUTHOR("Tigran Aivazian <tigran@veritas.com>"); MODULE_AUTHOR("Tigran Aivazian <tigran@veritas.com>");
...@@ -195,7 +205,8 @@ static void do_update_one(void *unused) ...@@ -195,7 +205,8 @@ static void do_update_one(void *unused)
struct cpuinfo_x86 *c = cpu_data + cpu_num; struct cpuinfo_x86 *c = cpu_data + cpu_num;
struct update_req *req = update_req + cpu_num; struct update_req *req = update_req + cpu_num;
unsigned int pf = 0, val[2], rev, sig; unsigned int pf = 0, val[2], rev, sig;
int i,found=0; unsigned long flags;
int i;
req->err = 1; /* assume update will fail on this cpu */ req->err = 1; /* assume update will fail on this cpu */
...@@ -216,8 +227,9 @@ static void do_update_one(void *unused) ...@@ -216,8 +227,9 @@ static void do_update_one(void *unused)
for (i=0; i<microcode_num; i++) for (i=0; i<microcode_num; i++)
if (microcode[i].sig == sig && microcode[i].pf == pf && if (microcode[i].sig == sig && microcode[i].pf == pf &&
microcode[i].ldrver == 1 && microcode[i].hdrver == 1) { microcode[i].ldrver == 1 && microcode[i].hdrver == 1) {
int sum = 0;
found=1; struct microcode *m = &microcode[i];
unsigned int *sump = (unsigned int *)(m+1);
printf("Microcode\n"); printf("Microcode\n");
printf(" Header Revision %d\n",microcode[i].hdrver); printf(" Header Revision %d\n",microcode[i].hdrver);
...@@ -234,27 +246,43 @@ static void do_update_one(void *unused) ...@@ -234,27 +246,43 @@ static void do_update_one(void *unused)
printf(" Loader Revision %x\n",microcode[i].ldrver); printf(" Loader Revision %x\n",microcode[i].ldrver);
printf(" Processor Flags %x\n\n",microcode[i].pf); printf(" Processor Flags %x\n\n",microcode[i].pf);
req->slot = i;
/* serialize access to update decision */
spin_lock_irqsave(&microcode_update_lock, flags);
/* trick, to work even if there was no prior update by the BIOS */ /* trick, to work even if there was no prior update by the BIOS */
wrmsr(MSR_IA32_UCODE_REV, 0, 0); wrmsr(MSR_IA32_UCODE_REV, 0, 0);
__asm__ __volatile__ ("cpuid" : : : "ax", "bx", "cx", "dx"); __asm__ __volatile__ ("cpuid" : : : "ax", "bx", "cx", "dx");
/* get current (on-cpu) revision into rev (ignore val[0]) */ /* get current (on-cpu) revision into rev (ignore val[0]) */
rdmsr(MSR_IA32_UCODE_REV, val[0], rev); rdmsr(MSR_IA32_UCODE_REV, val[0], rev);
if (microcode[i].rev < rev) { if (microcode[i].rev < rev) {
spin_unlock_irqrestore(&microcode_update_lock, flags);
printk(KERN_ERR printk(KERN_ERR
"microcode: CPU%d not 'upgrading' to earlier revision" "microcode: CPU%d not 'upgrading' to earlier revision"
" %d (current=%d)\n", cpu_num, microcode[i].rev, rev); " %d (current=%d)\n", cpu_num, microcode[i].rev, rev);
} else { return;
int sum = 0; } else if (microcode[i].rev == rev) {
struct microcode *m = &microcode[i]; /* notify the caller of success on this cpu */
unsigned int *sump = (unsigned int *)(m+1); req->err = 0;
spin_unlock_irqrestore(&microcode_update_lock, flags);
printk(KERN_ERR
"microcode: CPU%d already at revision"
" %d (current=%d)\n", cpu_num, microcode[i].rev, rev);
return;
}
/* Verify the checksum */
while (--sump >= (unsigned int *)m) while (--sump >= (unsigned int *)m)
sum += *sump; sum += *sump;
if (sum != 0) { if (sum != 0) {
req->err = 1;
spin_unlock_irqrestore(&microcode_update_lock, flags);
printk(KERN_ERR "microcode: CPU%d aborting, " printk(KERN_ERR "microcode: CPU%d aborting, "
"bad checksum\n", cpu_num); "bad checksum\n", cpu_num);
break; return;
} }
/* write microcode via MSR 0x79 */ /* write microcode via MSR 0x79 */
...@@ -268,20 +296,19 @@ static void do_update_one(void *unused) ...@@ -268,20 +296,19 @@ static void do_update_one(void *unused)
/* notify the caller of success on this cpu */ /* notify the caller of success on this cpu */
req->err = 0; req->err = 0;
req->slot = i; spin_unlock_irqrestore(&microcode_update_lock, flags);
printk(KERN_INFO "microcode: CPU%d updated from revision " printk(KERN_INFO "microcode: CPU%d updated from revision "
"%d to %d, date=%08x\n", "%d to %d, date=%08x\n",
cpu_num, rev, val[1], m->date); cpu_num, rev, val[1], microcode[i].date);
} return;
break;
} }
if(!found) printk(KERN_ERR
printk(KERN_ERR "microcode: CPU%d no microcode found! (sig=%x, pflags=%d)\n", "microcode: CPU%d no microcode found! (sig=%x, pflags=%d)\n",
cpu_num, sig, pf); cpu_num, sig, pf);
} }
static ssize_t microcode_read(struct file *file, char *buf, size_t len, loff_t *ppos) static ssize_t microcode_read(struct file *file, char *buf, size_t len, loff_t *ppos)
{ {
ssize_t ret = 0; ssize_t ret = 0;
...@@ -305,11 +332,15 @@ static ssize_t microcode_write(struct file *file, const char *buf, size_t len, l ...@@ -305,11 +332,15 @@ static ssize_t microcode_write(struct file *file, const char *buf, size_t len, l
{ {
ssize_t ret; ssize_t ret;
if (len % sizeof(struct microcode) != 0) { if (!len || len % sizeof(struct microcode) != 0) {
printk(KERN_ERR "microcode: can only write in N*%d bytes units\n", printk(KERN_ERR "microcode: can only write in N*%d bytes units\n",
sizeof(struct microcode)); sizeof(struct microcode));
return -EINVAL; return -EINVAL;
} }
if ((len >> PAGE_SHIFT) > num_physpages) {
printk(KERN_ERR "microcode: too much data (max %d pages)\n", num_physpages);
return -EINVAL;
}
down_write(&microcode_rwsem); down_write(&microcode_rwsem);
if (!mc_applied) { if (!mc_applied) {
mc_applied = kmalloc(smp_num_cpus*sizeof(struct microcode), mc_applied = kmalloc(smp_num_cpus*sizeof(struct microcode),
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment