Commit 2afc8160 authored by Tigran Aivazian's avatar Tigran Aivazian Committed by Linus Torvalds

[PATCH] update to microcode update driver

This contains the following changes:

a) changes from Intel to support the new microcode data format
   (backward compatible of course)

b) changes from me to remove the no longer needed features of the driver,
   namely we don't need to keep a copy of applied microcode in kernel
   memory.

   This feature was hardly useful in the days of regular devfs
   /dev/cpu/microcode file and now it is completely useless so I removed
   it (after taking into account all the feedback on linux-kernel I
   received since the announcement of the intention to do this)

These are rather critical because otherwise we can't really say Linux
fully supports the very latest Intel cpus (which require microcode in
the new format).
parent c31bc22f
...@@ -55,10 +55,18 @@ ...@@ -55,10 +55,18 @@
* Tigran Aivazian <tigran@veritas.com>, * Tigran Aivazian <tigran@veritas.com>,
* Serialize updates as required on HT processors due to speculative * Serialize updates as required on HT processors due to speculative
* nature of implementation. * nature of implementation.
* 1.11 22 Mar 2001 Tigran Aivazian <tigran@veritas.com> * 1.11 22 Mar 2002 Tigran Aivazian <tigran@veritas.com>
* Fix the panic when writing zero-length microcode chunk. * Fix the panic when writing zero-length microcode chunk.
* 1.12 29 Sep 2003 Nitin Kamble <nitin.a.kamble@intel.com>,
* Jun Nakajima <jun.nakajima@intel.com>
* Support for the microcode updates in the new format.
* 1.13 10 Oct 2003 Tigran Aivazian <tigran@veritas.com>
* Removed ->read() method and obsoleted MICROCODE_IOCFREE ioctl
* because we no longer hold a copy of applied microcode
* in kernel memory.
*/ */
#include <linux/init.h> #include <linux/init.h>
#include <linux/sched.h> #include <linux/sched.h>
#include <linux/module.h> #include <linux/module.h>
...@@ -72,264 +80,387 @@ ...@@ -72,264 +80,387 @@
#include <asm/uaccess.h> #include <asm/uaccess.h>
#include <asm/processor.h> #include <asm/processor.h>
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>");
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
#define MICRO_DEBUG 0 #define MICROCODE_VERSION "1.13"
#define MICRO_DEBUG 0
#if MICRO_DEBUG #if MICRO_DEBUG
#define printf(x...) printk(##x) #define dprintk(x...) printk(KERN_INFO x)
#else #else
#define printf(x...) #define dprintk(x...)
#endif #endif
/* read()/write()/ioctl() are serialized on this */ #define DEFAULT_UCODE_DATASIZE (2000) /* 2000 bytes */
static DECLARE_RWSEM(microcode_rwsem); #define MC_HEADER_SIZE (sizeof (microcode_header_t)) /* 48 bytes */
#define DEFAULT_UCODE_TOTALSIZE (DEFAULT_UCODE_DATASIZE + MC_HEADER_SIZE) /* 2048 bytes */
static struct microcode *microcode; /* array of 2048byte microcode blocks */ #define EXT_HEADER_SIZE (sizeof (struct extended_sigtable)) /* 20 bytes */
static unsigned int microcode_num; /* number of chunks in microcode */ #define EXT_SIGNATURE_SIZE (sizeof (struct extended_signature)) /* 12 bytes */
static char *mc_applied; /* array of applied microcode blocks */ #define DWSIZE (sizeof (u32))
static unsigned int mc_fsize; /* file size of /dev/cpu/microcode */ #define get_totalsize(mc) \
(((microcode_t *)mc)->hdr.totalsize ? \
((microcode_t *)mc)->hdr.totalsize : DEFAULT_UCODE_TOTALSIZE)
#define get_datasize(mc) \
(((microcode_t *)mc)->hdr.datasize ? \
((microcode_t *)mc)->hdr.datasize : DEFAULT_UCODE_DATASIZE)
#define sigmatch(s1, s2, p1, p2) (((s1) == (s2)) && ((p1) & (p2)))
#define exttable_size(et) ((et)->count * EXT_SIGNATURE_SIZE + EXT_HEADER_SIZE)
/* serialize access to the physical write to MSR 0x79 */
static spinlock_t microcode_update_lock = SPIN_LOCK_UNLOCKED;
static int microcode_open(struct inode *unused1, struct file *unused2) /* no concurrent ->write()s are allowed on /dev/cpu/microcode */
static DECLARE_MUTEX(microcode_sem);
static void *user_buffer; /* user area microcode data buffer */
static unsigned int user_buffer_size; /* it's size */
typedef enum mc_error_code {
MC_SUCCESS = 0,
MC_NOTFOUND = 1,
MC_MARKED = 2,
MC_ALLOCATED = 3,
} mc_error_code_t;
static struct ucode_cpu_info {
unsigned int sig;
unsigned int pf;
unsigned int rev;
unsigned int cksum;
mc_error_code_t err;
microcode_t *mc;
} ucode_cpu_info[NR_CPUS];
static int microcode_open (struct inode *unused1, struct file *unused2)
{ {
return capable(CAP_SYS_RAWIO) ? 0 : -EPERM; return capable(CAP_SYS_RAWIO) ? 0 : -EPERM;
} }
/* static void collect_cpu_info (void *unused)
* update_req[cpu].err is set to 1 if update failed on 'cpu', 0 otherwise
* if err==0, microcode[update_req[cpu].slot] points to applied block of microcode
*/
struct update_req {
int err;
int slot;
} update_req[NR_CPUS];
static void do_update_one(void *unused)
{ {
int cpu_num = smp_processor_id(); int cpu_num = smp_processor_id();
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 ucode_cpu_info *uci = ucode_cpu_info + cpu_num;
unsigned int pf = 0, val[2], rev, sig; unsigned int val[2];
unsigned long flags;
int i;
req->err = 1; /* assume update will fail on this cpu */ uci->sig = uci->pf = uci->rev = uci->cksum = 0;
uci->err = MC_NOTFOUND;
uci->mc = NULL;
if (c->x86_vendor != X86_VENDOR_INTEL || c->x86 < 6 || if (c->x86_vendor != X86_VENDOR_INTEL || c->x86 < 6 ||
cpu_has(c, X86_FEATURE_IA64)) { cpu_has(c, X86_FEATURE_IA64)) {
printk(KERN_ERR "microcode: CPU%d not a capable Intel processor\n", cpu_num); printk(KERN_ERR "microcode: CPU%d not a capable Intel processor\n", cpu_num);
return; return;
} else {
uci->sig = cpuid_eax(0x00000001);
if ((c->x86_model >= 5) || (c->x86 > 6)) {
/* get processor flags from MSR 0x17 */
rdmsr(MSR_IA32_PLATFORM_ID, val[0], val[1]);
uci->pf = 1 << ((val[1] >> 18) & 7);
}
} }
sig = c->x86_mask + (c->x86_model<<4) + (c->x86<<8); wrmsr(MSR_IA32_UCODE_REV, 0, 0);
__asm__ __volatile__ ("cpuid" : : : "ax", "bx", "cx", "dx");
/* get the current revision from MSR 0x8B */
rdmsr(MSR_IA32_UCODE_REV, val[0], uci->rev);
dprintk("microcode: collect_cpu_info : sig=0x%x, pf=0x%x, rev=0x%x\n",
uci->sig, uci->pf, uci->rev);
}
if ((c->x86_model >= 5) || (c->x86 > 6)) { static inline void mark_microcode_update (int cpu_num, microcode_header_t *mc_header, int sig, int pf, int cksum)
/* get processor flags from MSR 0x17 */ {
rdmsr(MSR_IA32_PLATFORM_ID, val[0], val[1]); struct ucode_cpu_info *uci = ucode_cpu_info + cpu_num;
pf = 1 << ((val[1] >> 18) & 7);
dprintk("Microcode Found.\n");
dprintk(" Header Revision 0x%x\n", mc_header->hdrver);
dprintk(" Loader Revision 0x%x\n", mc_header->ldrver);
dprintk(" Revision 0x%x \n", mc_header->rev);
dprintk(" Date %x/%x/%x\n",
((mc_header->date >> 24 ) & 0xff),
((mc_header->date >> 16 ) & 0xff),
(mc_header->date & 0xFFFF));
dprintk(" Signature 0x%x\n", sig);
dprintk(" Type 0x%x Family 0x%x Model 0x%x Stepping 0x%x\n",
((sig >> 12) & 0x3),
((sig >> 8) & 0xf),
((sig >> 4) & 0xf),
((sig & 0xf)));
dprintk(" Processor Flags 0x%x\n", pf);
dprintk(" Checksum 0x%x\n", cksum);
if (mc_header->rev < uci->rev) {
printk(KERN_ERR "microcode: CPU%d not 'upgrading' to earlier revision"
" 0x%x (current=0x%x)\n", cpu_num, mc_header->rev, uci->rev);
goto out;
} else if (mc_header->rev == uci->rev) {
/* notify the caller of success on this cpu */
uci->err = MC_SUCCESS;
printk(KERN_ERR "microcode: CPU%d already at revision"
" 0x%x (current=0x%x)\n", cpu_num, mc_header->rev, uci->rev);
goto out;
} }
for (i=0; i<microcode_num; i++) dprintk("microcode: CPU%d found a matching microcode update with "
if (microcode[i].sig == sig && microcode[i].pf == pf && " revision 0x%x (current=0x%x)\n", cpu_num, mc_header->rev, uci->rev);
microcode[i].ldrver == 1 && microcode[i].hdrver == 1) { uci->cksum = cksum;
int sum = 0; uci->pf = pf; /* keep the original mc pf for cksum calculation */
struct microcode *m = &microcode[i]; uci->err = MC_MARKED; /* found the match */
unsigned int *sump = (unsigned int *)(m+1); out:
return;
printf("Microcode\n"); }
printf(" Header Revision %d\n",microcode[i].hdrver);
printf(" Date %x/%x/%x\n", static int find_matching_ucodes (void)
((microcode[i].date >> 24 ) & 0xff), {
((microcode[i].date >> 16 ) & 0xff), int cursor = 0;
(microcode[i].date & 0xFFFF)); int error = 0;
printf(" Type %x Family %x Model %x Stepping %x\n",
((microcode[i].sig >> 12) & 0x3), while (cursor + MC_HEADER_SIZE < user_buffer_size) {
((microcode[i].sig >> 8) & 0xf), microcode_header_t mc_header;
((microcode[i].sig >> 4) & 0xf), void *newmc = NULL;
((microcode[i].sig & 0xf))); int i, sum, cpu_num, allocated_flag, total_size, data_size, ext_table_size;
printf(" Checksum %x\n",microcode[i].cksum);
printf(" Loader Revision %x\n",microcode[i].ldrver); if (copy_from_user(&mc_header, user_buffer + cursor, MC_HEADER_SIZE)) {
printf(" Processor Flags %x\n\n",microcode[i].pf); printk(KERN_ERR "microcode: error! Can not read user data\n");
error = -EFAULT;
req->slot = i; goto out;
}
/* serialize access to update decision */
spin_lock_irqsave(&microcode_update_lock, flags); total_size = get_totalsize(&mc_header);
if ((cursor + total_size > user_buffer_size) || (total_size < DEFAULT_UCODE_TOTALSIZE)) {
/* trick, to work even if there was no prior update by the BIOS */ printk(KERN_ERR "microcode: error! Bad data in microcode data file\n");
wrmsr(MSR_IA32_UCODE_REV, 0, 0); error = -EINVAL;
__asm__ __volatile__ ("cpuid" : : : "ax", "bx", "cx", "dx"); goto out;
}
/* get current (on-cpu) revision into rev (ignore val[0]) */
rdmsr(MSR_IA32_UCODE_REV, val[0], rev); data_size = get_datasize(&mc_header);
if ((data_size + MC_HEADER_SIZE > total_size) || (data_size < DEFAULT_UCODE_DATASIZE)) {
if (microcode[i].rev < rev) { printk(KERN_ERR "microcode: error! Bad data in microcode data file\n");
spin_unlock_irqrestore(&microcode_update_lock, flags); error = -EINVAL;
printk(KERN_INFO goto out;
"microcode: CPU%d not 'upgrading' to earlier revision" }
" %d (current=%d)\n", cpu_num, microcode[i].rev, rev);
return;
} else if (microcode[i].rev == rev) {
/* notify the caller of success on this cpu */
req->err = 0;
spin_unlock_irqrestore(&microcode_update_lock, flags);
printk(KERN_INFO
"microcode: CPU%d already at revision"
" %d (current=%d)\n", cpu_num, microcode[i].rev, rev);
return;
}
/* Verify the checksum */ if (mc_header.ldrver != 1 || mc_header.hdrver != 1) {
while (--sump >= (unsigned int *)m) printk(KERN_ERR "microcode: error! Unknown microcode update format\n");
sum += *sump; error = -EINVAL;
if (sum != 0) { goto out;
req->err = 1; }
spin_unlock_irqrestore(&microcode_update_lock, flags);
printk(KERN_ERR "microcode: CPU%d aborting, " for (cpu_num = 0; cpu_num < num_online_cpus(); cpu_num++) {
"bad checksum\n", cpu_num); struct ucode_cpu_info *uci = ucode_cpu_info + cpu_num;
return; if (uci->err != MC_NOTFOUND) /* already found a match or not an online cpu*/
continue;
if (sigmatch(mc_header.sig, uci->sig, mc_header.pf, uci->pf))
mark_microcode_update(cpu_num, &mc_header, mc_header.sig, mc_header.pf, mc_header.cksum);
}
ext_table_size = total_size - (MC_HEADER_SIZE + data_size);
if (ext_table_size) {
struct extended_sigtable ext_header;
struct extended_signature ext_sig;
int ext_sigcount;
if ((ext_table_size < EXT_HEADER_SIZE)
|| ((ext_table_size - EXT_HEADER_SIZE) % EXT_SIGNATURE_SIZE)) {
printk(KERN_ERR "microcode: error! Bad data in microcode data file\n");
error = -EINVAL;
goto out;
}
if (copy_from_user(&ext_header, user_buffer + cursor
+ MC_HEADER_SIZE + data_size, EXT_HEADER_SIZE)) {
printk(KERN_ERR "microcode: error! Can not read user data\n");
error = -EFAULT;
goto out;
}
if (ext_table_size != exttable_size(&ext_header)) {
printk(KERN_ERR "microcode: error! Bad data in microcode data file\n");
error = -EFAULT;
goto out;
} }
ext_sigcount = ext_header.count;
/* write microcode via MSR 0x79 */ for (i = 0; i < ext_sigcount; i++) {
wrmsr(MSR_IA32_UCODE_WRITE, (unsigned int)(m->bits), 0); if (copy_from_user(&ext_sig, user_buffer + cursor + MC_HEADER_SIZE + data_size + EXT_HEADER_SIZE
+ EXT_SIGNATURE_SIZE * i, EXT_SIGNATURE_SIZE)) {
/* serialize */ printk(KERN_ERR "microcode: error! Can not read user data\n");
__asm__ __volatile__ ("cpuid" : : : "ax", "bx", "cx", "dx"); error = -EFAULT;
goto out;
/* get the current revision from MSR 0x8B */ }
rdmsr(MSR_IA32_UCODE_REV, val[0], val[1]); for (cpu_num = 0; cpu_num < num_online_cpus(); cpu_num++) {
struct ucode_cpu_info *uci = ucode_cpu_info + cpu_num;
/* notify the caller of success on this cpu */ if (uci->err != MC_NOTFOUND) /* already found a match or not an online cpu*/
req->err = 0; continue;
spin_unlock_irqrestore(&microcode_update_lock, flags); if (sigmatch(ext_sig.sig, uci->sig, ext_sig.pf, uci->pf)) {
printk(KERN_INFO "microcode: CPU%d updated from revision " mark_microcode_update(cpu_num, &mc_header, ext_sig.sig, ext_sig.pf, ext_sig.cksum);
"%d to %d, date=%08x\n", }
cpu_num, rev, val[1], microcode[i].date); }
return; }
} }
/* now check if any cpu has matched */
printk(KERN_ERR for (cpu_num = 0, allocated_flag = 0, sum = 0; cpu_num < num_online_cpus(); cpu_num++) {
"microcode: CPU%d no microcode found! (sig=%x, pflags=%d)\n", if (ucode_cpu_info[cpu_num].err == MC_MARKED) {
cpu_num, sig, pf); struct ucode_cpu_info *uci = ucode_cpu_info + cpu_num;
if (!allocated_flag) {
allocated_flag = 1;
newmc = vmalloc(total_size);
if (!newmc) {
printk(KERN_ERR "microcode: error! Can not allocate memory\n");
error = -ENOMEM;
goto out;
}
if (copy_from_user(newmc + MC_HEADER_SIZE,
user_buffer + cursor + MC_HEADER_SIZE,
total_size - MC_HEADER_SIZE)) {
printk(KERN_ERR "microcode: error! Can not read user data\n");
vfree(newmc);
error = -EFAULT;
goto out;
}
memcpy(newmc, &mc_header, MC_HEADER_SIZE);
/* check extended table checksum */
if (ext_table_size) {
int ext_table_sum = 0;
i = ext_table_size / DWSIZE;
int * ext_tablep = (((void *) newmc) + MC_HEADER_SIZE + data_size);
while (i--) ext_table_sum += ext_tablep[i];
if (ext_table_sum) {
printk(KERN_WARNING "microcode: aborting, bad extended signature table checksum\n");
vfree(newmc);
error = -EINVAL;
goto out;
}
}
/* calculate the checksum */
i = (MC_HEADER_SIZE + data_size) / DWSIZE;
while (i--) sum += ((int *)newmc)[i];
sum -= (mc_header.sig + mc_header.pf + mc_header.cksum);
}
ucode_cpu_info[cpu_num].mc = newmc;
ucode_cpu_info[cpu_num].err = MC_ALLOCATED; /* mc updated */
if (sum + uci->sig + uci->pf + uci->cksum != 0) {
printk(KERN_ERR "microcode: CPU%d aborting, bad checksum\n", cpu_num);
error = -EINVAL;
goto out;
}
}
}
cursor += total_size; /* goto the next update patch */
} /* end of while */
out:
return error;
} }
static void do_update_one (void * unused)
static int do_microcode_update(void)
{ {
int i, error = 0, err; unsigned long flags;
struct microcode *m; unsigned int val[2];
int cpu_num = smp_processor_id();
struct ucode_cpu_info *uci = ucode_cpu_info + cpu_num;
if (on_each_cpu(do_update_one, NULL, 1, 1) != 0) { if (uci->mc == NULL) {
printk(KERN_ERR "microcode: IPI timeout, giving up\n"); printk(KERN_INFO "microcode: No suitable data for cpu %d\n", cpu_num);
return -EIO; return;
} }
for (i=0; i<NR_CPUS; i++) { /* serialize access to the physical write to MSR 0x79 */
err = update_req[i].err; spin_lock_irqsave(&microcode_update_lock, flags);
error += err;
if (!err) { /* write microcode via MSR 0x79 */
m = (struct microcode *)mc_applied + i; wrmsr(MSR_IA32_UCODE_WRITE, (unsigned int)(uci->mc->bits), 0);
memcpy(m, &microcode[update_req[i].slot], sizeof(struct microcode)); wrmsr(MSR_IA32_UCODE_REV, 0, 0);
}
} __asm__ __volatile__ ("cpuid" : : : "ax", "bx", "cx", "dx");
return error; /* get the current revision from MSR 0x8B */
rdmsr(MSR_IA32_UCODE_REV, val[0], val[1]);
/* notify the caller of success on this cpu */
uci->err = MC_SUCCESS;
spin_unlock_irqrestore(&microcode_update_lock, flags);
printk(KERN_INFO "microcode: CPU%d updated from revision "
"0x%x to 0x%x, date = %08x \n",
cpu_num, uci->rev, val[1], uci->mc->hdr.date);
return;
} }
static ssize_t microcode_read(struct file *file, char __user *buf, size_t len, loff_t *ppos) static int do_microcode_update (void)
{ {
ssize_t ret = 0; int i, error;
down_read(&microcode_rwsem); if (on_each_cpu(collect_cpu_info, NULL, 1, 1) != 0) {
if (*ppos >= mc_fsize) printk(KERN_ERR "microcode: Error! Could not run on all processors\n");
goto out; error = -EIO;
if (*ppos + len > mc_fsize)
len = mc_fsize - *ppos;
ret = -EFAULT;
if (copy_to_user(buf, mc_applied + *ppos, len))
goto out; goto out;
*ppos += len; }
ret = len;
if ((error = find_matching_ucodes())) {
printk(KERN_ERR "microcode: Error in the microcode data\n");
goto out_free;
}
if (on_each_cpu(do_update_one, NULL, 1, 1) != 0) {
printk(KERN_ERR "microcode: Error! Could not run on all processors\n");
error = -EIO;
}
out_free:
for (i = 0; i < num_online_cpus(); i++) {
if (ucode_cpu_info[i].mc) {
int j;
void *tmp = ucode_cpu_info[i].mc;
vfree(tmp);
for (j = i; j < num_online_cpus(); j++) {
if (ucode_cpu_info[j].mc == tmp)
ucode_cpu_info[j].mc = NULL;
}
}
}
out: out:
up_read(&microcode_rwsem); return error;
return ret;
} }
static ssize_t microcode_write(struct file *file, const char __user *buf, size_t len, loff_t *ppos) static ssize_t microcode_write (struct file *file, const char *buf, size_t len, loff_t *ppos)
{ {
ssize_t ret; ssize_t ret;
if (!len || len % sizeof(struct microcode) != 0) { if (len < DEFAULT_UCODE_TOTALSIZE) {
printk(KERN_ERR "microcode: can only write in N*%d bytes units\n", printk(KERN_ERR "microcode: not enough data\n");
sizeof(struct microcode));
return -EINVAL; return -EINVAL;
} }
if ((len >> PAGE_SHIFT) > num_physpages) { if ((len >> PAGE_SHIFT) > num_physpages) {
printk(KERN_ERR "microcode: too much data (max %ld pages)\n", num_physpages); printk(KERN_ERR "microcode: too much data (max %ld pages)\n", num_physpages);
return -EINVAL; return -EINVAL;
} }
down_write(&microcode_rwsem);
if (!mc_applied) {
mc_applied = kmalloc(NR_CPUS*sizeof(struct microcode),
GFP_KERNEL);
if (!mc_applied) {
up_write(&microcode_rwsem);
printk(KERN_ERR "microcode: out of memory for saved microcode\n");
return -ENOMEM;
}
}
microcode_num = len/sizeof(struct microcode);
microcode = vmalloc(len);
if (!microcode) {
ret = -ENOMEM;
goto out_unlock;
}
if (copy_from_user(microcode, buf, len)) { down(&microcode_sem);
ret = -EFAULT;
goto out_fsize;
}
if(do_microcode_update()) { user_buffer = (void *) buf;
ret = -EIO; user_buffer_size = (int) len;
goto out_fsize;
} else { ret = do_microcode_update();
mc_fsize = NR_CPUS * sizeof(struct microcode); if (!ret)
ret = (ssize_t)len; ret = (ssize_t)len;
}
out_fsize: up(&microcode_sem);
vfree(microcode);
out_unlock:
up_write(&microcode_rwsem);
return ret; return ret;
} }
static int microcode_ioctl(struct inode *inode, struct file *file, static int microcode_ioctl (struct inode *inode, struct file *file,
unsigned int cmd, unsigned long arg) unsigned int cmd, unsigned long arg)
{ {
switch(cmd) { switch (cmd) {
/*
* XXX: will be removed after microcode_ctl
* is updated to ignore failure of this ioctl()
*/
case MICROCODE_IOCFREE: case MICROCODE_IOCFREE:
down_write(&microcode_rwsem); return 0;
if (mc_applied) {
int bytes = NR_CPUS * sizeof(struct microcode);
kfree(mc_applied);
mc_applied = NULL;
printk(KERN_INFO "microcode: freed %d bytes\n", bytes);
mc_fsize = 0;
up_write(&microcode_rwsem);
return 0;
}
up_write(&microcode_rwsem);
return -ENODATA;
default: default:
return -EINVAL; return -EINVAL;
} }
...@@ -338,7 +469,6 @@ static int microcode_ioctl(struct inode *inode, struct file *file, ...@@ -338,7 +469,6 @@ static int microcode_ioctl(struct inode *inode, struct file *file,
static struct file_operations microcode_fops = { static struct file_operations microcode_fops = {
.owner = THIS_MODULE, .owner = THIS_MODULE,
.read = microcode_read,
.write = microcode_write, .write = microcode_write,
.ioctl = microcode_ioctl, .ioctl = microcode_ioctl,
.open = microcode_open, .open = microcode_open,
...@@ -347,17 +477,20 @@ static struct file_operations microcode_fops = { ...@@ -347,17 +477,20 @@ static struct file_operations microcode_fops = {
static struct miscdevice microcode_dev = { static struct miscdevice microcode_dev = {
.minor = MICROCODE_MINOR, .minor = MICROCODE_MINOR,
.name = "microcode", .name = "microcode",
.devfs_name = "cpu/microcode",
.fops = &microcode_fops, .fops = &microcode_fops,
}; };
static int __init microcode_init(void) static int __init microcode_init (void)
{ {
int error; int error;
error = misc_register(&microcode_dev); error = misc_register(&microcode_dev);
if (error) if (error) {
printk(KERN_ERR
"microcode: can't misc_register on minor=%d\n",
MICROCODE_MINOR);
return error; return error;
}
printk(KERN_INFO printk(KERN_INFO
"IA-32 Microcode Update Driver: v%s <tigran@veritas.com>\n", "IA-32 Microcode Update Driver: v%s <tigran@veritas.com>\n",
...@@ -365,14 +498,12 @@ static int __init microcode_init(void) ...@@ -365,14 +498,12 @@ static int __init microcode_init(void)
return 0; return 0;
} }
static void __exit microcode_exit(void) static void __exit microcode_exit (void)
{ {
misc_deregister(&microcode_dev); misc_deregister(&microcode_dev);
kfree(mc_applied);
printk(KERN_INFO "IA-32 Microcode Update Driver v%s unregistered\n", printk(KERN_INFO "IA-32 Microcode Update Driver v%s unregistered\n",
MICROCODE_VERSION); MICROCODE_VERSION);
} }
module_init(microcode_init) module_init(microcode_init)
module_exit(microcode_exit) module_exit(microcode_exit)
...@@ -498,7 +498,7 @@ unsigned long get_wchan(struct task_struct *p); ...@@ -498,7 +498,7 @@ unsigned long get_wchan(struct task_struct *p);
#define KSTK_EIP(tsk) (((unsigned long *)(4096+(unsigned long)(tsk)->thread_info))[1019]) #define KSTK_EIP(tsk) (((unsigned long *)(4096+(unsigned long)(tsk)->thread_info))[1019])
#define KSTK_ESP(tsk) (((unsigned long *)(4096+(unsigned long)(tsk)->thread_info))[1022]) #define KSTK_ESP(tsk) (((unsigned long *)(4096+(unsigned long)(tsk)->thread_info))[1022])
struct microcode { struct microcode_header {
unsigned int hdrver; unsigned int hdrver;
unsigned int rev; unsigned int rev;
unsigned int date; unsigned int date;
...@@ -506,10 +506,32 @@ struct microcode { ...@@ -506,10 +506,32 @@ struct microcode {
unsigned int cksum; unsigned int cksum;
unsigned int ldrver; unsigned int ldrver;
unsigned int pf; unsigned int pf;
unsigned int reserved[5]; unsigned int datasize;
unsigned int bits[500]; unsigned int totalsize;
unsigned int reserved[3];
};
struct microcode {
struct microcode_header hdr;
unsigned int bits[0];
};
typedef struct microcode microcode_t;
typedef struct microcode_header microcode_header_t;
/* microcode format is extended from prescott processors */
struct extended_signature {
unsigned int sig;
unsigned int pf;
unsigned int cksum;
}; };
struct extended_sigtable {
unsigned int count;
unsigned int cksum;
unsigned int reserved[3];
struct extended_signature sigs[0];
};
/* '6' because it used to be for P6 only (but now covers Pentium 4 as well) */ /* '6' because it used to be for P6 only (but now covers Pentium 4 as well) */
#define MICROCODE_IOCFREE _IO('6',0) #define MICROCODE_IOCFREE _IO('6',0)
......
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