Merge master.kernel.org:/pub/scm/linux/kernel/git/davej/agpgart
[pandora-kernel.git] / arch / i386 / kernel / microcode.c
index f8f1ec5..c8fa137 100644 (file)
@@ -1,7 +1,7 @@
 /*
  *     Intel CPU Microcode Update Driver for Linux
  *
- *     Copyright (C) 2000-2004 Tigran Aivazian
+ *     Copyright (C) 2000-2006 Tigran Aivazian <tigran@aivazian.fsnet.co.uk>
  *                   2006      Shaohua Li <shaohua.li@intel.com>
  *
  *     This driver allows to upgrade microcode on Intel processors
@@ -92,7 +92,7 @@
 #include <asm/processor.h>
 
 MODULE_DESCRIPTION("Intel CPU (IA-32) Microcode Update Driver");
-MODULE_AUTHOR("Tigran Aivazian <tigran@veritas.com>");
+MODULE_AUTHOR("Tigran Aivazian <tigran@aivazian.fsnet.co.uk>");
 MODULE_LICENSE("GPL");
 
 #define MICROCODE_VERSION      "1.14a"
@@ -187,8 +187,7 @@ static int microcode_sanity_check(void *mc)
 
        total_size = get_totalsize(mc_header);
        data_size = get_datasize(mc_header);
-       if ((data_size + MC_HEADER_SIZE > total_size)
-               || (data_size < DEFAULT_UCODE_DATASIZE)) {
+       if (data_size + MC_HEADER_SIZE > total_size) {
                printk(KERN_ERR "microcode: error! "
                        "Bad data size in microcode data file\n");
                return -EINVAL;
@@ -219,7 +218,7 @@ static int microcode_sanity_check(void *mc)
        /* check extended table checksum */
        if (ext_table_size) {
                int ext_table_sum = 0;
-               int * ext_tablep = (int *)ext_header;
+               int *ext_tablep = (int *)ext_header;
 
                i = ext_table_size / DWSIZE;
                while (i--)
@@ -365,8 +364,7 @@ static long get_next_ucode(void **mc, long offset)
                return -EFAULT;
        }
        total_size = get_totalsize(&mc_header);
