patch-2.3.47 linux/arch/i386/kernel/microcode.c
Next file: linux/arch/i386/kernel/mtrr.c
Previous file: linux/arch/i386/kernel/irq.c
Back to the patch index
Back to the overall index
- Lines: 213
- Date:
Fri Feb 18 15:11:13 2000
- Orig file:
v2.3.46/linux/arch/i386/kernel/microcode.c
- Orig date:
Wed Feb 16 17:03:51 2000
diff -u --recursive --new-file v2.3.46/linux/arch/i386/kernel/microcode.c linux/arch/i386/kernel/microcode.c
@@ -18,6 +18,8 @@
*
* 1.0 16 February 2000, Tigran Aivazian <tigran@sco.com>
* Initial release.
+ * 1.01 18 February 2000, Tigran Aivazian <tigran@sco.com>
+ * Added read() support + cleanups.
*/
#include <linux/init.h>
@@ -31,7 +33,7 @@
#include <asm/uaccess.h>
#include <asm/processor.h>
-#define MICROCODE_VERSION "1.0"
+#define MICROCODE_VERSION "1.01"
MODULE_DESCRIPTION("CPU (P6) microcode update driver");
MODULE_AUTHOR("Tigran Aivazian <tigran@ocston.org>");
@@ -40,11 +42,12 @@
/* VFS interface */
static int microcode_open(struct inode *, struct file *);
static int microcode_release(struct inode *, struct file *);
+static ssize_t microcode_read(struct file *, char *, size_t, loff_t *);
static ssize_t microcode_write(struct file *, const char *, size_t, loff_t *);
/* internal helpers to do the work */
-static void do_microcode_update(void);
+static int do_microcode_update(void);
static void do_update_one(void *);
/*
@@ -56,8 +59,10 @@
/* the actual array of microcode blocks, each 2048 bytes */
static struct microcode * microcode = NULL;
static unsigned int microcode_num = 0;
+static char *mc_applied = NULL; /* holds an array of applied microcode blocks */
static struct file_operations microcode_fops = {
+ read: microcode_read,
write: microcode_write,
open: microcode_open,
release: microcode_release,
@@ -71,21 +76,32 @@
static int __init microcode_init(void)
{
- /* write-only /proc/driver/microcode file, one day may become read-write.. */
- proc_microcode = create_proc_entry("microcode", S_IWUSR, proc_root_driver);
+ int size;
+
+ proc_microcode = create_proc_entry("microcode", S_IWUSR|S_IRUSR, proc_root_driver);
if (!proc_microcode) {
- printk(KERN_ERR "microcode: can't create /proc/driver/microcode entry\n");
+ printk(KERN_ERR "microcode: can't create /proc/driver/microcode\n");
return -ENOMEM;
}
proc_microcode->ops = µcode_inops;
- printk(KERN_ERR "P6 Microcode Update Driver v%s registered\n", MICROCODE_VERSION);
+ size = smp_num_cpus * sizeof(struct microcode);
+ mc_applied = kmalloc(size, GFP_KERNEL);
+ if (!mc_applied) {
+ remove_proc_entry("microcode", proc_root_driver);
+ printk(KERN_ERR "microcode: can't allocate memory for saved microcode\n");
+ return -ENOMEM;
+ }
+ memset(mc_applied, 0, size); /* so that reading from offsets corresponding to failed
+ update makes this obvious */
+ printk(KERN_INFO "P6 Microcode Update Driver v%s registered\n", MICROCODE_VERSION);
return 0;
}
static void __exit microcode_exit(void)
{
remove_proc_entry("microcode", proc_root_driver);
- printk(KERN_ERR "P6 Microcode Update Driver v%s unregistered\n", MICROCODE_VERSION);
+ kfree(mc_applied);
+ printk(KERN_INFO "P6 Microcode Update Driver v%s unregistered\n", MICROCODE_VERSION);
}
module_init(microcode_init);
@@ -116,23 +132,46 @@
return 0;
}
+/* a pointer to 'struct update_req' is passed to the IPI hanlder = do_update_one()
+ * 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_microcode_update(void)
+static int do_microcode_update(void)
{
- if (smp_call_function(do_update_one, NULL, 1, 0) != 0)
+ int i, error = 0, err;
+ struct microcode *m;
+
+ if (smp_call_function(do_update_one, (void *)update_req, 1, 1) != 0)
panic("do_microcode_update(): timed out waiting for other CPUs\n");
- do_update_one(NULL);
+ do_update_one((void *)update_req);
+
+ for (i=0; i<smp_num_cpus; i++) {
+ err = update_req[i].err;
+ error += err;
+ if (!err) {
+ m = (struct microcode *)mc_applied + i;
+ memcpy(m, µcode[update_req[i].slot], sizeof(struct microcode));
+ }
+ }
+ return error ? -EIO : 0;
}
-static void do_update_one(void *unused)
+static void do_update_one(void *arg)
{
+ struct update_req *req;
struct cpuinfo_x86 * c;
unsigned int pf = 0, val[2], rev, sig;
- int i, id;
-
- id = smp_processor_id();
- c = cpu_data + id;
+ int i, cpu_num;
+ cpu_num = smp_processor_id();
+ c = cpu_data + cpu_num;
+ req = (struct update_req *)arg + cpu_num;
+ req->err = 1; /* be pessimistic */
if (c->x86_vendor != X86_VENDOR_INTEL || c->x86 < 6)
return;
@@ -153,7 +192,7 @@
if (microcode[i].rev <= rev) {
printk(KERN_ERR
"microcode: CPU%d not 'upgrading' to earlier revision"
- " %d (current=%d)\n", id, microcode[i].rev, rev);
+ " %d (current=%d)\n", cpu_num, microcode[i].rev, rev);
} else {
int sum = 0;
struct microcode *m = µcode[i];
@@ -163,29 +202,47 @@
sum += *sump;
if (sum != 0) {
printk(KERN_ERR "microcode: CPU%d aborting, "
- "bad checksum\n", id);
+ "bad checksum\n", cpu_num);
break;
}
+
wrmsr(0x79, (unsigned int)(m->bits), 0);
__asm__ __volatile__ ("cpuid");
rdmsr(0x8B, val[0], val[1]);
+
+ req->err = 0;
+ req->slot = i;
printk(KERN_ERR "microcode: CPU%d microcode updated "
- "from revision %d to %d\n", id, rev, val[1]);
+ "from revision %d to %d, date=%08x\n",
+ cpu_num, rev, val[1], m->date);
}
break;
}
}
+static ssize_t microcode_read(struct file *file, char *buf, size_t len, loff_t *ppos)
+{
+ size_t fsize = smp_num_cpus * sizeof(struct microcode);
+
+ if (!proc_microcode->size || *ppos >= fsize)
+ return 0; /* EOF */
+ if (*ppos + len > fsize)
+ len = fsize - *ppos;
+ if (copy_to_user(buf, mc_applied + *ppos, len))
+ return -EFAULT;
+ *ppos += len;
+ return len;
+}
+
static ssize_t microcode_write(struct file *file, const char *buf, size_t len, loff_t *ppos)
{
+ ssize_t ret;
+
if (len % sizeof(struct microcode) != 0) {
printk(KERN_ERR "microcode: can only write in N*%d bytes units\n",
sizeof(struct microcode));
return -EINVAL;
- return -EINVAL;
}
- if (!capable(CAP_SYS_RAWIO))
- return -EPERM;
lock_kernel();
microcode_num = len/sizeof(struct microcode);
microcode = vmalloc(len);
@@ -198,8 +255,12 @@
unlock_kernel();
return -EFAULT;
}
- do_microcode_update();
+ ret = do_microcode_update();
+ if (!ret) {
+ proc_microcode->size = smp_num_cpus * sizeof(struct microcode);
+ ret = (ssize_t)len;
+ }
vfree(microcode);
unlock_kernel();
- return len;
+ return ret;
}
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)