-       if ((offset + total_size > user_buffer_size)
-               || (total_size < DEFAULT_UCODE_TOTALSIZE)) {
+       if (offset + total_size > user_buffer_size) {
                printk(KERN_ERR "microcode: error! Bad total size in microcode "
                                "data file\n");
                return -EINVAL;
@@ -386,7 +384,7 @@ static int do_microcode_update (void)
 {
        long cursor = 0;
        int error = 0;
-       void * new_mc;
+       void *new_mc;
        int cpu;
        cpumask_t old;
 
@@ -432,11 +430,6 @@ static ssize_t microcode_write (struct file *file, const char __user *buf, size_
 {
        ssize_t ret;
 
-       if (len < DEFAULT_UCODE_TOTALSIZE) {
-               printk(KERN_ERR "microcode: not enough data\n"); 
-               return -EINVAL;
-       }
-
        if ((len >> PAGE_SHIFT) > num_physpages) {
                printk(KERN_ERR "microcode: too much data (max %ld pages)\n", num_physpages);
                return -EINVAL;
@@ -508,8 +501,7 @@ static long get_next_ucode_from_buffer(void **mc, void *buf,
        mc_header = (microcode_header_t *)(buf + offset);
        total_size = get_totalsize(mc_header);
 
-       if ((offset + total_size > size)
-               || (total_size < DEFAULT_UCODE_TOTALSIZE)) {
+       if (offset + total_size > size) {
                printk(KERN_ERR "microcode: error! Bad data in microcode data file\n");
                return -EINVAL;
        }
@@ -531,7 +523,7 @@ static int cpu_request_microcode(int cpu)
        char name[30];
        struct cpuinfo_x86 *c = cpu_data + cpu;
        const struct firmware *firmware;
-       void * buf;
+       void *buf;
        unsigned long size;
        long offset = 0;
        int error;
@@ -585,7 +577,7 @@ static void microcode_init_cpu(int cpu)
        set_cpus_allowed(current, cpumask_of_cpu(cpu));
        mutex_lock(&microcode_mutex);
        collect_cpu_info(cpu);
-       if (uci->valid)
+       if (uci->valid && system_state == SYSTEM_RUNNING)
                cpu_request_microcode(cpu);
        mutex_unlock(&microcode_mutex);
        set_cpus_allowed(current, old);
@@ -602,6 +594,138 @@ static void microcode_fini_cpu(int cpu)
        mutex_unlock(&microcode_mutex);
 }
 
+static ssize_t reload_store(struct sys_device *dev, const char *buf, size_t sz)
+{
+       struct ucode_cpu_info *uci = ucode_cpu_info + dev->id;
+       char *end;
+       unsigned long val = simple_strtoul(buf, &end, 0);
+       int err = 0;
+       int cpu = dev->id;
+
+       if (end == buf)
+               return -EINVAL;
+       if (val == 1) {
+               cpumask_t old;
+
+               old = current->cpus_allowed;
+
+               lock_cpu_hotplug();
+               set_cpus_allowed(current, cpumask_of_cpu(cpu));
+
+               mutex_lock(&microcode_mutex);
+               if (uci->valid)
+                       err = cpu_request_microcode(cpu);
+               mutex_unlock(&microcode_mutex);
+               unlock_cpu_hotplug();
+               set_cpus_allowed(current, old);
+       }
+       if (err)
+               return err;
+       return sz;
+}
+
+static ssize_t version_show(struct sys_device *dev, char *buf)
+{
+       struct ucode_cpu_info *uci = ucode_cpu_info + dev->id;
+
+       return sprintf(buf, "0x%x\n", uci->rev);
+}
+
+static ssize_t pf_show(struct sys_device *dev, char *buf)
+{
+       struct ucode_cpu_info *uci = ucode_cpu_info + dev->id;
+
+       return sprintf(buf, "0x%x\n", uci->pf);
+}
+
+static SYSDEV_ATTR(reload, 0200, NULL, reload_store);
+static SYSDEV_ATTR(version, 0400, version_show, NULL);
+static SYSDEV_ATTR(processor_flags, 0400, pf_show, NULL);
+
+static struct attribute *mc_default_attrs[] = {
+       &attr_reload.attr,
+       &attr_version.attr,
+       &attr_processor_flags.attr,
+       NULL
+};
+
+static struct attribute_group mc_attr_group = {
+       .attrs = mc_default_attrs,
+       .name = "microcode",
+};
+
+static int mc_sysdev_add(struct sys_device *sys_dev)
+{
+       int err, cpu = sys_dev->id;
+       struct ucode_cpu_info *uci = ucode_cpu_info + cpu;
+
+       if (!cpu_online(cpu))
+               return 0;
+
+       pr_debug("Microcode:CPU %d added\n", cpu);
+       memset(uci, 0, sizeof(*uci));
+
+       err = sysfs_create_group(&sys_dev->kobj, &mc_attr_group);
+       if (err)
+               return err;
+
+       microcode_init_cpu(cpu);
+       return 0;
+}
+
+static int mc_sysdev_remove(struct sys_device *sys_dev)
+{
+       int cpu = sys_dev->id;
+
+       if (!cpu_online(cpu))
+               return 0;
+       pr_debug("Microcode:CPU %d removed\n", cpu);
+       microcode_fini_cpu(cpu);
+       sysfs_remove_group(&sys_dev->kobj, &mc_attr_group);
+       return 0;
+}
+
+static int mc_sysdev_resume(struct sys_device *dev)
+{
+       int cpu = dev->id;
+
+       if (!cpu_online(cpu))
+               return 0;
+       pr_debug("Microcode:CPU %d resumed\n", cpu);
+       /* only CPU 0 will apply ucode here */
+       apply_microcode(0);
+       return 0;
+}
+
+static struct sysdev_driver mc_sysdev_driver = {
+       .add = mc_sysdev_add,
+       .remove = mc_sysdev_remove,
+       .resume = mc_sysdev_resume,
+};
+
+static __cpuinit int
+mc_cpu_callback(struct notifier_block *nb, unsigned long action, void *hcpu)
+{
+       unsigned int cpu = (unsigned long)hcpu;
+       struct sys_device *sys_dev;
+
+       sys_dev = get_cpu_sysdev(cpu);
+       switch (action) {
+       case CPU_ONLINE:
+       case CPU_DOWN_FAILED:
+               mc_sysdev_add(sys_dev);
+               break;
+       case CPU_DOWN_PREPARE:
+               mc_sysdev_remove(sys_dev);
+               break;
+       }
+       return NOTIFY_OK;
+}
+
+static struct notifier_block __cpuinitdata mc_cpu_notifier = {
+       .notifier_call = mc_cpu_callback,
+};
+
 static int __init microcode_init (void)
 {
        int error;
@@ -616,14 +740,32 @@ static int __init microcode_init (void)
                return PTR_ERR(microcode_pdev);
        }
 
+       lock_cpu_hotplug();
+       error = sysdev_driver_register(&cpu_sysdev_class, &mc_sysdev_driver);
+       unlock_cpu_hotplug();
+       if (error) {
+               microcode_dev_exit();
+               platform_device_unregister(microcode_pdev);
+               return error;
+       }
+
+       register_hotcpu_notifier(&mc_cpu_notifier);
+
        printk(KERN_INFO 
-               "IA-32 Microcode Update Driver: v" MICROCODE_VERSION " <tigran@veritas.com>\n");
+               "IA-32 Microcode Update Driver: v" MICROCODE_VERSION " <tigran@aivazian.fsnet.co.uk>\n");
        return 0;
 }
 
 static void __exit microcode_exit (void)
 {
        microcode_dev_exit();
+
+       unregister_hotcpu_notifier(&mc_cpu_notifier);
+
+       lock_cpu_hotplug();
+       sysdev_driver_unregister(&cpu_sysdev_class, &mc_sysdev_driver);
+       unlock_cpu_hotplug();
+
        platform_device_unregister(microcode_pdev);
 }